Skip to content

Instantly share code, notes, and snippets.

@EncodeTheCode
Created March 9, 2026 21:45
Show Gist options
  • Select an option

  • Save EncodeTheCode/416fcdcd3768137e949f4acb6915f9b8 to your computer and use it in GitHub Desktop.

Select an option

Save EncodeTheCode/416fcdcd3768137e949f4acb6915f9b8 to your computer and use it in GitHub Desktop.
# weapon_cfg_singlefile.py
import struct, mmap
from collections import namedtuple
from array import array
Weapon = namedtuple("Weapon", "id name ammo fire_rate weapon_type sound damage")
# Header: magic(4) 'WCFG', version(1), 3 reserved bytes, count(uint32), names_size(uint32), sounds_size(uint32)
_HEADER_STRUCT = struct.Struct("<4sB3xIII")
_HEADER_SIZE = _HEADER_STRUCT.size
# Record (fixed-size 28 bytes):
# id:uint32, name_off:uint32, name_len:uint16, ammo:uint16,
# fire_rate:float32, weapon_type:uint8, pad:1, sound_off:uint32, sound_len:uint16, damage:float32
_RECORD_STRUCT = struct.Struct("<IIHHfBxIHf")
_RECORD_SIZE = _RECORD_STRUCT.size
_MAGIC = b"WCFG"
_VERSION = 1
# default enum map (numeric -> string)
DEFAULT_TYPES = {
0: "unknown",
1: "melee",
2: "firearm",
3: "taser",
4: "hand-taser",
}
class WeaponConfig:
"""Single-file, mmap-based loader that also exposes compact numeric arrays for fast direct access."""
__slots__ = ("_mm", "_count", "_names_off", "_sounds_off",
"ids", "ammo", "fire_rates", "types", "damages",
"id_to_index", "_types_map")
def __init__(self, path: str, types_map: dict = None, build_arrays: bool = True):
"""
path: path to binary .wcfg file
types_map: optional dict mapping numeric enum -> string
build_arrays: when True (default) build array.array buffers for quick numeric access
"""
f = open(path, "rb")
try:
mm = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)
finally:
f.close()
self._mm = mm
magic, ver, count, names_size, sounds_size = _HEADER_STRUCT.unpack_from(mm, 0)
if magic != _MAGIC:
raise ValueError("bad magic")
if ver != _VERSION:
raise ValueError("unsupported version")
self._count = count
records_bytes = count * _RECORD_SIZE
self._names_off = _HEADER_SIZE + records_bytes
self._sounds_off = self._names_off + names_size
self._types_map = types_map or DEFAULT_TYPES
# prepare containers (arrays are compact C arrays)
# ids: uint32, ammo: uint16, fire_rates: float32, types: uint8, damages: float32
self.ids = array("I") # unsigned 32-bit
self.ammo = array("H") # unsigned 16-bit
self.fire_rates = array("f") # 32-bit float
self.types = array("B") # unsigned 8-bit
self.damages = array("f") # 32-bit float
self.id_to_index = {}
# Build arrays and index in one pass (done once at load)
base = _HEADER_SIZE
for i in range(count):
off = base + i * _RECORD_SIZE
# unpack returns (id, name_off, name_len, ammo, fire_rate, wtype, sound_off, sound_len, damage)
id_, name_rel, name_len, ammo_v, fire_rate_v, wtype_v, sound_off, sound_len, damage_v = _RECORD_STRUCT.unpack_from(mm, off)
self.id_to_index[id_] = i
if build_arrays:
self.ids.append(id_)
self.ammo.append(ammo_v)
self.fire_rates.append(fire_rate_v)
self.types.append(wtype_v & 0xFF)
self.damages.append(damage_v)
def close(self):
mm = getattr(self, "_mm", None)
if mm:
mm.close()
self._mm = None
def __len__(self):
return self._count
# --- direct numeric access helpers ---------------------------------
# You can index the arrays directly:
# cfg.ids[5], cfg.fire_rates[5], cfg.ammo[5], cfg.types[5], cfg.damages[5]
# Or use convenience lookups:
def index_of(self, weapon_id: int):
"""Return zero-based index of the weapon with given id (KeyError if not present)."""
return self.id_to_index[weapon_id]
def fire_rate_by_id(self, weapon_id: int) -> float:
"""Direct fast lookup of fire_rate by id."""
idx = self.id_to_index[weapon_id]
return self.fire_rates[idx]
def id_by_index(self, index: int) -> int:
return self.ids[index]
# --- higher-level record access (lazy-decode strings) ----------------
def _unpack_at_index(self, index: int) -> Weapon:
if index < 0 or index >= self._count:
raise IndexError("index out of range")
rec_off = _HEADER_SIZE + index * _RECORD_SIZE
id_, name_rel, name_len, ammo_v, fire_rate_v, wtype_v, sound_off, sound_len, damage_v = _RECORD_STRUCT.unpack_from(self._mm, rec_off)
name = ""
if name_len:
start = self._names_off + name_rel
name = self._mm[start: start + name_len].decode("utf-8")
sound = ""
if sound_len:
sstart = self._sounds_off + sound_off
sound = self._mm[sstart: sstart + sound_len].decode("utf-8")
wtype_str = self._types_map.get(wtype_v, f"type#{wtype_v}")
return Weapon(id_, name, ammo_v, fire_rate_v, wtype_str, sound, damage_v)
def get_by_index(self, index: int) -> Weapon:
return self._unpack_at_index(index)
def get_by_id(self, weapon_id: int) -> Weapon:
return self._unpack_at_index(self.id_to_index[weapon_id])
def iter_records(self):
"""Iterator that yields Weapon namedtuples (lazy-decoding names/sounds)."""
for i in range(self._count):
yield self._unpack_at_index(i)
# ------------------- tiny writer (asset-build step) ---------------------
def write_weapon_file(path: str, weapons):
"""
weapons: iterable of dicts with keys:
id (int), name (str), ammo (int), fire_rate (float),
type (int), sound (str), damage (float)
Example:
{"id":1,"name":"Knife","ammo":0,"fire_rate":0.0,"type":1,"sound":"knife_stab","damage":18.0}
"""
names = bytearray()
sounds = bytearray()
records = bytearray()
for w in weapons:
id_ = int(w["id"])
name_b = (w.get("name") or "").encode("utf-8")
name_off = len(names)
name_len = len(name_b)
names.extend(name_b)
sound_b = (w.get("sound") or "").encode("utf-8")
sound_off = len(sounds)
sound_len = len(sound_b)
sounds.extend(sound_b)
ammo = int(w.get("ammo", 0)) & 0xFFFF
fire_rate = float(w.get("fire_rate", 0.0))
wtype = int(w.get("type", 0)) & 0xFF
damage = float(w.get("damage", 0.0))
records.extend(_RECORD_STRUCT.pack(
id_, name_off, name_len, ammo, fire_rate, wtype, sound_off, sound_len, damage
))
header = _HEADER_STRUCT.pack(_MAGIC, _VERSION, len(records) // _RECORD_SIZE, len(names), len(sounds))
with open(path, "wb") as f:
f.write(header)
f.write(records)
f.write(names)
f.write(sounds)
# ------------------- quick example usage -------------------------------
if __name__ == "__main__":
# build a test file (run once during build)
sample = [
{"id":1,"name":"Combat Knife","ammo":0,"fire_rate":0.0,"type":1,"sound":"knife_stab","damage":18.0},
{"id":2,"name":"9mm Pistol","ammo":15,"fire_rate":0.2,"type":2,"sound":"pistol_shot","damage":12.0},
{"id":3,"name":"Taser","ammo":0,"fire_rate":0.5,"type":3,"sound":"taser_zap","damage":5.0},
{"id":4,"name":"Hand-Taser","ammo":0,"fire_rate":0.6,"type":4,"sound":"handtaser_zap","damage":4.5},
]
write_weapon_file("weapons.wcfg", sample)
cfg = WeaponConfig("weapons.wcfg")
# Direct array access (very fast, O(1), zero unpack cost)
print("ids:", list(cfg.ids))
print("fire_rates:", list(cfg.fire_rates))
# get fire_rate by id (fast)
print("fire_rate of id=2 ->", cfg.fire_rate_by_id(2))
# get full record (lazy-decode strings)
print(cfg.get_by_id(2))
# iterate records
for r in cfg.iter_records():
print(r)
cfg.close()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment