From 6979a2efc8ea23b1777b29b023ce7ea608ae69bf Mon Sep 17 00:00:00 2001 From: Kolfering Date: Thu, 19 Sep 2024 14:56:36 +0200 Subject: [PATCH] Add net mml distribution from gatherer to improve synchronization between players --- Source_Files/Files/game_wad.cpp | 12 +- Source_Files/GameWorld/devices.cpp | 25 +++ Source_Files/GameWorld/dynamic_limits.cpp | 24 +++ Source_Files/GameWorld/dynamic_limits.h | 1 + Source_Files/GameWorld/items.cpp | 27 +++ Source_Files/GameWorld/items.h | 1 + Source_Files/GameWorld/media.cpp | 26 +++ Source_Files/GameWorld/media.h | 1 + Source_Files/GameWorld/monsters.cpp | 32 ++++ Source_Files/GameWorld/monsters.h | 2 + Source_Files/GameWorld/platforms.cpp | 16 ++ Source_Files/GameWorld/platforms.h | 1 + Source_Files/GameWorld/player.cpp | 48 ++++++ Source_Files/GameWorld/player.h | 1 + Source_Files/GameWorld/scenery.cpp | 16 ++ Source_Files/GameWorld/scenery.h | 1 + Source_Files/GameWorld/weapons.cpp | 14 ++ Source_Files/GameWorld/weapons.h | 1 + Source_Files/Misc/interface.cpp | 2 +- Source_Files/Misc/interface.h | 1 + Source_Files/Misc/preferences.cpp | 8 +- .../Network/StandaloneHub/StandaloneHub.cpp | 14 +- .../Network/StandaloneHub/StandaloneHub.h | 2 + Source_Files/Network/network.cpp | 49 ++++++ Source_Files/Network/network_dialogs.cpp | 7 +- Source_Files/Network/network_messages.h | 3 + Source_Files/XML/InfoTree.cpp | 11 ++ Source_Files/XML/InfoTree.h | 1 + Source_Files/XML/Plugins.cpp | 8 +- Source_Files/XML/Plugins.h | 4 +- Source_Files/XML/XML_LevelScript.cpp | 4 +- Source_Files/XML/XML_MakeRoot.cpp | 160 +++++++++++------- Source_Files/XML/XML_ParseTreeRoot.h | 11 +- Source_Files/shell.cpp | 16 +- Source_Files/shell.h | 3 +- 35 files changed, 458 insertions(+), 95 deletions(-) diff --git a/Source_Files/Files/game_wad.cpp b/Source_Files/Files/game_wad.cpp index 6eaafcc05..68cdedc59 100644 --- a/Source_Files/Files/game_wad.cpp +++ b/Source_Files/Files/game_wad.cpp @@ -793,12 +793,7 @@ bool goto_level( // LP: doing this here because level-specific MML may specify which level-specific // textures to load. ResetLevelScript(); - if (!game_is_networked || use_map_file(((game_info*)NetGetGameData())->parent_checksum)) - { - RunLevelScript(entry->level_number); - } -#if !defined(DISABLE_NETWORKING) /* If the game is networked, then I must call the network code to do the right */ /* thing with the map.. */ if(game_is_networked) @@ -807,10 +802,13 @@ bool goto_level( /* then calls process_map_wad on it. Non-server receives the map and then */ /* calls process_map_wad on it. */ success= NetChangeMap(entry); + + if (use_map_file(((game_info*)NetGetGameData())->parent_checksum)) + RunLevelScript(entry->level_number); } - else -#endif // !defined(DISABLE_NETWORKING) + else { + RunLevelScript(entry->level_number); /* Load it and then rock.. */ load_level_from_map(entry->level_number); if(error_pending()) success= false; diff --git a/Source_Files/GameWorld/devices.cpp b/Source_Files/GameWorld/devices.cpp index 9c53bc55b..a742c1bad 100644 --- a/Source_Files/GameWorld/devices.cpp +++ b/Source_Files/GameWorld/devices.cpp @@ -993,3 +993,28 @@ void parse_mml_control_panels(const InfoTree& root) } } } + +void write_net_mml_panels(InfoTree& root) +{ + InfoTree panel_root; + panel_root.put_attr("reach", control_panel_settings.ReachDistance); + panel_root.put_attr("horiz", control_panel_settings.ReachHorizontal); + panel_root.put_attr("single_energy", control_panel_settings.SingleEnergy); + panel_root.put_attr("single_energy_rate", control_panel_settings.SingleEnergyRate); + panel_root.put_attr("double_energy", control_panel_settings.DoubleEnergy); + panel_root.put_attr("double_energy_rate", control_panel_settings.DoubleEnergyRate); + panel_root.put_attr("triple_energy", control_panel_settings.TripleEnergy); + panel_root.put_attr("triple_energy_rate", control_panel_settings.TripleEnergyRate); + + for (int i = 0; i < NUMBER_OF_CONTROL_PANELS; i++) + { + const auto& panel = control_panel_definitions[i]; + InfoTree panel_node; + panel_node.put_attr("index", i); + panel_node.put_attr("type", panel._class); + panel_node.put_attr("item", panel.item); + panel_root.add_child("panel", panel_node); + } + + root.add_child("control_panels", panel_root); +} \ No newline at end of file diff --git a/Source_Files/GameWorld/dynamic_limits.cpp b/Source_Files/GameWorld/dynamic_limits.cpp index 09c8d04df..d0f617fdf 100644 --- a/Source_Files/GameWorld/dynamic_limits.cpp +++ b/Source_Files/GameWorld/dynamic_limits.cpp @@ -153,6 +153,30 @@ void parse_mml_dynamic_limits(const InfoTree& root) reallocate_dynamic_limits(); } +void write_net_mml_dynamic_limits(InfoTree& root) +{ + InfoTree dynamic_limit_root; + + std::vector> dynamic_limits_list = + { + {"objects", _dynamic_limit_objects}, + {"monsters", _dynamic_limit_monsters}, + {"paths", _dynamic_limit_paths}, + {"projectiles", _dynamic_limit_projectiles}, + {"effects", _dynamic_limit_effects}, + {"local_collision", _dynamic_limit_local_collision}, + {"global_collision", _dynamic_limit_global_collision} + }; + + for (const auto& dynamic_limits_item : dynamic_limits_list) + { + InfoTree dynamic_item_node; + dynamic_item_node.put_attr("value", get_dynamic_limit(dynamic_limits_item.second)); + dynamic_limit_root.add_child(dynamic_limits_item.first, dynamic_item_node); + } + + root.add_child("dynamic_limits", dynamic_limit_root); +} // Accessor uint16 get_dynamic_limit(int which) { diff --git a/Source_Files/GameWorld/dynamic_limits.h b/Source_Files/GameWorld/dynamic_limits.h index e5db5a2ea..8e8ae3229 100644 --- a/Source_Files/GameWorld/dynamic_limits.h +++ b/Source_Files/GameWorld/dynamic_limits.h @@ -53,6 +53,7 @@ enum { class InfoTree; void parse_mml_dynamic_limits(const InfoTree& root); +void write_net_mml_dynamic_limits(InfoTree& root); void reset_mml_dynamic_limits(); // Accessor diff --git a/Source_Files/GameWorld/items.cpp b/Source_Files/GameWorld/items.cpp index 9f6ba25c3..342ac697d 100644 --- a/Source_Files/GameWorld/items.cpp +++ b/Source_Files/GameWorld/items.cpp @@ -884,3 +884,30 @@ void parse_mml_items(const InfoTree& root) shape.read_shape(def.base_shape); } } + + +void write_net_mml_items(InfoTree& root) +{ + InfoTree items_root; + for (int i = 0; i < NUMBER_OF_DEFINED_ITEMS; i++) + { + const auto& item = item_definitions[i]; + InfoTree item_node; + item_node.put_attr("index", i); + item_node.put_attr("maximum", item.maximum_count_per_player); + item_node.put_attr("invalid", item.invalid_environments); + item_node.put_attr("type", item.item_kind); + + for (int j = 0; j < NUMBER_OF_GAME_DIFFICULTY_LEVELS; j++) + { + InfoTree difficulty_node; + difficulty_node.put_attr("index", j); + difficulty_node.put_attr("maximum", item.extended_maximum_count[j]); + item_node.add_child("difficulty", difficulty_node); + } + + items_root.add_child("item", item_node); + } + + root.add_child("items", items_root); +} \ No newline at end of file diff --git a/Source_Files/GameWorld/items.h b/Source_Files/GameWorld/items.h index 101f904e0..95449d4da 100644 --- a/Source_Files/GameWorld/items.h +++ b/Source_Files/GameWorld/items.h @@ -130,6 +130,7 @@ void animate_items(void); class InfoTree; void parse_mml_items(const InfoTree& root); +void write_net_mml_items(InfoTree& root); void reset_mml_items(); #endif diff --git a/Source_Files/GameWorld/media.cpp b/Source_Files/GameWorld/media.cpp index 4b7b75221..c3115a505 100644 --- a/Source_Files/GameWorld/media.cpp +++ b/Source_Files/GameWorld/media.cpp @@ -409,3 +409,29 @@ void parse_mml_liquids(const InfoTree& root) } } } + +void write_net_mml_liquids(InfoTree& root) +{ + InfoTree liquid_root; + for (int i = 0; i < NUMBER_OF_MEDIA_TYPES; i++) + { + const auto& media = media_definitions[i]; + InfoTree liquid_node; + liquid_node.put_attr("index", i); + liquid_node.put_attr("damage_freq", media.damage_frequency); + liquid_node.write_damage(media.damage); + + for (int j = 0; j < NUMBER_OF_MEDIA_DETONATION_TYPES; j++) + { + const auto& effect = media.detonation_effects[j]; + InfoTree effect_node; + effect_node.put_attr("type", j); + effect_node.put_attr("which", effect); + liquid_node.add_child("effect", effect_node); + } + + liquid_root.add_child("liquid", liquid_node); + } + + root.add_child("liquids", liquid_root); +} \ No newline at end of file diff --git a/Source_Files/GameWorld/media.h b/Source_Files/GameWorld/media.h index 095463f1e..e4f850a7b 100644 --- a/Source_Files/GameWorld/media.h +++ b/Source_Files/GameWorld/media.h @@ -172,6 +172,7 @@ uint8 *pack_media_data(uint8 *Stream, media_data* Objects, size_t Count); class InfoTree; void parse_mml_liquids(const InfoTree& root); void reset_mml_liquids(); +void write_net_mml_liquids(InfoTree& root); #endif diff --git a/Source_Files/GameWorld/monsters.cpp b/Source_Files/GameWorld/monsters.cpp index 9863bd9b6..b5de5e39c 100644 --- a/Source_Files/GameWorld/monsters.cpp +++ b/Source_Files/GameWorld/monsters.cpp @@ -3923,6 +3923,24 @@ void reset_mml_damage_kicks() } } +void write_net_mml_damage_kicks(InfoTree& root) +{ + InfoTree damage_root; + for (int i = 0; i < NUMBER_OF_DAMAGE_TYPES; i++) + { + const auto& damage = damage_kick_definitions[i]; + InfoTree damage_node; + damage_node.put_attr("index", i); + damage_node.put_attr("base", damage.base_value); + damage_node.put_attr("mult", damage.delta_vitality_multiplier); + damage_node.put_attr("vertical", damage.is_also_vertical); + damage_node.put_attr("death_action", damage.death_action); + damage_root.add_child("kick", damage_node); + } + + root.add_child("damage_kicks", damage_root); +} + void parse_mml_damage_kicks(const InfoTree& root) { // back up old values first @@ -3965,3 +3983,17 @@ void parse_mml_monsters(const InfoTree& root) monster_must_be_exterminated[index] = exterminate; } } + +void write_net_mml_monsters(InfoTree& root) +{ + InfoTree monster_root; + for (int i = 0; i < NUMBER_OF_MONSTER_TYPES; i++) + { + InfoTree monster_node; + monster_node.put_attr("index", i); + monster_node.put_attr("must_be_exterminated", monster_must_be_exterminated[i]); + monster_root.add_child("monster", monster_node); + } + + root.add_child("monsters", monster_root); +} \ No newline at end of file diff --git a/Source_Files/GameWorld/monsters.h b/Source_Files/GameWorld/monsters.h index 799ea8a24..f7fd537bb 100644 --- a/Source_Files/GameWorld/monsters.h +++ b/Source_Files/GameWorld/monsters.h @@ -345,8 +345,10 @@ void init_monster_definitions(); class InfoTree; void parse_mml_damage_kicks(const InfoTree& root); +void write_net_mml_damage_kicks(InfoTree& root); void reset_mml_damage_kicks(); void parse_mml_monsters(const InfoTree& root); +void write_net_mml_monsters(InfoTree& root); void reset_mml_monsters(); #endif diff --git a/Source_Files/GameWorld/platforms.cpp b/Source_Files/GameWorld/platforms.cpp index 3d0ba2236..01fa1a7db 100644 --- a/Source_Files/GameWorld/platforms.cpp +++ b/Source_Files/GameWorld/platforms.cpp @@ -1289,3 +1289,19 @@ void parse_mml_platforms(const InfoTree& root) } } } + +void write_net_mml_platforms(InfoTree& root) +{ + InfoTree platforms_root; + for (int i = 0; i < NUMBER_OF_PLATFORM_TYPES; i++) + { + const auto& platform = platform_definitions[i]; + InfoTree platform_node; + platform_node.put_attr("index", i); + platform_node.put_attr("item", platform.key_item_index); + platform_node.write_damage(platform.damage); + platforms_root.add_child("platform", platform_node); + } + + root.add_child("platforms", platforms_root); +} \ No newline at end of file diff --git a/Source_Files/GameWorld/platforms.h b/Source_Files/GameWorld/platforms.h index 69789a952..809a355a8 100644 --- a/Source_Files/GameWorld/platforms.h +++ b/Source_Files/GameWorld/platforms.h @@ -318,6 +318,7 @@ uint8 *pack_platform_data(uint8 *Stream, platform_data *Objects, size_t Count); class InfoTree; void parse_mml_platforms(const InfoTree& root); +void write_net_mml_platforms(InfoTree& root); void reset_mml_platforms(); #endif diff --git a/Source_Files/GameWorld/player.cpp b/Source_Files/GameWorld/player.cpp index 373a75c40..4ddf32630 100644 --- a/Source_Files/GameWorld/player.cpp +++ b/Source_Files/GameWorld/player.cpp @@ -2551,3 +2551,51 @@ void parse_mml_player(const InfoTree& root) } } +void write_net_mml_player(InfoTree& root) +{ + InfoTree player_root; + player_root.put_attr("energy", player_settings.InitialEnergy); + player_root.put_attr("oxygen", player_settings.InitialOxygen); + player_root.put_attr("stripped", player_settings.StrippedEnergy); + player_root.put_attr("oxygen_deplete", player_settings.OxygenDepletion); + player_root.put_attr("oxygen_replenish", player_settings.OxygenReplenishment); + player_root.put_attr("vulnerability", player_settings.Vulnerability); + player_root.put_attr("guided", player_settings.PlayerShotsGuided); + player_root.put_attr("half_visual_arc", player_settings.PlayerHalfVisualArc); + player_root.put_attr("half_vertical_visual_arc", player_settings.PlayerHalfVertVisualArc); + player_root.put_attr("visual_range", player_settings.PlayerHalfVisualArc); + player_root.put_attr("dark_visual_range", player_settings.PlayerHalfVertVisualArc); + player_root.put_attr("single_energy", player_settings.SingleEnergy); + player_root.put_attr("double_energy", player_settings.DoubleEnergy); + player_root.put_attr("triple_energy", player_settings.TripleEnergy); + player_root.put_attr("can_swim", player_settings.CanSwim); + + for (int i = 0; i < NUMBER_OF_PLAYER_INITIAL_ITEMS; i++) + { + InfoTree item_node; + item_node.put_attr("index", i); + item_node.put_attr("type", player_initial_items[i]); + player_root.add_child("item", item_node); + } + + InfoTree powerup_assign_node; + powerup_assign_node.put_attr("invincibility", player_powerups.Powerup_Invincibility); + powerup_assign_node.put_attr("invisibility", player_powerups.Powerup_Invisibility); + powerup_assign_node.put_attr("infravision", player_powerups.Powerup_Infravision); + powerup_assign_node.put_attr("extravision", player_powerups.Powerup_Extravision); + powerup_assign_node.put_attr("triple_energy", player_powerups.Powerup_TripleEnergy); + powerup_assign_node.put_attr("double_energy", player_powerups.Powerup_DoubleEnergy); + powerup_assign_node.put_attr("energy", player_powerups.Powerup_Energy); + powerup_assign_node.put_attr("oxygen", player_powerups.Powerup_Oxygen); + player_root.add_child("powerup_assign", powerup_assign_node); + + InfoTree powerup_node; + powerup_node.put_attr("invincibility", kINVINCIBILITY_DURATION); + powerup_node.put_attr("invisibility", kINVISIBILITY_DURATION); + powerup_node.put_attr("infravision", kINFRAVISION_DURATION); + powerup_node.put_attr("extravision", kEXTRAVISION_DURATION); + player_root.add_child("powerup", powerup_node); + + root.add_child("player", player_root); +} + diff --git a/Source_Files/GameWorld/player.h b/Source_Files/GameWorld/player.h index c1cc45bd9..caf42979d 100644 --- a/Source_Files/GameWorld/player.h +++ b/Source_Files/GameWorld/player.h @@ -580,6 +580,7 @@ int get_ticks_since_local_player_in_terminal(); class InfoTree; void parse_mml_player(const InfoTree& root); +void write_net_mml_player(InfoTree& root); void reset_mml_player(); #endif diff --git a/Source_Files/GameWorld/scenery.cpp b/Source_Files/GameWorld/scenery.cpp index 3dceac1df..1907a9372 100644 --- a/Source_Files/GameWorld/scenery.cpp +++ b/Source_Files/GameWorld/scenery.cpp @@ -306,3 +306,19 @@ void parse_mml_scenery(const InfoTree& root) reset_scenery_solidity(); } + +void write_net_mml_scenery(InfoTree& root) +{ + InfoTree scenery_root; + for (int i = 0; i < NUMBER_OF_SCENERY_DEFINITIONS; i++) + { + const auto& scenery = scenery_definitions[i]; + InfoTree scenery_node; + scenery_node.put_attr("index", i); + scenery_node.put_attr("flags", scenery.flags); + scenery_node.put_attr("destruction", scenery.destroyed_effect); + scenery_root.add_child("object", scenery_node); + } + + root.add_child("scenery", scenery_root); +} \ No newline at end of file diff --git a/Source_Files/GameWorld/scenery.h b/Source_Files/GameWorld/scenery.h index 7ae309e2d..8f76498ef 100644 --- a/Source_Files/GameWorld/scenery.h +++ b/Source_Files/GameWorld/scenery.h @@ -51,6 +51,7 @@ bool get_damaged_scenery_collection(short scenery_type, short& collection); class InfoTree; void parse_mml_scenery(const InfoTree& root); +void write_net_mml_scenery(InfoTree& root); void reset_mml_scenery(); #endif diff --git a/Source_Files/GameWorld/weapons.cpp b/Source_Files/GameWorld/weapons.cpp index f8549466c..7eb175560 100644 --- a/Source_Files/GameWorld/weapons.cpp +++ b/Source_Files/GameWorld/weapons.cpp @@ -4580,3 +4580,17 @@ void parse_mml_weapons(const InfoTree& root) order.read_indexed("weapon", weapon_ordering_array[index], NUMBER_OF_WEAPONS); } } + +void write_net_mml_weapons(InfoTree& root) +{ + InfoTree weapons_root; + for (int i = 0; i < NUMBER_OF_WEAPONS; i++) + { + InfoTree weapon_node; + weapon_node.put_attr("index", i); + weapon_node.put_attr("weapon", weapon_ordering_array[i]); + weapons_root.add_child("order", weapon_node); + } + + root.add_child("weapons", weapons_root); +} \ No newline at end of file diff --git a/Source_Files/GameWorld/weapons.h b/Source_Files/GameWorld/weapons.h index 41d8ca040..e18a041f2 100644 --- a/Source_Files/GameWorld/weapons.h +++ b/Source_Files/GameWorld/weapons.h @@ -220,6 +220,7 @@ size_t get_number_of_weapon_types(); class InfoTree; void parse_mml_weapons(const InfoTree& root); +void write_net_mml_weapons(InfoTree& root); void reset_mml_weapons(); #endif diff --git a/Source_Files/Misc/interface.cpp b/Source_Files/Misc/interface.cpp index e171b0af3..cbd7ab5a8 100644 --- a/Source_Files/Misc/interface.cpp +++ b/Source_Files/Misc/interface.cpp @@ -670,6 +670,7 @@ static bool make_restored_game_relevant(bool inNetgame, const player_start_data* // ZZZ: this will get called (eventually) shortly after NetUpdateJoinState() returns netStartingResumeGame bool join_networked_resume_game() { + ResetLevelScript(); bool success = true; // Get the saved-game data @@ -695,7 +696,6 @@ bool join_networked_resume_game() bool found_map = false; if(success) { - ResetLevelScript(); uint32 theParentChecksum = theWadHeader.parent_checksum; found_map = use_map_file(theParentChecksum); diff --git a/Source_Files/Misc/interface.h b/Source_Files/Misc/interface.h index 728c178f9..54ff2b5e2 100644 --- a/Source_Files/Misc/interface.h +++ b/Source_Files/Misc/interface.h @@ -455,6 +455,7 @@ class InfoTree; void parse_mml_infravision(const InfoTree& root); void reset_mml_infravision(); void parse_mml_control_panels(const InfoTree& root); +void write_net_mml_panels(InfoTree& root); void reset_mml_control_panels(); #endif diff --git a/Source_Files/Misc/preferences.cpp b/Source_Files/Misc/preferences.cpp index 4042e2b93..b1b9d752e 100644 --- a/Source_Files/Misc/preferences.cpp +++ b/Source_Files/Misc/preferences.cpp @@ -1538,8 +1538,8 @@ static void graphics_dialog(void *arg) write_preferences(); ResetAllMMLValues(); - LoadBaseMMLScripts(true); - Plugins::instance()->load_mml(true); + LoadBaseMMLScripts(mml_loading_mode::menu_only); + Plugins::instance()->load_mml(mml_loading_mode::menu_only); change_screen_mode(&graphics_preferences->screen_mode, true); clear_screen(true); @@ -3091,8 +3091,8 @@ static void plugins_dialog(void* arg) write_preferences(); ResetAllMMLValues(); - LoadBaseMMLScripts(true); - Plugins::instance()->load_mml(true); + LoadBaseMMLScripts(mml_loading_mode::menu_only); + Plugins::instance()->load_mml(mml_loading_mode::menu_only); Plugins::instance()->set_map_checksum(get_current_map_checksum()); LoadLevelScripts(get_map_file()); diff --git a/Source_Files/Network/StandaloneHub/StandaloneHub.cpp b/Source_Files/Network/StandaloneHub/StandaloneHub.cpp index 33de155e5..81b81d521 100644 --- a/Source_Files/Network/StandaloneHub/StandaloneHub.cpp +++ b/Source_Files/Network/StandaloneHub/StandaloneHub.cpp @@ -152,6 +152,7 @@ bool StandaloneHub::GetGameDataFromGatherer() _map_message.reset(); _physics_message.reset(); _lua_message.reset(); + _mml_message.reset(); if (auto client = _gatherer ? _gatherer : _gatherer_client.lock()) { @@ -175,9 +176,12 @@ bool StandaloneHub::GetGameDataFromGatherer() case kZIPPED_MAP_MESSAGE: _map_message = std::unique_ptr(static_cast(message)); break; + case kZIPPED_MML_MESSAGE: + _mml_message = std::unique_ptr(static_cast(message)); + break; case kEND_GAME_DATA_MESSAGE: delete message; - return _map_message && _topology_message; + return _map_message && _topology_message && _mml_message; default: delete message; break; @@ -210,4 +214,12 @@ int StandaloneHub::GetLuaData(uint8** data) *data = _lua_message->buffer(); return _lua_message->length(); +} + +int StandaloneHub::GetMMLData(uint8** data) +{ + if (!_mml_message) return 0; + + *data = _mml_message->buffer(); + return _mml_message->length(); } \ No newline at end of file diff --git a/Source_Files/Network/StandaloneHub/StandaloneHub.h b/Source_Files/Network/StandaloneHub/StandaloneHub.h index cb3ee9152..29581ab95 100644 --- a/Source_Files/Network/StandaloneHub/StandaloneHub.h +++ b/Source_Files/Network/StandaloneHub/StandaloneHub.h @@ -34,6 +34,7 @@ class StandaloneHub { std::unique_ptr _lua_message; std::unique_ptr _map_message; std::unique_ptr _physics_message; + std::unique_ptr _mml_message; int _port; bool _start_game_signal = false; bool _end_game_signal = false; @@ -62,6 +63,7 @@ class StandaloneHub { int GetMapData(uint8** data); int GetPhysicsData(uint8** data); int GetLuaData(uint8** data); + int GetMMLData(uint8** data); }; #endif \ No newline at end of file diff --git a/Source_Files/Network/network.cpp b/Source_Files/Network/network.cpp index 2c67be571..967d0f250 100644 --- a/Source_Files/Network/network.cpp +++ b/Source_Files/Network/network.cpp @@ -971,6 +971,26 @@ static void handleLuaMessage(BigChunkOfDataMessage *luaMessage, CommunicationsCh } } +static byte* handlerMMLBuffer = NULL; +static size_t handlerMMLLength = 0; + +static void handleMMLMessage(BigChunkOfDataMessage* mmlMessage, CommunicationsChannel*) { + if (netState == netStartingUp || netState == netDown) { + if (handlerMMLBuffer) { + delete[] handlerMMLBuffer; + handlerMMLBuffer = NULL; + } + handlerMMLLength = mmlMessage->length(); + if (handlerMMLLength > 0) { + handlerMMLBuffer = new byte[handlerMMLLength]; + memcpy(handlerMMLBuffer, mmlMessage->buffer(), handlerMMLLength); + } + } + else { + logAnomaly("unexpected MML message received (netState is %i)", netState); + } +} + static byte *handlerMapBuffer = NULL; static size_t handlerMapLength = 0; @@ -1179,6 +1199,7 @@ static void handleUnexpectedMessage(Message *inMessage, CommunicationsChannel *) static TypedMessageHandlerFunction helloMessageHandler(&handleHelloMessage); static TypedMessageHandlerFunction joinPlayerMessageHandler(&handleJoinPlayerMessage); static TypedMessageHandlerFunction luaMessageHandler(&handleLuaMessage); +static TypedMessageHandlerFunction mmlMessageHandler(&handleMMLMessage); static TypedMessageHandlerFunction mapMessageHandler(&handleMapMessage); static TypedMessageHandlerFunction networkChatMessageHandler(&handleNetworkChatMessage); static TypedMessageHandlerFunction physicsMessageHandler(&handlePhysicsMessage); @@ -1313,6 +1334,7 @@ bool NetEnter(bool use_remote_hub) inflater->learnPrototype(JoinPlayerMessage()); inflater->learnPrototype(LuaMessage()); inflater->learnPrototype(ZippedLuaMessage()); + inflater->learnPrototype(ZippedMMLMessage()); inflater->learnPrototype(MapMessage()); inflater->learnPrototype(ZippedMapMessage()); inflater->learnPrototype(NetworkChatMessage()); @@ -1339,6 +1361,7 @@ bool NetEnter(bool use_remote_hub) joinDispatcher->setHandlerForType(&joinPlayerMessageHandler, JoinPlayerMessage::kType); joinDispatcher->setHandlerForType(&luaMessageHandler, LuaMessage::kType); joinDispatcher->setHandlerForType(&luaMessageHandler, ZippedLuaMessage::kType); + joinDispatcher->setHandlerForType(&mmlMessageHandler, ZippedMMLMessage::kType); joinDispatcher->setHandlerForType(&mapMessageHandler, MapMessage::kType); joinDispatcher->setHandlerForType(&mapMessageHandler, ZippedMapMessage::kType); joinDispatcher->setHandlerForType(&networkChatMessageHandler, NetworkChatMessage::kType); @@ -2470,6 +2493,21 @@ OSErr NetDistributeGameDataToAllPlayers(byte *wad_buffer, } } + byte* mml_buffer = NULL; + size_t mml_length; + +#ifdef A1_NETWORK_STANDALONE_HUB + mml_length = StandaloneHub::Instance()->GetMMLData(&mml_buffer); +#else + auto mml_data = GenerateMMLForNet(); + mml_length = mml_data.size(); + mml_buffer = mml_data.data(); +#endif + + ZippedMMLMessage zippedMMLMessage(mml_buffer, mml_length); + std::unique_ptr uninflatedMessage(zippedMMLMessage.deflate()); + std::for_each(zipCapableChannels.begin(), zipCapableChannels.end(), std::bind(&CommunicationsChannel::enqueueOutgoingMessage, std::placeholders::_1, *uninflatedMessage)); + #ifdef A1_NETWORK_STANDALONE_HUB lua_length = StandaloneHub::Instance()->GetLuaData(&lua_buffer); SetNetscriptStatus(lua_length); @@ -2572,6 +2610,12 @@ byte *NetReceiveGameData(bool do_physics) } else { do_netscript = false; } + + if (handlerMMLLength > 0) { + ParseMMLFromData((char*)handlerMMLBuffer, handlerMMLLength); + handlerMMLBuffer = NULL; + handlerMMLLength = 0; + } draw_progress_bar(10, 10); close_progress_dialog(); @@ -2594,6 +2638,11 @@ byte *NetReceiveGameData(bool do_physics) handlerLuaBuffer = NULL; handlerLuaLength = 0; } + if (handlerMMLLength > 0) { + delete[] handlerMMLBuffer; + handlerMMLBuffer = NULL; + handlerMMLLength = 0; + } alert_user(infoError, strNETWORK_ERRORS, netErrMapDistribFailed, 1); } diff --git a/Source_Files/Network/network_dialogs.cpp b/Source_Files/Network/network_dialogs.cpp index 7e34c9dbf..426dcae97 100644 --- a/Source_Files/Network/network_dialogs.cpp +++ b/Source_Files/Network/network_dialogs.cpp @@ -188,6 +188,11 @@ static uint16 network_gather_remote_hub() uint16 remote_hub_id = 0; open_progress_dialog(_connecting_to_remote_hub); + load_film_profile(FILM_PROFILE_DEFAULT); + reset_mml_dynamic_limits(); + LoadBaseMMLScripts(mml_loading_mode::net_sync_only); + Plugins::instance()->load_mml(mml_loading_mode::net_sync_only); + const auto& remote_hubs = gMetaserverClient->get_remoteHubServers(); std::unordered_map remote_hubs_pings; @@ -368,7 +373,7 @@ bool GatherDialog::GatherNetworkGameByRunning () m_startWidget->set_callback(std::bind(&GatherDialog::StartGameHit, this)); m_ungatheredWidget->SetItemSelectedCallback(std::bind(&GatherDialog::gathered_player, this, std::placeholders::_1)); - m_startWidget->deactivate (); + m_startWidget->activate (); NetSetGatherCallbacks(this); diff --git a/Source_Files/Network/network_messages.h b/Source_Files/Network/network_messages.h index f552d7e41..a54a37b54 100644 --- a/Source_Files/Network/network_messages.h +++ b/Source_Files/Network/network_messages.h @@ -57,6 +57,7 @@ enum { kREMOTE_HUB_READY_MESSAGE, kREMOTE_HUB_RESPONSE_MESSAGE, kREMOTE_HUB_REQUEST_MESSAGE, + kZIPPED_MML_MESSAGE }; template @@ -344,6 +345,8 @@ typedef TemplatizedDataMessage LuaMessage; typedef TemplatizedDataMessage ZippedLuaMessage; +typedef TemplatizedDataMessage ZippedMMLMessage; + class NetworkChatMessage : public SmallMessageHelper { diff --git a/Source_Files/XML/InfoTree.cpp b/Source_Files/XML/InfoTree.cpp index dfd3ad6ed..6e87f60e4 100644 --- a/Source_Files/XML/InfoTree.cpp +++ b/Source_Files/XML/InfoTree.cpp @@ -316,6 +316,17 @@ bool InfoTree::read_damage(damage_definition& def) const return status; } +void InfoTree::write_damage(const damage_definition& def) +{ + InfoTree damage_node; + damage_node.put_attr("type", def.type); + damage_node.put_attr("flags", def.flags); + damage_node.put_attr("base", def.base); + damage_node.put_attr("random", def.random); + damage_node.put_attr("scale", def.scale); + add_child("damage", damage_node); +} + bool InfoTree::read_font(FontSpecifier& font) const { bool status = false; diff --git a/Source_Files/XML/InfoTree.h b/Source_Files/XML/InfoTree.h index 5278bee49..1f6deae4b 100644 --- a/Source_Files/XML/InfoTree.h +++ b/Source_Files/XML/InfoTree.h @@ -98,6 +98,7 @@ class InfoTree : public boost::property_tree::iptree bool read_color(rgb_color& color) const; bool read_shape(shape_descriptor& descriptor, bool allow_empty = true) const; bool read_damage(damage_definition& definition) const; + void write_damage(const damage_definition& def); bool read_font(FontSpecifier& font) const; bool read_path(std::string key, FileSpecifier& file) const; diff --git a/Source_Files/XML/Plugins.cpp b/Source_Files/XML/Plugins.cpp index bffe2a939..13c1ed5ac 100644 --- a/Source_Files/XML/Plugins.cpp +++ b/Source_Files/XML/Plugins.cpp @@ -157,7 +157,7 @@ bool Plugins::enable(const boost::filesystem::path& path) return false; } -static void load_mmls(const Plugin& plugin, bool load_menu_mml_only) +static void load_mmls(const Plugin& plugin, mml_loading_mode loading_mode) { ScopedSearchPath ssp(plugin.directory); for (std::vector::const_iterator it = plugin.mmls.begin(); it != plugin.mmls.end(); ++it) @@ -165,7 +165,7 @@ static void load_mmls(const Plugin& plugin, bool load_menu_mml_only) FileSpecifier file; if (file.SetNameWithPath(it->c_str())) { - ParseMMLFromFile(file, load_menu_mml_only); + ParseMMLFromFile(file, loading_mode); } else { @@ -174,14 +174,14 @@ static void load_mmls(const Plugin& plugin, bool load_menu_mml_only) } } -void Plugins::load_mml(bool load_menu_mml_only) { +void Plugins::load_mml(mml_loading_mode loading_mode) { validate(); for (std::vector::iterator it = m_plugins.begin(); it != m_plugins.end(); ++it) { if (it->valid()) { - load_mmls(*it, load_menu_mml_only); + load_mmls(*it, loading_mode); } } } diff --git a/Source_Files/XML/Plugins.h b/Source_Files/XML/Plugins.h index 9ddf6a081..5487fb236 100644 --- a/Source_Files/XML/Plugins.h +++ b/Source_Files/XML/Plugins.h @@ -31,7 +31,7 @@ #include #include - +#include "XML_ParseTreeRoot.h" #include "FileHandler.h" struct ScenarioInfo { @@ -93,7 +93,7 @@ class Plugins { void invalidate() { m_validated = false; } void set_mode(GameMode mode) { m_mode = mode; } GameMode mode() { return m_mode; } - void load_mml(bool load_menu_mml_only); + void load_mml(mml_loading_mode loading_mode); void load_shapes_patches(bool opengl); diff --git a/Source_Files/XML/XML_LevelScript.cpp b/Source_Files/XML/XML_LevelScript.cpp index 5679b84f2..694e66b2a 100644 --- a/Source_Files/XML/XML_LevelScript.cpp +++ b/Source_Files/XML/XML_LevelScript.cpp @@ -220,8 +220,8 @@ void ResetLevelScript() // reset values to engine defaults first ResetAllMMLValues(); // then load the base stuff (from Scripts folder and whatnot) - LoadBaseMMLScripts(false); - Plugins::instance()->load_mml(false); + LoadBaseMMLScripts(mml_loading_mode::all); + Plugins::instance()->load_mml(mml_loading_mode::all); } diff --git a/Source_Files/XML/XML_MakeRoot.cpp b/Source_Files/XML/XML_MakeRoot.cpp index c300f8172..6ba81d2c8 100644 --- a/Source_Files/XML/XML_MakeRoot.cpp +++ b/Source_Files/XML/XML_MakeRoot.cpp @@ -91,78 +91,112 @@ void ResetAllMMLValues() reset_mml_default_levels(); } -static void _ParseAllMML(const InfoTree& fileroot, bool load_menu_mml_only) +static void _ParseAllMML(const InfoTree& fileroot, mml_loading_mode loading_mode) { for (const InfoTree &root : fileroot.children_named("marathon")) { - for (const InfoTree &child : root.children_named("stringset")) - parse_mml_stringset(child); - for (const InfoTree &child : root.children_named("interface")) - parse_mml_interface(child); - for (const InfoTree& child : root.children_named("player_name")) - parse_mml_player_name(child); - for (const InfoTree& child : root.children_named("scenario")) - parse_mml_scenario(child); - for (const InfoTree& child : root.children_named("logging")) - parse_mml_logging(child); - for (const InfoTree& child : root.children_named("sounds")) - parse_mml_sounds(child); - for (const InfoTree& child : root.children_named("faders")) - parse_mml_faders(child); - - if (load_menu_mml_only) continue; - - for (const InfoTree &child : root.children_named("motion_sensor")) - parse_mml_motion_sensor(child); - for (const InfoTree &child : root.children_named("overhead_map")) - parse_mml_overhead_map(child); - for (const InfoTree &child : root.children_named("infravision")) - parse_mml_infravision(child); - for (const InfoTree &child : root.children_named("animated_textures")) - parse_mml_animated_textures(child); - for (const InfoTree &child : root.children_named("control_panels")) - parse_mml_control_panels(child); - for (const InfoTree &child : root.children_named("platforms")) - parse_mml_platforms(child); - for (const InfoTree &child : root.children_named("liquids")) - parse_mml_liquids(child); - for (const InfoTree &child : root.children_named("player")) - parse_mml_player(child); - for (const InfoTree &child : root.children_named("view")) - parse_mml_view(child); - for (const InfoTree &child : root.children_named("weapons")) - parse_mml_weapons(child); - for (const InfoTree &child : root.children_named("items")) - parse_mml_items(child); - for (const InfoTree &child : root.children_named("damage_kicks")) - parse_mml_damage_kicks(child); - for (const InfoTree &child : root.children_named("monsters")) - parse_mml_monsters(child); - for (const InfoTree &child : root.children_named("scenery")) - parse_mml_scenery(child); - for (const InfoTree &child : root.children_named("landscapes")) - parse_mml_landscapes(child); - for (const InfoTree &child : root.children_named("texture_loading")) - parse_mml_texture_loading(child); - for (const InfoTree &child : root.children_named("opengl")) - parse_mml_opengl(child); - for (const InfoTree &child : root.children_named("software")) - parse_mml_software(child); - for (const InfoTree &child : root.children_named("dynamic_limits")) - parse_mml_dynamic_limits(child); - for (const InfoTree &child : root.children_named("console")) - parse_mml_console(child); - for (const InfoTree &child : root.children_named("default_levels")) - parse_mml_default_levels(child); + if (loading_mode != mml_loading_mode::net_sync_only) + { + for (const InfoTree& child : root.children_named("stringset")) + parse_mml_stringset(child); + for (const InfoTree& child : root.children_named("interface")) + parse_mml_interface(child); + for (const InfoTree& child : root.children_named("player_name")) + parse_mml_player_name(child); + for (const InfoTree& child : root.children_named("scenario")) + parse_mml_scenario(child); + for (const InfoTree& child : root.children_named("logging")) + parse_mml_logging(child); + for (const InfoTree& child : root.children_named("sounds")) + parse_mml_sounds(child); + for (const InfoTree& child : root.children_named("faders")) + parse_mml_faders(child); + } + + if (loading_mode == mml_loading_mode::all) + { + for (const InfoTree& child : root.children_named("motion_sensor")) + parse_mml_motion_sensor(child); + for (const InfoTree& child : root.children_named("overhead_map")) + parse_mml_overhead_map(child); + for (const InfoTree& child : root.children_named("infravision")) + parse_mml_infravision(child); + for (const InfoTree& child : root.children_named("animated_textures")) + parse_mml_animated_textures(child); + for (const InfoTree& child : root.children_named("view")) + parse_mml_view(child); + for (const InfoTree& child : root.children_named("landscapes")) + parse_mml_landscapes(child); + for (const InfoTree& child : root.children_named("texture_loading")) + parse_mml_texture_loading(child); + for (const InfoTree& child : root.children_named("opengl")) + parse_mml_opengl(child); + for (const InfoTree& child : root.children_named("software")) + parse_mml_software(child); + for (const InfoTree& child : root.children_named("console")) + parse_mml_console(child); + for (const InfoTree& child : root.children_named("default_levels")) + parse_mml_default_levels(child); + } + + if (loading_mode != mml_loading_mode::menu_only) + { + for (const InfoTree& child : root.children_named("control_panels")) + parse_mml_control_panels(child); + for (const InfoTree& child : root.children_named("platforms")) + parse_mml_platforms(child); + for (const InfoTree& child : root.children_named("liquids")) + parse_mml_liquids(child); + for (const InfoTree& child : root.children_named("player")) + parse_mml_player(child); + for (const InfoTree& child : root.children_named("weapons")) + parse_mml_weapons(child); + for (const InfoTree& child : root.children_named("items")) + parse_mml_items(child); + for (const InfoTree& child : root.children_named("damage_kicks")) + parse_mml_damage_kicks(child); + for (const InfoTree& child : root.children_named("monsters")) + parse_mml_monsters(child); + for (const InfoTree& child : root.children_named("scenery")) + parse_mml_scenery(child); + for (const InfoTree& child : root.children_named("dynamic_limits")) + parse_mml_dynamic_limits(child); + } } } -bool ParseMMLFromFile(const FileSpecifier& FileSpec, bool load_menu_mml_only) +std::vector GenerateMMLForNet() +{ + InfoTree root; + InfoTree marathon_root; + + write_net_mml_player(marathon_root); + write_net_mml_weapons(marathon_root); + write_net_mml_monsters(marathon_root); + write_net_mml_platforms(marathon_root); + write_net_mml_panels(marathon_root); + write_net_mml_items(marathon_root); + write_net_mml_damage_kicks(marathon_root); + write_net_mml_scenery(marathon_root); + write_net_mml_liquids(marathon_root); + write_net_mml_dynamic_limits(marathon_root); + + root.add_child("marathon", marathon_root); + + std::ostringstream oss; + root.save_xml(oss); + + auto mml = oss.str(); + std::vector mml_data(mml.begin(), mml.end()); + return mml_data; +} + +bool ParseMMLFromFile(const FileSpecifier& FileSpec, mml_loading_mode loading_mode) { bool parse_error = false; try { InfoTree fileroot = InfoTree::load_xml(FileSpec); - _ParseAllMML(fileroot, load_menu_mml_only); + _ParseAllMML(fileroot, loading_mode); } catch (const InfoTree::parse_error& ex) { logError("Error parsing MML file (%s): %s", FileSpec.GetPath(), ex.what()); parse_error = true; @@ -185,7 +219,7 @@ bool ParseMMLFromData(const char *buffer, size_t buflen) try { std::istringstream strm(std::string(buffer, buflen)); InfoTree fileroot = InfoTree::load_xml(strm); - _ParseAllMML(fileroot, false); + _ParseAllMML(fileroot, mml_loading_mode::all); } catch (const InfoTree::parse_error& ex) { logError("Error parsing MML data: %s", ex.what()); parse_error = true; diff --git a/Source_Files/XML/XML_ParseTreeRoot.h b/Source_Files/XML/XML_ParseTreeRoot.h index 855f11a00..9aa18f3c6 100644 --- a/Source_Files/XML/XML_ParseTreeRoot.h +++ b/Source_Files/XML/XML_ParseTreeRoot.h @@ -29,11 +29,20 @@ */ #include +#include + +enum class mml_loading_mode +{ + all, + menu_only, + net_sync_only +}; extern void ResetAllMMLValues(); // reset everything that's been changed to hard-coded defaults class FileSpecifier; -extern bool ParseMMLFromFile(const FileSpecifier& filespec, bool load_menu_mml_only); +extern bool ParseMMLFromFile(const FileSpecifier& filespec, mml_loading_mode loading_mode); extern bool ParseMMLFromData(const char *buffer, size_t buflen); +extern std::vector GenerateMMLForNet(); #endif diff --git a/Source_Files/shell.cpp b/Source_Files/shell.cpp index c209ad24c..955d77914 100644 --- a/Source_Files/shell.cpp +++ b/Source_Files/shell.cpp @@ -478,7 +478,7 @@ void initialize_application(void) load_film_profile(FILM_PROFILE_DEFAULT); // Parse MML files - LoadBaseMMLScripts(true); + LoadBaseMMLScripts(mml_loading_mode::menu_only); // Check for presence of strings if (!TS_IsPresent(strERRORS) || !TS_IsPresent(strFILENAMES)) { @@ -499,7 +499,7 @@ void initialize_application(void) // Parse MML files again, now that we have a new dir to search initialize_fonts(false); - LoadBaseMMLScripts(true); + LoadBaseMMLScripts(mml_loading_mode::menu_only); } } @@ -569,7 +569,7 @@ void initialize_application(void) graphics_preferences->screen_mode.fullscreen = false; write_preferences(); - Plugins::instance()->load_mml(true); + Plugins::instance()->load_mml(mml_loading_mode::menu_only); // SDL_WM_SetCaption(application_name, application_name); @@ -1684,7 +1684,7 @@ void dump_screen(void) #endif } -static bool _ParseMMLDirectory(DirectorySpecifier& dir, bool load_menu_mml_only) +static bool _ParseMMLDirectory(DirectorySpecifier& dir, mml_loading_mode loading_mode) { // Get sorted list of files in directory vector de; @@ -1707,20 +1707,20 @@ static bool _ParseMMLDirectory(DirectorySpecifier& dir, bool load_menu_mml_only) FileSpecifier file_name = dir + i->name; // Parse file - ParseMMLFromFile(file_name, load_menu_mml_only); + ParseMMLFromFile(file_name, loading_mode); } return true; } -void LoadBaseMMLScripts(bool load_menu_mml_only) +void LoadBaseMMLScripts(mml_loading_mode loading_mode) { vector ::const_iterator i = data_search_path.begin(), end = data_search_path.end(); while (i != end) { DirectorySpecifier path = *i + "MML"; - _ParseMMLDirectory(path, load_menu_mml_only); + _ParseMMLDirectory(path, loading_mode); path = *i + "Scripts"; - _ParseMMLDirectory(path, load_menu_mml_only); + _ParseMMLDirectory(path, loading_mode); i++; } } diff --git a/Source_Files/shell.h b/Source_Files/shell.h index 64c9badfd..10f9fbbc9 100644 --- a/Source_Files/shell.h +++ b/Source_Files/shell.h @@ -40,6 +40,7 @@ Dec 29, 2000 (Loren Petrich): */ #include "cstypes.h" +#include "XML_ParseTreeRoot.h" #include class FileSpecifier; @@ -109,7 +110,7 @@ enum // input devices void global_idle_proc(void); // Load the base MML scripts: -void LoadBaseMMLScripts(bool load_menu_mml_only); +void LoadBaseMMLScripts(mml_loading_mode load_menu_mml_only); // Application and directory info: char *expand_symbolic_paths(char *dest, const char *src, int maxlen);