Skip to content

Research target: G:\GitHub\OpenTTD-patches (jgrpp source workspace) Reference resources: G:\game\openttd-jgrpp (compiled full game, read-only) Research date: 2026-08-14 Branch: jgrpp (current HEAD 5b5c452e1b, approximately version 16.0)


1. Project Overview ​

OpenTTD jgrpp (JGR's Patchpack) is a well-known enhanced fork based on OpenTTD. The source code in this workspace corresponds to version 16.0, containing many JGR-specific features (tracerestrict, programmable signals, scheduled dispatch, template replacement, enhanced signals, etc.).

ItemDescription
LanguageC++20 (CMAKE_CXX_STANDARD 20, no extensions)
BuildCMake (≥3.17), src/CMakeLists.txt organized by directory
Script EngineSquirrel (src/3rdparty/squirrel/), used for AI / GameScript / template scripts
NetworkCustom TCP/UDP protocol layer (src/network/), HTTP using WinHttp (Windows) / libcurl (others) / JS (Emscripten)
Savesrc/saveload/ + src/sl/ custom binary format
Threadingsrc/thread.h + src/worker_thread.cpp (WorkerThreadPool task pool), src/timer/ timers

2. Source Directory Structure and Module Organization ​

Top level: CMakeLists.txt is the root build script; src/ contains all C++ source code; bin/ contains runtime data (AI scripts, language files, etc.); media/, os/, cmake/, docs/ contain resources, platform code, build scripts, and documentation respectively.

Main subdirectories within src/ and their responsibilities:

DirectoryResponsibility
src/core/Basic utilities: pool types, bitsets, containers, thread wrappers, bit operations, etc.
src/network/Networking: server/client/UDP/HTTP/content download/admin protocol (core/ subdirectory for protocol basics)
src/script/Script runtime framework: Squirrel wrapper, instances, configuration; api/ contains all API classes exposed to AI/GS
src/ai/NoAI framework (AI instances, scanner, configuration, GUI)
src/game/GameScript framework (GS instances, configuration, GUI)
src/newgrf/NewGRF decoding and processing
src/pathfinder/Pathfinding (YAPF, NPF)
src/saveload/, src/sl/Save/load
src/blitter/, src/video/, src/fontcache/, src/music/, src/sound/Rendering/audio/video backends
src/lang/Localization string sources (generated by strgen)
src/table/Static tables; table/settings/*.ini are setting definition sources (generated by settingsgen)
src/3rdparty/Third-party libraries (squirrel, llvm, icu, etc.)
src/timer/, src/os/, src/misc/Timers, platform, miscellaneous

Top-level scattered files are named by system (e.g., rail_gui.cpp, group_cmd.cpp, vehicle.cpp, order_cmd.cpp, economy.cpp), following OpenTTD conventions: *_cmd for command logic, *_gui for windows, *_base/_type/_func for data structures and inline functions.


3. Build Method ​

  • CMake three steps: cmake -B build .. → cmake --build build → Output openttd.exe. The repository includes build.sh / build-dedicated.sh.
  • Dependencies: Declared in vcpkg.json (zlib, lzma, lzo, zstd, png, SDL2, freetype, harfbuzz, icu, opus, etc.); Windows uses WinHttp (no curl needed), non-Windows uses libcurl (CMakeLists.txt:121-127).
  • Host tools: strgen (language files), settingsgen (generates setting code from src/table/settings/*.ini), squirrel_export (generates Squirrel bindings from src/script/api/script_*.hpp).
  • Key generated files: generated/script/api/<ai|gs>/...sq.hpp (API bindings, automatically discovered by file(GLOB script_*.hpp), new API classes do not need to modify registration lists, just add .hpp and include .cpp in src/script/api/CMakeLists.txt source list); generated/rev.cpp; generated/ottdres.rc.
  • Settings system: The modern version has been changed to INI-driven — src/table/settings/*.ini (containing [SDTC_VAR] sections, cat=SC_* categories, flags, post_cb, etc.), settingsgen generates settings_*.cpp/h; corresponding struct members are in src/settings_type.h (e.g., NetworkSettings starting at line 575).

4. Five Feature System Implementation Locations ​

4.1 Resource Download (Content Download / BaNaNaSplit) ​

Point of InterestLocation
Content client main classsrc/network/network_content.h/.cpp — ClientNetworkContentSocketHandler (also ContentCallback + HTTPCallback)
Content download GUIsrc/network/network_content_gui.cpp/.h
HTTP clientsrc/network/core/http.h/.cpp — NetworkHTTPSocketHandler::Connect(uri, callback, data), async event-driven (non-blocking, main loop polling)
Content server connection stringsrc/network/core/config.cpp — NetworkContentServerConnectionString(): environment variable OTTD_CONTENT_SERVER_CS, default content.openttd.org (TCP metadata protocol)
Mirror URIsrc/network/core/config.cpp — NetworkContentMirrorUriString(): environment variable OTTD_CONTENT_MIRROR_URI, default https://binaries.openttd.org/bananas
Download flowDownloadSelectedContent() → DownloadSelectedContentHTTP() (POST all content IDs to mirror, mirror returns a multi-file tar stream, written file by file) → AfterDownload() does gunzip + TarScanner extraction
DecompressionGunzipFile() (zlib), TarScanner/ExtractTar (src/tar_type.h / src/fileio.cpp)
Threading facilities (usable for parallelism)src/worker_thread.h/.cpp — WorkerThreadPool + EnqueueJob; src/thread.h is a platform thread wrapper

Current conclusion: ① Only 1 mirror, configurable only via environment variable, no in-game setting; ② Download is single-connection, serial (one POST for all files); ③ Decompression is synchronous on the main thread. → Multi-threaded/multi-mirror modification points are clear.

4.2 Server Online Player / Company Limits ​

Point of InterestLocation
Client limit constantsrc/network/network_type.h:21 — static const uint MAX_CLIENTS = 255;
Client poolSame file ClientPoolIDTag : PoolIDTraits<uint16_t, MAX_CLIENTS + 1, 0xFFFF>; ClientID is uint32_t
Company ID poolsrc/company_type.h — CompanyIDTag : PoolIDTraits<uint8_t, 0xF, 0xFF> → MAX_COMPANIES = CompanyID::End().base() = 15; fake companies occupy 253/254/255
Company maskSame file CompanyMask : BaseBitSet<CompanyMask, CompanyID, uint16_t> (16-bit, can only track 16 companies)
Server acceptance checksrc/network/network_server.cpp:360 — _network_clients_connected < MAX_CLIENTS; static_assert(NetworkClientSocketPool::MAX_SIZE == MAX_CLIENTS + 1)
Client settingssrc/table/settings/network_settings.ini:231/241 — network.max_companies (def 15, max MAX_COMPANIES), network.max_clients (def 25, max MAX_CLIENTS); struct in src/settings_type.h NetworkSettings
Protocol bit width (hard constraint)src/network/core/network_game_info.cpp — companies_max and clients_max are both sent as Send_uint8/Recv_uint8 (lines 251-296, 422-432)
Server list displaysrc/network/network_gui.cpp:519 etc.

Current conclusion:

  • Client limit = 255 is already the protocol limit (uint8 field + Game Coordinator/server browser compatibility). To break through, network_game_info related fields must be changed to uint16 (full client↔server chain + UDP broadcast + Game Coordinator protocol), which is an external protocol change.
  • Company limit = 15 (CompanyIDTag's End=0xF). Can be safely raised to 252 (End=0xFC): underlying type is still uint8, save byte width unchanged (compatible with old saves), need to simultaneously widen CompanyMask (uint16→uint32) and interface/loop assumptions.

4.3 Vehicle Grouping ​

Point of InterestLocation
Group data structuresrc/group.h — Group : GroupPool::PoolItem (name/owner/vehicle_type/flags/livery/statistics/parent/number); GroupID, DEFAULT_GROUP, IsDefaultGroupID/IsAllGroupID/IsTopLevelGroupID
Group commandssrc/group_cmd.cpp — CmdCreateGroup(536), CmdDeleteGroup(585), CmdAlterGroup(646), CmdAddVehicleGroup, CmdAddSharedVehicleGroup (adds shared-order vehicles to an existing group, around line 718 AddVehicleToGroup)
Command registrationsrc/group_cmd.h:27-35 — DEF_CMD_TUPLE_NT(Commands::XXX, CmdXXX, {}, CommandType::RouteManagement, CmdDataT<...>); enum in src/command_type.h enum class Commands (starting at line 492)
Group GUIsrc/group_gui.cpp/.h, src/vehiclelist.cpp
Vehicle↔Groupsrc/vehicle_base.h (Vehicle::group_id), SetTrainGroupID/UpdateTrainGroupID (group.h:130-131)
Orders/shared dispatchsrc/order_base.h (OrderList, VehicleOrdersID), src/order_cmd.cpp, src/order_func.h, src/schdispatch.h/.cpp (scheduled dispatch, bound to order list)
Group statisticsGroupStatistics (group.h:60-66), GetGroupNumVehicle etc. (group.h:125-128)

Current conclusion: CmdAddSharedVehicleGroup (adds shared-order vehicles of one vehicle to a group) and CmdCreateGroupFromList (create group from list) already exist, but there is no complete "auto-create/assign groups by shared orders" logic. The path for a new AutoGroupSharedOrders command (iterate all company primary vehicles → aggregate by OrderList → auto-create groups and assign) is clear.

4.4 Build Tooltip (Price Display at Mouse Cursor When Building Rails, etc.) ​

Point of InterestLocation
Rail build GUI/logicsrc/rail_gui.cpp (BuildRailToolbarWindow), src/rail_cmd.cpp, src/rail.h/.cpp; roads road_gui.cpp/road_cmd.cpp
Cost estimationDoCommand in each *_cmd.cpp returns CommandCost; GUI can use DC_QUERY_COST mode for pricing
Tile under cursorsrc/viewport_func.h:36 — GetTileBelowCursor(); _cursor.pos (screen coordinates); src/viewport.cpp:1056
Existing text display mechanismsrc/texteff.hpp — AddTextEffect(msg, x, y, duration, mode, ...) (world coordinate floating text), UpdateTextEffect; src/texteff.cpp
Existing build hint UXRail toolbar OnPlaceDrag shows selected area and cost accumulation (_thd tilehighlight, src/tilehighlight_func.h); status bar statusbar_gui.cpp can display tool cost
Per-frame refresh pointEach toolbar window OnMouseLoop / viewport.cpp HandleMouseEvents (lines 5422/5733)

Current conclusion: No "price tooltip at mouse cursor." Can use AddTextEffect anchored to the tile under the cursor (follows the cursor tile), or self-draw a screen coordinate tooltip; cost can be queried with DC_QUERY_COST for a single tile + accumulated selection area.

4.5 AI Interface (NoAI Retained + Whole-Game-Aware AI) ​

Point of InterestLocation
NoAI frameworksrc/ai/ — ai_core.cpp (AICore main loop), ai_instance.cpp (AIInstance/Squirrel VM), ai_scanner.cpp (scans ai/ directory), ai_gui.cpp (selection/configuration), ai_config.cpp
GameScript frameworksrc/game/ — game_core.cpp, game_instance.cpp, etc. (GS is "deity mode," higher privileges than AI)
Script API classessrc/script/api/script_*.hpp/.cpp (script_company, script_map, script_vehicle, script_industry, script_town, script_game, script_admin, etc., 60+ classes total)
API auto-registrationsrc/script/api/CMakeLists.txt — file(GLOB script_*.hpp) auto-generates ai_*.sq.hpp/gs_*.sq.hpp bindings; .cpp must be added to source list (starting at line 235)
Squirrel binding macrossrc/script/squirrel_class.hpp — DefSQClass / DefSQStaticMethod; ai/ai_controller.sq.hpp is the AI controller binding
Instance schedulingsrc/script/script_instance.cpp, src/script/script_suspend.hpp (suspend/resume), events script_event*
Company access controlsrc/script/api/script_object.hpp:318 — ScriptObject::GetCompany(); ScriptCompanyMode (IsDeity()) distinguishes GS deity mode; company API parameter validation ResolveCompanyID, EnforceCompanyModeValid
Script settingssrc/table/settings/script_settings.ini (game.script.*); AI instance configuration ai_config.cpp

Current conclusion: In the modern script API, some competitor information (e.g., GetBankBalance) is no longer restricted, but there is no aggregated API for whole-game awareness (no unified entry point for "enumerate all companies / global economy / global map statistics"), nor a toggle-controlled "global AI" access point. Adding a ScriptGlobal API class (GLOB auto-registered) + a game.script setting to "allow AI global awareness" is sufficient; GS is always available (deity mode), AI is controlled by the toggle — achieving "retain NoAI, add whole-game-aware AI, with access control."


5. Modification Risk Overview ​

FeatureMain Files ChangedRisk
F1 Download multi-source/multi-threadsrc/table/settings/network_settings.ini, src/settings_type.h, src/network/core/config.cpp, src/network/network_content.h/.cppMedium (network callback state machine needs care)
F2 Limit expansionsrc/company_type.h, src/table/settings/network_settings.ini, src/network/core/network_game_info.cpp (optional uint16)Low-Medium (company 252 low risk; clients >255 requires protocol change)
F3 Vehicle auto-groupingsrc/group_cmd.h/.cpp, src/command_type.h, src/console_cmds.cpp, src/group_gui.cpp, src/lang/english.txtLow
F4 Build tooltipNew src/construction_cost_tip.h/.cpp, src/rail_gui.cpp/road_gui.cpp hooks, src/lang/english.txtLow-Medium
F5 Whole-game-aware AINew src/script/api/script_global.hpp/.cpp, src/script/api/CMakeLists.txt, src/table/settings/script_settings.ini, example AI bin/ai/GlobalAI/Low (framework auto-registers)

Note: All modifications are based on the local jgrpp branch workspace, not compiled/verified (no usable build toolchain available on this machine); all patches can be reviewed via git diff and then git apply.

Last updated: