Initial commit: Transcience MVP

Top-down twin-stick bullet-hell, Godot 4.7, server-authoritative dedicated
server with client-side prediction. Clients send input only; the server
resolves every hit for both players and enemies (no PvP).

- SimWorld: whole simulation as plain RefCounted objects (no nodes, no
  physics server), ~0.24ms/tick at peak load -- runs headless for free and
  drives 78 tests in under a second
- BulletPool: struct-of-arrays bullet storage, replicated as spawn/despawn
  events rather than per-tick state
- Emitter framework (Ring/AimedSpread/WallGap/ArcSweep) shared by trash
  enemies and bosses -- a new boss is data in src/content/content.gd, no
  simulation changes
- The Warden of the Fold: stationary 4-phase boss built entirely on that
  format
- Lobby hub with a portal into on-demand dungeon instances; one process
  hosts the hub plus every concurrent dungeon
- Emergency escape: 3s server-owned channel, cancelled by damage
- tools/check.sh, test.sh (GUT), smoke.sh (real server + bot clients over
  ENet), bench.gd; git hooks wired to the same scripts
- docs/ARCHITECTURE.md, NETCODE.md, WORKFLOW.md, ROADMAP.md
This commit is contained in:
2026-09-03 16:03:57 +02:00
commit c4beeae38f
385 changed files with 28725 additions and 0 deletions
+68
View File
@@ -0,0 +1,68 @@
@tool
static var GutUserPreferences = load("res://addons/gut/gui/gut_user_preferences.gd")
static var temp_directory = 'user://gut_temp_directory/'
static var editor_run_gut_config_path = 'gut_editor_config.json':
# This avoids having to use path_join wherever we want to reference this
# path. The value is not supposed to change. Could it be a constant
# instead? Probably, but I didn't like repeating the directory part.
# Do I like that this is a bit witty. Absolutely.
get: return temp_directory.path_join(editor_run_gut_config_path)
# Should this print a message or something instead? Probably, but then I'd
# be repeating even more code than if this was just a constant. So I didn't,
# even though I wanted to make the message a easter eggish fun message.
# I didn't, so this dumb comment will have to serve as the easter eggish fun.
set(v):
print("Be sure to document your code. Never trust comments.")
static var editor_run_bbcode_results_path = 'gut_editor.bbcode':
get: return temp_directory.path_join(editor_run_bbcode_results_path)
set(v): pass
static var editor_run_json_results_path = 'gut_editor.json':
get: return temp_directory.path_join(editor_run_json_results_path)
set(v): pass
static var editor_shortcuts_path = 'gut_editor_shortcuts.cfg' :
get: return temp_directory.path_join(editor_shortcuts_path)
set(v): pass
static var run_externally_options_path = 'gut_editor_run_externally.cfg' :
get: return temp_directory.path_join(run_externally_options_path)
set(v): pass
static var _user_prefs = null
static var user_prefs = _user_prefs :
# workaround not being able to reference EditorInterface when not in
# the editor. This shouldn't be referenced by anything not in the
# editor.
get:
if(_user_prefs == null and Engine.is_editor_hint()):
# This is sometimes used when not in the editor. Avoid parser error
# for EditorInterface.
_user_prefs = GutUserPreferences.new(GutUtils.get_editor_interface().get_editor_settings())
return _user_prefs
static var gut_plugin = null
static func create_temp_directory():
DirAccess.make_dir_recursive_absolute(temp_directory)
static func is_being_edited_in_editor(which):
if(!Engine.is_editor_hint()):
return false
var trav = which
var is_scene_root = false
var editor_root = which.get_tree().edited_scene_root
while(trav != null and !is_scene_root):
is_scene_root = editor_root == trav
if(!is_scene_root):
trav = trav.get_parent()
return is_scene_root