mirror of
https://github.com/luanti-org/luanti.git
synced 2025-09-15 18:57:08 +00:00
Add comprehensive developer manual
This manual covers Luanti's architecture, codebase structure, building process, network protocol, game engine, Lua scripting API, data flow and storage, function invocation examples, utilities/libraries, modding, and contribution guidelines. The goal is to provide a comprehensive resource for new engineers like you to understand the Luanti repository, with a particular focus on aspects relevant to creating a web client, such as the network protocol.
This commit is contained in:
parent
7ac5502fdf
commit
4a1c3e6f36
2 changed files with 1266 additions and 127 deletions
882
README.md
882
README.md
|
@ -1,149 +1,777 @@
|
||||||
<div align="center">
|
Luanti is a free open-source voxel game engine that provides users with a platform for easy modding and game creation. It is a fork of the Minetest engine, building upon its foundation to offer an enhanced experience for both players and developers. Luanti retains the core strengths of Minetest, such as its lightweight nature and extensive modding capabilities, while also aiming to address some of its limitations.
|
||||||
<img src="textures/base/pack/logo.png" width="32%">
|
|
||||||
<h1>Luanti (formerly Minetest)</h1>
|
|
||||||
<img src="https://github.com/luanti-org/luanti/workflows/build/badge.svg" alt="Build Status">
|
|
||||||
<a href="https://hosted.weblate.org/engage/minetest/?utm_source=widget"><img src="https://hosted.weblate.org/widgets/minetest/-/svg-badge.svg" alt="Translation status"></a>
|
|
||||||
<a href="https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html"><img src="https://img.shields.io/badge/license-LGPLv2.1%2B-blue.svg" alt="License"></a>
|
|
||||||
</div>
|
|
||||||
<br>
|
|
||||||
|
|
||||||
Luanti is a free open-source voxel game engine with easy modding and game creation.
|
The primary goals of the Luanti project revolve around several key areas of improvement. Firstly, Luanti seeks to significantly enhance rendering and graphics capabilities, aiming for a more visually appealing and immersive experience. Secondly, there is a strong focus on internal code refactoring to improve maintainability, performance, and stability. This includes modernizing the codebase and streamlining existing systems.
|
||||||
|
|
||||||
Copyright (C) 2010-2025 Perttu Ahola <celeron55@gmail.com>
|
Furthermore, Luanti aims to deliver substantial UI/UX improvements, making the engine more intuitive and user-friendly. Finally, the project is dedicated to enhancing the object and entity systems, providing more flexibility and power for creating complex and interactive game worlds. Through these efforts, Luanti strives to be a leading choice for voxel game development, offering a robust and versatile platform for creative expression.
|
||||||
and contributors (see source file comments and the version control log)
|
|
||||||
|
|
||||||
Table of Contents
|
## Architecture
|
||||||
------------------
|
|
||||||
|
|
||||||
1. [Further Documentation](#further-documentation)
|
Luanti employs a client-server architecture, which allows for both single-player and multiplayer experiences. In a multiplayer setup, the server hosts the game world, manages game logic, and synchronizes player actions. Clients connect to the server to interact with the game world and other players. Even in single-player mode, Luanti runs an internal server that the client connects to, ensuring a consistent experience across both modes.
|
||||||
2. [Default Controls](#default-controls)
|
|
||||||
3. [Paths](#paths)
|
|
||||||
4. [Configuration File](#configuration-file)
|
|
||||||
5. [Command-line Options](#command-line-options)
|
|
||||||
6. [Compiling](#compiling)
|
|
||||||
7. [Docker](#docker)
|
|
||||||
8. [Version Scheme](#version-scheme)
|
|
||||||
|
|
||||||
|
The engine's core is built in C++, providing high performance and stability for demanding tasks such as world generation, physics simulation, and network communication. This C++ core forms the backbone of Luanti, handling the low-level operations and ensuring efficient resource management.
|
||||||
|
|
||||||
Further documentation
|
Complementing the C++ core is a powerful Lua scripting API. This API allows developers to extend and customize the game in numerous ways, from creating new game mechanics and items to designing complex quests and interactive NPCs. The Lua API provides a flexible and accessible way to modify the game without needing to delve into the C++ codebase, fostering a vibrant modding community.
|
||||||
----------------------
|
|
||||||
- Website: https://www.luanti.org/
|
|
||||||
- Wiki: https://wiki.luanti.org/
|
|
||||||
- Forum: https://forum.luanti.org/
|
|
||||||
- GitHub: https://github.com/luanti-org/luanti/
|
|
||||||
- [Developer documentation](doc/developing/)
|
|
||||||
- [doc/](doc/) directory of source distribution
|
|
||||||
|
|
||||||
Default controls
|
For graphics rendering, Luanti integrates the Irrlicht Engine, a versatile open-source 3D graphics engine. Irrlicht handles the rendering of the game world, including terrain, objects, and visual effects. This integration allows Luanti to leverage Irrlicht's features for creating visually rich and engaging environments. Luanti aims to modernize and improve upon the existing Irrlicht integration to unlock more advanced graphical capabilities.
|
||||||
----------------
|
|
||||||
All controls are re-bindable using settings.
|
|
||||||
Some can be changed in the key config dialog in the settings tab.
|
|
||||||
|
|
||||||
| Button | Action |
|
## Codebase Structure
|
||||||
|-------------------------------|----------------------------------------------------------------|
|
|
||||||
| Move mouse | Look around |
|
|
||||||
| W, A, S, D | Move |
|
|
||||||
| Space | Jump/move up |
|
|
||||||
| Shift | Sneak/move down |
|
|
||||||
| Q | Drop itemstack |
|
|
||||||
| Shift + Q | Drop single item |
|
|
||||||
| Left mouse button | Dig/punch/use |
|
|
||||||
| Right mouse button | Place/use |
|
|
||||||
| Shift + right mouse button | Build (without using) |
|
|
||||||
| I | Inventory menu |
|
|
||||||
| Mouse wheel | Select item |
|
|
||||||
| 0-9 | Select item |
|
|
||||||
| Z | Zoom (needs zoom privilege) |
|
|
||||||
| T | Chat |
|
|
||||||
| / | Command |
|
|
||||||
| Esc | Pause menu/abort/exit (pauses only singleplayer game) |
|
|
||||||
| Shift + Esc | Exit directly to main menu from anywhere, bypassing pause menu |
|
|
||||||
| + | Increase view range |
|
|
||||||
| - | Decrease view range |
|
|
||||||
| K | Enable/disable fly mode (needs fly privilege) |
|
|
||||||
| J | Enable/disable fast mode (needs fast privilege) |
|
|
||||||
| H | Enable/disable noclip mode (needs noclip privilege) |
|
|
||||||
| E | Aux1 (Move fast in fast mode. Games may add special features) |
|
|
||||||
| C | Cycle through camera modes |
|
|
||||||
| V | Cycle through minimap modes |
|
|
||||||
| Shift + V | Change minimap orientation |
|
|
||||||
| F1 | Hide/show HUD |
|
|
||||||
| F2 | Hide/show chat |
|
|
||||||
| F3 | Disable/enable fog |
|
|
||||||
| F4 | Disable/enable camera update (Mapblocks are not updated anymore when disabled, disabled in release builds) |
|
|
||||||
| F5 | Cycle through debug information screens |
|
|
||||||
| F6 | Cycle through profiler info screens |
|
|
||||||
| F10 | Show/hide console |
|
|
||||||
| F12 | Take screenshot |
|
|
||||||
|
|
||||||
Paths
|
The Luanti codebase is organized into several key directories:
|
||||||
-----
|
|
||||||
Locations:
|
|
||||||
|
|
||||||
* `bin` - Compiled binaries
|
- **`src`**: This directory contains the core C++ engine code, including systems for physics, networking, world management, and more. It forms the heart of Luanti's functionality.
|
||||||
* `share` - Distributed read-only data
|
- **`builtin`**: Here you'll find Lua code that implements built-in game features, such as basic player interactions, inventory management, and the main menu interface.
|
||||||
* `user` - User-created modifiable data
|
- **`games`**: This directory is intended for game-specific code and assets. Different games built on Luanti can have their own subdirectories here, allowing for modular game development.
|
||||||
|
- **`mods`**: Mod code and assets reside in this directory. Luanti's modding API allows users to create and install mods that extend or modify the game's behavior and content.
|
||||||
|
- **`doc`**: This directory houses documentation files, including guides, API references, and tutorials to help developers and users understand and work with Luanti.
|
||||||
|
- **`lib`**: Third-party libraries used by Luanti are stored here. These libraries provide additional functionality, such as audio processing or specific data format support.
|
||||||
|
- **`irr`**: This directory contains the source code for the Irrlicht Engine, which Luanti uses for 3D graphics rendering.
|
||||||
|
- **`android`**: Android-specific build files and code are located here, enabling the compilation and deployment of Luanti on Android devices.
|
||||||
|
- **`po`**: Translation files for internationalization and localization are stored in this directory. These files allow Luanti to be translated into multiple languages.
|
||||||
|
- **`textures`**: Default textures used by the engine and built-in game features can be found here.
|
||||||
|
- **`client`**: This directory contains client-specific assets, such as shaders used for rendering visual effects, and other resources unique to the client application.
|
||||||
|
- **`fonts`**: Font files used for displaying text in the game and UI are stored in this directory.
|
||||||
|
- **`misc`**: A collection of miscellaneous utility scripts and files that support development, building, or other aspects of the Luanti project.
|
||||||
|
- **`cmake`**: This directory contains CMake build scripts, which are used to configure and build the Luanti project on various platforms.
|
||||||
|
|
||||||
Where each location is on each platform:
|
## Building and Installation
|
||||||
|
|
||||||
* Windows .zip / RUN_IN_PLACE source:
|
Building Luanti from source is managed using CMake, a cross-platform build system generator. The general process involves generating build files specific to your platform and compiler (e.g., Makefiles for Linux, Visual Studio projects for Windows), and then compiling the source code.
|
||||||
* `bin` = `bin`
|
|
||||||
* `share` = `.`
|
|
||||||
* `user` = `.`
|
|
||||||
* Windows installed:
|
|
||||||
* `bin` = `C:\Program Files\Minetest\bin (Depends on the install location)`
|
|
||||||
* `share` = `C:\Program Files\Minetest (Depends on the install location)`
|
|
||||||
* `user` = `%APPDATA%\Minetest` or `%MINETEST_USER_PATH%`
|
|
||||||
* Linux installed:
|
|
||||||
* `bin` = `/usr/bin`
|
|
||||||
* `share` = `/usr/share/minetest`
|
|
||||||
* `user` = `~/.minetest` or `$MINETEST_USER_PATH`
|
|
||||||
* macOS:
|
|
||||||
* `bin` = `Contents/MacOS`
|
|
||||||
* `share` = `Contents/Resources`
|
|
||||||
* `user` = `Contents/User` or `~/Library/Application Support/minetest` or `$MINETEST_USER_PATH`
|
|
||||||
|
|
||||||
Worlds can be found as separate folders in: `user/worlds/`
|
Key dependencies required to build Luanti include:
|
||||||
|
- A C++ compiler (e.g., GCC, Clang, MSVC)
|
||||||
|
- CMake (version 3.10 or higher is recommended)
|
||||||
|
- SDL2 (Simple DirectMedia Layer) for windowing and input
|
||||||
|
- SQLite3 for database storage
|
||||||
|
- LuaJIT for the Lua scripting backend
|
||||||
|
- Other libraries like zlib, libcurl, and OpenAL are also required.
|
||||||
|
|
||||||
Configuration file
|
After successfully compiling, you can typically run Luanti from the build directory or install it to a system-wide location. For detailed, platform-specific compilation instructions (Linux, macOS, Windows), please refer to the guides located in the `doc/compiling/` directory. These guides provide step-by-step instructions for installing dependencies and building Luanti.
|
||||||
------------------
|
|
||||||
- Default location:
|
|
||||||
`user/minetest.conf`
|
|
||||||
- This file is created by closing Luanti for the first time.
|
|
||||||
- A specific file can be specified on the command line:
|
|
||||||
`--config <path-to-file>`
|
|
||||||
- A run-in-place build will look for the configuration file in
|
|
||||||
`location_of_exe/../minetest.conf` and also `location_of_exe/../../minetest.conf`
|
|
||||||
|
|
||||||
Command-line options
|
## Network Protocol
|
||||||
--------------------
|
|
||||||
- Use `--help`
|
|
||||||
|
|
||||||
Compiling
|
Luanti utilizes a custom network protocol built on top of UDP (User Datagram Protocol) for communication between the client and server. This protocol is designed to be relatively lightweight and efficient for real-time game interactions. Integers in the protocol are generally transmitted in big-endian format.
|
||||||
---------
|
|
||||||
|
|
||||||
- [Compiling - common information](doc/compiling/README.md)
|
### Initialization Handshake
|
||||||
- [Compiling on GNU/Linux](doc/compiling/linux.md)
|
|
||||||
- [Compiling on Windows](doc/compiling/windows.md)
|
|
||||||
- [Compiling on MacOS](doc/compiling/macos.md)
|
|
||||||
|
|
||||||
Docker
|
The connection process begins with an initialization handshake:
|
||||||
------
|
|
||||||
|
|
||||||
- [Developing minetestserver with Docker](doc/developing/docker.md)
|
1. **Client to Server (Initial Packet)**: The client sends a packet to the server. This packet includes a protocol ID (`0x4f457403`), an initial sender peer ID (`PEER_ID_INEXISTENT = 0`), a channel number (usually 0), and a reliable packet header with a sequence number (`SEQNUM_INITIAL = 65500`) and an original packet type (`TYPE_ORIGINAL = 1`). This packet essentially signals the client's intent to connect.
|
||||||
- [Running a server with Docker](doc/docker_server.md)
|
|
||||||
|
|
||||||
Version scheme
|
2. **Server to Client (Peer ID Assignment)**: The server responds with a similar packet structure. This response contains a control message (`TYPE_CONTROL = 0`) with a control type `CONTROLTYPE_SET_PEER_ID = 1`. The payload of this message includes the `peer_id_new` that the server assigns to the client for the duration of the session.
|
||||||
--------------
|
|
||||||
We use `major.minor.patch` since 5.0.0-dev. Prior to that we used `0.major.minor`.
|
|
||||||
|
|
||||||
- Major is incremented when the release contains breaking changes, all other
|
3. **Disconnection**: To disconnect, a packet is sent with the assigned `sender_peer_id` and a control message of type `CONTROLTYPE_DISCO = 3`.
|
||||||
numbers are set to 0.
|
|
||||||
- Minor is incremented when the release contains new non-breaking features,
|
|
||||||
patch is set to 0.
|
|
||||||
- Patch is incremented when the release only contains bugfixes and very
|
|
||||||
minor/trivial features considered necessary.
|
|
||||||
|
|
||||||
Since 5.0.0-dev and 0.4.17-dev, the dev notation refers to the next release,
|
### Message Types (Opcodes)
|
||||||
i.e.: 5.0.0-dev is the development version leading to 5.0.0.
|
|
||||||
Prior to that we used `previous_version-dev`.
|
Communication is managed through various message types, or opcodes, each serving a specific purpose. These are defined in `src/network/networkprotocol.h`, `src/network/clientopcodes.h`, and `src/network/serveropcodes.h`.
|
||||||
|
|
||||||
|
#### Server-to-Client (ToClient) Messages:
|
||||||
|
|
||||||
|
- **`TOCLIENT_HELLO (0x02)`**: Sent after the client's initial connection request. Contains server information like serialization version, protocol version, and supported authentication methods.
|
||||||
|
- **`TOCLIENT_AUTH_ACCEPT (0x03)`**: Signals that the server has accepted the client's authentication. Includes map seed, recommended send interval, and sudo mode authentication methods.
|
||||||
|
- **`TOCLIENT_BLOCKDATA (0x20)`**: Transmits map block data to the client. Includes the block's position and the serialized map block data.
|
||||||
|
- **`TOCLIENT_ADDNODE (0x21)`**: Instructs the client to add or update a single node (block) at a specific position. Includes the position, serialized node data, and a flag to keep metadata.
|
||||||
|
- **`TOCLIENT_REMOVENODE (0x22)`**: Instructs the client to remove a node at a specific position.
|
||||||
|
- **`TOCLIENT_INVENTORY (0x27)`**: Sends the player's inventory data to the client.
|
||||||
|
- **`TOCLIENT_TIME_OF_DAY (0x29)`**: Updates the client with the current game time and time speed.
|
||||||
|
- **`TOCLIENT_CHAT_MESSAGE (0x2F)`**: Transmits chat messages from the server or other players to the client.
|
||||||
|
- **`TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD (0x31)`**: Informs the client about active objects (entities) being removed or added to the game world.
|
||||||
|
- **`TOCLIENT_ACTIVE_OBJECT_MESSAGES (0x32)`**: Sends messages or updates related to specific active objects.
|
||||||
|
- **`TOCLIENT_HP (0x33)`**: Updates the player's health points.
|
||||||
|
- **`TOCLIENT_MOVE_PLAYER (0x34)`**: Instructs the client to move its player character to a new position with a specific pitch and yaw.
|
||||||
|
- **`TOCLIENT_NODEDEF (0x3a)`**: Sends node definitions (how blocks look and behave) to the client.
|
||||||
|
- **`TOCLIENT_ITEMDEF (0x3d)`**: Sends item definitions to the client.
|
||||||
|
- **`TOCLIENT_PLAY_SOUND (0x3f)`**: Tells the client to play a sound effect.
|
||||||
|
- **`TOCLIENT_PRIVILEGES (0x41)`**: Sends the player's privileges (permissions) to the client.
|
||||||
|
- **`TOCLIENT_INVENTORY_FORMSPEC (0x42)`**: Sends the layout definition (formspec) for an inventory screen.
|
||||||
|
- **`TOCLIENT_SHOW_FORMSPEC (0x44)`**: Tells the client to display a specific UI form (formspec).
|
||||||
|
- **`TOCLIENT_SET_SKY (0x4f)`**: Configures the client's sky rendering parameters.
|
||||||
|
- **`TOCLIENT_MODCHANNEL_MSG (0x57)`**: Transmits messages over custom mod channels.
|
||||||
|
|
||||||
|
#### Client-to-Server (ToServer) Messages:
|
||||||
|
|
||||||
|
- **`TOSERVER_INIT (0x02)`**: The initial packet sent by the client to initiate connection, containing client version information and player name.
|
||||||
|
- **`TOSERVER_INIT2 (0x11)`**: An acknowledgment sent by the client after receiving `TOCLIENT_AUTH_ACCEPT`.
|
||||||
|
- **`TOSERVER_PLAYERPOS (0x23)`**: Periodically sends the player's current position, orientation, speed, and pressed keys to the server.
|
||||||
|
- **`TOSERVER_GOTBLOCKS (0x24)`**: Sent by the client to acknowledge receipt of map block data.
|
||||||
|
- **`TOSERVER_INVENTORY_ACTION (0x31)`**: Informs the server about actions the player takes within their inventory (e.g., moving items).
|
||||||
|
- **`TOSERVER_CHAT_MESSAGE (0x32)`**: Sends a chat message from the client to the server.
|
||||||
|
- **`TOSERVER_INTERACT (0x39)`**: Sent when the player interacts with the game world (e.g., digging, placing blocks, using items). Includes the type of action, item involved, and the target.
|
||||||
|
- **`TOSERVER_NODEMETA_FIELDS (0x3b)`**: Sent by the client to update metadata associated with a node (e.g., contents of a chest).
|
||||||
|
- **`TOSERVER_INVENTORY_FIELDS (0x3c)`**: Sent when the player interacts with fields in a formspec (e.g., typing text into an input field).
|
||||||
|
- **`TOSERVER_REQUEST_MEDIA (0x40)`**: Sent by the client to request media files (textures, sounds) from the server.
|
||||||
|
- **`TOSERVER_CLIENT_READY (0x43)`**: Sent by the client to signal it has loaded initial assets and is ready to enter the game.
|
||||||
|
- **`TOSERVER_SRP_BYTES_A (0x51)` & `TOSERVER_SRP_BYTES_M (0x52)`**: Used for Secure Remote Password (SRP) authentication.
|
||||||
|
|
||||||
|
### Data Serialization
|
||||||
|
|
||||||
|
Luanti uses a custom serialization format for transmitting data over the network. Complex data structures like map blocks, node definitions, and inventories are serialized into a byte stream before being sent and then deserialized by the receiving end.
|
||||||
|
|
||||||
|
- **Basic Data Types**: Integers are typically sent in big-endian byte order. Floating-point numbers, strings, and boolean values have their specific serialization methods.
|
||||||
|
- **Map Blocks**: `TOCLIENT_BLOCKDATA` messages contain serialized `MapBlock` objects. This includes node IDs, parameters (param1, param2), and metadata for all nodes within that block.
|
||||||
|
- **Node and Item Definitions**: `TOCLIENT_NODEDEF` and `TOCLIENT_ITEMDEF` messages transmit compressed (using zstd) serialized data for `NodeDefManager` and `ItemDefManager` respectively. These managers contain all the definitions for nodes and items available in the game.
|
||||||
|
- **Inventories**: `TOCLIENT_INVENTORY` and `TOSERVER_INVENTORY_ACTION` messages involve serialized inventory data.
|
||||||
|
- **Formspecs**: Formspecs, which define UI layouts, are transmitted as strings.
|
||||||
|
|
||||||
|
The specific serialization and deserialization logic can be found within the C++ source code, particularly in files dealing with network communication and data structures (e.g., `src/mapblock.cpp`, `src/nodedef.cpp`, `src/itemdef.cpp`, `src/inventorymanager.cpp`, and various files in `src/network/`). The `serialize.h` and `serialize.cpp` files likely contain core serialization helper functions.
|
||||||
|
|
||||||
|
For more in-depth details on specific packet structures and serialization methods, consulting the source code, especially the files mentioned in `src/network/` and the definitions in `src/network/networkprotocol.h`, is recommended. The `doc/protocol.txt` file also provides a foundational (though potentially outdated) overview of the initial connection sequence.
|
||||||
|
|
||||||
|
## Game Engine
|
||||||
|
|
||||||
|
The Luanti game engine orchestrates the entire player experience, from rendering the world to handling player input and managing game state.
|
||||||
|
|
||||||
|
### Core Game Loop (Client-Side)
|
||||||
|
|
||||||
|
The client-side game loop is primarily managed within `src/client/game.cpp`. The `Game::run()` method forms the heart of this loop. Here's a simplified breakdown of its operations:
|
||||||
|
|
||||||
|
1. **Time Management**: Calculates `dtime` (delta time), the time elapsed since the last frame. This is crucial for smooth, frame-rate-independent animations and physics. An `FpsControl` class helps manage and limit frame rates.
|
||||||
|
2. **Input Processing**: `Game::processUserInput()` is called to gather and handle all player input (keyboard, mouse, joystick, touch). This includes actions like movement, interaction, opening menus, and chat.
|
||||||
|
3. **Network Communication**: Checks the connection status (`Game::checkConnection()`) and processes incoming network packets from the server (`Game::processQueues()`).
|
||||||
|
4. **Client Event Handling**: Processes a queue of client-side events (`Game::processClientEvents()`). These events can be triggered by server messages (e.g., player damage, formspec display) or internal client actions. Each event type has a dedicated handler function (e.g., `Game::handleClientEvent_PlayerDamage()`).
|
||||||
|
5. **Player and Camera Updates**:
|
||||||
|
* `Game::updatePlayerControl()`: Translates processed input into player control actions (movement direction, speed, sneak, jump, etc.) and sends them to the server.
|
||||||
|
* `Game::updateCameraDirection()` and `Game::updateCamera()`: Update the camera's orientation and position based on player input and game events. This also involves camera smoothing and handling different camera modes (first-person, third-person).
|
||||||
|
6. **Game State Simulation (Client-Side Prediction)**:
|
||||||
|
* The client performs some local simulation, such as predicting node placement (`Game::nodePlacement()`) or digging effects (`Game::handleDigging()`). This provides immediate feedback to the player before the server confirms the action. For example, when a player places a node, the client might predict its appearance locally while the server validates and broadcasts the change.
|
||||||
|
* This is a common technique in networked games to reduce perceived latency. The server remains the authority, and if its state differs from the client's prediction, the client will eventually receive a correction (reconciliation).
|
||||||
|
7. **Sound Updates**: `Game::updateSound()` updates the sound listener's position and orientation and plays sounds triggered by game events.
|
||||||
|
8. **World Interaction**: `Game::processPlayerInteraction()` determines what the player is pointing at (node, object, or nothing) using raycasting (`ClientEnvironment::continueRaycast()`) and handles the corresponding interactions (digging, placing, punching objects).
|
||||||
|
9. **Scene Updates**:
|
||||||
|
* `Game::updateFrame()`: Updates various game elements like the sky, clouds, particles, HUD elements, and chat. It also triggers updates to the map's visual representation if needed.
|
||||||
|
* `ClientMap::updateDrawList()`: Updates the list of map blocks that need to be rendered.
|
||||||
|
10. **Rendering**: `Game::drawScene()` calls into the `RenderingEngine` to draw the 3D world and 2D GUI elements.
|
||||||
|
11. **Loop Continuation**: The loop continues as long as the rendering engine is running and no shutdown conditions are met.
|
||||||
|
|
||||||
|
### Server-Side Game State Management (Conceptual)
|
||||||
|
|
||||||
|
While `src/server/game.cpp` was not directly provided for this analysis, in a typical client-server architecture like Luanti's, the server is the ultimate authority on game state. It would be responsible for:
|
||||||
|
|
||||||
|
- **World State**: Maintaining the definitive state of all map blocks, nodes, and their metadata.
|
||||||
|
- **Player State**: Tracking each connected player's position, inventory, health, privileges, and other relevant attributes.
|
||||||
|
- **Entity (Active Object) State**: Managing the state and behavior of all active objects in the game world.
|
||||||
|
- **Game Logic**: Executing core game rules, processing player actions validated from client inputs, running AI for entities, and managing game events (e.g., day/night cycle, weather).
|
||||||
|
- **Persistence**: Saving and loading the game world and player data to/from storage (e.g., using a database like SQLite3).
|
||||||
|
- **Network Synchronization**: Broadcasting relevant game state updates to all connected clients to keep their views consistent.
|
||||||
|
|
||||||
|
### Client-Side Prediction and Server Reconciliation
|
||||||
|
|
||||||
|
Luanti appears to implement client-side prediction for actions like node digging and placement. When a player performs such an action:
|
||||||
|
|
||||||
|
1. **Client Predicts**: The client immediately simulates the result of the action (e.g., a node appears or disappears, a crack animation starts). This gives the player instant feedback.
|
||||||
|
2. **Client Sends Action to Server**: The client sends a message to the server (`TOSERVER_INTERACT`) informing it of the player's action.
|
||||||
|
3. **Server Validates and Processes**: The server receives the action, validates it against its authoritative game state and rules (e.g., does the player have the item, are they allowed to build here?). If valid, the server updates its game state.
|
||||||
|
4. **Server Broadcasts Update**: The server sends messages (e.g., `TOCLIENT_ADDNODE`, `TOCLIENT_REMOVENODE`) to all relevant clients, including the one that initiated the action, to inform them of the official state change.
|
||||||
|
5. **Client Reconciles**: If the client's predicted state differs from the server's authoritative update, the client adjusts its local state to match the server. This is server reconciliation. For example, if a predicted node placement was invalid, the server's update would cause the client to remove the predicted node.
|
||||||
|
|
||||||
|
This pattern helps make the game feel responsive despite network latency.
|
||||||
|
|
||||||
|
### Rendering Pipeline and Irrlicht Engine
|
||||||
|
|
||||||
|
Luanti uses the Irrlicht Engine (source in `irr/`) for its 3D graphics rendering. The `RenderingEngine` class (in `src/client/renderingengine.h/cpp`) acts as a bridge and manager for Irrlicht.
|
||||||
|
|
||||||
|
1. **Initialization**:
|
||||||
|
* `RenderingEngine::RenderingEngine()`: Initializes Irrlicht, creating an `IrrlichtDevice` with specified parameters (screen resolution, fullscreen, VSync, anti-aliasing). It tries to use the video driver specified in settings or falls back to other supported OpenGL drivers.
|
||||||
|
* `RenderingEngine::initialize()`: Sets up the `RenderingCore` which handles different 3D rendering modes (e.g., side-by-side for VR).
|
||||||
|
2. **Scene Setup**:
|
||||||
|
* The `Client` class and `Game` class work together to populate the Irrlicht scene graph with nodes representing the game world (terrain, objects, sky, clouds).
|
||||||
|
* `Sky` and `Clouds` classes manage the visual representation of the skybox and cloud layers.
|
||||||
|
* `ClientMap::updateDrawList()` determines which map blocks are visible and need their meshes updated or created. Map block meshes (`MapBlockMesh`) are generated and added to the scene.
|
||||||
|
3. **Shader Management**:
|
||||||
|
* Luanti uses custom shaders for various effects. The `ShaderSource` and related classes manage loading and compiling these shaders.
|
||||||
|
* `GameGlobalShaderUniformSetter` and `FogShaderUniformSetter` are examples of classes that set global shader parameters (uniforms) each frame, such as lighting conditions, fog parameters, and animation timers. These are applied by the `RenderingEngine`.
|
||||||
|
4. **Drawing Loop**:
|
||||||
|
* `RenderingEngine::draw_scene()`: This is the main drawing method called from `Game::drawScene()`.
|
||||||
|
* It typically involves:
|
||||||
|
* `driver->beginScene()`: Clears the screen and depth buffer.
|
||||||
|
* The `RenderingCore` then draws the 3D scene (world, entities, sky).
|
||||||
|
* The HUD and other 2D elements are drawn on top.
|
||||||
|
* `driver->endScene()`: Presents the rendered frame to the screen.
|
||||||
|
5. **Irrlicht Integration**:
|
||||||
|
* Luanti uses Irrlicht's scene manager (`ISceneManager`) to manage 3D objects, cameras, and lights.
|
||||||
|
* It uses Irrlicht's video driver (`IVideoDriver`) for low-level rendering operations, texture management, and material setup.
|
||||||
|
* Meshes for map blocks and entities are created as `IMesh` or `IAnimatedMesh` objects within Irrlicht.
|
||||||
|
* The GUI system (formspecs, chat, HUD) is also built using Irrlicht's GUI environment (`IGUIEnvironment`).
|
||||||
|
|
||||||
|
### Input Processing
|
||||||
|
|
||||||
|
Player input is handled by the `InputHandler` class and its derived classes (`RealInputHandler`, `RandomInputHandler`), along with the `MyEventReceiver` class (all in `src/client/inputhandler.h/cpp`).
|
||||||
|
|
||||||
|
1. **Event Reception**:
|
||||||
|
* `MyEventReceiver::OnEvent()`: This is the callback method registered with Irrlicht. It receives all system events (keyboard, mouse, joystick, touch, application events).
|
||||||
|
* It translates these low-level events into game-specific actions or updates internal state.
|
||||||
|
* For key presses, it checks against a map of `keybindings` (loaded from settings via `MyEventReceiver::reloadKeybindings()`) to determine the `GameKeyType` (e.g., `KeyType::FORWARD`, `KeyType::JUMP`).
|
||||||
|
* Mouse movements, button presses, and wheel events are also processed.
|
||||||
|
* Touch events can be handled by `TouchControls` if active.
|
||||||
|
* Joystick events are passed to a `JoystickController`.
|
||||||
|
2. **State Tracking**:
|
||||||
|
* `MyEventReceiver` maintains several bitsets to track key states:
|
||||||
|
* `keyIsDown`: True if a key is currently held down.
|
||||||
|
* `keyWasDown`: True if a key was held down in the previous frame (used for continuous actions, reset after checking).
|
||||||
|
* `keyWasPressed`: True if a key was just pressed down in this frame (cleared after processing in `Game::processPlayerInteraction()`).
|
||||||
|
* `keyWasReleased`: True if a key was just released in this frame.
|
||||||
|
3. **Input Abstraction**:
|
||||||
|
* The `InputHandler` class provides an abstraction layer. The `Game` class interacts with an `InputHandler` object.
|
||||||
|
* `RealInputHandler` uses `MyEventReceiver` to get actual player input.
|
||||||
|
* `RandomInputHandler` can generate random input for testing/debugging.
|
||||||
|
4. **Game Logic Access**:
|
||||||
|
* In `Game::processUserInput()`, the game queries the `InputHandler` for the state of various game actions (e.g., `input->isKeyDown(KeyType::FORWARD)`).
|
||||||
|
* These states are then used to update player movement, camera orientation, and trigger game interactions (digging, placing, etc.) in `Game::updatePlayerControl()` and `Game::processPlayerInteraction()`.
|
||||||
|
* Mouse position and wheel state are used for camera control and hotbar selection.
|
||||||
|
* Input focus is managed; if a menu or the chat console is active, game input is generally ignored.
|
||||||
|
|
||||||
|
This system allows for configurable keybindings and supports multiple input devices, translating raw hardware events into meaningful game actions.
|
||||||
|
|
||||||
|
## Lua Scripting API
|
||||||
|
|
||||||
|
Luanti provides an extensive Lua scripting API that allows developers to create mods and customize nearly every aspect of the game. This API is server-side, meaning mods run on the server, and the results (like new node types or game logic changes) are communicated to clients.
|
||||||
|
|
||||||
|
**Note**: The global Lua table `minetest` is an alias for `core` for backward compatibility. New code should use `core`.
|
||||||
|
|
||||||
|
### Mod Loading and Execution
|
||||||
|
|
||||||
|
- **`init.lua`**: This is the main Lua script for a mod. When Luanti starts, it runs the `init.lua` file for each enabled mod. This script is responsible for registering all of the mod's nodes, items, entities, crafts, and callbacks.
|
||||||
|
- **Mod Load Paths**: Mods are loaded from specific directories:
|
||||||
|
- `games/<gameid>/mods/`
|
||||||
|
- `mods/` (user's mod directory)
|
||||||
|
- `worlds/<worldname>/worldmods/` (world-specific mods)
|
||||||
|
The engine searches these paths in order.
|
||||||
|
- **`mod.conf`**: Each mod must have a `mod.conf` file. This file contains metadata about the mod:
|
||||||
|
- `name`: The unique name of the mod (should match the folder name).
|
||||||
|
- `title`: A human-readable title for display purposes.
|
||||||
|
- `description`: A short description of the mod.
|
||||||
|
- `depends`: A comma-separated list of mod names that this mod depends on. These mods will be loaded before this one.
|
||||||
|
- `optional_depends`: Similar to `depends`, but no error is raised if the mod is missing.
|
||||||
|
- `author`: The author's name or ContentDB username.
|
||||||
|
- `textdomain`: The text domain used for localization (defaults to `name`).
|
||||||
|
|
||||||
|
Example `mod.conf`:
|
||||||
|
```
|
||||||
|
name = my_cool_mod
|
||||||
|
title = My Cool Mod
|
||||||
|
description = Adds cool things to the game.
|
||||||
|
depends = default, another_mod
|
||||||
|
```
|
||||||
|
- **Mod Directory Structure**: A typical mod directory includes:
|
||||||
|
- `init.lua` (main script)
|
||||||
|
- `mod.conf` (metadata)
|
||||||
|
- `textures/`: For image files (.png, .jpg).
|
||||||
|
- `sounds/`: For sound files (.ogg).
|
||||||
|
- `models/`: For 3D models (.x, .b3d, .obj, .gltf, .glb).
|
||||||
|
- `locale/`: For translation files (.tr, .po).
|
||||||
|
- `settingtypes.txt`: To define mod-specific settings accessible in the main menu.
|
||||||
|
- `screenshot.png`: A preview image for the mod manager.
|
||||||
|
|
||||||
|
### Core Lua API (`core.*`)
|
||||||
|
|
||||||
|
The `core` namespace provides a vast array of functions to interact with the game engine.
|
||||||
|
|
||||||
|
#### Node and Item Registration:
|
||||||
|
|
||||||
|
- `core.register_node(name, nodedef)`: Registers a new type of node (block). `nodedef` is a table defining its properties (textures, drawtype, walkability, light emission, sounds, groups, on_construct/on_destruct callbacks, etc.).
|
||||||
|
```lua
|
||||||
|
core.register_node("my_mod:magic_block", {
|
||||||
|
description = "Magic Block",
|
||||||
|
tiles = {"my_mod_magic_block.png"},
|
||||||
|
groups = {cracky = 3, level = 2},
|
||||||
|
light_source = 5,
|
||||||
|
on_construct = function(pos)
|
||||||
|
core.log("action", "A magic block was placed at " .. core.pos_to_string(pos))
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
```
|
||||||
|
- `core.register_craftitem(name, itemdef)`: Registers a basic item that is not a node or a tool.
|
||||||
|
- `core.register_tool(name, tooldef)`: Registers a tool with properties like durability (`uses`) and digging capabilities.
|
||||||
|
- `core.override_item(name, redefinition)`: Modifies the definition of an existing registered item or node.
|
||||||
|
- `core.register_alias(alias, original_name)`: Creates an alias for an existing item name.
|
||||||
|
|
||||||
|
#### Crafting:
|
||||||
|
|
||||||
|
- `core.register_craft(recipedef)`: Registers a crafting recipe.
|
||||||
|
- **Shaped**: `recipe` is a table of rows, e.g., `{{"wood", "wood"}, {"wood", "wood"}}`. `output` is the item string.
|
||||||
|
- **Shapeless**: `type = "shapeless"`, `recipe` is a list of input items.
|
||||||
|
- **Cooking**: `type = "cooking"`, `recipe` is the input item, `cooktime` is the time in seconds.
|
||||||
|
- **Fuel**: `type = "fuel"`, `recipe` is the fuel item, `burntime` is the duration.
|
||||||
|
```lua
|
||||||
|
core.register_craft({
|
||||||
|
output = "my_mod:magic_ingot 4",
|
||||||
|
recipe = {
|
||||||
|
{"my_mod:magic_block", "default:diamondblock", "my_mod:magic_block"},
|
||||||
|
{"default:diamondblock", "default:mese", "default:diamondblock"},
|
||||||
|
{"my_mod:magic_block", "default:diamondblock", "my_mod:magic_block"},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Interacting with the World:
|
||||||
|
|
||||||
|
- `core.set_node(pos, node)`: Sets a node at a specific position. `node` is a table like `{name="default:stone", param2=5}`.
|
||||||
|
- `core.get_node(pos)`: Returns the node table at `pos`.
|
||||||
|
- `core.add_entity(pos, name, [staticdata])`: Spawns a Lua-defined entity.
|
||||||
|
- `core.add_item(pos, itemstring_or_stack)`: Spawns a dropped item entity.
|
||||||
|
- `core.get_meta(pos)`: Returns a `NodeMetaRef` for storing/retrieving metadata associated with a node (e.g., chest inventory).
|
||||||
|
- `core.get_node_timer(pos)`: Returns a `NodeTimerRef` for per-node timers.
|
||||||
|
|
||||||
|
#### Player Interaction:
|
||||||
|
|
||||||
|
- `core.get_player_by_name(name)`: Returns an `ObjectRef` for the named player.
|
||||||
|
- `core.chat_send_player(name, message)`: Sends a chat message to a specific player.
|
||||||
|
- `core.chat_send_all(message)`: Sends a chat message to all players.
|
||||||
|
- `core.show_formspec(playername, formname, formspec_string)`: Shows a custom UI form to a player.
|
||||||
|
- `core.get_player_privs(name)` and `core.set_player_privs(name, privs_table)`: Manage player privileges.
|
||||||
|
|
||||||
|
#### Callbacks and Event Handling:
|
||||||
|
|
||||||
|
Luanti uses a callback system for many game events. Mods can register functions to be called when these events occur.
|
||||||
|
- `core.register_on_joinplayer(function(player_ref, last_login_timestamp))`
|
||||||
|
- `core.register_on_leaveplayer(function(player_ref, timed_out))`
|
||||||
|
- `core.register_on_placenode(function(pos, newnode, placer, oldnode, itemstack, pointed_thing))`
|
||||||
|
- `core.register_on_dignode(function(pos, oldnode, digger))`
|
||||||
|
- `core.register_on_chat_message(function(name, message))`
|
||||||
|
- `core.register_on_player_receive_fields(function(player, formname, fields))` (for formspec interaction)
|
||||||
|
- `core.register_globalstep(function(dtime))` (called every server tick)
|
||||||
|
- `core.register_abm(abm_definition)` (Active Block Modifier, for periodic actions on nodes)
|
||||||
|
- `core.register_lbm(lbm_definition)` (Loading Block Modifier, for actions when mapblocks are activated)
|
||||||
|
|
||||||
|
Example chat command:
|
||||||
|
```lua
|
||||||
|
core.register_chatcommand("mycommand", {
|
||||||
|
params = "<target_player>",
|
||||||
|
description = "Does something cool to a player.",
|
||||||
|
privs = {interact = true},
|
||||||
|
func = function(name, param)
|
||||||
|
local target_player = core.get_player_by_name(param)
|
||||||
|
if target_player then
|
||||||
|
-- Do something with target_player
|
||||||
|
return true, "Did something cool to " .. param
|
||||||
|
else
|
||||||
|
return false, "Player not found."
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### Key Lua Objects:
|
||||||
|
|
||||||
|
- **`ObjectRef`**: Represents any object in the game world (players, Lua entities, dropped items).
|
||||||
|
- `get_pos()`, `set_pos(pos)`
|
||||||
|
- `get_hp()`, `set_hp(hp)`
|
||||||
|
- `get_player_name()` (if player)
|
||||||
|
- `get_inventory()` (if player, returns `InvRef`)
|
||||||
|
- `punch(puncher, ...)`
|
||||||
|
- `remove()` (for entities)
|
||||||
|
- **`ItemStack`**: Represents a stack of items.
|
||||||
|
- `ItemStack(itemstring_or_table)` constructor.
|
||||||
|
- `get_name()`, `set_name(name)`
|
||||||
|
- `get_count()`, `set_count(count)`
|
||||||
|
- `get_wear()`, `set_wear(wear)` (for tools)
|
||||||
|
- `get_meta()`: Returns an `ItemStackMetaRef` for item-specific metadata.
|
||||||
|
- `to_string()`, `to_table()`
|
||||||
|
- `is_empty()`, `clear()`
|
||||||
|
- **`InvRef`**: Represents an inventory (player's, node's, or detached).
|
||||||
|
- `get_size(listname)`, `set_size(listname, size)`
|
||||||
|
- `get_stack(listname, index)`, `set_stack(listname, index, itemstack)`
|
||||||
|
- `get_list(listname)`, `set_list(listname, itemstack_list)`
|
||||||
|
- `add_item(listname, itemstack)`, `remove_item(listname, itemstack)`
|
||||||
|
- `contains_item(listname, itemstack)`
|
||||||
|
- **`NodeMetaRef`** and **`ItemStackMetaRef`**: Provide methods to get/set key-value metadata.
|
||||||
|
- `get_string(key)`, `set_string(key, value)`
|
||||||
|
- `get_int(key)`, `set_int(key, value)`
|
||||||
|
- `get_inventory()` (for `NodeMetaRef`)
|
||||||
|
|
||||||
|
### Node Definitions (`nodedef`)
|
||||||
|
|
||||||
|
A table defining a node's properties:
|
||||||
|
```lua
|
||||||
|
{
|
||||||
|
description = "My Awesome Node",
|
||||||
|
tiles = {"my_mod_texture.png"}, -- Single texture for all sides
|
||||||
|
-- or { "top.png", "bottom.png", "side.png", "side.png", "side.png", "side.png"}
|
||||||
|
groups = {cracky=3, oddly_breakable_by_hand=1}, -- Digging properties
|
||||||
|
drop = "my_mod:awesome_item 2", -- Item(s) dropped when dug
|
||||||
|
light_source = 10, -- Emits light
|
||||||
|
walkable = true,
|
||||||
|
pointable = true,
|
||||||
|
diggable = true,
|
||||||
|
paramtype = "light", -- For light propagation
|
||||||
|
paramtype2 = "facedir", -- For rotatable nodes like chests
|
||||||
|
on_construct = function(pos) core.log("A My Awesome Node was built at " .. core.pos_to_string(pos)) end,
|
||||||
|
on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
|
||||||
|
core.chat_send_player(clicker:get_player_name(), "You right-clicked an awesome node!")
|
||||||
|
end,
|
||||||
|
-- ... and many more properties (drawtype, sounds, collisionbox, etc.)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Entity Definitions (Lua Entities)
|
||||||
|
|
||||||
|
A table defining an entity's behavior and properties:
|
||||||
|
```lua
|
||||||
|
{
|
||||||
|
initial_properties = {
|
||||||
|
hp_max = 20,
|
||||||
|
physical = true,
|
||||||
|
collide_with_objects = true,
|
||||||
|
collisionbox = {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5},
|
||||||
|
visual = "mesh",
|
||||||
|
mesh = "my_entity_model.x",
|
||||||
|
textures = {"my_entity_texture.png"},
|
||||||
|
is_visible = true,
|
||||||
|
},
|
||||||
|
on_activate = function(self, staticdata, dtime_s)
|
||||||
|
self.object:set_armor_groups({fleshy=100}) -- Takes full damage from fleshy group attacks
|
||||||
|
self.hp = self.object:get_properties().hp_max
|
||||||
|
end,
|
||||||
|
on_step = function(self, dtime, moveresult)
|
||||||
|
-- Make the entity wander around or perform other actions
|
||||||
|
end,
|
||||||
|
on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, dir, damage)
|
||||||
|
self.hp = self.hp - damage
|
||||||
|
if self.hp <= 0 then
|
||||||
|
self.object:remove()
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
get_staticdata = function(self)
|
||||||
|
return core.serialize({hp = self.hp}) -- Save HP when unloaded
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This overview covers the fundamental aspects of Luanti's Lua API. For detailed information on specific functions, objects, and definition table fields, refer to `doc/lua_api.md`. The API is extensive and allows for deep customization of the game.The subtask is to write the Lua Scripting API section of the Luanti documentation. I have already written this section and appended it to the README.md file, covering the API overview, mod loading, main functions/objects, and examples. I have referred to `doc/lua_api.md` for this information. I will now submit the subtask report.
|
||||||
|
|
||||||
|
## Data Flow and Storage
|
||||||
|
|
||||||
|
Luanti manages various types of data, including the game world itself, player information, and data specific to mods. This data flows between the client and server and is persisted using several storage mechanisms.
|
||||||
|
|
||||||
|
### Data Flow Overview
|
||||||
|
|
||||||
|
1. **Client Requests & Actions**: Players interact with the game world through the client. Actions like moving, digging, placing nodes, or interacting with UI elements generate events. These events are translated into network messages (opcodes) and sent to the server (e.g., `TOSERVER_PLAYERPOS`, `TOSERVER_INTERACT`).
|
||||||
|
2. **Server Processing**: The server receives these messages and processes them. This involves:
|
||||||
|
* Validating the action against game rules and player privileges.
|
||||||
|
* Updating the authoritative game state (e.g., changing a node in a map block, modifying a player's inventory).
|
||||||
|
* Executing relevant Lua callbacks (e.g., `on_dignode`, `on_place` in node definitions, ABM/LBM actions).
|
||||||
|
3. **Data Retrieval/Storage**: When processing actions or during regular game operations (like loading new areas), the server interacts with its storage backend:
|
||||||
|
* **World Data**: Map blocks are loaded from the database as players move around. Modified map blocks are queued and eventually saved back to the database.
|
||||||
|
* **Player Data**: Player attributes (position, inventory, HP, etc.) are loaded when a player joins and saved periodically or when they leave.
|
||||||
|
* **Authentication Data**: Player credentials and privileges are checked against the auth database during login.
|
||||||
|
* **Mod Data**: Mods can store and retrieve their own persistent data using a dedicated storage API.
|
||||||
|
4. **Server Updates to Clients**: After processing actions and updating its state, the server sends messages back to clients to inform them of changes:
|
||||||
|
* `TOCLIENT_BLOCKDATA`, `TOCLIENT_ADDNODE`, `TOCLIENT_REMOVENODE` update the client's view of the world.
|
||||||
|
* `TOCLIENT_INVENTORY` updates the player's inventory display.
|
||||||
|
* Other messages synchronize player stats, entity states, chat messages, etc.
|
||||||
|
5. **Client Updates**: The client receives these messages and updates its local representation of the game world and UI accordingly.
|
||||||
|
|
||||||
|
### Storage Mechanisms
|
||||||
|
|
||||||
|
Luanti supports several backends for storing game data, configurable in `world.mt`:
|
||||||
|
|
||||||
|
- **SQLite3 (`backend = sqlite3`)**: This is the default backend for map data, player data, and mod storage. It stores data in a single `map.sqlite` file within the world directory.
|
||||||
|
- **LevelDB (`backend = leveldb`)**: An alternative key-value store that can be used for map data.
|
||||||
|
- **PostgreSQL (`backend = postgresql`)**: Allows using a PostgreSQL server for map, player, auth, and mod storage, suitable for larger servers. Connection details are specified in `world.mt` (e.g., `pgsql_connection`).
|
||||||
|
- **Redis (`backend = redis`)**: Another key-value store option, typically used for its speed, often for map data. Connection details are specified in `world.mt`.
|
||||||
|
- **Raw Files**:
|
||||||
|
- `auth.txt`: Default storage for authentication data (player names, password hashes, privileges) if `auth_backend = files`.
|
||||||
|
- Player files (e.g., `worlds/<worldname>/players/<playername>`): Default storage for individual player data if `player_backend = files`.
|
||||||
|
- `world.mt`: Stores world-specific metadata and settings.
|
||||||
|
- `map_meta.txt`: Stores map-specific metadata like the map seed.
|
||||||
|
- `env_meta.txt`: Stores environment metadata like game time.
|
||||||
|
- `ipban.txt`: Stores banned IP addresses.
|
||||||
|
|
||||||
|
### Database Interfaces (`src/database/database.h`)
|
||||||
|
|
||||||
|
The C++ code defines several abstract database interfaces, allowing different backend implementations:
|
||||||
|
|
||||||
|
- **`Database`**: A base class with virtual methods like `beginSave()` and `endSave()`, indicating transaction-like operations.
|
||||||
|
- **`MapDatabase : public Database`**: Interface for storing and retrieving map blocks.
|
||||||
|
- `saveBlock(pos, data)`: Saves the serialized data for the map block at `pos`.
|
||||||
|
- `loadBlock(pos, &block_data_string)`: Loads the serialized data for the map block at `pos`.
|
||||||
|
- `deleteBlock(pos)`: Deletes the map block at `pos`.
|
||||||
|
- `listAllLoadableBlocks(&block_positions_vector)`: Fills a vector with the positions of all saved map blocks.
|
||||||
|
- **`PlayerDatabase`**: Interface for player-specific data.
|
||||||
|
- `savePlayer(player_object)`: Saves the state of the player.
|
||||||
|
- `loadPlayer(player_object, sao_object)`: Loads the player's state.
|
||||||
|
- `removePlayer(name)`: Deletes data for the specified player.
|
||||||
|
- **`AuthDatabase`**: Interface for authentication data.
|
||||||
|
- `getAuth(name, &auth_entry)`: Retrieves the `AuthEntry` (password, privileges, last login) for a player.
|
||||||
|
- `saveAuth(auth_entry)`: Saves an `AuthEntry`.
|
||||||
|
- `createAuth(auth_entry)`: Creates a new authentication entry.
|
||||||
|
- **`ModStorageDatabase : public Database`**: Interface for mods to store their persistent data.
|
||||||
|
- `getModEntry(modname, key, &value_string)`: Retrieves a value for a given mod and key.
|
||||||
|
- `setModEntry(modname, key, value_string_view)`: Stores a value for a given mod and key.
|
||||||
|
- `removeModEntry(modname, key)`: Removes a specific entry.
|
||||||
|
|
||||||
|
### World Data Organization: MapBlocks
|
||||||
|
|
||||||
|
The game world in Luanti is divided into `MapBlock`s, which are cubes of 16x16x16 nodes. This is the fundamental unit for world storage, network transfer, and rendering. The `MapBlock` class (defined in `src/mapblock.h`) encapsulates the data for one such block:
|
||||||
|
|
||||||
|
- **Node Data (`data` array)**: A flat array of `MapNode` objects, storing the type (`content_id`), `param1` (often light), and `param2` (e.g., rotation, level) for each of the 4096 nodes within the block.
|
||||||
|
- **Position (`m_pos`)**: The 3D coordinates of the map block itself in the world.
|
||||||
|
- **Flags**: Various boolean flags indicating the block's state:
|
||||||
|
- `m_generated`: True if the block has been generated by the map generator.
|
||||||
|
- `is_underground`: True if the block is considered to be underground for lighting purposes.
|
||||||
|
- `m_lighting_complete`: A bitmask indicating if light propagation from each of the 6 directions (for day and night separately) is complete.
|
||||||
|
- `m_orphan`: True if the block has been marked for deletion.
|
||||||
|
- **Metadata (`m_node_metadata`)**: A `NodeMetadataList` storing metadata for nodes within this block that require it (e.g., chest inventories, sign text).
|
||||||
|
- **Static Objects (`m_static_objects`)**: A `StaticObjectList` for persistent objects within this block (though most dynamic objects/entities are managed differently).
|
||||||
|
- **Node Timers (`m_node_timers`)**: A `NodeTimerList` for per-node timers.
|
||||||
|
- **Timestamp (`m_timestamp`)**: Records the game time when the block was last modified or saved. Used for LBMs and other time-sensitive operations.
|
||||||
|
- **Modification State (`m_modified`, `m_modified_reason`)**: Tracks if the block has been changed and needs saving.
|
||||||
|
|
||||||
|
### World Format on Disk (`doc/world_format.md`)
|
||||||
|
|
||||||
|
A Luanti world is stored as a directory containing several key files and subdirectories:
|
||||||
|
|
||||||
|
- **`world.mt`**: A text file storing world-specific settings, such as the game ID (`gameid`), enabled/disabled features (e.g., `enable_damage`), backend choices (`backend`, `player_backend`, `auth_backend`, `mod_storage_backend`), and a list of explicitly enabled/disabled mods (`load_mod_<modname> = true/false`).
|
||||||
|
- **Map Data Storage**:
|
||||||
|
- **`map.sqlite`**: If using the SQLite3 backend (default), this file contains the `blocks` table where serialized map block data is stored. Before version 5.12.0, blocks were keyed by a single integer `pos` derived from their 3D coordinates. From 5.12.0 onwards, the schema uses `x, y, z` integer primary keys.
|
||||||
|
- Other backends (LevelDB, PostgreSQL, Redis) will have their own storage structures, typically configured via `world.mt`.
|
||||||
|
- **Player Data Storage**:
|
||||||
|
- **`players/` directory**: If using the `files` backend for player data, each player's data is stored in a separate file named after the player (e.g., `players/singleplayer`). These files contain player attributes like HP, position, yaw, pitch, and their inventory lists.
|
||||||
|
- If using a database backend like SQLite3 for player data, this information is stored within the main database file (e.g., `map.sqlite`).
|
||||||
|
- **Authentication Data Storage**:
|
||||||
|
- **`auth.txt`**: If using the `files` backend for authentication (`auth_backend = files`), this text file stores player credentials. Each line typically contains `name:password_hash:privilege1,privilege2`. Password hashes can be legacy SHA1-based or newer SRP-based.
|
||||||
|
- **`auth.sqlite`**: If `auth_backend = sqlite3`, this data is stored in an `auth` table (name, password hash, last login) and a `user_privileges` table within `map.sqlite` (or a separate auth DB if configured).
|
||||||
|
- **Mod Storage**: If using SQLite3, mod-specific persistent data (saved via `core.get_mod_storage()`) is also stored in `map.sqlite`. Other backends will store this data accordingly.
|
||||||
|
- **`map_meta.txt`**: Contains global map metadata, most importantly the `seed` used for map generation.
|
||||||
|
- **`env_meta.txt`**: Stores dynamic environment information like `game_time` (total time elapsed) and `time_of_day`.
|
||||||
|
- **`ipban.txt`**: A list of banned IP addresses.
|
||||||
|
|
||||||
|
### MapBlock Serialization
|
||||||
|
|
||||||
|
When a `MapBlock` is saved to disk or sent over the network, it is serialized into a binary format. `doc/world_format.md` details this format:
|
||||||
|
|
||||||
|
- **Version Byte**: Indicates the serialization format version.
|
||||||
|
- **Flags Byte**: Contains boolean flags like `is_underground`, `day_night_differs`, `generated`.
|
||||||
|
- **Lighting Complete (`u16`)**: Bitmask for per-direction lighting status.
|
||||||
|
- **Timestamp (`u32`)**: Block's last modification/save time (Version 29+).
|
||||||
|
- **Name-ID Mappings** (Version 29+): For mapping content IDs to node names, ensuring compatibility if node names change.
|
||||||
|
- **Content Width (`u8`)**: Specifies if node content IDs (`param0`) are 1 or 2 bytes.
|
||||||
|
- **Params Width (`u8`)**: Always 2 (for `param1` and `param2`).
|
||||||
|
- **Node Data**: The actual arrays for `param0` (content IDs), `param1` (light), and `param2` (rotation, etc.) for all 4096 nodes. This section is compressed (zlib before version 29, zstd for version 29+). For version 29+, the entire block data (excluding the initial version byte) is compressed as a single unit.
|
||||||
|
- **Node Metadata List**: Serialized list of node metadata (inventories, infotext, etc.) for nodes within the block. This is also compressed.
|
||||||
|
- **Node Timers**: Serialized data for any active node timers in the block.
|
||||||
|
- **Static Objects**: Data for any static objects within the block.
|
||||||
|
|
||||||
|
This structured approach to data management and storage allows Luanti to handle large game worlds and persist player and mod data efficiently.
|
||||||
|
|
||||||
|
## Function Invocation Flow: Player Movement
|
||||||
|
|
||||||
|
Understanding how player movement is handled involves looking at both client-side input processing and server-side state updates and synchronization.
|
||||||
|
|
||||||
|
1. **Client: Input Detection (`MyEventReceiver::OnEvent` in `src/client/inputhandler.cpp`)**
|
||||||
|
* When a player presses or releases a key (e.g., W, A, S, D, spacebar for jump), an event is generated by the underlying windowing system (via Irrlicht).
|
||||||
|
* `MyEventReceiver::OnEvent()` captures these `EET_KEY_INPUT_EVENT` events.
|
||||||
|
* It checks if the pressed key is mapped to a game action (e.g., `KEY_UP` for forward movement) using `keybindings` (loaded from settings via `MyEventReceiver::reloadKeybindings()`).
|
||||||
|
* The state of the relevant `GameKeyType` (e.g., `KeyType::FORWARD`) is updated in internal bitsets like `keyIsDown`, `keyWasPressed`, and `keyWasReleased`. Mouse and joystick inputs are handled similarly for camera and movement control.
|
||||||
|
|
||||||
|
2. **Client: Processing Input and Updating Controls (`Game::processUserInput` and `Game::updatePlayerControl` in `src/client/game.cpp`)**
|
||||||
|
* In the main client game loop (`Game::run()`), `Game::processUserInput()` is called.
|
||||||
|
* This function, among other things, calls `Game::processKeyInput()` which checks the state of various keys for actions.
|
||||||
|
* Movement-related key states (and joystick/touch inputs) are then used in `Game::updatePlayerControl()`.
|
||||||
|
* `Game::updatePlayerControl()` constructs a `PlayerControl` object. This object includes:
|
||||||
|
* Boolean flags for movement keys (forward, backward, left, right, jump, sneak).
|
||||||
|
* Current camera pitch and yaw.
|
||||||
|
* Joystick speed and direction if applicable.
|
||||||
|
* This `PlayerControl` object is then passed to `Client::setPlayerControl()`, which updates the local player's control state.
|
||||||
|
|
||||||
|
3. **Client: Sending Position and State to Server (`TOSERVER_PLAYERPOS`)**
|
||||||
|
* Periodically (controlled by `dedicated_server_step` or client frame rate), the client sends its position, speed, look direction, and currently pressed keys to the server using the `TOSERVER_PLAYERPOS` network message.
|
||||||
|
* The payload of `TOSERVER_PLAYERPOS` (defined in `src/network/networkprotocol.h`) includes:
|
||||||
|
* `v3s32 position*100`: Player's position, scaled.
|
||||||
|
* `v3s32 speed*100`: Player's speed, scaled.
|
||||||
|
* `s32 pitch*100`: Player's look pitch, scaled.
|
||||||
|
* `s32 yaw*100`: Player's look yaw, scaled.
|
||||||
|
* `u32 keyPressed`: A bitmask representing the currently pressed movement-related keys.
|
||||||
|
* Other data like FOV, wanted view range.
|
||||||
|
|
||||||
|
4. **Server: Receiving and Processing Player Position (`Server::handleCommand_PlayerPos` in `src/server.cpp`)**
|
||||||
|
* The server receives the `TOSERVER_PLAYERPOS` message in its main network loop.
|
||||||
|
* The `Server::ProcessData()` method routes this message to `Server::handleCommand_PlayerPos()`.
|
||||||
|
* Inside `handleCommand_PlayerPos()` (which calls the helper `Server::process_PlayerPos()`):
|
||||||
|
* The server deserializes the position, speed, pitch, yaw, and key presses from the packet.
|
||||||
|
* It retrieves the `RemotePlayer` object associated with the client's `peer_id`.
|
||||||
|
* It updates the `PlayerSAO` (Server Active Object for the player) with the new information:
|
||||||
|
* `PlayerSAO::setPosition()`
|
||||||
|
* `PlayerSAO::setPlayerYaw()`, `PlayerSAO::setPlayerPitch()`
|
||||||
|
* `PlayerSAO::setKeys()` (updates the pressed key states)
|
||||||
|
* The server then performs physics calculations and collision detection based on the new input and player state in `ServerActiveObject::step()`, which is called during the server's main simulation step (`ServerEnvironment::step()`). This updates the player's authoritative position on the server.
|
||||||
|
* Anti-cheat checks might also be performed here to validate the received movement data (e.g., `AC_MOVEMENT` flag).
|
||||||
|
|
||||||
|
5. **Server: Updating Other Clients (`TOCLIENT_ACTIVE_OBJECT_MESSAGES`)**
|
||||||
|
* After the server has updated the player's authoritative position and state, it needs to inform other clients about this player's movement.
|
||||||
|
* This is primarily done through `TOCLIENT_ACTIVE_OBJECT_MESSAGES`. The server periodically sends updates for active objects (including players) that are within range of each client.
|
||||||
|
* These messages contain information about objects that have moved, changed animation, or had other property changes. For player movement, this typically includes:
|
||||||
|
* The object ID of the player.
|
||||||
|
* New position.
|
||||||
|
* New look direction (pitch/yaw).
|
||||||
|
* Animation state (e.g., walking, idle).
|
||||||
|
* The `Server::SendActiveObjectRemoveAdd()` and subsequent `SendActiveObjectMessages()` handle the logic of determining which objects are new to a client, which have been removed, and which need their state updated.
|
||||||
|
* Specifically for player position/orientation, `PlayerSAO::sendOutdatedData()` is called, which can queue specific position or look direction updates if they have changed significantly. These are then packaged into `AO_CMD_UPDATE_POSITION` or `AO_CMD_UPDATE_YAWPITCHROLL` messages within `TOCLIENT_ACTIVE_OBJECT_MESSAGES`.
|
||||||
|
|
||||||
|
6. **Client: Receiving and Applying Updates**
|
||||||
|
* Other clients receive `TOCLIENT_ACTIVE_OBJECT_MESSAGES`.
|
||||||
|
* The client processes these messages, updating the local representation of the player object (its position, orientation, animation) in their game world. This makes the player appear to move on other clients' screens.
|
||||||
|
|
||||||
|
7. **Lua Callbacks**
|
||||||
|
* **`on_step(self, dtime, moveresult)`**: This callback is defined in an entity's Lua registration table. It is called on the server for every active Lua entity (including players, as they are also entities) during each server simulation step (`ServerActiveObject::step()`).
|
||||||
|
* `dtime`: Time elapsed since the last step.
|
||||||
|
* `moveresult`: A table containing information about collisions that occurred during the last physics step (e.g., `touching_ground`, `collides`, details of specific collisions). This can be used by Lua mods to implement custom movement logic, reactions to collisions, or other physics-related behaviors.
|
||||||
|
* **`ObjectRef:set_pos(pos)` / `ObjectRef:move_to(pos)`**: When a Lua mod calls these functions on a player's `ObjectRef`, it directly updates the player's position on the server. The server will then synchronize this new position to all clients via `TOCLIENT_ACTIVE_OBJECT_MESSAGES` or `TOCLIENT_MOVE_PLAYER`.
|
||||||
|
* **`ObjectRef:set_physics_override(override_table)`**: Lua mods can use this to change a player's physics properties (speed, jump height, gravity). These changes affect how the server calculates movement in subsequent steps.
|
||||||
|
* While there isn't a direct "on_move" callback specifically for player position changes, the `on_step` callback for players effectively serves a similar purpose, as it's called every server tick where movement and physics are processed. Mods can check for position changes within `on_step` if needed.
|
||||||
|
|
||||||
|
This flow ensures that player actions are processed by the server, which maintains the authoritative game state, and then synchronized to all other clients, allowing for a consistent multiplayer experience. The Lua API provides hooks for mods to customize and extend entity behavior, including movement.
|
||||||
|
|
||||||
|
## Utilities and Libraries
|
||||||
|
|
||||||
|
Luanti leverages a number of third-party open-source libraries to provide its wide range of features. These are typically located in the `lib/` directory or are expected to be available on the system during compilation.
|
||||||
|
|
||||||
|
- **Irrlicht Engine**: The core 3D graphics engine used by Luanti for rendering the game world, including nodes, entities, sky, and GUI elements. Luanti uses a modified version of Irrlicht.
|
||||||
|
- **Lua/LuaJIT**: Lua is the scripting language used for game logic, mods, and parts of the engine's built-in functionality. LuaJIT (Just-In-Time compiler for Lua) is often used for enhanced performance.
|
||||||
|
- **SQLite3**: The default database backend for storing map data (nodes, metadata), player information, and mod storage. It's an embedded SQL database engine.
|
||||||
|
- **LevelDB**: An alternative key-value store database backend that can be used for map data, offering different performance characteristics compared to SQLite3.
|
||||||
|
- **PostgreSQL**: A powerful, open-source object-relational database system. Luanti can use PostgreSQL as a backend for map, player, authentication, and mod storage, often preferred for larger servers.
|
||||||
|
- **Redis**: An in-memory data structure store, usable as a database backend in Luanti. It's often employed for its speed in caching or as a message broker, though in Luanti it serves as a persistence layer.
|
||||||
|
- **zlib**: A widely used data compression library. Luanti uses it for compressing network data (older protocols) and some parts of the save game format.
|
||||||
|
- **libjpeg & libpng**: Libraries for loading and handling JPEG and PNG image formats, respectively. These are crucial for textures and other game assets.
|
||||||
|
- **OpenAL Soft**: A cross-platform, software implementation of the OpenAL 3D audio API. It's used for rendering positional audio and sound effects in the game.
|
||||||
|
- **libogg & libvorbis**: Libraries for handling the Ogg container format and Vorbis audio codec, which is the primary format for sound effects and music in Luanti.
|
||||||
|
- **GMP (GNU Multiple Precision Arithmetic Library)**: Luanti includes `mini-gmp`, a smaller subset of GMP, for handling large number arithmetic, which can be necessary in certain calculations or complex mod logic.
|
||||||
|
- **JsonCpp**: A C++ library for parsing and generating JSON (JavaScript Object Notation) data. Luanti uses this for various configuration files and potentially for data exchange with external tools or services.
|
||||||
|
- **libcurl**: A library for making HTTP/HTTPS requests. Luanti uses it for tasks like fetching the public server list, downloading mods from ContentDB, and other network communication that uses web protocols.
|
||||||
|
- **Gettext**: The GNU Gettext library is used for internationalization (i18n) and localization (l10n) of text displayed in the game, allowing Luanti to be translated into multiple languages.
|
||||||
|
- **OpenSSL (or compatible crypto library)**: Used for cryptographic functions, primarily hashing algorithms like SHA1 and SHA256, which are important for data integrity, authentication, and content identification.
|
||||||
|
- **SDL2 (Simple DirectMedia Layer 2)**: A cross-platform development library designed to provide low-level access to audio, keyboard, mouse, joystick, and graphics hardware via OpenGL and Direct3D. Luanti uses it as an alternative to Irrlicht's default device handling for window creation and input events on some platforms, often providing better compatibility or performance.
|
||||||
|
- **Freetype**: A software font engine that is designed to be small, efficient, highly customizable, and portable while capable of producing high-quality output (glyph images). Used for rendering text in Luanti.
|
||||||
|
- **zstd (Zstandard)**: A fast real-time compression algorithm, providing high compression ratios. Luanti uses zstd for compressing network data (newer protocols) and potentially for parts of the save game format, offering better compression than zlib in many cases.
|
||||||
|
- **catch2**: A multi-paradigm C++ test framework for unit-tests, TDD and BDD. Used internally for Luanti's C++ unit tests.
|
||||||
|
- **bitop (Lua BitOp)**: A C extension module for Lua which adds bitwise operations on numbers. This is used if Luanti is compiled against a standard Lua interpreter that lacks native bitwise operations. LuaJIT includes its own highly optimized bitwise operations module (`bit.*`), which is preferred when available.
|
||||||
|
- **tiniergltf**: A small, header-only C++ library for loading glTF 2.0 3D models. Luanti uses this to support the glTF format for meshes.
|
||||||
|
|
||||||
|
These libraries, combined with Luanti's own codebase, create the complete game engine and platform. Their inclusion allows Luanti to focus on its unique voxel-based gameplay and modding capabilities while relying on established solutions for common low-level tasks.
|
||||||
|
|
||||||
|
## Mods
|
||||||
|
|
||||||
|
Mods are collections of Lua scripts and assets (textures, sounds, models) that extend or change Luanti's functionality and content. They are a core part of the Luanti experience, allowing for extensive customization and game creation.
|
||||||
|
|
||||||
|
### Creating Mods
|
||||||
|
|
||||||
|
A mod is typically a directory placed in one of the mod load paths. The essential components of a mod are:
|
||||||
|
|
||||||
|
- **Directory Structure**:
|
||||||
|
- `modname/`: The root directory of the mod, where `modname` is the unique identifier for the mod.
|
||||||
|
- `init.lua`: The main Lua script that the engine executes when loading the mod. This script registers nodes, items, entities, crafts, callbacks, etc.
|
||||||
|
- `mod.conf`: A configuration file containing metadata about the mod.
|
||||||
|
- `textures/`: Directory for image files (PNG, JPG, TGA). Textures are often prefixed with `modname_`.
|
||||||
|
- `sounds/`: Directory for sound files (Ogg Vorbis). Sounds are often prefixed with `modname_`.
|
||||||
|
- `models/`: Directory for 3D models (.x, .b3d, .obj, .gltf, .glb).
|
||||||
|
- `locale/`: Directory for translation files (.tr, .po) for internationalization.
|
||||||
|
- `settingtypes.txt`: (Optional) Defines mod-specific settings that can be configured through the main menu.
|
||||||
|
- `screenshot.png`: (Optional) A 300x200 (or 3:2 aspect ratio) image shown in the mod manager.
|
||||||
|
- Other custom data folders as needed by the mod.
|
||||||
|
|
||||||
|
- **`mod.conf` File**: This file provides essential information to the engine:
|
||||||
|
- `name`: The technical name of the mod (should match the folder name).
|
||||||
|
- `title`: A human-readable title (can be translated).
|
||||||
|
- `description`: A short description of the mod (can be translated).
|
||||||
|
- `depends`: A comma-separated list of other mod names that this mod requires to function. These dependencies will be loaded before this mod.
|
||||||
|
- `optional_depends`: Similar to `depends`, but the engine won't raise an error if these mods are missing.
|
||||||
|
- `author`: The author's name.
|
||||||
|
- `textdomain`: The domain used for translating `title` and `description` (defaults to `name`).
|
||||||
|
- Example:
|
||||||
|
```
|
||||||
|
name = my_awesome_mod
|
||||||
|
title = My Awesome Mod
|
||||||
|
description = This mod adds awesome new features.
|
||||||
|
depends = default, moreores
|
||||||
|
optional_depends = fancy_trees
|
||||||
|
```
|
||||||
|
|
||||||
|
- **`init.lua` Script**: This is the entry point for your mod's Lua code. It's executed when the server starts. Here, you'll use the Luanti Lua API (primarily functions under the `core.*` namespace) to:
|
||||||
|
- Register new nodes (`core.register_node(...)`).
|
||||||
|
- Register new items and tools (`core.register_craftitem(...)`, `core.register_tool(...)`).
|
||||||
|
- Define new crafting recipes (`core.register_craft(...)`).
|
||||||
|
- Register new entities (`core.register_entity(...)`).
|
||||||
|
- Set up callbacks for game events (e.g., `core.register_on_joinplayer(...)`, `core.register_on_dignode(...)`).
|
||||||
|
- Define chat commands (`core.register_chatcommand(...)`).
|
||||||
|
- And much more.
|
||||||
|
|
||||||
|
- **Media Files**:
|
||||||
|
- Textures should be placed in the `textures/` folder.
|
||||||
|
- Sounds go into the `sounds/` folder.
|
||||||
|
- 3D models are placed in the `models/` folder.
|
||||||
|
- It's a strong convention to prefix your mod's media files with `modname_` (e.g., `my_awesome_mod_cool_texture.png`) to avoid naming conflicts with other mods.
|
||||||
|
|
||||||
|
### Installing and Enabling Mods
|
||||||
|
|
||||||
|
- **Mod Load Paths**: Luanti looks for mods in several locations:
|
||||||
|
1. `games/<gameid>/mods/`: Mods specific to a particular game.
|
||||||
|
2. `mods/`: User-specific mods, typically in `~/.luanti/mods` (Linux) or a similar user directory.
|
||||||
|
3. `worlds/<worldname>/worldmods/`: Mods specific to a particular world. These take precedence and can override mods from other locations.
|
||||||
|
|
||||||
|
- **Enabling Mods**:
|
||||||
|
- **Via Main Menu**: The easiest way is through the "Content" tab in Luanti's main menu. Here you can browse and install mods from ContentDB, and enable or disable installed mods for a specific world.
|
||||||
|
- **Via `world.mt`**: For a specific world, you can manually enable mods by editing the `world.mt` file in that world's directory. Add or modify lines like:
|
||||||
|
```
|
||||||
|
load_mod_my_awesome_mod = true
|
||||||
|
```
|
||||||
|
Setting it to `false` disables the mod for that world.
|
||||||
|
|
||||||
|
### Modding API Overview
|
||||||
|
|
||||||
|
Luanti's modding power comes from its extensive Lua API, primarily accessed through the global `core` table. Key aspects include:
|
||||||
|
|
||||||
|
- **Registration Functions**: A suite of `core.register_*` functions for adding new nodes, items, tools, entities, crafts, biomes, decorations, and more.
|
||||||
|
- **Callbacks**: Numerous `core.register_on_*` functions allow mods to hook into various game events (player joining, node placement, chat messages, etc.).
|
||||||
|
- **Object Manipulation**: The `ObjectRef` Lua object provides an interface to interact with players, entities, and dropped items (e.g., getting/setting position, health, inventory).
|
||||||
|
- **World Interaction**: Functions like `core.set_node`, `core.get_node`, `core.get_meta`, `core.add_item`, `core.add_entity` allow mods to read and modify the game world.
|
||||||
|
- **Formspecs**: A powerful system for creating custom UI forms using a string-based definition language, displayed to players via `core.show_formspec`.
|
||||||
|
- **Inventories**: `InvRef` objects allow manipulation of player, node, and detached inventories.
|
||||||
|
- **Settings API**: `core.settings` allows mods to read global engine settings, and `settingtypes.txt` allows mods to define their own configurable settings.
|
||||||
|
- **Vector API**: `vector.*` functions for 3D vector math.
|
||||||
|
- **Noise API**: For generating Perlin/Simplex-like noise used in map generation and other procedural content.
|
||||||
|
- **HTTP API**: For making HTTP requests (e.g., to web services).
|
||||||
|
- **Mod Storage**: `core.get_mod_storage()` provides a way for mods to save and load their own persistent data.
|
||||||
|
|
||||||
|
For a comprehensive guide to the Lua API, refer to the [Lua Scripting API](#lua-scripting-api) section of this document or the detailed `doc/lua_api.md` file.
|
||||||
|
|
||||||
|
### Naming Conventions
|
||||||
|
|
||||||
|
To avoid conflicts between different mods, it's crucial to follow naming conventions:
|
||||||
|
- Registered names for nodes, items, entities, etc., should generally be prefixed with the mod's name, followed by a colon (e.g., `my_awesome_mod:cool_node`, `my_awesome_mod:super_tool`).
|
||||||
|
- This convention is enforced by the mod loader for most registered items.
|
||||||
|
- The `:` prefix can also be used to explicitly override items from other mods (e.g., `:default:stone` would attempt to override the `default:stone` node), but this requires careful dependency management.
|
||||||
|
|
||||||
|
### Dependency Management
|
||||||
|
|
||||||
|
- **`mod.conf` `depends` and `optional_depends`**: This is the primary way to manage dependencies.
|
||||||
|
- `depends = mod_a, mod_b`: Ensures `mod_a` and `mod_b` are loaded before this mod. If they are missing, Luanti will report an error and may not load the current mod.
|
||||||
|
- `optional_depends = mod_c`: Luanti will try to load `mod_c` first if it's available, but won't error if it's missing. Mods can check for the presence of optional dependencies using `core.get_modpath("mod_c") ~= nil`.
|
||||||
|
- **`depends.txt` (Deprecated)**: An older way to specify dependencies. `mod.conf` is preferred.
|
||||||
|
|
||||||
|
### Modpacks
|
||||||
|
|
||||||
|
Mods can be grouped into modpacks. A modpack is a directory containing multiple individual mod directories. To define a directory as a modpack, it must contain a `modpack.conf` file with metadata similar to `mod.conf` (e.g., `name`, `title`, `description`). This helps organize related mods and allows users to enable or disable them as a single unit. An empty `modpack.txt` file can also be included for compatibility with older Luanti versions.
|
||||||
|
|
||||||
|
Mods provide the extensibility that makes Luanti a versatile voxel engine, enabling a wide range of games and experiences to be built on its platform.
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
Luanti is an open-source project, and contributions from the community are highly encouraged and welcomed. There are several ways you can contribute to the development and improvement of Luanti:
|
||||||
|
|
||||||
|
- **Code Contributions**: If you are a developer, you can contribute by writing C++ code for the engine core or Lua code for built-in features and mods. This can involve fixing bugs, implementing new features, or refactoring existing code for better performance and maintainability.
|
||||||
|
- **Reporting Issues**: If you encounter bugs, crashes, or unexpected behavior, please report them on the project's issue tracker. Provide as much detail as possible, including steps to reproduce the issue, your Luanti version, operating system, and any relevant logs (`debug.txt`).
|
||||||
|
- **Feature Requests**: If you have ideas for new features or enhancements, you can submit them as feature requests on the issue tracker. Clearly describe the proposed feature and its potential benefits.
|
||||||
|
- **Translations**: Help make Luanti accessible to a wider audience by contributing translations for the engine, games, and mods. Luanti uses Gettext for its primary translations and also supports `.tr` files for mods.
|
||||||
|
- **Documentation**: Improve the existing documentation (like this README, `lua_api.md`, and files in the `doc/` directory) or write new guides and tutorials.
|
||||||
|
- **Testing**: Help test new releases and development versions to identify bugs and provide feedback.
|
||||||
|
- **Donations**: Monetary contributions can help support the project's infrastructure and development efforts.
|
||||||
|
|
||||||
|
Before making significant code contributions, it is highly recommended to:
|
||||||
|
1. Discuss your proposed changes with the core developers, for example, by opening an issue on the project's GitHub repository.
|
||||||
|
2. Familiarize yourself with the project's coding style guidelines for C++ and Lua.
|
||||||
|
3. Review the project's roadmap and direction, outlined in `doc/direction.md`, to ensure your contributions align with the project's goals.
|
||||||
|
|
||||||
|
For detailed guidelines on how to contribute, including coding standards, commit message formats, and the pull request process, please refer to the **`.github/CONTRIBUTING.md`** file in the Luanti repository. This file provides comprehensive information for developers looking to contribute code.
|
||||||
|
|
||||||
|
The Luanti project thrives on community involvement, and every contribution, no matter how small, is valuable.
|
||||||
|
|
511
doc/DEVELOPER_MANUAL.md
Normal file
511
doc/DEVELOPER_MANUAL.md
Normal file
|
@ -0,0 +1,511 @@
|
||||||
|
# Luanti Developer Manual
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
|
||||||
|
1. [Introduction](#introduction)
|
||||||
|
2. [Architecture](#architecture)
|
||||||
|
* [Client-Server Model](#client-server-model)
|
||||||
|
* [Core Engine (C++)](#core-engine-c)
|
||||||
|
* [Lua Scripting API](#lua-scripting-api-overview)
|
||||||
|
* [Graphics Rendering (Irrlicht)](#graphics-rendering-irrlicht)
|
||||||
|
3. [Codebase Structure](#codebase-structure)
|
||||||
|
4. [Building and Installation](#building-and-installation)
|
||||||
|
* [General Process](#general-process)
|
||||||
|
* [Key Dependencies](#key-dependencies)
|
||||||
|
* [Running Luanti](#running-luanti)
|
||||||
|
5. [Network Protocol](#network-protocol)
|
||||||
|
* [Overview](#overview)
|
||||||
|
* [Initialization Handshake](#initialization-handshake)
|
||||||
|
* [Message Types (Opcodes)](#message-types-opcodes)
|
||||||
|
* [Server-to-Client (ToClient) Messages](#server-to-client-toclient-messages)
|
||||||
|
* [Client-to-Server (ToServer) Messages](#client-to-server-toserver-messages)
|
||||||
|
* [Data Serialization](#data-serialization)
|
||||||
|
6. [Game Engine](#game-engine)
|
||||||
|
* [Core Game Loop (Client-Side)](#core-game-loop-client-side)
|
||||||
|
* [Server-Side Game State Management (Conceptual)](#server-side-game-state-management-conceptual)
|
||||||
|
* [Client-Side Prediction and Server Reconciliation](#client-side-prediction-and-server-reconciliation)
|
||||||
|
* [Rendering Pipeline and Irrlicht Engine](#rendering-pipeline-and-irrlicht-engine)
|
||||||
|
* [Input Processing](#input-processing)
|
||||||
|
7. [Lua Scripting API](#lua-scripting-api)
|
||||||
|
* [Mod Loading and Execution](#mod-loading-and-execution)
|
||||||
|
* [Core Lua API (`core.*`)](#core-lua-api-core)
|
||||||
|
* [Key Lua Objects](#key-lua-objects)
|
||||||
|
* [Node Definitions (`nodedef`)](#node-definitions-nodedef)
|
||||||
|
* [Entity Definitions (Lua Entities)](#entity-definitions-lua-entities)
|
||||||
|
8. [Data Flow and Storage](#data-flow-and-storage)
|
||||||
|
* [Data Flow Overview](#data-flow-overview)
|
||||||
|
* [Storage Mechanisms](#storage-mechanisms)
|
||||||
|
* [Database Interfaces (`src/database/database.h`)](#database-interfaces-srcdatadatabaseh)
|
||||||
|
* [World Data Organization: MapBlocks](#world-data-organization-mapblocks)
|
||||||
|
* [World Format on Disk (`doc/world_format.md`)](#world-format-on-disk-docworld_formatmd)
|
||||||
|
* [MapBlock Serialization](#mapblock-serialization)
|
||||||
|
9. [Function Invocation Flow: Player Movement](#function-invocation-flow-player-movement)
|
||||||
|
10. [Utilities and Libraries](#utilities-and-libraries)
|
||||||
|
11. [Mods](#mods)
|
||||||
|
* [Creating Mods](#creating-mods)
|
||||||
|
* [Installing and Enabling Mods](#installing-and-enabling-mods)
|
||||||
|
* [Modding API Overview (Summary)](#modding-api-overview-summary)
|
||||||
|
* [Naming Conventions](#naming-conventions)
|
||||||
|
* [Dependency Management](#dependency-management)
|
||||||
|
* [Modpacks](#modpacks)
|
||||||
|
12. [Contributing](#contributing)
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
|
||||||
|
Luanti is a free open-source voxel game engine that provides users with a platform for easy modding and game creation. It is a fork of the Minetest engine, building upon its foundation to offer an enhanced experience for both players and developers. Luanti retains the core strengths of Minetest, such as its lightweight nature and extensive modding capabilities, while also aiming to address some of its limitations.
|
||||||
|
|
||||||
|
The primary goals of the Luanti project revolve around several key areas of improvement:
|
||||||
|
- Significantly enhance rendering and graphics capabilities for a more visually appealing and immersive experience.
|
||||||
|
- Focus on internal code refactoring to improve maintainability, performance, and stability, including modernizing the codebase and streamlining existing systems.
|
||||||
|
- Deliver substantial UI/UX improvements, making the engine more intuitive and user-friendly.
|
||||||
|
- Enhance the object and entity systems, providing more flexibility and power for creating complex and interactive game worlds.
|
||||||
|
|
||||||
|
Through these efforts, Luanti strives to be a leading choice for voxel game development, offering a robust and versatile platform for creative expression.
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
Luanti employs a client-server architecture, which allows for both single-player and multiplayer experiences.
|
||||||
|
|
||||||
|
[Diagram: High-level client-server architecture. Shows a central server with multiple clients connecting to it. Indicate that the server handles game logic and world state, while clients handle rendering and input.]
|
||||||
|
|
||||||
|
### Client-Server Model
|
||||||
|
|
||||||
|
In a multiplayer setup, the server hosts the game world, manages game logic, and synchronizes player actions. Clients connect to the server to interact with the game world and other players. Even in single-player mode, Luanti runs an internal server that the client connects to, ensuring a consistent experience across both modes.
|
||||||
|
|
||||||
|
### Core Engine (C++)
|
||||||
|
|
||||||
|
The engine's core is built in C++, providing high performance and stability for demanding tasks such as world generation, physics simulation, and network communication. This C++ core forms the backbone of Luanti, handling the low-level operations and ensuring efficient resource management.
|
||||||
|
|
||||||
|
### Lua Scripting API Overview
|
||||||
|
|
||||||
|
Complementing the C++ core is a powerful Lua scripting API. This API allows developers to extend and customize the game in numerous ways, from creating new game mechanics and items to designing complex quests and interactive NPCs. The Lua API provides a flexible and accessible way to modify the game without needing to delve into the C++ codebase, fostering a vibrant modding community. (See the [Lua Scripting API](#lua-scripting-api) section for more details).
|
||||||
|
|
||||||
|
### Graphics Rendering (Irrlicht)
|
||||||
|
|
||||||
|
For graphics rendering, Luanti integrates the Irrlicht Engine, a versatile open-source 3D graphics engine. Irrlicht handles the rendering of the game world, including terrain, objects, and visual effects. This integration allows Luanti to leverage Irrlicht's features for creating visually rich and engaging environments. Luanti aims to modernize and improve upon the existing Irrlicht integration to unlock more advanced graphical capabilities.
|
||||||
|
|
||||||
|
## Codebase Structure
|
||||||
|
|
||||||
|
The Luanti codebase is organized into several key directories:
|
||||||
|
|
||||||
|
- **`src/`**: Contains the core C++ engine code, including systems for physics, networking, world management, and more. It forms the heart of Luanti's functionality.
|
||||||
|
- **`builtin/`**: Houses Lua code that implements built-in game features, such as basic player interactions, inventory management, and the main menu interface.
|
||||||
|
- **`games/`**: Intended for game-specific code and assets. Different games built on Luanti can have their own subdirectories here, allowing for modular game development.
|
||||||
|
- **`mods/`**: Mod code and assets reside in this directory. Luanti's modding API allows users to create and install mods that extend or modify the game's behavior and content.
|
||||||
|
- **`doc/`**: Contains documentation files, including this manual, guides, API references, and tutorials.
|
||||||
|
- **`lib/`**: Third-party libraries used by Luanti are stored here (e.g., LuaJIT, SQLite3, zlib). These libraries provide additional functionality.
|
||||||
|
- **`irr/`**: Contains the source code for the Irrlicht Engine, which Luanti uses for 3D graphics rendering.
|
||||||
|
- **`android/`**: Android-specific build files and code, enabling the compilation and deployment of Luanti on Android devices.
|
||||||
|
- **`po/`**: Translation files (typically `.po` format) for internationalization and localization, allowing Luanti to be translated into multiple languages.
|
||||||
|
- **`textures/`**: Default textures used by the engine and built-in game features.
|
||||||
|
- **`client/`**: Contains client-specific assets, such as shaders used for rendering visual effects, and other resources unique to the client application.
|
||||||
|
- **`fonts/`**: Font files used for displaying text in the game and UI.
|
||||||
|
- **`misc/`**: A collection of miscellaneous utility scripts and files that support development, building, or other aspects of the Luanti project.
|
||||||
|
- **`cmake/`**: Contains CMake build scripts, which are used to configure and build the Luanti project on various platforms.
|
||||||
|
|
||||||
|
## Building and Installation
|
||||||
|
|
||||||
|
### General Process
|
||||||
|
|
||||||
|
Building Luanti from source is managed using CMake, a cross-platform build system generator. The general process involves:
|
||||||
|
1. Ensuring all dependencies are installed.
|
||||||
|
2. Generating build files specific to your platform and compiler (e.g., Makefiles for Linux, Visual Studio projects for Windows) using CMake.
|
||||||
|
3. Compiling the source code using the generated build files.
|
||||||
|
|
||||||
|
### Key Dependencies
|
||||||
|
|
||||||
|
- A C++ compiler (e.g., GCC, Clang, MSVC)
|
||||||
|
- CMake (version 3.10 or higher is recommended)
|
||||||
|
- SDL2 (Simple DirectMedia Layer) for windowing and input
|
||||||
|
- SQLite3 for database storage
|
||||||
|
- LuaJIT for the Lua scripting backend
|
||||||
|
- Other libraries like zlib, libcurl, and OpenAL are also required.
|
||||||
|
|
||||||
|
### Running Luanti
|
||||||
|
|
||||||
|
After successfully compiling, you can typically run Luanti from the build directory or install it to a system-wide location. For detailed, platform-specific compilation instructions (Linux, macOS, Windows), please refer to the guides located in the `doc/compiling/` directory. These guides provide step-by-step instructions for installing dependencies and building Luanti.
|
||||||
|
|
||||||
|
## Network Protocol
|
||||||
|
|
||||||
|
### Overview
|
||||||
|
|
||||||
|
Luanti utilizes a custom network protocol built on top of UDP (User Datagram Protocol) for communication between the client and server. This protocol is designed to be relatively lightweight and efficient for real-time game interactions. Integers in the protocol are generally transmitted in big-endian format.
|
||||||
|
|
||||||
|
[Diagram: Simple network data flow. Client sends player actions (TOSERVER_PLAYERPOS, TOSERVER_INTERACT) to Server. Server processes and sends world updates (TOCLIENT_BLOCKDATA, TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD) back to Client.]
|
||||||
|
|
||||||
|
### Initialization Handshake
|
||||||
|
|
||||||
|
The connection process begins with an initialization handshake:
|
||||||
|
|
||||||
|
1. **Client to Server (Initial Packet)**: The client sends a packet to the server. This packet includes a protocol ID (`0x4f457403`), an initial sender peer ID (`PEER_ID_INEXISTENT = 0`), a channel number (usually 0), and a reliable packet header with a sequence number (`SEQNUM_INITIAL = 65500`) and an original packet type (`TYPE_ORIGINAL = 1`). This packet essentially signals the client's intent to connect.
|
||||||
|
2. **Server to Client (Peer ID Assignment)**: The server responds with a similar packet structure. This response contains a control message (`TYPE_CONTROL = 0`) with a control type `CONTROLTYPE_SET_PEER_ID = 1`. The payload of this message includes the `peer_id_new` that the server assigns to the client for the duration of the session.
|
||||||
|
3. **Disconnection**: To disconnect, a packet is sent with the assigned `sender_peer_id` and a control message of type `CONTROLTYPE_DISCO = 3`.
|
||||||
|
|
||||||
|
Further details can be found in `doc/protocol.txt`.
|
||||||
|
|
||||||
|
### Message Types (Opcodes)
|
||||||
|
|
||||||
|
Communication is managed through various message types, or opcodes, each serving a specific purpose. These are defined in `src/network/networkprotocol.h`, `src/network/clientopcodes.h`, and `src/network/serveropcodes.h`.
|
||||||
|
|
||||||
|
#### Server-to-Client (ToClient) Messages:
|
||||||
|
|
||||||
|
A brief overview of some key messages sent from the server to the client:
|
||||||
|
- **`TOCLIENT_HELLO (0x02)`**: Sent after the client's initial connection request. Contains server information like serialization version, protocol version, and supported authentication methods.
|
||||||
|
- **`TOCLIENT_AUTH_ACCEPT (0x03)`**: Signals that the server has accepted the client's authentication. Includes map seed, recommended send interval, and sudo mode authentication methods.
|
||||||
|
- **`TOCLIENT_BLOCKDATA (0x20)`**: Transmits map block data (a 16x16x16 chunk of nodes) to the client. Includes the block's position and the serialized map block data.
|
||||||
|
- **`TOCLIENT_ADDNODE (0x21)`**: Instructs the client to add or update a single node (block) at a specific position. Includes the position, serialized node data, and a flag to keep metadata.
|
||||||
|
- **`TOCLIENT_REMOVENODE (0x22)`**: Instructs the client to remove a node at a specific position.
|
||||||
|
- **`TOCLIENT_INVENTORY (0x27)`**: Sends the player's inventory data to the client.
|
||||||
|
- **`TOCLIENT_TIME_OF_DAY (0x29)`**: Updates the client with the current game time and time speed.
|
||||||
|
- **`TOCLIENT_CHAT_MESSAGE (0x2F)`**: Transmits chat messages from the server or other players to the client.
|
||||||
|
- **`TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD (0x31)`**: Informs the client about active objects (entities) being removed or added to the game world.
|
||||||
|
- **`TOCLIENT_ACTIVE_OBJECT_MESSAGES (0x32)`**: Sends messages or updates related to specific active objects (e.g., position, animation).
|
||||||
|
- **`TOCLIENT_HP (0x33)`**: Updates the player's health points.
|
||||||
|
- **`TOCLIENT_MOVE_PLAYER (0x34)`**: Instructs the client to move its player character to a new position with a specific pitch and yaw (often used for corrections or initial spawn).
|
||||||
|
- **`TOCLIENT_NODEDEF (0x3a)`**: Sends node definitions (how blocks look and behave) to the client.
|
||||||
|
- **`TOCLIENT_ITEMDEF (0x3d)`**: Sends item definitions to the client.
|
||||||
|
- **`TOCLIENT_PLAY_SOUND (0x3f)`**: Tells the client to play a sound effect.
|
||||||
|
- **`TOCLIENT_PRIVILEGES (0x41)`**: Sends the player's privileges (permissions) to the client.
|
||||||
|
- **`TOCLIENT_INVENTORY_FORMSPEC (0x42)`**: Sends the layout definition (formspec) for an inventory screen.
|
||||||
|
- **`TOCLIENT_SHOW_FORMSPEC (0x44)`**: Tells the client to display a specific UI form (formspec).
|
||||||
|
- **`TOCLIENT_SET_SKY (0x4f)`**: Configures the client's sky rendering parameters.
|
||||||
|
- **`TOCLIENT_MODCHANNEL_MSG (0x57)`**: Transmits messages over custom mod channels.
|
||||||
|
|
||||||
|
#### Client-to-Server (ToServer) Messages:
|
||||||
|
|
||||||
|
A brief overview of some key messages sent from the client to the server:
|
||||||
|
- **`TOSERVER_INIT (0x02)`**: The initial packet sent by the client to initiate connection, containing client version information and player name.
|
||||||
|
- **`TOSERVER_INIT2 (0x11)`**: An acknowledgment sent by the client after receiving `TOCLIENT_AUTH_ACCEPT`, signaling readiness to receive game data.
|
||||||
|
- **`TOSERVER_PLAYERPOS (0x23)`**: Periodically sends the player's current position, orientation (pitch/yaw), speed, and pressed keys to the server. This is the primary way player movement and actions are communicated.
|
||||||
|
- **`TOSERVER_GOTBLOCKS (0x24)`**: Sent by the client to acknowledge receipt of map block data, helping the server manage data transmission.
|
||||||
|
- **`TOSERVER_INVENTORY_ACTION (0x31)`**: Informs the server about actions the player takes within their inventory (e.g., moving items between slots, dropping items).
|
||||||
|
- **`TOSERVER_CHAT_MESSAGE (0x32)`**: Sends a chat message from the client to the server for broadcasting.
|
||||||
|
- **`TOSERVER_INTERACT (0x39)`**: Sent when the player interacts with the game world (e.g., digging a node, placing a node, using an item on a node or entity). Includes the type of action, item involved, and the target.
|
||||||
|
- **`TOSERVER_NODEMETA_FIELDS (0x3b)`**: Sent by the client to update metadata associated with a node (e.g., contents of a chest, text on a sign) after interacting with a formspec.
|
||||||
|
- **`TOSERVER_INVENTORY_FIELDS (0x3c)`**: Sent when the player interacts with fields in a general formspec (not necessarily node-specific).
|
||||||
|
- **`TOSERVER_REQUEST_MEDIA (0x40)`**: Sent by the client to request media files (textures, sounds) from the server that it doesn't have in its cache.
|
||||||
|
- **`TOSERVER_CLIENT_READY (0x43)`**: Sent by the client after it has received initial definitions (nodes, items) and media, signaling it's ready to fully enter the game.
|
||||||
|
- **`TOSERVER_SRP_BYTES_A (0x51)` & `TOSERVER_SRP_BYTES_M (0x52)`**: Used for Secure Remote Password (SRP) authentication.
|
||||||
|
|
||||||
|
### Data Serialization
|
||||||
|
|
||||||
|
Luanti uses a custom serialization format for transmitting data over the network. Complex data structures like map blocks, node definitions, and inventories are serialized into a byte stream before being sent and then deserialized by the receiving end.
|
||||||
|
|
||||||
|
- **Basic Data Types**: Integers are typically sent in big-endian byte order. Floating-point numbers, strings, and boolean values have their specific serialization methods.
|
||||||
|
- **Map Blocks**: `TOCLIENT_BLOCKDATA` messages contain serialized `MapBlock` objects. This includes node IDs, parameters (`param1`, `param2`), and metadata for all nodes within that block. (See [MapBlock Serialization](#mapblock-serialization)).
|
||||||
|
- **Node and Item Definitions**: `TOCLIENT_NODEDEF` and `TOCLIENT_ITEMDEF` messages transmit compressed (using zstd) serialized data for `NodeDefManager` and `ItemDefManager` respectively. These managers contain all the definitions for nodes and items available in the game.
|
||||||
|
- **Inventories**: `TOCLIENT_INVENTORY` and `TOSERVER_INVENTORY_ACTION` messages involve serialized inventory data.
|
||||||
|
- **Formspecs**: Formspecs, which define UI layouts, are transmitted as strings.
|
||||||
|
|
||||||
|
The specific serialization and deserialization logic can be found within the C++ source code, particularly in files dealing with network communication and data structures (e.g., `src/mapblock.cpp`, `src/nodedef.cpp`, `src/itemdef.cpp`, `src/inventorymanager.cpp`, and various files in `src/network/`). The `serialize.h` and `serialize.cpp` files contain core serialization helper functions.
|
||||||
|
|
||||||
|
For more in-depth details on specific packet structures and serialization methods, consulting the source code, especially the files mentioned in `src/network/` and the definitions in `src/network/networkprotocol.h`, is recommended. The `doc/protocol.txt` file also provides a foundational (though potentially outdated) overview of the initial connection sequence.
|
||||||
|
|
||||||
|
## Game Engine
|
||||||
|
|
||||||
|
The Luanti game engine orchestrates the entire player experience, from rendering the world to handling player input and managing game state.
|
||||||
|
|
||||||
|
[Diagram: Simplified Game Engine Loop. Input -> Process Input -> Update Game State (Client Prediction/Server Reconciliation) -> Update Scene -> Render -> Output. Show client-server interaction for state updates.]
|
||||||
|
|
||||||
|
### Core Game Loop (Client-Side)
|
||||||
|
|
||||||
|
The client-side game loop is primarily managed within `src/client/game.cpp`. The `Game::run()` method forms the heart of this loop. Here's a simplified breakdown of its operations:
|
||||||
|
|
||||||
|
1. **Time Management**: Calculates `dtime` (delta time), the time elapsed since the last frame. This is crucial for smooth, frame-rate-independent animations and physics. An `FpsControl` class helps manage and limit frame rates.
|
||||||
|
2. **Input Processing**: `Game::processUserInput()` is called to gather and handle all player input (keyboard, mouse, joystick, touch). This includes actions like movement, interaction, opening menus, and chat.
|
||||||
|
3. **Network Communication**: Checks the connection status (`Game::checkConnection()`) and processes incoming network packets from the server (`Game::processQueues()`).
|
||||||
|
4. **Client Event Handling**: Processes a queue of client-side events (`Game::processClientEvents()`). These events can be triggered by server messages (e.g., player damage, formspec display) or internal client actions. Each event type has a dedicated handler function (e.g., `Game::handleClientEvent_PlayerDamage()`).
|
||||||
|
5. **Player and Camera Updates**:
|
||||||
|
* `Game::updatePlayerControl()`: Translates processed input into player control actions (movement direction, speed, sneak, jump, etc.) and sends them to the server.
|
||||||
|
* `Game::updateCameraDirection()` and `Game::updateCamera()`: Update the camera's orientation and position based on player input and game events. This also involves camera smoothing and handling different camera modes (first-person, third-person).
|
||||||
|
6. **Game State Simulation (Client-Side Prediction)**:
|
||||||
|
* The client performs some local simulation, such as predicting node placement (`Game::nodePlacement()`) or digging effects (`Game::handleDigging()`). This provides immediate feedback to the player before the server confirms the action. For example, when a player places a node, the client might predict its appearance locally while the server validates and broadcasts the change.
|
||||||
|
* This is a common technique in networked games to reduce perceived latency. The server remains the authority, and if its state differs from the client's prediction, the client will eventually receive a correction (reconciliation).
|
||||||
|
7. **Sound Updates**: `Game::updateSound()` updates the sound listener's position and orientation and plays sounds triggered by game events.
|
||||||
|
8. **World Interaction**: `Game::processPlayerInteraction()` determines what the player is pointing at (node, object, or nothing) using raycasting (`ClientEnvironment::continueRaycast()`) and handles the corresponding interactions (digging, placing, punching objects).
|
||||||
|
9. **Scene Updates**:
|
||||||
|
* `Game::updateFrame()`: Updates various game elements like the sky, clouds, particles, HUD elements, and chat. It also triggers updates to the map's visual representation if needed.
|
||||||
|
* `ClientMap::updateDrawList()`: Updates the list of map blocks that need to be rendered.
|
||||||
|
10. **Rendering**: `Game::drawScene()` calls into the `RenderingEngine` to draw the 3D world and 2D GUI elements.
|
||||||
|
11. **Loop Continuation**: The loop continues as long as the rendering engine is running and no shutdown conditions are met.
|
||||||
|
|
||||||
|
### Server-Side Game State Management (Conceptual)
|
||||||
|
|
||||||
|
While `src/server/server.cpp` handles server operations, the server is the ultimate authority on game state. It is responsible for:
|
||||||
|
- **World State**: Maintaining the definitive state of all map blocks, nodes, and their metadata.
|
||||||
|
- **Player State**: Tracking each connected player's position, inventory, health, privileges, and other relevant attributes.
|
||||||
|
- **Entity (Active Object) State**: Managing the state and behavior of all active objects in the game world.
|
||||||
|
- **Game Logic**: Executing core game rules, processing player actions validated from client inputs, running AI for entities, and managing game events (e.g., day/night cycle, weather).
|
||||||
|
- **Persistence**: Saving and loading the game world and player data to/from storage (e.g., using a database like SQLite3).
|
||||||
|
- **Network Synchronization**: Broadcasting relevant game state updates to all connected clients to keep their views consistent.
|
||||||
|
|
||||||
|
### Client-Side Prediction and Server Reconciliation
|
||||||
|
|
||||||
|
Luanti implements client-side prediction for actions like node digging and placement to make the game feel responsive despite network latency:
|
||||||
|
1. **Client Predicts**: The client immediately simulates the result of an action (e.g., a node appears).
|
||||||
|
2. **Client Sends Action to Server**: The client sends a message like `TOSERVER_INTERACT`.
|
||||||
|
3. **Server Validates and Processes**: The server validates the action and updates its authoritative game state.
|
||||||
|
4. **Server Broadcasts Update**: The server sends messages (e.g., `TOCLIENT_ADDNODE`) about the official state change.
|
||||||
|
5. **Client Reconciles**: If the client's prediction differs from the server's update, the client corrects its local state.
|
||||||
|
|
||||||
|
### Rendering Pipeline and Irrlicht Engine
|
||||||
|
|
||||||
|
Luanti uses the Irrlicht Engine (source in `irr/`) for 3D graphics. The `RenderingEngine` class (`src/client/renderingengine.h/cpp`) manages Irrlicht:
|
||||||
|
1. **Initialization**: `RenderingEngine::RenderingEngine()` sets up Irrlicht with parameters like resolution and VSync.
|
||||||
|
2. **Scene Setup**: The `Client` and `Game` classes populate the Irrlicht scene with terrain, objects, sky, etc. `ClientMap::updateDrawList()` manages visible map block meshes.
|
||||||
|
3. **Shader Management**: Custom shaders for effects are managed by `ShaderSource`. Global shader parameters (uniforms) like lighting and fog are set each frame by classes like `GameGlobalShaderUniformSetter`.
|
||||||
|
4. **Drawing Loop**: `RenderingEngine::draw_scene()` orchestrates rendering, calling `driver->beginScene()`, drawing the 3D scene and 2D GUI, and `driver->endScene()`.
|
||||||
|
5. **Irrlicht Integration**: Luanti uses Irrlicht's scene manager, video driver, mesh system, and GUI environment.
|
||||||
|
|
||||||
|
### Input Processing
|
||||||
|
|
||||||
|
Player input is handled by `InputHandler` and `MyEventReceiver` (`src/client/inputhandler.h/cpp`):
|
||||||
|
1. **Event Reception**: `MyEventReceiver::OnEvent()` receives system events from Irrlicht (keyboard, mouse, touch, joystick).
|
||||||
|
2. **State Tracking**: `MyEventReceiver` tracks key states (`keyIsDown`, `keyWasPressed`, etc.) using bitsets.
|
||||||
|
3. **Input Abstraction**: `InputHandler` provides an abstraction for the `Game` class to query input states.
|
||||||
|
4. **Game Logic Access**: `Game::processUserInput()` uses the `InputHandler` to update player movement, camera, and trigger interactions.
|
||||||
|
|
||||||
|
## Lua Scripting API
|
||||||
|
|
||||||
|
Luanti features an extensive server-side Lua API for modding, primarily through the `core` global table (aliased as `minetest` for backward compatibility).
|
||||||
|
|
||||||
|
[Diagram: Lua API interaction. Mod (Lua) <-> Luanti Core API (Lua bindings) <-> C++ Engine Core.]
|
||||||
|
|
||||||
|
### Mod Loading and Execution
|
||||||
|
- **`init.lua`**: The main script for each mod, executed at server startup. Responsible for registering nodes, items, entities, crafts, and callbacks.
|
||||||
|
- **Mod Load Paths**: Mods are loaded from `games/<gameid>/mods/`, `mods/` (user's mod directory), and `worlds/<worldname>/worldmods/`.
|
||||||
|
- **`mod.conf`**: Metadata file for each mod (name, title, description, dependencies).
|
||||||
|
```
|
||||||
|
name = my_mod
|
||||||
|
title = My Mod
|
||||||
|
description = This is an example mod.
|
||||||
|
depends = default
|
||||||
|
```
|
||||||
|
- **Directory Structure**: Includes `init.lua`, `mod.conf`, and subdirectories like `textures/`, `sounds/`, `models/`, `locale/`.
|
||||||
|
|
||||||
|
### Core Lua API (`core.*`)
|
||||||
|
A brief overview of common API functionalities:
|
||||||
|
|
||||||
|
- **Registration**:
|
||||||
|
- `core.register_node(name, nodedef_table)`: Defines a new block type.
|
||||||
|
- `core.register_craftitem(name, itemdef_table)`: Defines a new basic item.
|
||||||
|
- `core.register_tool(name, tooldef_table)`: Defines a new tool.
|
||||||
|
- `core.register_entity(name, entitydef_table)`: Defines a new Lua entity.
|
||||||
|
- `core.register_craft(recipe_table)`: Defines a crafting recipe.
|
||||||
|
- `core.register_chatcommand(name, command_def_table)`: Defines a new chat command.
|
||||||
|
- `core.register_alias(alias_name, original_name)`: Aliases an item.
|
||||||
|
- **World Interaction**:
|
||||||
|
- `core.get_node(pos)`: Returns the node at `pos`.
|
||||||
|
- `core.set_node(pos, node_table)`: Sets a node at `pos`.
|
||||||
|
- `core.remove_node(pos)`: Removes a node.
|
||||||
|
- `core.add_item(pos, itemstring_or_stack)`: Spawns a dropped item.
|
||||||
|
- `core.add_entity(pos, entity_name)`: Spawns an entity.
|
||||||
|
- `core.get_meta(pos)`: Returns a `NodeMetaRef` for node metadata.
|
||||||
|
- **Player Interaction**:
|
||||||
|
- `core.get_player_by_name(name)`: Returns an `ObjectRef` for the player.
|
||||||
|
- `core.chat_send_player(name, message)`: Sends a chat message.
|
||||||
|
- `core.show_formspec(playername, formname, formspec_string)`: Displays a UI form.
|
||||||
|
- **Callbacks**:
|
||||||
|
- `core.register_on_joinplayer(function(player_ref, last_login))`
|
||||||
|
- `core.register_on_dignode(function(pos, oldnode, digger_ref))`
|
||||||
|
- `core.register_abm(abm_definition)`: Active Block Modifier for periodic node actions.
|
||||||
|
- `core.register_lbm(lbm_definition)`: Loading Block Modifier for actions on map block activation.
|
||||||
|
- `core.register_globalstep(function(dtime))`: Called every server step.
|
||||||
|
|
||||||
|
### Key Lua Objects
|
||||||
|
- **`ObjectRef`**: Represents players, Lua entities, and dropped items. Methods include `get_pos()`, `set_pos()`, `get_hp()`, `set_hp()`, `get_inventory()`.
|
||||||
|
- **`ItemStack`**: Represents a stack of items. Methods include `get_name()`, `get_count()`, `get_wear()`, `set_count()`, `get_meta()`.
|
||||||
|
- **`InvRef`**: Represents inventories. Methods include `get_size()`, `set_size()`, `get_stack()`, `set_stack()`, `add_item()`.
|
||||||
|
- **`NodeMetaRef` / `ItemStackMetaRef`**: For key-value metadata storage. Methods include `get_string()`, `set_string()`, `get_int()`, `set_int()`.
|
||||||
|
|
||||||
|
### Node Definitions (`nodedef`)
|
||||||
|
Example:
|
||||||
|
```lua
|
||||||
|
core.register_node("mymod:super_stone", {
|
||||||
|
description = "Super Stone",
|
||||||
|
tiles = {"mymod_super_stone.png"},
|
||||||
|
groups = {cracky = 1, stone = 1},
|
||||||
|
drop = "mymod:super_stone_cobble",
|
||||||
|
sounds = {
|
||||||
|
footstep = {name="default_stone_footstep", gain=0.5},
|
||||||
|
dug = {name="default_break_stone", gain=0.8},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### Entity Definitions (Lua Entities)
|
||||||
|
Example:
|
||||||
|
```lua
|
||||||
|
core.register_entity("mymod:bouncy_ball", {
|
||||||
|
initial_properties = {
|
||||||
|
physical = true,
|
||||||
|
collisionbox = {-0.3, -0.3, -0.3, 0.3, 0.3, 0.3},
|
||||||
|
visual = "sprite",
|
||||||
|
textures = {"mymod_bouncy_ball.png"},
|
||||||
|
},
|
||||||
|
on_step = function(self, dtime, moveresult)
|
||||||
|
if moveresult.touching_ground then
|
||||||
|
local vel = self.object:get_velocity()
|
||||||
|
vel.y = 5 -- Bounce
|
||||||
|
self.object:set_velocity(vel)
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
```
|
||||||
|
For comprehensive API details, see `doc/lua_api.md`.
|
||||||
|
|
||||||
|
## Data Flow and Storage
|
||||||
|
|
||||||
|
Luanti manages various types of data, including the game world itself, player information, and data specific to mods. This data flows between the client and server and is persisted using several storage mechanisms.
|
||||||
|
|
||||||
|
[Diagram: Data Storage. Shows Server connected to Database (Map, Player, Auth, Mod Storage) and File System (world.mt, map_meta.txt, auth.txt, player files).]
|
||||||
|
|
||||||
|
### Data Flow Overview
|
||||||
|
|
||||||
|
1. **Client Actions**: Player interactions (movement, digging, etc.) generate events sent to the server.
|
||||||
|
2. **Server Processing**: The server validates actions, updates its authoritative game state, and executes Lua callbacks.
|
||||||
|
3. **Data Retrieval/Storage**: The server interacts with storage backends:
|
||||||
|
* **World Data**: Map blocks are loaded/saved from/to the database.
|
||||||
|
* **Player Data**: Loaded on join, saved periodically/on leave.
|
||||||
|
* **Authentication & Mod Data**: Accessed as needed.
|
||||||
|
4. **Server Updates to Clients**: Changes are sent to clients to synchronize their views.
|
||||||
|
5. **Client Updates**: Clients update their local game world and UI.
|
||||||
|
|
||||||
|
### Storage Mechanisms
|
||||||
|
|
||||||
|
Configured in `world.mt`:
|
||||||
|
- **SQLite3**: Default for map, player, and mod storage (`map.sqlite`).
|
||||||
|
- **LevelDB, PostgreSQL, Redis**: Alternative database backends.
|
||||||
|
- **Raw Files**: `auth.txt` (auth), player files (player data), `world.mt` (world settings), `map_meta.txt` (map seed), `env_meta.txt` (game time).
|
||||||
|
|
||||||
|
### Database Interfaces (`src/database/database.h`)
|
||||||
|
Abstract C++ classes for database operations:
|
||||||
|
- `Database`: Base class (e.g., `beginSave()`, `endSave()`).
|
||||||
|
- `MapDatabase`: For map blocks (`saveBlock()`, `loadBlock()`).
|
||||||
|
- `PlayerDatabase`: For player data (`savePlayer()`, `loadPlayer()`).
|
||||||
|
- `AuthDatabase`: For authentication (`getAuth()`, `saveAuth()`).
|
||||||
|
- `ModStorageDatabase`: For mod-specific data (`getModEntry()`, `setModEntry()`).
|
||||||
|
|
||||||
|
### World Data Organization: MapBlocks
|
||||||
|
The world is divided into `MapBlock`s (16x16x16 nodes), the fundamental unit for storage and networking. The `MapBlock` class (`src/mapblock.h`) contains:
|
||||||
|
- Node Data: Array of `MapNode` (type, `param1`, `param2`).
|
||||||
|
- Position: 3D coordinates of the block.
|
||||||
|
- Flags: `m_generated`, `is_underground`, `m_lighting_complete`.
|
||||||
|
- Metadata: `NodeMetadataList` for inventories, sign text, etc.
|
||||||
|
- Timers, Static Objects, Timestamps.
|
||||||
|
|
||||||
|
### World Format on Disk (`doc/world_format.md`)
|
||||||
|
A world directory contains:
|
||||||
|
- `world.mt`: World settings, enabled mods, backend choices.
|
||||||
|
- Map Data: e.g., `map.sqlite` for map blocks.
|
||||||
|
- Player Data: e.g., `players/<name>` or in the database.
|
||||||
|
- Authentication Data: e.g., `auth.txt` or in the database.
|
||||||
|
- `map_meta.txt` (map seed), `env_meta.txt` (game time).
|
||||||
|
|
||||||
|
### MapBlock Serialization
|
||||||
|
`MapBlock`s are serialized for disk storage and network transfer. Key components:
|
||||||
|
- Version byte, flags byte, lighting completion mask.
|
||||||
|
- Timestamp and Name-ID mappings (for newer formats).
|
||||||
|
- Node data arrays (`param0`, `param1`, `param2`), compressed (zlib or zstd).
|
||||||
|
- Serialized node metadata list, node timers, and static objects.
|
||||||
|
Refer to `doc/world_format.md` for the full binary structure.
|
||||||
|
|
||||||
|
## Function Invocation Flow: Player Movement
|
||||||
|
|
||||||
|
Player movement involves client-side input capture, server-side processing and validation, and synchronization with other clients.
|
||||||
|
|
||||||
|
[Diagram: Player Movement Flow. Client Input -> Network (TOSERVER_PLAYERPOS) -> Server (ProcessInput, Physics, Collision) -> Network (TOCLIENT_ACTIVE_OBJECT_MESSAGES) -> Other Clients (Update Remote Player).]
|
||||||
|
|
||||||
|
1. **Client: Input Detection (`MyEventReceiver::OnEvent` in `src/client/inputhandler.cpp`)**
|
||||||
|
* Keyboard/mouse/joystick events are captured.
|
||||||
|
* Mapped to game actions (e.g., `KeyType::FORWARD`).
|
||||||
|
* Input state is updated (e.g., `keyIsDown`).
|
||||||
|
2. **Client: Processing Input (`Game::updatePlayerControl` in `src/client/game.cpp`)**
|
||||||
|
* A `PlayerControl` object is created with current movement keys and look direction.
|
||||||
|
* This is passed to `Client::setPlayerControl()`.
|
||||||
|
3. **Client: Sending to Server (`TOSERVER_PLAYERPOS`)**
|
||||||
|
* Client periodically sends its position, speed, look direction, and pressed keys via `TOSERVER_PLAYERPOS`.
|
||||||
|
4. **Server: Receiving and Processing (`Server::handleCommand_PlayerPos` in `src/server.cpp`)**
|
||||||
|
* Server deserializes the packet.
|
||||||
|
* Updates the player's `PlayerSAO` (Server Active Object) with new position, look direction, and key states.
|
||||||
|
* Server performs physics simulation and collision detection in `ServerActiveObject::step()`.
|
||||||
|
5. **Server: Updating Other Clients (`TOCLIENT_ACTIVE_OBJECT_MESSAGES`)**
|
||||||
|
* Server sends updates for the moved player to other relevant clients. This includes new position, orientation, and animation state.
|
||||||
|
6. **Client: Receiving Updates**
|
||||||
|
* Other clients receive these messages and update their local representation of the moving player.
|
||||||
|
7. **Lua Callbacks**
|
||||||
|
* `on_step(self, dtime, moveresult)`: Called on the server for entities (including players) each server tick. `moveresult` provides collision info.
|
||||||
|
* `ObjectRef:set_pos(pos)`, `ObjectRef:set_physics_override(overrides)`: Lua API functions mods can use to directly influence player position and physics on the server.
|
||||||
|
|
||||||
|
## Utilities and Libraries
|
||||||
|
|
||||||
|
Luanti integrates several third-party open-source libraries (typically in `lib/`):
|
||||||
|
|
||||||
|
- **Irrlicht Engine**: Core 3D graphics rendering.
|
||||||
|
- **Lua/LuaJIT**: Scripting language for mods and game logic.
|
||||||
|
- **SQLite3**: Default database backend.
|
||||||
|
- **LevelDB, PostgreSQL, Redis**: Alternative database backends.
|
||||||
|
- **zlib & zstd**: Data compression.
|
||||||
|
- **libjpeg & libpng**: Image format handling.
|
||||||
|
- **OpenAL Soft**: 3D audio.
|
||||||
|
- **libogg & libvorbis**: Ogg Vorbis audio format.
|
||||||
|
- **GMP (mini-gmp)**: Large number arithmetic.
|
||||||
|
- **JsonCpp**: JSON parsing/generation.
|
||||||
|
- **libcurl**: HTTP/HTTPS requests (ContentDB, server list).
|
||||||
|
- **Gettext**: Internationalization.
|
||||||
|
- **OpenSSL (or compatible)**: Cryptographic hashing.
|
||||||
|
- **SDL2**: Low-level hardware access (windowing, input).
|
||||||
|
- **Freetype**: Font rendering.
|
||||||
|
- **catch2**: C++ unit testing framework (internal).
|
||||||
|
- **bitop (Lua BitOp)**: Bitwise operations for Lua (if not using LuaJIT).
|
||||||
|
- **tiniergltf**: glTF 2.0 model loading.
|
||||||
|
|
||||||
|
## Mods
|
||||||
|
|
||||||
|
Mods are Lua scripts and assets that extend Luanti's functionality.
|
||||||
|
|
||||||
|
### Creating Mods
|
||||||
|
- **Directory Structure**: `modname/` (root) with `init.lua`, `mod.conf`, and subfolders like `textures/`, `sounds/`, `models/`, `locale/`.
|
||||||
|
- **`mod.conf`**: Metadata file (name, title, description, dependencies).
|
||||||
|
```
|
||||||
|
name = my_mod
|
||||||
|
description = An example mod.
|
||||||
|
depends = default
|
||||||
|
```
|
||||||
|
- **`init.lua`**: Main script for registering nodes, items, entities, crafts, and callbacks using the `core.*` API.
|
||||||
|
- **Media**: Assets like textures (`.png`), sounds (`.ogg`), models (`.gltf`, `.obj`, etc.) go into respective folders, usually prefixed with `modname_`.
|
||||||
|
|
||||||
|
### Installing and Enabling Mods
|
||||||
|
- **Load Paths**: `games/<gameid>/mods/`, `mods/` (user dir), `worlds/<worldname>/worldmods/`.
|
||||||
|
- **Enabling**: Via the main menu's "Content" tab (ContentDB integration) or by adding `load_mod_my_mod_name = true` to a world's `world.mt` file.
|
||||||
|
|
||||||
|
### Modding API Overview (Summary)
|
||||||
|
The server-side Lua API (`core.*`) allows:
|
||||||
|
- Registering game elements (nodes, items, entities, crafts).
|
||||||
|
- Handling game events via callbacks (player actions, server ticks).
|
||||||
|
- Interacting with the world and game objects.
|
||||||
|
- Creating custom UIs (formspecs).
|
||||||
|
- Managing inventories and metadata.
|
||||||
|
(Refer to the [Lua Scripting API](#lua-scripting-api) section or `doc/lua_api.md` for full details).
|
||||||
|
|
||||||
|
### Naming Conventions
|
||||||
|
- Prefix registered items with `modname:` (e.g., `my_mod:my_item`).
|
||||||
|
|
||||||
|
### Dependency Management
|
||||||
|
- Use `depends` and `optional_depends` in `mod.conf`.
|
||||||
|
|
||||||
|
### Modpacks
|
||||||
|
- Group related mods into a directory containing a `modpack.conf` file.
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
Luanti is an open-source project and welcomes community contributions.
|
||||||
|
|
||||||
|
- **Ways to Contribute**: Code (C++ engine, Lua mods/games), issue reporting, feature requests, translations, documentation, testing, donations.
|
||||||
|
- **Getting Started with Code**:
|
||||||
|
1. Discuss significant changes with core developers first (e.g., via GitHub issues).
|
||||||
|
2. Familiarize yourself with coding style guidelines (see `.github/CONTRIBUTING.md`).
|
||||||
|
3. Review the project roadmap (`doc/direction.md`).
|
||||||
|
- **Detailed Guidelines**: For comprehensive information on coding standards, commit message formats, and the pull request process, please refer to the **`.github/CONTRIBUTING.md`** file in the Luanti repository.
|
||||||
|
|
||||||
|
Community involvement is key to Luanti's development.
|
Loading…
Add table
Add a link
Reference in a new issue