
Run the browser game locally first, then make the verified service available through a controlled public HTTP endpoint
This guide walks through the documented source-based setup for the Doudizhu HTML5 game using Python, Tornado, MySQL, and the supplied database schema. We explain the repository provenance issue that should be resolved before installation, show the documented commands without silently changing their meaning, and verify the game at its stated local address. Once the local application works, we connect it to a Localtonet HTTP tunnel so remote players can reach it without inbound router port forwarding, firewall changes, VPN setup, or a public IP address. We also cover credential safety, tunnel lifecycle, routine operation, and practical troubleshooting.
๐ What's in this guide
What this Doudizhu deployment contains
Doudizhu, also known as Dou Dizhu, is a multiplayer card game. The repository covered by this guide describes an HTML5 implementation with a Python backend based on Tornado and MySQL. Its frontend uses the Phaser game engine. The checked repository contains separate client and server directories, a root-level requirements.txt file, and a root-level schema.sql file.
The installation has three distinct layers. MySQL stores the application data, Python runs the backend, and the browser loads the web client through the backend's HTTP listener. The documented startup command is executed from the server directory, while dependency installation and schema import are performed from the repository root. Preserving those working directories matters because relative paths used by the project may depend on them.
python3 app.py from the repository's server directory. The project documentation requires Python 3.8 or newer.
schema.sql file is imported before the server starts.
http://127.0.0.1:8080 as the local page to open after startup.
Keeping local installation separate from remote publication is an important diagnostic technique. If the game does not work at its documented loopback address, adding a tunnel will not repair the application, database, dependencies, or startup process. First establish a healthy local service. Only then introduce the additional networking layer.
The available project documentation establishes the runtime prerequisites, schema import, dependency installation, database connection variable, startup command, and local endpoint. It does not document production deployment, operating-system support, a process supervisor, a reverse proxy, database migrations, backup procedures, user authentication, or application-level access controls. We do not invent those details here.
Resolve the repository provenance mismatch before installing

There is a notable inconsistency in the available project material. The repository reviewed for this guide is svzdev/doudizhu, but the Quick Start text inside that repository tells readers to clone mailgyc/doudizhu. Those are different repository owners. A setup guide should not silently replace one URL with the other or imply that the two repositories are guaranteed to contain identical code.
This matters because the source you download determines which dependency declarations, SQL schema, server code, and frontend files you execute. Repository forks can diverge even when they share a common history. A clone instruction embedded in a README can also become stale while the surrounding repository continues to exist.
Decide which repository and revision you intend to trust before importing its SQL or installing its Python packages. Confirm that the checked-out directory contains the expected requirements.txt, schema.sql, and server directory. Do not treat the README's different clone destination as automatically equivalent to the repository being viewed.
The most defensible workflow is to obtain the exact source tree that you have reviewed, record its repository and revision for your own deployment notes, and then continue from that source tree's root directory. If you intentionally follow the README literally, its documented clone command is:
git clone https://github.com/mailgyc/doudizhu
cd doudizhu
That command is reproduced to explain the project's documented Quick Start, not to resolve the ownership discrepancy. If your reviewed source is instead the svzdev/doudizhu repository, use the download or clone information shown by that repository and verify the resulting revision yourself. This guide does not claim that one location is canonical, newer, safer, or interchangeable with the other because the available evidence does not establish that.
The repository page does not list a release, and the supplied evidence does not establish a current maintenance or support policy. Treat this as source code that needs review rather than as a maintained packaged service with a documented upgrade contract. Before exposing it beyond a trusted environment, inspect its dependencies, application behavior, and access controls according to your own risk requirements.
Prerequisites and decisions to make first
The project documentation lists Python 3.8 or newer and MySQL 5.7 or newer as dependencies. It also uses git in the Quick Start, pip3 to install Python packages, and a Unix-style export command to define the database connection string.
| Requirement | Documented expectation | Why it is needed | Important limitation |
|---|---|---|---|
| Python | Python 3.8 or newer | Runs the Tornado-based server through python3 app.py |
No upper-version compatibility range is documented |
| MySQL | MySQL 5.7 or newer | Stores data defined by schema.sql |
No alternative database engine is documented |
| Python packages | Install from requirements.txt using pip3 |
Provides the application's Python dependencies | The available instructions do not specify a virtual environment workflow |
| Database connection | DATABASE_URI using a MySQL and aiomysql URI |
Tells the application how to reach its database | You must supply credentials valid for your own MySQL installation |
| Local browser access | http://127.0.0.1:8080 |
Provides the documented local verification endpoint | The endpoint must work before adding remote access |
| Localtonet client | Run it on the machine that can reach the game | Establishes an outbound connection to a Localtonet relay | The public tunnel works only while the client and tunnel are running |
Check the installed versions
Before modifying the database or installing dependencies, confirm that the commands used by the project are available. The following version checks do not alter the system:
python3 --version
pip3 --version
mysql --version
git --version
Python must satisfy the stated 3.8-or-newer requirement, and MySQL must satisfy the stated 5.7-or-newer requirement. A version number alone cannot prove that every dependency will work with the newest runtime available. If package installation reports compatibility errors, use the error output and the versions constrained in requirements.txt to investigate rather than assuming any arbitrary Python release is supported.
Prepare a dedicated database identity
The README's example connection string contains the MySQL root account and a literal example password. Copying that value into a remotely reachable deployment would be poor credential hygiene. Create and use credentials appropriate to your own MySQL environment, limit their privileges to what this application needs, and do not commit the resulting connection string to the source repository.
Exact SQL statements for creating a least-privilege account are not provided because account-management syntax, authentication plugins, and administrative practices can vary across supported MySQL installations. Use your MySQL administrator's approved procedure. The account used to import the schema may also require broader privileges than the account used by the running application.
Understand the documented shell environment
The Quick Start uses export DATABASE_URI=..., which is Unix-style shell syntax and sets the variable for the current shell session and child processes. The available project instructions do not provide PowerShell, Windows Command Prompt, container, service-manager, or system-wide environment configuration. We therefore keep the commands in their documented shell form and do not claim unverified platform support.
The database URI contains a username and password. Avoid posting it in screenshots, issue reports, logs, shell transcripts, or shared configuration. A Localtonet device token is also sensitive and device-specific. Never place either secret in browser-facing files or in this game's public content.
Install and configure Doudizhu from source
Perform these steps from the source tree you selected after resolving the repository discrepancy. The sequence follows the project's Quick Start: enter the repository, import the schema, install requirements, enter the server directory, define DATABASE_URI, and start the application.
Enter the reviewed source directory
Obtain the exact repository and revision you intend to run, then change into its root doudizhu directory. Confirm that schema.sql, requirements.txt, and the server directory are present before continuing. Do not proceed from an unrelated directory that happens to have the same name.
Import the supplied MySQL schema
From the repository root, run the documented schema import. The MySQL client prompts for the selected account's password. Review schema.sql before executing it so you understand the database objects and privileges it expects to create or modify.
mysql --user=root -p < schema.sql
Install the declared Python dependencies
While still in the repository root, ask pip3 to install the packages declared by the project. Read any build or compatibility errors in full. Do not move on to startup if installation fails.
pip3 install -r requirements.txt
Change into the server directory
The documented launch command runs from the server directory. Enter it before defining the connection and starting the application.
cd server
Set the database connection URI
Export DATABASE_URI in the same shell that will launch the server. Replace the placeholders with a valid MySQL username and password for the database created or expected by the imported schema. The documented database name is ddz, the host is 127.0.0.1, and the port shown is 3306.
export DATABASE_URI='mysql+aiomysql://DB_USER:DB_PASSWORD@127.0.0.1:3306/ddz'
Start the game server
Launch the application from the server directory and keep the terminal open while testing. Watch the terminal for Python tracebacks, database errors, missing modules, or address-binding failures.
python3 app.py
The project documentation uses this example URI:
export DATABASE_URI=mysql+aiomysql://root:123456@127.0.0.1:3306/ddz
Treat it as an illustration of the required URI structure, not as a recommended credential. In particular, do not change a MySQL root password to 123456 merely to match the example. Your configured URI and your actual database account must agree.
What each connection-string component means
The mysql+aiomysql prefix identifies the MySQL dialect and aiomysql driver expected by the application's database layer. The username and password authenticate the application. 127.0.0.1 directs it to MySQL on the same machine, 3306 is the port shown by the documentation, and ddz is the documented database name.
If your MySQL deployment differs, the URI must reflect your real configuration. However, the project evidence does not document remote database hosts, alternate ports, TLS parameters, special-character escaping, or socket-based connections. Consult your own MySQL and database-driver configuration when departing from the supplied local example.
With the documented export approach, opening a new shell normally means setting DATABASE_URI again before running python3 app.py. The project does not document a persistent secrets file or service configuration, so do not invent one without reviewing how the application loads configuration.
Verify the game locally before creating a tunnel

