Skip to content

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

1 Commit

Folders and files

Repository files navigation

DDNet-PyEngine

An exact port of the DDNet (DDRaceNetwork) physics core to Python.

Ported 1:1 from the DDNet-20.0 sources:

C++ Python
src/game/collision.cpp/.h ddnet_physics/collision.py
src/game/gamecore.cpp/.h ddnet_physics/gamecore.py
src/game/tuning.h ddnet_physics/tuning.py
src/game/teamscore.cpp/.h ddnet_physics/teams.py
src/game/prng.cpp/.h ddnet_physics/prng.py
src/game/mapitems.h ddnet_physics/tiles.py
src/base/vmath.h + math.h ddnet_physics/vmath.py
src/game/server/entities/character.cpp (DDRace layer) ddnet_physics/ddrace.py
src/game/server/entities/projectile.cpp ddnet_physics/weapons.py

What's inside

  • Collision — every layer and every tile. game / front / tele / speedup / switch (with the derived door layer) / tune. All TILE_* constants from mapitems.h: stoppers, through, hookblocker, CP movers, time checkpoints, teleporters, and so on.
  • Tee physics core (CCharacterCore): gravity, movement (MoveBox with velramp), jumps (ground/air, m_Jumps support), the full hook state machine (tele-hook with deterministic out selection via PCG-XSH-RR, hook drag, hook time), tee-vs-tee collision and hooking, movement restrictions from stopper tiles.
  • DDRace layer (server physics): freeze / deep freeze / live freeze and every tile effect from DDRacePostCoreTick (teleporters, evil tele, speedups, CP movers, solo, endless hook, unlimited jumps, walljump, telegun, ...), plus weapon impulses (hammer, shotgun pull, explosion, jetpack) and projectile physics.
  • Tuning — every parameter from tuning.h with the same ×100 quantization as CTuneParam in the game.
  • Teams (CTeamsCore), PRNG (CPrng), teleporter indexes.
  • Loading real .map files via the optional twmap library.

The core is pure Python 3.9+, zero dependencies. DDNet collision is grid-based (tilemap + 28×28 AABB), so no external collision library is needed — the mechanics match the C++ operation by operation.

Installation

Requires Python 3.9+; the physics core itself has zero dependencies.

git clone https://github.com/lothiann/DDNet-PyEngine.git
cd DDNet-PyEngine
pip install -r requirements.txt   # only needed for loading .map files

Quick start

Simulating a tee

from ddnet_physics import Sim, Collision, TILE_SOLID

col = Collision.blank(50, 30)
col.fill_game_rect(0, 25, 50, 5, TILE_SOLID)      # floor

sim = Sim(col)                                    # sandbox
core = sim.add_tee(0, (16 * 32, 10 * 32))         # id, position (x, y)

for _ in range(60):                               # 50 ticks = 1 second
    sim.step()

sim.set_input(0, jump=1, direction=-1)            # jump while moving left
sim.step()
print(core.pos, core.vel, core.hook_state)

sim.set_input(cid, ...) accepts: direction, target_x, target_y, jump, hook.

Collision directly

from ddnet_physics import Collision, Vec2, TILE_SOLID

col = Collision.blank(10, 10)
col.set_game(5, 5, TILE_SOLID)

col.check_point(170, 170)                       # inside a solid tile?
col.test_box(Vec2(160, 160), Vec2(28, 28))      # tee AABB inside a solid?
col.intersect_line(Vec2(0, 0), Vec2(320, 320))  # ray -> (tile, point, point before)
pos, vel, grounded = col.move_box(
    Vec2(100, 100), Vec2(300, 0), Vec2(28, 28), Vec2(0, 0)
)
col.get_move_restrictions(Vec2(160, 208))       # CANTMOVE_* bitmask

Real maps

from ddnet_physics import Sim, load_collision, find_entities, ENTITY_SPAWN

col = load_collision("maps/LearnToPlay.map")
spawns = find_entities(col, ENTITY_SPAWN)

sim = Sim(col)
core = sim.add_tee(0, spawns[0])

Tick order

Sim.step() replicates the DDNet server order (gameworld.cpp -> character.cpp):

  1. every tee: core.tick(use_input=True) — with sv_no_weak_hook 0 the deferred tick (tee collision / hook influence) runs inside;
  2. every tee: core.move() (+ quantization, like the real server).

Sim(col, no_weak_hook=True) corresponds to sv_no_weak_hook 1: tick(do_deferred_tick=False) for everyone first, then tick_deferred() for everyone, then move(). Sim(col, quantize=False) disables the position/velocity quantization.

Every method is also available manually — the classes mirror the C++ API:

core = CharacterCore(collision, world, teams)
core.reset()
core.input.direction = -1
core.tick(use_input=True)   # gravity, input, hook
core.move()                 # position integration
core.tick_deferred()        # tee-vs-tee forces (if not already run in tick)
core.triggered_events       # COREEVENT_* bitmask for this tick
core.quantize()             # rounding, like on the network

How exact is "exact"

  • Formulas, constants, tuning quantization and the order of operations are 1:1 with DDNet-20.0 (including quirks like the condition in TileExistsNext).
  • The only difference: C++ computes in 32-bit float, Python in 64-bit double. The port is slightly more precise; trajectories match, but over very long simulations a microscopic divergence from the client is possible.
  • Sim(quantize=True) (default) rounds the state after every tick like the real server (positions to integers, velocities to 1/256).

Verified: a standing tee keeps the bit-identical position for hundreds of ticks (with and without quantization), a ground jump returns it to the same pixel, and pushing into a wall stops the tee exactly at the tile boundary.

What is not included

Race timing / score bookkeeping (start, finish, time checkpoints as time — not as movement) is game logic, not physics. The DDRace movement layer (freeze, tile effects, weapon impulses) lives in ddnet_physics/ddrace.py and ddnet_physics/weapons.py.

Structure

ddnet_physics/
├── vmath.py        # Vec2 + math (vmath.h/math.h)
├── tiles.py        # all tile/layer/protocol constants
├── tuning.py       # Tuning — physics parameters (tuning.h)
├── collision.py    # Collision — the whole CCollision
├── gamecore.py     # CharacterCore / World — CCharacterCore / CWorldCore
├── teams.py        # TeamsCore — CTeamsCore
├── prng.py         # Prng — PCG-XSH-RR (prng.cpp)
├── map_loader.py   # .map loading (twmap)
├── ddrace.py       # DDRace layer: freeze + tile effects (character.cpp)
├── weapons.py      # weapons & projectile physics (character.cpp/projectile.cpp)
└── sim.py          # Sim — wrapper with the server tick order
tests/test_physics.py
examples/basic.py

Tests

python -m unittest discover -s tests

License

This project is a Python port of the DDNet sources and is distributed under the DDNet license (a zlib-style license) — see LICENSE.

Upstream: ddnet/ddnet — Teeworlds Copyright (C) 2007-2014 Magnus Auvinen, DDRace Copyright (C) 2010-2011 Shereef Marzouk, DDNet Copyright (C) Dennis Felsing.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages