diff --git a/demo/addons/libmaszyna/blinker.gd b/demo/addons/libmaszyna/cabin/blinker.gd similarity index 100% rename from demo/addons/libmaszyna/blinker.gd rename to demo/addons/libmaszyna/cabin/blinker.gd diff --git a/demo/addons/libmaszyna/rotator.gd b/demo/addons/libmaszyna/cabin/rotator.gd similarity index 100% rename from demo/addons/libmaszyna/rotator.gd rename to demo/addons/libmaszyna/cabin/rotator.gd diff --git a/demo/addons/libmaszyna/train_button.gd b/demo/addons/libmaszyna/cabin/train_button.gd similarity index 100% rename from demo/addons/libmaszyna/train_button.gd rename to demo/addons/libmaszyna/cabin/train_button.gd diff --git a/demo/addons/libmaszyna/train_gauge.gd b/demo/addons/libmaszyna/cabin/train_gauge.gd similarity index 100% rename from demo/addons/libmaszyna/train_gauge.gd rename to demo/addons/libmaszyna/cabin/train_gauge.gd diff --git a/demo/addons/libmaszyna/train_knob.gd b/demo/addons/libmaszyna/cabin/train_knob.gd similarity index 100% rename from demo/addons/libmaszyna/train_knob.gd rename to demo/addons/libmaszyna/cabin/train_knob.gd diff --git a/demo/addons/libmaszyna/train_sound_3d.gd b/demo/addons/libmaszyna/cabin/train_sound_3d.gd similarity index 100% rename from demo/addons/libmaszyna/train_sound_3d.gd rename to demo/addons/libmaszyna/cabin/train_sound_3d.gd diff --git a/demo/addons/libmaszyna/train_switch.gd b/demo/addons/libmaszyna/cabin/train_switch.gd similarity index 100% rename from demo/addons/libmaszyna/train_switch.gd rename to demo/addons/libmaszyna/cabin/train_switch.gd diff --git a/demo/addons/libmaszyna/developer_console.gd b/demo/addons/libmaszyna/developer_console.gd deleted file mode 100644 index 39c1dd9f..00000000 --- a/demo/addons/libmaszyna/developer_console.gd +++ /dev/null @@ -1,64 +0,0 @@ -extends Node - - -@export var visible:bool = false: - set(x): - if Console: - Console.set_visible(x) - visible = x - - -func _ready() -> void: - Console.control.visible = visible - Console.add_command("broadcast", self.console_broadcast, ["command", "p1", "p2"], 1, "Broadcast message to all trains") - Console.add_command("send", self.console_send, ["train", "command", "p1", "p2"], 2, "Send message to a train") - Console.add_command("trains", self.console_list_trains, 0, 0, "List trains") - Console.add_command("commands", self.console_list_train_commands, 0, 0, "List available train commands") - Console.add_command("get", self.console_get_train_state, ["train", "parameter"], 1, "Get train state / parameter") - Console.add_command("prop", self.console_get_config_value, ["train", "property"], 2, "Get train config property") - Console.add_command("props", self.console_get_config_properties, ["train"], 1, "List train config properties") - - TrainSystem.train_log_updated.connect(self.console_print_train_log) - TrainSystem.log_updated.connect(self.console_print_log) - -func console_get_config_value(train, property): - Console.print_line("%s" % TrainSystem.get_config_property(train, property)) - -func console_get_config_properties(train): - var props = TrainSystem.get_supported_config_properties(train) - var lines = [] - for prop in props: - lines.append("%s=%s" % [prop, TrainSystem.get_config_property(train, prop)]) - Console.print_line("%s" % "\n".join(lines)) - -func console_broadcast(command, p1=null, p2=null): - #Console.print_line("Broadcasting command: %s(%s, %s)" % [command, p1, p2], true) - TrainSystem.broadcast_command(command, p1, p2) - -func console_send(train, command, p1=null, p2=null): - TrainSystem.send_command(train, command, p1, p2) - -func console_list_trains(): - Console.print_line("%s" % "\n".join(TrainSystem.get_registered_trains())) - -func console_list_train_commands(): - var commands = TrainSystem.get_supported_commands() - commands.sort() - Console.print_line("%s" % "\n".join(commands)) - -func console_print_train_log(train_id, loglevel, line): - console_print_log(loglevel, "%s: %s" % [train_id, line]) - -func console_print_log(loglevel, line): - if loglevel >= TrainSystem.TrainLogLevel.TRAINLOGLEVEL_ERROR: - Console.print_line("[color=red]%s[/color]" % [line]) - elif loglevel == TrainSystem.TrainLogLevel.TRAINLOGLEVEL_WARNING: - Console.print_line("[color=orange]%s[/color]" % [line]) - else: - Console.print_line("%s" % [line]) - -func console_get_train_state(train, key=null): - var out = TrainSystem.get_train_state(train) - if key: - out = out.get(key) - Console.print_line("%s" % [out]) diff --git a/demo/addons/libmaszyna/libmaszyna.gd b/demo/addons/libmaszyna/libmaszyna.gd index f9c50200..5b48e3fb 100644 --- a/demo/addons/libmaszyna/libmaszyna.gd +++ b/demo/addons/libmaszyna/libmaszyna.gd @@ -2,8 +2,31 @@ extends EditorPlugin func _enter_tree(): - add_autoload_singleton("Console", "res://addons/libmaszyna/console.gd") + add_autoload_singleton("PlayerSystem", "res://addons/libmaszyna/player/player_system.gd") + add_autoload_singleton("MultiPlayerManager", "res://addons/libmaszyna/player/multiplayer_manager.gd") + add_autoload_singleton("Console", "res://addons/libmaszyna/utility/console.gd") + add_custom_project_setting("maszyna/game/multiplayer/host", "127.0.0.1", TYPE_STRING) + add_custom_project_setting("maszyna/game/multiplayer/port", 9797, TYPE_INT) func _exit_tree(): remove_autoload_singleton("Console") + remove_autoload_singleton("MultiPlayerManager") + remove_autoload_singleton("PlayerSystem") + + + +func add_custom_project_setting(name: String, default_value, type: int, hint: int = PROPERTY_HINT_NONE, hint_string: String = "") -> void: + + if ProjectSettings.has_setting(name): return + + var setting_info: Dictionary = { + "name": name, + "type": type, + "hint": hint, + "hint_string": hint_string + } + + ProjectSettings.set_setting(name, default_value) + ProjectSettings.add_property_info(setting_info) + ProjectSettings.set_initial_value(name, default_value) diff --git a/demo/addons/libmaszyna/player/multiplayer_manager.gd b/demo/addons/libmaszyna/player/multiplayer_manager.gd new file mode 100644 index 00000000..efd4f857 --- /dev/null +++ b/demo/addons/libmaszyna/player/multiplayer_manager.gd @@ -0,0 +1,17 @@ +extends Node + +func host_game(ip, port) -> int: + var peer = ENetMultiplayerPeer.new() + peer.set_bind_ip(ip) + var err = peer.create_server(port, 32) + if err == OK: + multiplayer.multiplayer_peer = peer + return err + + +func connect_game(ip, port) -> int: + var peer = ENetMultiplayerPeer.new() + var err = peer.create_client(ip, port) + if err == OK: + multiplayer.multiplayer_peer = peer + return err diff --git a/demo/addons/libmaszyna/player/player_info.gd b/demo/addons/libmaszyna/player/player_info.gd new file mode 100644 index 00000000..e3b0398f --- /dev/null +++ b/demo/addons/libmaszyna/player/player_info.gd @@ -0,0 +1,9 @@ +extends Resource +class_name PlayerInfo + +enum PlayerType { PLAYER_TYPE_LOCAL, PLAYER_TYPE_REMOTE } + +@export var peer_id:int +@export var name:String +@export var type:PlayerType +@export var train_id:String diff --git a/demo/addons/libmaszyna/player/player_system.gd b/demo/addons/libmaszyna/player/player_system.gd new file mode 100644 index 00000000..8acfb0f5 --- /dev/null +++ b/demo/addons/libmaszyna/player/player_system.gd @@ -0,0 +1,152 @@ +extends Node +#class_name PlayerSystem + +var _players = {} +var _trains_occupied = {} # train_id to peer_id map + +var nick:String = "" +var occupied_train: String = "" + + +signal error_received(error: String) +signal kicked_off_train(peer_id: int, train_id: String) + +func _ready(): + multiplayer.peer_connected.connect(_on_peer_connected) + multiplayer.peer_disconnected.connect(_on_peer_disconnected) + multiplayer.connected_to_server.connect(_on_connected_to_server) + multiplayer.server_disconnected.connect(_on_server_disconnected) + + + +func _error(msg): + error_received.emit(msg) + +func _on_connected_to_server(): + LogSystem.info("Connected to server successfully") + +func _on_server_disconnected(): + LogSystem.info("Disconnected from server") + +func _on_peer_connected(peer_id: int): + LogSystem.info("Peer connected: %d" % peer_id) + register_player.rpc(peer_id) + set_player_name.rpc(peer_id, nick) + if occupied_train: + enter_train.rpc(peer_id, occupied_train) + + +func _on_peer_disconnected(peer_id: int): + unregister_player.rpc(peer_id) + LogSystem.info("Peer disconnected: %d" % peer_id) + +func _require_registered_player(peer_id): + if not _players.has(peer_id): + _error("Player not registered: %s" % peer_id) + false + else: + return true + +func _require_train_occupied_by_player(peer_id, train_id): + if _require_registered_player(peer_id): + if not train_id in _trains_occupied: + _error("Train is not occupied: %s" % train_id) + return false + if not _trains_occupied.has(train_id): + _error("Train is not occupied: %s" % train_id) + return false + return true + else: + return false + + +func recevie_error(message): + error_received.emit(message) + + +@rpc("any_peer", "call_local") +func register_player(peer_id:int, name:String = "", type=PlayerInfo.PlayerType.PLAYER_TYPE_LOCAL): + if _players.has(peer_id): + _error("Player already registered: %s" % peer_id) + return + + var p = PlayerInfo.new() + p.name = name if not name.is_empty() else "Player %d" % peer_id + p.type = type + p.peer_id = peer_id + + _players[peer_id] = p + + +@rpc("any_peer", "call_local") +func unregister_player(peer_id:int): + if _require_registered_player(peer_id): + _players.erase(peer_id) + + +func get_all_players(): + return _players.values() + + +func get_my_peer_id(): + return multiplayer.get_unique_id() + + +func set_player_name(peer_id:int, nick: String): + if not nick.is_empty() and _require_registered_player(peer_id): + receive_player_name.rpc(peer_id, nick) + +@rpc("any_peer", "call_local") +func receive_player_name(peer_id:int, nick: String): + _players[peer_id].name = nick + LogSystem.warning("Player %d changed nick to %s" % [peer_id, nick]) + +func get_player_name(peer_id:int): + if _require_registered_player(peer_id): + return _players[peer_id].name + +@rpc("any_peer", "call_local") +func enter_train(peer_id:int, train_id:String): + if _require_registered_player(peer_id): + if _trains_occupied.has(train_id): + _error("Train is already occupied: %s" % train_id) + if multiplayer.get_remote_sender_id() == 1: + #kicked_off_train.emit(peer_id, train_id) + kick_off_from_train.rpc(peer_id, train_id) + #handle_train_occupy_conflict.rpc_id(peer_id, train_id, _trains_occupied[train_id]) + return + _trains_occupied[train_id] = peer_id + _players[peer_id].train_id = train_id + + +@rpc("any_peer", "call_local") +func exit_train(peer_id:int, train_id:String): + if _require_train_occupied_by_player(peer_id, train_id): + if _trains_occupied[train_id] == peer_id: + _trains_occupied.erase(train_id) + _players[peer_id].train_id = "" + else: + _error("Train is occupied by someone else") + + +@rpc("any_peer", "call_local") +func kick_off_from_train(peer_id:int, train_id:String): + kicked_off_train.emit(peer_id, train_id) + +@rpc("any_peer","call_remote") +func handle_train_occupy_conflict(train_id:String, actual_peer_id:int): + print("HERE") + _trains_occupied[train_id] = actual_peer_id + kicked_off_train.emit(multiplayer.get_remote_sender_id(), train_id) + + +func send_train_command(peer_id:int, train_id:String, command: String, p1 = null, p2 = null): + if _require_train_occupied_by_player(peer_id, train_id): + TrainSystem.send_command(train_id, command, p1, p2) + +func send_message(message: String): + receive_message.rpc(multiplayer.get_unique_id(), message) + +@rpc("any_peer", "call_remote") +func receive_message(peer_id:int, message: String): + LogSystem.info("%s: %s" % [get_player_name(peer_id), message]) diff --git a/demo/addons/libmaszyna/powered_train_part.gd b/demo/addons/libmaszyna/train/powered_train_part.gd similarity index 100% rename from demo/addons/libmaszyna/powered_train_part.gd rename to demo/addons/libmaszyna/train/powered_train_part.gd diff --git a/demo/addons/libmaszyna/console.gd b/demo/addons/libmaszyna/utility/console.gd similarity index 100% rename from demo/addons/libmaszyna/console.gd rename to demo/addons/libmaszyna/utility/console.gd diff --git a/demo/addons/libmaszyna/utility/developer_console.gd b/demo/addons/libmaszyna/utility/developer_console.gd new file mode 100644 index 00000000..81d9348a --- /dev/null +++ b/demo/addons/libmaszyna/utility/developer_console.gd @@ -0,0 +1,135 @@ +extends Node + + +@export var visible:bool = false: + set(x): + if Console: + Console.set_visible(x) + visible = x + + +func _ready() -> void: + Console.control.visible = visible + Console.add_command("broadcast", self.console_broadcast, ["command", "p1", "p2"], 1, "Broadcast message to all trains") + Console.add_command("send", self.console_send, ["train", "command", "p1", "p2"], 2, "Send message to a train") + Console.add_command("trains", self.console_list_trains, 0, 0, "List trains") + Console.add_command("commands", self.console_list_train_commands, 0, 0, "List available train commands") + Console.add_command("get", self.console_get_train_state, ["train", "parameter"], 1, "Get train state / parameter") + Console.add_command("prop", self.console_get_config_value, ["train", "property"], 2, "Get train config property") + Console.add_command("props", self.console_get_config_properties, ["train"], 1, "List train config properties") + Console.add_command("server", self.console_host_server, ["address"], 0, "Host a game server") + Console.add_command("connect", self.console_connect_server, ["address"], 0, "Connect to a game server") + Console.add_command("nick", self.console_set_nick, ["nick"], 1, "Set your nickname") + Console.add_command("players", self.console_list_players, [], 0, "List all players") + Console.add_command("say", self.console_say, ["message"], 1, "Say message") + + #TrainSystem.train_log_updated.connect(self.console_print_train_log) + LogSystem.log_updated.connect(self.console_print_log) + PlayerSystem.error_received.connect(func(msg): LogSystem.error(msg)) + + +func _parse_host_ip_from_address(address:String = ""): + var port:int + var host:String + + if address.is_empty(): + host = ProjectSettings.get_setting("maszyna/game/multiplayer/host", "127.0.0.1") + if not host: + console_print_log(LogSystem.LogLevel.LOGLEVEL_WARNING, "Host is not configured in ProjectSetttings") + port = int(ProjectSettings.get_setting("maszyna/game/multiplayer/port", 9797)) + if not port: + console_print_log(LogSystem.LogLevel.LOGLEVEL_WARNING, "Port is not configured in ProjectSetttings") + else: + var _parts = address.split(":") + host = _parts[0].strip_edges() + port = int(_parts[1].strip_edges()) + if host.is_empty(): + host = ProjectSettings.get_setting("maszyna/game/multiplayer/host", "127.0.0.1") + + if not port or not host: + console_print_log(LogSystem.LogLevel.LOGLEVEL_ERROR, "Incorrect address \"%s:%s\"" % [host, port]) + return [null, null] + + return [host, port] + +func console_list_players(): + for player:PlayerInfo in PlayerSystem.get_all_players(): + if player: + if player.train_id: + Console.print_line("%s: %s [in %s]" % [player.peer_id, player.name, player.train_id]) + else: + Console.print_line("%s: %s" % [player.peer_id, player.name]) + +func console_say(message: String): + PlayerSystem.send_message(message) + +func console_set_nick(nick:String): + PlayerSystem.nick = nick + PlayerSystem.set_player_name(PlayerSystem.get_my_peer_id(), nick) + +func console_host_server(address:String = ""): + var host_and_port = _parse_host_ip_from_address(address) + var host = host_and_port[0] + var port = host_and_port[1] + + if host and port: + var err = MultiPlayerManager.host_game(host_and_port[0], host_and_port[1]) + if err == OK: + LogSystem.info("Server started at %s:%s" % [host, port]) + else: + LogSystem.error("Cannot host a game server (%s)" % err) + + +func console_connect_server(address:String = ""): + var host_and_port = _parse_host_ip_from_address(address) + var host = host_and_port[0] + var port = host_and_port[1] + if host and port: + var err = MultiPlayerManager.connect_game(host, port) + if err == OK: + LogSystem.info("Connected to the server %s:%s" % [host, port]) + else: + LogSystem.error("Can't connect to server (%s)" % err) + + +func console_get_config_value(train, property): + Console.print_line("%s" % TrainSystem.get_config_property(train, property)) + +func console_get_config_properties(train): + var props = TrainSystem.get_supported_config_properties(train) + var lines = [] + for prop in props: + lines.append("%s=%s" % [prop, TrainSystem.get_config_property(train, prop)]) + Console.print_line("%s" % "\n".join(lines)) + +func console_broadcast(command, p1=null, p2=null): + #Console.print_line("Broadcasting command: %s(%s, %s)" % [command, p1, p2], true) + TrainSystem.broadcast_command(command, p1, p2) + +func console_send(train, command, p1=null, p2=null): + TrainSystem.send_command(train, command, p1, p2) + +func console_list_trains(): + Console.print_line("%s" % "\n".join(TrainSystem.get_registered_trains())) + +func console_list_train_commands(): + var commands = TrainSystem.get_supported_commands() + commands.sort() + Console.print_line("%s" % "\n".join(commands)) + +func console_print_train_log(train_id, loglevel, line): + console_print_log(loglevel, "%s: %s" % [train_id, line]) + +func console_print_log(loglevel, line): + if loglevel >= LogSystem.LogLevel.LOGLEVEL_ERROR: + Console.print_line("[color=red]%s[/color]" % [line]) + elif loglevel == LogSystem.LogLevel.LOGLEVEL_WARNING: + Console.print_line("[color=orange]%s[/color]" % [line]) + else: + Console.print_line("%s" % [line]) + +func console_get_train_state(train, key=null): + var out = TrainSystem.get_train_state(train) + if key: + out = out.get(key) + Console.print_line("%s" % [out]) diff --git a/demo/addons/libmaszyna/developer_console.tscn b/demo/addons/libmaszyna/utility/developer_console.tscn similarity index 57% rename from demo/addons/libmaszyna/developer_console.tscn rename to demo/addons/libmaszyna/utility/developer_console.tscn index 15f6a6a7..5b6c27b2 100644 --- a/demo/addons/libmaszyna/developer_console.tscn +++ b/demo/addons/libmaszyna/utility/developer_console.tscn @@ -1,6 +1,6 @@ [gd_scene load_steps=2 format=3 uid="uid://dgm10m7u26drx"] -[ext_resource type="Script" path="res://addons/libmaszyna/developer_console.gd" id="1_vako3"] +[ext_resource type="Script" path="res://addons/libmaszyna/utility/developer_console.gd" id="1_vako3"] [node name="DeveloperConsole" type="Node"] script = ExtResource("1_vako3") diff --git a/demo/demo.tscn b/demo/demo.tscn index ef24dac5..b5b54a20 100644 --- a/demo/demo.tscn +++ b/demo/demo.tscn @@ -1,7 +1,7 @@ [gd_scene load_steps=9 format=3 uid="uid://djqacw65swdqc"] [ext_resource type="Script" path="res://demo.gd" id="1_76ghk"] -[ext_resource type="PackedScene" uid="uid://dgm10m7u26drx" path="res://addons/libmaszyna/developer_console.tscn" id="2_fdv2n"] +[ext_resource type="PackedScene" uid="uid://dgm10m7u26drx" path="res://addons/libmaszyna/utility/developer_console.tscn" id="2_fdv2n"] [ext_resource type="PackedScene" uid="uid://c5hi8nsm1d2hb" path="res://sm_42v_1.tscn" id="2_t8uqe"] [ext_resource type="PackedScene" uid="uid://1v37c7y4cwcx" path="res://gauge.tscn" id="3_dosfj"] [ext_resource type="PackedScene" uid="uid://br6moafqr8ykn" path="res://debug_panel.tscn" id="3_p4ciw"] @@ -270,9 +270,6 @@ command = "brake_releaser" [node name="SM42" parent="." instance=ExtResource("2_t8uqe")] train_id = "sm42" -[node name="TestowySilnikElektryczny" parent="SM42" index="1"] -enabled = false - [connection signal="button_up" from="UI/MoverSwitches/General/MainIncrease" to="." method="_on_main_increase_button_up"] [connection signal="button_up" from="UI/MoverSwitches/General/MainDecrease" to="." method="_on_main_decrease_button_up"] [connection signal="button_up" from="UI/MoverSwitches/General/Forward" to="." method="_on_forward_button_up"] diff --git a/demo/project.godot b/demo/project.godot index 14a362b9..e7e62f13 100644 --- a/demo/project.godot +++ b/demo/project.godot @@ -17,7 +17,9 @@ config/icon="res://icon.svg" [autoload] -Console="*res://addons/libmaszyna/console.gd" +Console="*res://addons/libmaszyna/utility/console.gd" +PlayerSystem="*res://addons/libmaszyna/player/player_system.gd" +MultiPlayerManager="*res://addons/libmaszyna/player/multiplayer_manager.gd" [editor_plugins] diff --git a/src/core/LogSystem.cpp b/src/core/LogSystem.cpp new file mode 100644 index 00000000..bb21cfb6 --- /dev/null +++ b/src/core/LogSystem.cpp @@ -0,0 +1,42 @@ +#include "./LogSystem.hpp" + +namespace godot { + const char *LogSystem::LOG_UPDATED_SIGNAL = "log_updated"; + + void LogSystem::_bind_methods() { + ClassDB::bind_method(D_METHOD("log", "loglevel", "line"), &LogSystem::log); + ClassDB::bind_method(D_METHOD("debug", "line"), &LogSystem::debug); + ClassDB::bind_method(D_METHOD("info", "line"), &LogSystem::info); + ClassDB::bind_method(D_METHOD("warning", "line"), &LogSystem::warning); + ClassDB::bind_method(D_METHOD("error", "line"), &LogSystem::error); + ADD_SIGNAL(MethodInfo( + LOG_UPDATED_SIGNAL, PropertyInfo(Variant::INT, "loglevel"), PropertyInfo(Variant::STRING, "line"))); + BIND_ENUM_CONSTANT(LOGLEVEL_DEBUG); + BIND_ENUM_CONSTANT(LOGLEVEL_INFO); + BIND_ENUM_CONSTANT(LOGLEVEL_WARNING); + BIND_ENUM_CONSTANT(LOGLEVEL_ERROR); + } + + LogSystem::LogSystem() {} + + void LogSystem::log(const LogLevel level, const String &line) { + emit_signal(LOG_UPDATED_SIGNAL, level, line); + } + + void LogSystem::debug(const String &line) { + log(LogLevel::LOGLEVEL_DEBUG, line); + } + + void LogSystem::info(const String &line) { + log(LogLevel::LOGLEVEL_INFO, line); + } + + void LogSystem::warning(const String &line) { + log(LogLevel::LOGLEVEL_WARNING, line); + } + + void LogSystem::error(const String &line) { + log(LogLevel::LOGLEVEL_ERROR, line); + } + +} // namespace godot diff --git a/src/core/LogSystem.hpp b/src/core/LogSystem.hpp new file mode 100644 index 00000000..8b44d1e4 --- /dev/null +++ b/src/core/LogSystem.hpp @@ -0,0 +1,44 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace godot { + + class LogSystem : public RefCounted { + GDCLASS(LogSystem, RefCounted); + + private: + public: + static const char *LOG_UPDATED_SIGNAL; + + inline static LogSystem *get_instance() { + return dynamic_cast(godot::Engine::get_singleton()->get_singleton("LogSystem")); + } + + enum LogLevel { + LOGLEVEL_DEBUG = 0, + LOGLEVEL_INFO, + LOGLEVEL_WARNING, + LOGLEVEL_ERROR, + }; + + LogSystem(); + ~LogSystem() override = default; + + void log(const LogLevel level, const String &line); + void debug(const String &line); + void info(const String &line); + void warning(const String &line); + void error(const String &line); + + protected: + static void _bind_methods(); + }; +} // namespace godot +VARIANT_ENUM_CAST(LogSystem::LogLevel) diff --git a/src/core/TrainController.cpp b/src/core/TrainController.cpp index cd3a5da6..2a083502 100644 --- a/src/core/TrainController.cpp +++ b/src/core/TrainController.cpp @@ -14,6 +14,7 @@ namespace godot { const char *TrainController::MOVER_CONFIG_CHANGED_SIGNAL = "mover_config_changed"; const char *TrainController::MOVER_INITIALIZED_SIGNAL = "mover_initialized"; + const char *TrainController::STATE_CHANGED = "state_changed"; const char *TrainController::POWER_CHANGED_SIGNAL = "power_changed"; const char *TrainController::COMMAND_RECEIVED = "command_received"; const char *TrainController::RADIO_TOGGLED = "radio_toggled"; @@ -22,12 +23,11 @@ namespace godot { void TrainController::_bind_methods() { ClassDB::bind_method(D_METHOD("get_state"), &TrainController::get_state); + ClassDB::bind_method(D_METHOD("set_state", "state"), &TrainController::set_state); + ClassDB::bind_method(D_METHOD("get_occupied"), &TrainController::get_occupied); + ClassDB::bind_method(D_METHOD("set_occupied", "occupied"), &TrainController::set_occupied); ClassDB::bind_method(D_METHOD("get_config"), &TrainController::get_config); - ADD_PROPERTY( - PropertyInfo( - Variant::DICTIONARY, "state", PROPERTY_HINT_NONE, "", - PROPERTY_USAGE_READ_ONLY | PROPERTY_USAGE_DEFAULT), - "", "get_state"); + ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "state"), "set_state", "get_state"); ADD_PROPERTY( PropertyInfo( Variant::DICTIONARY, "config", PROPERTY_HINT_NONE, "", @@ -59,7 +59,6 @@ namespace godot { ClassDB::bind_method( D_METHOD("radio_channel_decrease", "step"), &TrainController::radio_channel_decrease, DEFVAL(1)); ClassDB::bind_method(D_METHOD("update_mover"), &TrainController::update_mover); - ClassDB::bind_method(D_METHOD("update_state"), &TrainController::update_state); ClassDB::bind_method(D_METHOD("update_config"), &TrainController::update_config); ClassDB::bind_method(D_METHOD("set_train_id"), &TrainController::set_train_id); @@ -82,6 +81,7 @@ namespace godot { ClassDB::bind_method(D_METHOD("get_radio_channel_max"), &TrainController::get_radio_channel_min); ADD_PROPERTY(PropertyInfo(Variant::STRING, "train_id"), "set_train_id", "get_train_id"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "occupied"), "set_occupied", "get_occupied"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "type_name"), "set_type_name", "get_type_name"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dimensions/mass"), "set_mass", "get_mass"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "power"), "set_power", "get_power"); @@ -92,6 +92,7 @@ namespace godot { ADD_SIGNAL(MethodInfo(MOVER_CONFIG_CHANGED_SIGNAL)); ADD_SIGNAL(MethodInfo(MOVER_INITIALIZED_SIGNAL)); + ADD_SIGNAL(MethodInfo(STATE_CHANGED)); ADD_SIGNAL(MethodInfo(POWER_CHANGED_SIGNAL, PropertyInfo(Variant::BOOL, "is_powered"))); ADD_SIGNAL(MethodInfo(RADIO_TOGGLED, PropertyInfo(Variant::BOOL, "is_enabled"))); ADD_SIGNAL(MethodInfo(RADIO_CHANNEL_CHANGED, PropertyInfo(Variant::INT, "channel"))); @@ -235,7 +236,18 @@ namespace godot { } } + void TrainController::set_occupied(const bool p_occupied) { + occupied = p_occupied; + } + + bool TrainController::get_occupied() const { + return occupied; + } + void TrainController::_process_mover(const double delta) { + if (!occupied) { + return; + } TLocation mock_location; TRotation mock_rotation; mover->ComputeTotalForce(delta); @@ -247,13 +259,12 @@ namespace godot { _handle_mover_update(); } - void TrainController::update_state() { - _handle_mover_update(); - } - void TrainController::_handle_mover_update() { state.merge(get_mover_state(), true); + mark_state_changed(); + } + void TrainController::_on_state_changed() { const bool new_is_powered = (state.get("power24_available", false) || state.get("power110_available", false)); if (prev_is_powered != new_is_powered) { prev_is_powered = new_is_powered; // FIXME: I don't like this @@ -281,6 +292,12 @@ namespace godot { _update_mover_config_if_dirty(); _process_mover(delta); + + if (state_changed) { + state_changed = false; + emit_signal(STATE_CHANGED); + _on_state_changed(); + } } void TrainController::_do_update_internal_mover(TMoverParameters *mover) const { @@ -362,6 +379,7 @@ namespace godot { TMoverParameters *mover = get_mover(); if (mover != nullptr) { _do_fetch_state_from_mover(mover, state); + mark_state_changed(); } else { UtilityFunctions::push_warning("TrainController::get_mover_state() failed: internal mover not initialized"); } @@ -431,6 +449,11 @@ namespace godot { return state; } + void TrainController::set_state(const Dictionary &p_state) { + state = p_state; + mark_state_changed(); + } + void TrainController::emit_command_received_signal(const String &command, const Variant &p1, const Variant &p2) { emit_signal(COMMAND_RECEIVED, command, p1, p2); } @@ -482,4 +505,8 @@ namespace godot { void TrainController::radio(const bool p_enabled) { mover->Radio = p_enabled; } + + void TrainController::mark_state_changed() { + state_changed = true; + } } // namespace godot diff --git a/src/core/TrainController.hpp b/src/core/TrainController.hpp index 6c20c275..31c16dbf 100644 --- a/src/core/TrainController.hpp +++ b/src/core/TrainController.hpp @@ -23,6 +23,7 @@ namespace godot { void initialize_mover(); bool _dirty = false; // Refreshes all elements bool _dirty_prop = false; // Refreshes only TrainController's properties + bool occupied = true; Dictionary state; Dictionary config; Dictionary internal_state; @@ -39,9 +40,12 @@ namespace godot { bool prev_radio_enabled = false; int prev_radio_channel = radio_channel; + bool state_changed = false; + String axle_arrangement = ""; void _collect_train_parts(const Node *node, Vector &train_parts); + void _on_state_changed(); private: void _update_mover_config_if_dirty(); @@ -58,11 +62,13 @@ namespace godot { void _do_fetch_config_from_mover(TMoverParameters *mover, Dictionary &config) const; void _do_fetch_state_from_mover(TMoverParameters *mover, Dictionary &state); void _process_mover(double delta); + void _notification(int p_what); public: static const char *MOVER_CONFIG_CHANGED_SIGNAL; static const char *MOVER_INITIALIZED_SIGNAL; + static const char *STATE_CHANGED; static const char *POWER_CHANGED_SIGNAL; static const char *COMMAND_RECEIVED; static const char *RADIO_TOGGLED; @@ -73,7 +79,6 @@ namespace godot { void update_config(const Dictionary &p_config); void set_config_property(String &key, Variant &p_value); void _process(double delta) override; - void _notification(int p_what); void send_command(const StringName &command, const Variant &p1 = Variant(), const Variant &p2 = Variant()); void battery(const bool p_enabled); void main_controller_increase(const int p_step = 1); @@ -89,8 +94,8 @@ namespace godot { void broadcast_command(const String &command, const Variant &p1 = Variant(), const Variant &p2 = Variant()); void register_command(const String &command, const Callable &callable); void unregister_command(const String &command, const Callable &callable); - void update_state(); void update_mover(); + void mark_state_changed(); TMoverParameters *get_mover() const; static void _bind_methods(); @@ -113,6 +118,8 @@ namespace godot { int get_radio_channel_max() const; void set_radio_channel_max(const int p_value); String get_axle_arrangement() const; + void set_occupied(const bool p_occupied); + bool get_occupied() const; void set_state(const Dictionary &p_state); Dictionary get_state(); diff --git a/src/core/TrainPart.cpp b/src/core/TrainPart.cpp index 7e2b1f4f..34dcaa33 100644 --- a/src/core/TrainPart.cpp +++ b/src/core/TrainPart.cpp @@ -46,6 +46,8 @@ namespace godot { } } + void TrainPart::_do_initialize_train_controller(TrainController *train_controller) {} + void TrainPart::_notification(int p_what) { if (Engine::get_singleton()->is_editor_hint()) { return; @@ -63,6 +65,7 @@ namespace godot { if (train_controller_node != nullptr) { train_controller_node->connect( TrainController::MOVER_CONFIG_CHANGED_SIGNAL, Callable(this, "update_mover")); + _do_initialize_train_controller(train_controller_node); } if (enabled) { _register_commands(); @@ -83,25 +86,25 @@ namespace godot { } } - void TrainPart::log(const TrainSystem::TrainLogLevel level, const String &line) { + void TrainPart::log(const LogSystem::LogLevel level, const String &line) { if (train_controller_node != nullptr) { TrainSystem::get_instance()->log(train_controller_node->get_train_id(), level, line); } } void TrainPart::log_debug(const String &line) { - log(TrainSystem::TrainLogLevel::TRAINLOGLEVEL_DEBUG, line); + log(LogSystem::LogLevel::LOGLEVEL_DEBUG, line); } void TrainPart::log_info(const String &line) { - log(TrainSystem::TrainLogLevel::TRAINLOGLEVEL_INFO, line); + log(LogSystem::LogLevel::LOGLEVEL_INFO, line); } void TrainPart::log_warning(const String &line) { - log(TrainSystem::TrainLogLevel::TRAINLOGLEVEL_WARNING, line); + log(LogSystem::LogLevel::LOGLEVEL_WARNING, line); } void TrainPart::log_error(const String &line) { - log(TrainSystem::TrainLogLevel::TRAINLOGLEVEL_ERROR, line); + log(LogSystem::LogLevel::LOGLEVEL_ERROR, line); } void TrainPart::register_command(const String &command, const Callable &callback) { @@ -127,7 +130,7 @@ namespace godot { _dirty = false; } - if (enabled) { + if (enabled && train_controller_node->get_occupied()) { _process_mover(delta); } @@ -153,6 +156,7 @@ namespace godot { if (mover != nullptr) { _do_process_mover(mover, delta); train_controller_node->get_state().merge(get_mover_state(), true); + train_controller_node->mark_state_changed(); } } } diff --git a/src/core/TrainPart.hpp b/src/core/TrainPart.hpp index 9ee57ece..c647ef16 100644 --- a/src/core/TrainPart.hpp +++ b/src/core/TrainPart.hpp @@ -1,6 +1,7 @@ #pragma once #include #include +#include "./LogSystem.hpp" #include "./TrainSystem.hpp" #include "TrainController.hpp" @@ -48,6 +49,7 @@ namespace godot { virtual void _do_fetch_config_from_mover(TMoverParameters *mover, Dictionary &config); virtual void _do_process_mover(TMoverParameters *mover, double delta); + virtual void _do_initialize_train_controller(TrainController *train_controller); virtual void _register_commands(); virtual void _unregister_commands(); @@ -62,7 +64,7 @@ namespace godot { void unregister_command(const String &command, const Callable &callback); void send_command(const String &command, const Variant &p1 = Variant(), const Variant &p2 = Variant()); void broadcast_command(const String &command, const Variant &p1 = Variant(), const Variant &p2 = Variant()); - void log(const TrainSystem::TrainLogLevel level, const String &line); + void log(const LogSystem::LogLevel level, const String &line); void log_debug(const String &line); void log_info(const String &line); void log_warning(const String &line); diff --git a/src/core/TrainSystem.cpp b/src/core/TrainSystem.cpp index 135a9310..30f679c4 100644 --- a/src/core/TrainSystem.cpp +++ b/src/core/TrainSystem.cpp @@ -2,6 +2,8 @@ #include "./TrainSystem.hpp" namespace godot { + const char *TrainSystem::TRAIN_LOG_UPDATED_SIGNAL = "train_log_updated"; + void TrainSystem::_bind_methods() { ClassDB::bind_method(D_METHOD("register_train", "train_id", "train"), &TrainSystem::register_train); ClassDB::bind_method(D_METHOD("unregister_train", "train_id"), &TrainSystem::unregister_train); @@ -27,16 +29,9 @@ namespace godot { D_METHOD("unregister_command", "train_id", "command", "callable"), &TrainSystem::unregister_command); ClassDB::bind_method(D_METHOD("get_train_state", "train_id"), &TrainSystem::get_train_state); ClassDB::bind_method(D_METHOD("log", "train_id", "loglevel", "line"), &TrainSystem::log); - ClassDB::bind_method(D_METHOD("global_log", "loglevel", "line"), &TrainSystem::global_log); - ADD_SIGNAL(MethodInfo( - "train_log_updated", PropertyInfo(Variant::STRING, "train"), PropertyInfo(Variant::INT, "loglevel"), - PropertyInfo(Variant::STRING, "line"))); ADD_SIGNAL(MethodInfo( - "log_updated", PropertyInfo(Variant::INT, "loglevel"), PropertyInfo(Variant::STRING, "line"))); - BIND_ENUM_CONSTANT(TRAINLOGLEVEL_DEBUG); - BIND_ENUM_CONSTANT(TRAINLOGLEVEL_INFO); - BIND_ENUM_CONSTANT(TRAINLOGLEVEL_WARNING); - BIND_ENUM_CONSTANT(TRAINLOGLEVEL_ERROR); + TRAIN_LOG_UPDATED_SIGNAL, PropertyInfo(Variant::STRING, "train"), + PropertyInfo(Variant::INT, "loglevel"), PropertyInfo(Variant::STRING, "line"))); } TrainSystem::TrainSystem() {} @@ -69,7 +64,7 @@ namespace godot { TrainController *train = get_train(train_id); if (train == nullptr) { - log(train_id, TrainLogLevel::TRAINLOGLEVEL_ERROR, "Train is not registered"); + log(train_id, LogSystem::LogLevel::LOGLEVEL_ERROR, "Train is not registered"); UtilityFunctions::push_error("Train is not registered: ", train_id); Dictionary empty; return empty; @@ -85,7 +80,7 @@ namespace godot { auto it = trains.find(train_id); if (it == trains.end()) { - log(train_id, TrainLogLevel::TRAINLOGLEVEL_ERROR, "Train is not registered in"); + log(train_id, LogSystem::LogLevel::LOGLEVEL_ERROR, "Train is not registered in"); UtilityFunctions::push_error("Train is not registered: ", train_id); return Dictionary(); } @@ -98,18 +93,14 @@ namespace godot { return props.get(property_name, ""); } - void TrainSystem::log(const String &train_id, const TrainLogLevel level, const String &line) { + void TrainSystem::log(const String &train_id, const LogSystem::LogLevel level, const String &line) { emit_signal("train_log_updated", train_id, level, line); - } - - void TrainSystem::global_log(const TrainLogLevel level, const String &line) { - // FIXME: move to LogSystem??? - emit_signal("log_updated", level, line); + LogSystem::get_instance()->log(level, line); } void TrainSystem::register_train(const String &train_id, TrainController *train) { if (is_train_registered(train_id)) { - log(train_id, TrainLogLevel::TRAINLOGLEVEL_ERROR, "Train is already registered!"); + log(train_id, LogSystem::LogLevel::LOGLEVEL_ERROR, "Train is already registered!"); UtilityFunctions::push_error("Train is already registered: ", train_id); } else { trains[train_id] = train; @@ -118,7 +109,7 @@ namespace godot { void TrainSystem::register_command(const String &train_id, const String &command, const Callable &callback) { if (!is_train_registered(train_id)) { - log(train_id, TrainLogLevel::TRAINLOGLEVEL_ERROR, "Train is not registered in"); + log(train_id, LogSystem::LogLevel::LOGLEVEL_ERROR, "Train is not registered in"); UtilityFunctions::push_error("Train is not registered: ", train_id); return; } @@ -130,7 +121,7 @@ namespace godot { if ((static_cast(commands[command])).has(train_id)) { - log(train_id, TrainLogLevel::TRAINLOGLEVEL_ERROR, "Command is already registered: " + command); + log(train_id, LogSystem::LogLevel::LOGLEVEL_ERROR, "Command is already registered: " + command); UtilityFunctions::push_error("Command ", command, " is already registered for train ", train_id); return; } @@ -141,20 +132,20 @@ namespace godot { void TrainSystem::unregister_command(const String &train_id, const String &command, const Callable &callback) { if (!is_train_registered(train_id)) { - log(train_id, TrainLogLevel::TRAINLOGLEVEL_ERROR, "Train is not registered"); + log(train_id, LogSystem::LogLevel::LOGLEVEL_ERROR, "Train is not registered"); UtilityFunctions::push_error("Train is not registered: ", train_id); return; } if (!is_command_supported(command)) { - global_log(TrainLogLevel::TRAINLOGLEVEL_ERROR, "Cannot unregister unknown command: " + command); + LogSystem::get_instance()->error("Cannot unregister unknown command: " + command); UtilityFunctions::push_error("Unknown command: ", command); } if (commands.has(command)) { Dictionary _trains = static_cast(commands[command]); if (!_trains.has(train_id)) { - log(train_id, TrainLogLevel::TRAINLOGLEVEL_ERROR, "Command is not registered: " + command); + log(train_id, LogSystem::LogLevel::LOGLEVEL_ERROR, "Command is not registered: " + command); UtilityFunctions::push_error("Command ", command, " is not registered for train ", train_id); return; } @@ -167,7 +158,7 @@ namespace godot { void TrainSystem::unregister_train(const String &train_id) { if (!is_train_registered(train_id)) { - log(train_id, TrainLogLevel::TRAINLOGLEVEL_ERROR, "Train is not registered"); + log(train_id, LogSystem::LogLevel::LOGLEVEL_ERROR, "Train is not registered"); UtilityFunctions::push_error("Train is not registered: ", train_id); return; } @@ -214,7 +205,7 @@ namespace godot { auto it = trains.find(train_id); if (it == trains.end()) { - log(train_id, TrainLogLevel::TRAINLOGLEVEL_ERROR, "Train is not registered"); + log(train_id, LogSystem::LogLevel::LOGLEVEL_ERROR, "Train is not registered"); UtilityFunctions::push_error("Train is not registered: ", train_id); return; } @@ -224,7 +215,7 @@ namespace godot { Dictionary _trains = static_cast(commands[command]); if (!_trains.has(train_id)) { - log(train_id, TrainLogLevel::TRAINLOGLEVEL_WARNING, "train cannot handle command: " + command); + log(train_id, LogSystem::LogLevel::LOGLEVEL_WARNING, "train cannot handle command: " + command); UtilityFunctions::push_warning("Train \"", train_id, "\" cannot handle command \"", command, "\""); return; } @@ -249,7 +240,7 @@ namespace godot { } c.callv(args); #if DEBUG_MODE - log(train_id, TrainLogLevel::TRAINLOGLEVEL_DEBUG, + log(train_id, LogSystem::LogLevel::LOGLEVEL_DEBUG, "received command " + command + "(" + String(", ").join(args) + ")"); if (arg_required != argc) { UtilityFunctions::push_warning( @@ -261,17 +252,16 @@ namespace godot { UtilityFunctions::push_error("Callable ", c, " is invalid"); } } else { - log(train_id, TrainLogLevel::TRAINLOGLEVEL_ERROR, "Unknown command: " + command); + log(train_id, LogSystem::LogLevel::LOGLEVEL_ERROR, "Unknown command: " + command); ERR_PRINT("Unknown command: " + command); } - train->update_state(); train->emit_command_received_signal(command, p1, p2); } void TrainSystem::broadcast_command(const String &command, const Variant &p1, const Variant &p2) { if (!is_command_supported(command)) { - global_log(TrainLogLevel::TRAINLOGLEVEL_ERROR, "Unknown command: " + command); + LogSystem::get_instance()->error("Unknown command: " + command); ERR_PRINT("Unknown command: " + command); return; } diff --git a/src/core/TrainSystem.hpp b/src/core/TrainSystem.hpp index 75b97c41..4689d66d 100644 --- a/src/core/TrainSystem.hpp +++ b/src/core/TrainSystem.hpp @@ -7,7 +7,7 @@ #include #include #include -#include "./TrainLogLevel.hpp" +#include "./LogSystem.hpp" namespace godot { @@ -21,16 +21,12 @@ namespace godot { Dictionary commands; public: + static const char *TRAIN_LOG_UPDATED_SIGNAL; + inline static TrainSystem *get_instance() { return dynamic_cast(godot::Engine::get_singleton()->get_singleton("TrainSystem")); } - enum TrainLogLevel { - TRAINLOGLEVEL_DEBUG = 0, - TRAINLOGLEVEL_INFO, - TRAINLOGLEVEL_WARNING, - TRAINLOGLEVEL_ERROR, - }; TrainSystem(); ~TrainSystem() override = default; @@ -54,10 +50,7 @@ namespace godot { void broadcast_command(const String &command, const Variant &p1 = Variant(), const Variant &p2 = Variant()); bool is_command_supported(const String &command); - void log(const String &train_id, const TrainLogLevel level, const String &line); - - // FIXME: move to LogSystem??? - void global_log(const TrainLogLevel level, const String &line); + void log(const String &train_id, const LogSystem::LogLevel level, const String &line); Dictionary get_train_state(const String &train_id); @@ -65,4 +58,3 @@ namespace godot { static void _bind_methods(); }; } // namespace godot -VARIANT_ENUM_CAST(TrainSystem::TrainLogLevel) diff --git a/src/engines/TrainEngine.cpp b/src/engines/TrainEngine.cpp index 155a7a8d..63603424 100644 --- a/src/engines/TrainEngine.cpp +++ b/src/engines/TrainEngine.cpp @@ -11,6 +11,8 @@ namespace godot { ClassDB::bind_method(D_METHOD("set_motor_param_table"), &TrainEngine::set_motor_param_table); ClassDB::bind_method(D_METHOD("get_motor_param_table"), &TrainEngine::get_motor_param_table); ClassDB::bind_method(D_METHOD("main_switch", "enabled"), &TrainEngine::main_switch); + ClassDB::bind_method( + D_METHOD("_on_train_controller_state_changed"), &TrainEngine::_on_train_controller_state_changed); ADD_PROPERTY( PropertyInfo( Variant::ARRAY, "motor_param_table", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT, @@ -45,7 +47,6 @@ namespace godot { } void TrainEngine::_do_fetch_state_from_mover(TMoverParameters *mover, Dictionary &state) { - bool previous_main_switch = static_cast(state.get("main_switch_enabled", false)); state["main_switch_enabled"] = mover->Mains; state["Mm"] = mover->Mm; state["Mw"] = mover->Mw; @@ -66,12 +67,6 @@ namespace godot { state["line_breaker_delay"] = mover->CtrlDelay; state["line_breaker_initial_delay"] = mover->InitialCtrlDelay; state["line_breaker_closes_at_no_power"] = mover->LineBreakerClosesOnlyAtNoPowerPos; - - if (!previous_main_switch && (static_cast(state["main_switch_enabled"]))) { - emit_signal("engine_start"); - } else if (previous_main_switch && !(static_cast(state["main_switch_enabled"]))) { - emit_signal("engine_stop"); - } } void TrainEngine::_do_fetch_config_from_mover(TMoverParameters *mover, Dictionary &config) { @@ -101,4 +96,21 @@ namespace godot { unregister_command("main_switch", Callable(this, "main_switch")); } + void TrainEngine::_do_initialize_train_controller(TrainController *train_controller) { + TrainPart::_do_initialize_train_controller(train_controller); + train_controller->connect(TrainController::STATE_CHANGED, Callable(this, "_on_train_controller_state_changed")); + } + + void TrainEngine::_on_train_controller_state_changed() { + Dictionary state = train_controller_node->get_state(); + + if (!previous_main_switch && (static_cast(state["main_switch_enabled"]))) { + emit_signal("engine_start"); + } else if (previous_main_switch && !(static_cast(state["main_switch_enabled"]))) { + emit_signal("engine_stop"); + } + + previous_main_switch = state["main_switch_enabled"]; + } + } // namespace godot diff --git a/src/engines/TrainEngine.hpp b/src/engines/TrainEngine.hpp index 1a884c8c..d3c7cb1e 100644 --- a/src/engines/TrainEngine.hpp +++ b/src/engines/TrainEngine.hpp @@ -19,11 +19,16 @@ namespace godot { void _do_fetch_config_from_mover(TMoverParameters *mover, Dictionary &config) override; void _register_commands() override; void _unregister_commands() override; + void _do_initialize_train_controller(TrainController *train_controller) override; + + private: + bool previous_main_switch = false; public: TypedArray get_motor_param_table(); void set_motor_param_table(const TypedArray p_wwlist); void main_switch(const bool p_enabled); + void _on_train_controller_state_changed(); TrainEngine(); ~TrainEngine() override = default; diff --git a/src/register_types.cpp b/src/register_types.cpp index a9d5a047..d568ffc6 100644 --- a/src/register_types.cpp +++ b/src/register_types.cpp @@ -7,6 +7,7 @@ #include "brakes/TrainBrake.hpp" #include "core/GenericTrainPart.hpp" +#include "core/LogSystem.hpp" #include "core/TrainController.hpp" #include "core/TrainPart.hpp" #include "core/TrainSystem.hpp" @@ -20,6 +21,7 @@ using namespace godot; TrainSystem *train_system_singleton = nullptr; +LogSystem *log_system_singleton = nullptr; void initialize_libmaszyna_module(ModuleInitializationLevel p_level) { if (p_level == MODULE_INITIALIZATION_LEVEL_EDITOR) { @@ -38,9 +40,12 @@ void initialize_libmaszyna_module(ModuleInitializationLevel p_level) { GDREGISTER_CLASS(TrainController); GDREGISTER_CLASS(TrainSecuritySystem); GDREGISTER_CLASS(TrainSystem); + GDREGISTER_CLASS(LogSystem); train_system_singleton = memnew(TrainSystem); + log_system_singleton = memnew(LogSystem); Engine::get_singleton()->register_singleton("TrainSystem", train_system_singleton); + Engine::get_singleton()->register_singleton("LogSystem", log_system_singleton); } } @@ -51,6 +56,7 @@ void uninitialize_libmaszyna_module(ModuleInitializationLevel p_level) { if (train_system_singleton) { Engine::get_singleton()->unregister_singleton("TrainSystem"); + Engine::get_singleton()->unregister_singleton("LogSystem"); // memdelete(train_system_singleton); // train_system_singleton = nullptr; } diff --git a/src/systems/TrainSecuritySystem.cpp b/src/systems/TrainSecuritySystem.cpp index 46681dfd..4bc84fee 100644 --- a/src/systems/TrainSecuritySystem.cpp +++ b/src/systems/TrainSecuritySystem.cpp @@ -76,6 +76,9 @@ namespace godot { ClassDB::bind_method(D_METHOD("set_ca_max_hold_time"), &TrainSecuritySystem::set_ca_max_hold_time); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ca_max_hold_time"), "set_ca_max_hold_time", "get_ca_max_hold_time"); ClassDB::bind_method(D_METHOD("security_acknowledge", "enabled"), &TrainSecuritySystem::security_acknowledge); + ClassDB::bind_method( + D_METHOD("_on_train_controller_state_changed"), + &TrainSecuritySystem::_on_train_controller_state_changed); ADD_SIGNAL(MethodInfo("blinking_changed", PropertyInfo(Variant::BOOL, "state"))); ADD_SIGNAL(MethodInfo("beeping_changed", PropertyInfo(Variant::BOOL, "state"))); @@ -166,8 +169,6 @@ namespace godot { } void TrainSecuritySystem::_do_fetch_state_from_mover(TMoverParameters *mover, Dictionary &state) { - const bool prev_beeping = state["beeping"]; - const bool prev_blinking = state["blinking"]; state["beeping"] = mover->SecuritySystem.is_beeping(); state["blinking"] = mover->SecuritySystem.is_blinking(); state["radiostop_available"] = mover->SecuritySystem.radiostop_available(); @@ -177,13 +178,6 @@ namespace godot { state["braking"] = mover->SecuritySystem.is_braking(); state["engine_blocked"] = mover->SecuritySystem.is_engine_blocked(); state["separate_acknowledge"] = mover->SecuritySystem.has_separate_acknowledge(); - - if (prev_blinking != static_cast(state["blinking"])) { - emit_signal("blinking_changed", state["blinking"]); - } - if (prev_beeping != static_cast(state["beeping"])) { - emit_signal("beeping_changed", state["beeping"]); - } } void TrainSecuritySystem::_do_update_internal_mover(TMoverParameters *mover) { @@ -234,4 +228,22 @@ namespace godot { mover->SecuritySystem.acknowledge_release(); } } + + void TrainSecuritySystem::_do_initialize_train_controller(TrainController *train_controller) { + train_controller->connect(TrainController::STATE_CHANGED, Callable(this, "_on_train_controller_state_changed")); + } + + void TrainSecuritySystem::_on_train_controller_state_changed() { + Dictionary state = train_controller_node->get_state(); + + if (prev_blinking != static_cast(state["blinking"])) { + emit_signal("blinking_changed", state["blinking"]); + } + if (prev_beeping != static_cast(state["beeping"])) { + emit_signal("beeping_changed", state["beeping"]); + } + + prev_beeping = state["beeping"]; + prev_blinking = state["blinking"]; + } } // namespace godot diff --git a/src/systems/TrainSecuritySystem.hpp b/src/systems/TrainSecuritySystem.hpp index aa2e541e..99d97de5 100644 --- a/src/systems/TrainSecuritySystem.hpp +++ b/src/systems/TrainSecuritySystem.hpp @@ -14,6 +14,8 @@ namespace godot { protected: void _do_update_internal_mover(TMoverParameters *mover) override; void _do_fetch_state_from_mover(TMoverParameters *mover, Dictionary &state) override; + void _do_initialize_train_controller(TrainController *train_controller) override; + void _on_train_controller_state_changed(); void _register_commands() override; void _unregister_commands() override; @@ -43,6 +45,9 @@ namespace godot { double shp_magnet_distance = 0.0; // MagnetLocation -> SecuritySystem->MagnetLocation double ca_max_hold_time = 0.0; // MaxHoldTime -> SecuritySystem->MaxHoldTime + bool prev_beeping = false; + bool prev_blinking = false; + public: void security_acknowledge(const bool p_enabled);