With python3 app.py still running, open the documented address from a browser on the same machine:
http://127.0.0.1:8080
A useful local verification is more than observing that a process remains open. Confirm that the browser can load the page, that expected static assets appear, and that the application reaches a usable game interface rather than displaying an HTTP error or an empty response. Keep the server terminal visible while loading the page because backend exceptions may provide the most direct explanation for a failure.
The address 127.0.0.1 is the IPv4 loopback address. It refers to the machine making the connection. A browser on another computer cannot use 127.0.0.1:8080 to reach this server because, on that other computer, the address refers back to itself. This is exactly why local verification should happen on the host and why a tunnel is useful for remote browser access.
Use a clear local success checklist
- The Python process starts without immediately exiting.
- The terminal does not show an unresolved import or missing-package error.
- The database connection does not fail with an authentication, connection, or missing-database error.
- The browser reaches
http://127.0.0.1:8080. - The response is the expected game page rather than a generic browser connection error.
- Required frontend assets load sufficiently for the interface to operate.
The supplied project evidence does not define an automated health endpoint, test command, default user account, expected database row count, or formal gameplay test. We therefore cannot claim that a particular API request or test suite proves full application health. Browser verification at the documented endpoint, combined with inspection of server output, is the supported baseline available here.
A public URL cannot compensate for an application that is not listening, cannot authenticate to MySQL, or fails while serving its frontend. If the local page does not work, stop at this stage and correct the application problem before introducing Localtonet.
Provide remote browser access with a Localtonet HTTP tunnel

Once Doudizhu works locally, a Localtonet HTTP tunnel can publish the HTTP service. Our client application runs on the device that can reach the game and establishes an outbound connection to a Localtonet relay server. This avoids inbound router port forwarding, firewall changes, VPN setup, and the need for a public IP address.
For this deployment, the local HTTP target is the documented game endpoint: IP address 127.0.0.1 and port 8080, provided the Localtonet client runs on the same machine as Doudizhu. If the client runs on another device, 127.0.0.1 would refer to that other device instead, so it would not reach the game. In that arrangement, use an address that is genuinely reachable from the client device and ensure the game is configured to listen appropriately. The project documentation does not describe non-loopback binding, so this guide does not prescribe an unverified application option for that topology.
HTTP tunnels can use Random Sub Domain, Custom Sub Domain, or Custom Domain as their Process Type. All three serve the target content at a public HTTPS address. Availability can vary, and exact custom-domain DNS requirements should be checked against the current dashboard and documentation rather than guessed.
| HTTP Process Type | Public addressing model | When to consider it |
|---|---|---|
| Random Sub Domain | A generated public subdomain | Useful when you need an assigned address without selecting a specific name |
| Custom Sub Domain | A selected subdomain where supported | Useful when a recognizable supported subdomain is preferable |
| Custom Domain | Your own domain name | Useful when you have a domain and can complete the current required DNS configuration |
Follow the current Localtonet HTTP tunnel documentation alongside the dashboard because server choices, availability, and interface details can change. Do not hardcode a relay server code from an unrelated example. Select an available value shown for your account and current deployment.
Install and run the Localtonet client
Run our client on the Doudizhu host whenever possible. That placement lets it reach the loopback service at 127.0.0.1:8080 directly. Keep the game server running during setup and verification.
Authenticate or select the device
Use the device-specific authentication token associated with the client that will run this tunnel. Keep the token private and do not paste it into the game configuration, source code, screenshots, or public documentation.
Select an available relay server
Choose a relay server or region currently offered by the dashboard. Available values can vary by plan, region, or product state, so use the live selection rather than copying a server code from another deployment.
Create an HTTP tunnel to the local game
Choose an HTTP tunnel and an appropriate Process Type. Set the local target IP address to 127.0.0.1 and the local target port to 8080 when the Localtonet client and game run on the same host.
Start the tunnel and open its assigned address
Creating a tunnel does not make it active. Press Start, wait until the selected device and tunnel are connected, then open the assigned public HTTPS URL in a remote browser.
Stop or delete access when it is no longer needed
Stop the tunnel to end the active publication, or delete it if the configuration is no longer required. The public endpoint is available only while the selected client device is connected and the tunnel is running.
Test from outside the host
Open the assigned public HTTPS URL from another browser or device. Check that it displays the same application you verified locally. If local access works but the public address does not, inspect the Localtonet client connection, tunnel status, selected device, local target, and whether the Python process is still listening.
The tunnel changes how traffic reaches the application, not how Doudizhu communicates with MySQL. The database should remain behind the application. Do not create a separate public database tunnel merely because the browser game is public. The browser needs the game's HTTP endpoint, not direct access to MySQL.
Only expose code and data you are prepared to make reachable from the internet. The available Doudizhu documentation does not establish application authentication, authorization, abuse prevention, a security maintenance policy, or production hardening. Review the code and dependency risks, use least-privilege database credentials, share the URL deliberately, and stop the tunnel when remote access is unnecessary.
Operate and maintain the deployment safely
This setup has several independent components that must remain healthy: MySQL, the Python application, the Localtonet client, and the tunnel itself. A failure in any required component changes what users observe. Thinking in layers makes routine operation and incident diagnosis more predictable.
Starting a new session
Start MySQL using the procedure appropriate to your system. Open a shell, enter the project's server directory, set DATABASE_URI, and launch python3 app.py. Verify http://127.0.0.1:8080 locally. Then make sure the Localtonet client is connected and start the existing HTTP tunnel.
The project evidence does not provide a service definition or background-process command. If you later add a process supervisor, startup service, container, or secrets manager, treat that as a separate deployment design and test it carefully. Do not assume that a configuration intended for one operating system applies to another.
Stopping remote access versus stopping the game
These actions have different effects. Stopping the Localtonet tunnel removes the active public path while the local game may continue running. Stopping the Localtonet client also prevents the selected device's tunnel from remaining connected. Stopping python3 app.py removes the local HTTP service, so a running tunnel no longer has a healthy target. Stopping MySQL may leave the Python process present but can break database-dependent behavior.
| Component stopped | Local result | Remote result |
|---|---|---|
| Localtonet tunnel | The game can remain available at its local endpoint | The configured public tunnel is not active |
| Localtonet client | The game can remain available locally | Tunnels assigned to that disconnected client cannot provide active access |
| Python game server | 127.0.0.1:8080 no longer serves the game |
The tunnel has no working local HTTP target |
| MySQL | Database-backed game behavior can fail | Remote users encounter the same application-side failure through the tunnel |
Upgrades and dependency changes
Record the repository and revision currently deployed before pulling changes. Review changes to requirements.txt, schema.sql, configuration handling, and server behavior before applying them. The available documentation does not describe database migrations or backward compatibility, so rerunning schema.sql against an existing database should not be treated as an upgrade procedure unless you have inspected the script and confirmed that behavior yourself.
Back up data according to your MySQL administration practices before making schema or application changes. The repository evidence does not specify an application-aware backup and restore process, so verify restoration in a safe environment rather than assuming that a copied file or untested dump is sufficient.
Access review
Periodically review whether the public tunnel is still required, which device token owns it, and whether the selected source revision is still acceptable for internet exposure. Stop or delete obsolete tunnels. Rotate database credentials if they are disclosed, and replace a device token through the appropriate account workflow if it is exposed. Do not place credentials in issue reports while requesting support.
Troubleshooting Doudizhu and the HTTP tunnel
The schema import fails
Confirm that the MySQL server is running and that the mysql command can reach it. Check the exact error returned by the client. Authentication failures point to the selected MySQL account or its authentication configuration. Permission failures indicate that the importing account cannot perform an operation required by schema.sql. SQL syntax or compatibility errors should be compared with the documented MySQL 5.7-or-newer requirement and the actual script content.
Also verify that the command is being run from the repository root, where schema.sql exists. A shell error saying that the file cannot be found is a local path problem, not a database problem.
pip3 install reports an error
First confirm that the selected Python version is at least 3.8 and that pip3 belongs to the Python installation you intend to use. Read the first meaningful package error rather than only the final failure summary. A missing compiler, unavailable package build, incompatible dependency, or permission failure requires a different remedy.
The project documentation supplies only pip3 install -r requirements.txt. It does not document operating-system packages, a lockfile workflow, or a supported matrix of newer Python versions. Avoid changing dependency versions blindly because doing so can create a configuration that no longer matches the source.
The application reports a database connection failure
Check that DATABASE_URI was set in the same shell used to start python3 app.py. Verify the username, password, host, port, and database name. The documented local structure uses 127.0.0.1:3306/ddz. Confirm that MySQL is actually reachable there and that the selected application account has access to the expected database.
If you copied the placeholder command, replace DB_USER and DB_PASSWORD with real values. If a password contains characters with special meaning in a URI, consult the database driver's connection-string rules. The project evidence does not provide an escaping recipe, so guessing could produce an incorrect or insecure value.
python3 app.py cannot find a module or file
A missing Python module usually means dependency installation did not complete for the Python environment running the application. A missing project file can indicate that the command is being executed from the wrong directory or from a different source revision. Confirm that you ran the startup command from doudizhu/server and that the dependency installation completed against the intended Python installation.
The browser cannot open 127.0.0.1:8080
Look at the server terminal first. If the process exited, correct its reported error and restart it. If it remains running, confirm that the address and port are exactly those documented. Another process may already be using port 8080, but the project evidence does not describe a supported alternate-port configuration. Do not invent a command-line flag. Inspect the application configuration or source before changing the listener.
The page opens locally but not through Localtonet
Verify each boundary in order:
- The Doudizhu process is still running.
- The local page still loads at
http://127.0.0.1:8080on the host. - The Localtonet client is running on that same host, or can otherwise reach the configured target.
- The selected device token corresponds to the connected client.
- The HTTP tunnel target is the correct IP address and port.
- The tunnel has been started rather than merely created.
- The remote browser is using the assigned public URL.
If the client is on another device and the tunnel target is 127.0.0.1, the client is attempting to reach itself, not the Doudizhu host. Moving the Localtonet client to the game host is the straightforward topology for the documented loopback listener.
The public page loads but gameplay does not work correctly
Compare the behavior with the local page. If the same problem occurs locally, troubleshoot the application, database, frontend assets, and selected source revision. If it occurs only through the public URL, inspect browser developer messages and server output for assumptions about URLs or connection behavior. The supplied project documentation does not state whether every multiplayer transport and frontend URL is designed for operation behind a public HTTPS tunnel, so specific code-level changes cannot be prescribed without reviewing the deployed revision.
Test MySQL, then the Python process, then the local browser endpoint, then the Localtonet client, and finally the public URL. This order avoids confusing an application failure with a tunnel failure and usually identifies the first broken boundary.
Frequently asked questions
What versions of Python and MySQL does this Doudizhu project require?
The project documentation lists Python 3.8 or newer and MySQL 5.7 or newer. It does not establish an upper compatibility boundary for either dependency, so a newer version number alone does not guarantee that every package or SQL statement is compatible.
Which Doudizhu repository should I clone?
Resolve that choice explicitly before installation. The reviewed repository is svzdev/doudizhu, while its README tells readers to clone mailgyc/doudizhu. The available evidence does not prove that they are identical or establish which one is canonical. Review and record the exact repository and revision you intend to execute.
Should I use the root database account and password shown in the README?
No. The example demonstrates the DATABASE_URI format, but it should not be treated as a secure credential recommendation. Use credentials appropriate to your MySQL environment, apply least privilege, and keep the connection string out of source control, screenshots, logs, and public pages.
Why must Doudizhu work locally before I create the tunnel?
A Localtonet HTTP tunnel forwards requests to the configured local service. It does not install dependencies, start Python, repair the database, or fix application errors. Verifying http://127.0.0.1:8080 first proves that a functioning local target exists.
Does Localtonet require router port forwarding or a public IP address?
No. Our client establishes an outbound connection to a Localtonet relay server, so this workflow does not require inbound router port forwarding, firewall changes, VPN setup, or a public IP address.
Why should the Localtonet client run on the same machine as the game?
The documented game address uses 127.0.0.1, which is reachable only from the same machine. Running our client there lets the tunnel target that loopback listener directly. If the client runs elsewhere, its own 127.0.0.1 does not refer to the Doudizhu host.
Is the public Doudizhu address always online after I create it?
No. Creating a tunnel does not start it. The selected Localtonet client must be connected, the tunnel must be running, and the Doudizhu service must still be available at its local target. MySQL must also remain available for database-dependent behavior.
Does this setup make Doudizhu production-ready?
No. It follows the documented source installation and provides remote HTTP access to the resulting local service. The available project evidence does not define production hardening, authentication, authorization, monitoring, process supervision, backups, upgrades, or a security maintenance policy. Evaluate those areas before exposing the application to an untrusted audience.
Connect your verified Doudizhu server with Localtonet
After the game loads successfully at 127.0.0.1:8080, run our client on the same host, create an HTTP tunnel to that local target, and start the tunnel when remote browser access is needed.