This document provides a detailed architectural overview of the server.dll module from Europa 1400: Gold Edition. Its purpose is to document the server's design and behavior, including its core components, subsystems, and data management strategies.
The server.dll module is a multithreaded, single-instance game server for Europa 1400: Gold Edition. It is designed to manage the complete lifecycle of a multiplayer game session, from initialization and player management to game state synchronization and final shutdown. The server operates on a client-server model where one client is designated as the "serving host," with the authority to load the initial game state.
The server's lifecycle is managed through a sequence of initialization, execution, and shutdown steps:
+----------------+ +------------------+ +------------------+ +----------------+
| `Init` |----->| `srv_GameLoop` |----->| `srv_InitServer` |----->| Main Loop |
| (Export) | | (Thread Start) | | (Initialization) | | (Active) |
+----------------+ +------------------+ +------------------+ +----------------+
^
|
+----------------+ +------------------+ +------------------+
| `Exit` |<-----| (Shutdown) |<-----| (Cleanup) |
| (Export) | | `g_bShutdownFlag`| | |
+----------------+ +------------------+ +------------------+
The server uses a simple multithreading model with two primary threads:
- Main Thread: The thread that loads the
server.dlllibrary. It is responsible for calling theInitandExitfunctions to start and stop the server. - Game Loop Thread: A dedicated thread, created by
Init, that runs thesrv_GameLoopfunction. This design isolates the main server logic from the main thread, preventing the game from freezing if the server performs blocking operations.
The core of the server is the srv_GameLoop, which continuously processes game logic and network events. The loop's behavior is conditional, based on the game state (e.g., lobby vs. active game).
+------------------------------------+
| `srv_GameLoop` |
+------------------------------------+
| |
| +----------------------------+ |
| | `srv_InitServer` | |
| +----------------------------+ |
| |
| WHILE `!g_bShutdownFlag` |
| | |
| | +-------------------------+ |
| | | `srv_AcceptClient` | |
| | +-------------------------+ |
| | |
| | +-------------------------+ |
| | | `srv_ProcessClientCmds` | |
| | +-------------------------+ |
| | |
| | IF `is_game_active()` | |
| | | | |
| | | +--------------------+ | |
| | | | `sim_Update` | | |
| | | +--------------------+ | |
| | | | |
| | END_IF | |
| | |
| | +-------------------------+ |
| | | `Sleep(1)` | |
| | +-------------------------+ |
| | |
| END_WHILE |
| |
+------------------------------------+
The networking subsystem uses a non-blocking TCP model with Winsock. It queues incoming and outgoing commands to avoid blocking the main game loop.
- Command Queuing: Incoming commands are placed in a queue by
srv_RecvFromClientand processed bysrv_ProcessClientCommands. Outgoing commands are queued withenqueue_client_commandand sent bysrv_SendQueuedCommands. - Command Dispatch:
srv_DispatchGameCommanduses a jump table to map command IDs to their correspondingcm_*handler functions, allowing for efficient command processing.
+-----------------+ +----------------------+ +-----------------------+ +-------------------+
| Client Action |-->| `srv_RecvFromClient` |-->| `enqueue_client_cmd` |-->| Command Queue |
+-----------------+ +----------------------+ +-----------------------+ +-------------------+
|
v
+-----------------+ +----------------------+ +-----------------------+ +-------------------+
| Server Response |<--| `srv_SendData` |<--| `cm_*` Handler |<--| `srv_DispatchCmd` |
+-----------------+ +----------------------+ +-----------------------+ +-------------------+
The server uses a custom memory pool allocator to manage memory for game objects, which is more efficient than standard dynamic allocation.
m_alloc_init: Initializes the memory manager.m_pool_alloc: Allocates a fixed-size block from a pre-allocated pool.m_pool_free: Returns a block to the pool.
This system is designed to minimize memory fragmentation and reduce the overhead of frequent allocations and deallocations.
+------------------------------------+
| Memory Pool |
+------------------------------------+
| |
| +-----------+ +-----------+ |
| | Free Block| | Used Block| ... |
| +-----------+ +-----------+ |
| |
+------------------------------------+
The server uses a virtual file system (VFS) to manage access to game data files. This system supports reading from compressed archives, using the statically linked zlib library for decompression.
vfs_open: Opens a file from the VFS, handling both compressed and uncompressed data.file_read: Reads data from a file, performing decompression and CRC32 checks as needed.
+----------------+ +----------------+ +----------------+ +----------------+
| `vfs_open` |-->| `file_read` |-->| `zlib_inflate` |-->| Game Data |
+----------------+ +----------------+ +----------------+ +----------------+
The server includes a robust error handling system to ensure stability.
unhandledExceptionHandler: A top-level exception filter that catches unhandled exceptions.writeArchiveFile: Generates a crash dump file containing the exception context and memory state, which is crucial for debugging.
+------------------+ +---------------------+ +--------------------+
| Exception |-->| `unhandledExFilter` |-->| `writeArchiveFile` |
+------------------+ +---------------------+ +--------------------+
Game state synchronization is a critical process that occurs when a game is loaded. The "serving host" is responsible for providing the initial game state, which is then broadcast to all other clients.
- The serving host sends the game state to the server.
- The server receives the data and uses
srv_LoadGameStateto process it. srv_LoadGameStatecalls a series ofhandleLoad*functions to deserialize the data.ls_ID2Ptris called to perform "pointer fix-ups," converting object IDs into direct memory pointers, which is essential for re-establishing object relationships.
+--------------+ +---------------------+ +-----------------------+ +-------------------+
| Serving Host |-->| `srv_LoadGameState` |-->| `handleLoad*` funcs |-->| `ls_ID2Ptr` |
| (Client) | | (Server) | | (Deserialization) | | (Pointer Fix-up) |
+--------------+ +---------------------+ +-----------------------+ +-------------------+
The game state is stored in a collection of global data structures that are initialized at startup and modified during gameplay. Key structures include:
g_client_slots: An array of structures representing connected clients.g_player_data: An array storing data for all players in the game.g_building_data: An array for all buildings.g_object_data: An array for all game objects.
The server loads initial game data from several files:
game.ini: Contains server configuration settings.aemter.dat: Contains data for the "Amt" (office/rank) system.
This document describes the architecture of the server.dll module.
The server is initialized in the srv_InitServer function. This function is called by srv_GameLoop when the server is started. The initialization process consists of the following steps:
- Error Handling: The
errorHandlerInitfunction is called to set up an error handler for the server. - Memory Allocation: The
m_alloc_initfunction is called to initialize the memory manager. - Game Data Loading: The
gm_openfunction is called to load the game data from theA_Geb.datandA_Obj.datfiles. - Networking: The
srv_InitSocketsfunction is called to initialize the server's network sockets.
The main game loop is in the srv_GameLoop function. The game loop consists of the following phases:
- Waiting for Players: The server waits for all players to connect and be ready before starting the game.
- Game State Sync: The server synchronizes the game state with all the clients.
- Main Loop: The server processes client commands, runs the game simulation, and sends updates to the clients.
- Shutdown: The server cleans up resources and shuts down.
The server uses TCP for network communication. The server listens for incoming connections on port 7531. The server uses a custom command protocol to communicate with the clients.
The server uses a custom command protocol to communicate with the clients. The command protocol is based on a command table located at 0x1002c3f8. The first byte of the command is used as an index into this table to find the corresponding command handler function.
Each client has a command queue. When a client sends a command to the server, the server adds the command to the client's command queue. The server then processes the commands in the command queue in the order they were received.
The command protocol has the following command types:
- Lobby Commands: These commands are used to manage the game lobby.
- Game Data Transfer Commands: These commands are used to transfer game data between the server and the clients.
- Game Commands: These commands are used to control the game.
The server uses a custom memory management system. The functions for memory management (m_init, m_alloc, etc.) are not called directly. Instead, they are called via a dispatch mechanism.
- A string name (e.g., "m_init") is passed to a dispatcher function.
- The dispatcher looks up the corresponding function pointer.
- The dispatcher calls the function pointer.
This means the addresses for the memory functions are resolved at runtime and are not static.
The game uses a variety of data structures to represent the game state. These data structures are defined in the types.h file. The following are some of the most important data structures:
player_t: Represents a player in the game.building_t: Represents a building in the game.item_t: Represents an item in the game.citizen_t: Represents a citizen in the game.office_t: Represents an office or rank in the game.dynasty_t: Represents a dynasty in the game.town_t: Represents a town in the game.alchemist_t: Represents an alchemist in the game.
The server uses the following third-party libraries:
zlib: The server uses thezliblibrary for data compression. Thezlibversion used is1.1.3.