Skip to content

Commit

Permalink
starting batch of MP support
Browse files Browse the repository at this point in the history
  • Loading branch information
chadvandy committed Sep 24, 2022
1 parent a01a1a3 commit e5e376e
Show file tree
Hide file tree
Showing 13 changed files with 370 additions and 939 deletions.
58 changes: 10 additions & 48 deletions script/_lib/mod/mct.lua
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,13 @@ function mct:load_modules()
self._MCT_PROFILE = load_module("profile", obj_path)

---@type MCT.Registry
self.registry = load_module("registry", core_path)
self.registry = load_module("registry", obj_path)

self.ui = load_module("main", ui_path)

---@type MCT.Sync
self.sync = load_module("sync", obj_path)

if __game_mode == __lib_type_battle then
load_module("battle", ui_path)
elseif __game_mode == __lib_type_campaign then
Expand Down Expand Up @@ -157,11 +160,6 @@ function mct:get_option_type(key)
return self._MCT_TYPES[key]
end

--- TODO kill?
function mct:mp_prep()

end

--- TODO clean this the fuck up
function mct:load_and_start(loading_game_context, is_mp)
self._initialized = true
Expand Down Expand Up @@ -190,48 +188,12 @@ function mct:load_and_start(loading_game_context, is_mp)

if __game_mode == __lib_type_campaign then
if is_mp then
out("Is MP! Pre-pre-first-tick")
cm:add_pre_first_tick_callback(function()
out("Pre first tick callback in mct_mp")
if not cm:get_saved_value("mct_mp_init") then
out("MP INIT not done")
local my_faction = cm:get_local_faction_name(true)
--[[local their_faction = ""
local faction_keys = cm:get_human_factions()
if faction_keys[1] == my_faction then
their_faction = faction_keys[2]
else
their_faction = faction_keys[1]
end]]

local is_host = core:svr_load_bool("local_is_host")
if is_host then
CampaignUI.TriggerCampaignScriptEvent(0, "mct_host|"..my_faction)
end

-- if not cm:get_saved_value("mct_mp_init") then
cm:set_saved_value("mct_mp_init", true)
-- end
--self.settings:mp_load()

--trigger()
else
out("MP INIT done")
-- self.settings:mp_load()
-- -- trigger during pre-first-tick-callback to prevent time fuckery
-- trigger(true)
end
end)
out("Pre load game callback")
self.registry:load_game(loading_game_context)
out("Post load game callback")
--trigger(true)

self:mp_prep()
out("Post mp_prep()")

VLib.Log("Pre load game callback")
self.registry:load(loading_game_context)
VLib.Log("Post load game callback")
-- self.registry:load_game(loading_game_context)

cm:add_saving_game_callback(function(context) out("save game callback pre") self.registry:save_game(context) end)
trigger(true)
else
self.registry:load(loading_game_context)

Expand Down Expand Up @@ -337,7 +299,7 @@ function mct:finalize()
end
end
end
ClMultiplayerEvents.notifyEvent("MctMpFinalized", 0, mct_data)
MultiplayerCommunicator:TriggerEvent("MctMpFinalized", 0, mct_data)

self.registry:local_only_finalize(true)
else
Expand Down
130 changes: 65 additions & 65 deletions script/_lib/mod/mp_event_communication.lua
Original file line number Diff line number Diff line change
@@ -1,90 +1,87 @@
--- TODO clean this up a bit!

--- 100% credit to Vanishoxyact. He built this system for easier support of long strings being passed between PC's, because there's a limit to the size of the strings that can be sent. Thanks Vanish!

if not core:is_campaign() then
return
end

---@class VanishMP
ClMultiplayerEvents = {};
local SEPARATOR = "|";
local MAX_EVENT_STR_LENGTH = 100;

local currentEventParts = {};

-- Receiving
local function processSinglePartEvent(payload, callback)
local tableFunc = loadstring(payload);
local table = tableFunc();
callback(table);
---@class MP_Communicator
MultiplayerCommunicator = {
__separator = "|",
__max_str_len = 100,

current_event_parts = {},
}

---@param payload string
---@param callback function
function MultiplayerCommunicator:process_single_part_event(payload, callback)
local table_func = loadstring(payload);
if table_func then
local table = table_func();
callback(table);
end
end

local function processMultiPartEvent(partNumber, totalParts, payload, callback)
out("MultiplayerEvents: processing multipart event " .. partNumber .. " of " .. totalParts .. " payload " .. payload);
local partNumberValue = tonumber(partNumber, 10);
local totalPartsNumber = tonumber(totalParts, 10);
currentEventParts[partNumberValue] = payload;
if partNumberValue == totalPartsNumber then
---@param part_number number
---@param total number
---@param payload string
---@param callback function
function MultiplayerCommunicator:process_multiple_part_event(part_number, total, payload, callback)
out("MultiplayerEvents: processing multipart event " .. part_number .. " of " .. total .. " payload " .. payload);

self.current_event_parts[part_number] = payload;
if part_number == total then
out("MultiplayerEvents: last part of multipart event received, recombining.");
local completeEvent = "";
for i=1, totalPartsNumber do
completeEvent = completeEvent .. currentEventParts[i];
for i=1, total do
completeEvent = completeEvent .. self.current_event_parts[i];
end
out("MultiplayerEvents: recombine successful, processing event with full payload - " .. completeEvent);
processSinglePartEvent(completeEvent, callback);
currentEventParts = {};
self:process_single_part_event(completeEvent, callback);
self.current_event_parts = {};
end
end

local function processReceivedEvent(expectedEventName, eventContext, callback)
out("MultiplayerEvents: received event - " .. eventContext);
local eventName, partNumber, totalParts, payload = string.match(eventContext, "(.-)" .. SEPARATOR .. "(%d-)/(%d-)" .. SEPARATOR .. "(.*)");
if eventName and partNumber and totalParts and payload then
if eventName ~= expectedEventName then
out("MultiplayerEvents: error processing event. Expected event name was " .. expectedEventName .. " but got " .. eventName);
function MultiplayerCommunicator:process_event(expected_event_name, event_context, callback)
out("MultiplayerEvents: received event - " .. event_context);
local event_name, part_number, total_parts, payload = string.match(event_context, "(.-)" .. self.__separator .. "(%d-)/(%d-)" .. self.__separator .. "(.*)");
if event_name and part_number and total_parts and payload then
if event_name ~= expected_event_name then
out("MultiplayerEvents: error processing event. Expected event name was " .. expected_event_name .. " but got " .. event_name);
return;
end
processMultiPartEvent(partNumber, totalParts, payload, callback);
self:process_multiple_part_event(tonumber(part_number), tonumber(total_parts), payload, callback);
else
local eventName, payload = string.match(eventContext, "(.-)" .. SEPARATOR .. "(.*)");
if eventName ~= expectedEventName then
out("MultiplayerEvents: error processing event. Expected event name was " .. expectedEventName .. " but got " .. eventName);
local eventName, payload = string.match(event_context, "(.-)" .. self.__separator .. "(.*)");
if eventName ~= expected_event_name then
out("MultiplayerEvents: error processing event. Expected event name was " .. expected_event_name .. " but got " .. eventName);
return;
end
processSinglePartEvent(payload, callback);
self:process_single_part_event(payload, callback);
end
end

--v function(eventName: string, listenerName:string, callback: function(map<string, string>))
function ClMultiplayerEvents.registerForEvent(eventName, listenerName, callback)
function MultiplayerCommunicator:RegisterForEvent(event_name, listener_name, callback)
core:add_listener(
listenerName,
listener_name,
"UITrigger",
function(context)
return context:trigger():starts_with(eventName .. SEPARATOR);
return context:trigger():starts_with(event_name .. self.__separator);
end,
function(context)
processReceivedEvent(eventName, context:trigger(), callback);
self:process_event(event_name, context:trigger(), callback);
end,
true
);
end


-- Sending
--v function(tab: any) --> string
local function GetTableSaveState(tab)
local ret = "return {"..cm:process_table_save(tab).."}";
return ret;
end

local function sendEvent(factionCqi, eventString)
local function send_event(factionCqi, eventString)
out("MultiplayerEvents: sending event to other player - " .. eventString);
CampaignUI.TriggerCampaignScriptEvent(factionCqi, eventString);
end

local function createSplitEvent(eventNumber, totalEventCount, eventPayloadCapacity, tableString)
local function create_split_event_string(eventNumber, totalEventCount, eventPayloadCapacity, tableString)
local countLeftString = tostring(eventNumber);
for i=string.len(countLeftString), 2 do
countLeftString = "0" .. countLeftString;
Expand All @@ -94,35 +91,38 @@ local function createSplitEvent(eventNumber, totalEventCount, eventPayloadCapaci
countRightString = "0" .. countRightString;
end
local eventPayloadPart = string.sub(tableString, eventPayloadCapacity * (eventNumber - 1) + 1, eventPayloadCapacity * eventNumber);
local fullEventString = countLeftString .. "/" .. countRightString .. SEPARATOR .. eventPayloadPart;
local fullEventString = countLeftString .. "/" .. countRightString .. MultiplayerCommunicator.__separator .. eventPayloadPart;
return fullEventString;
end

local function splitEventAndNotify(eventName, factionCqi, tableString)
local function split_event_and_notify(eventName, factionCqi, tableString)
local tableStringLength = string.len(tableString);
local eventPrefix = eventName .. SEPARATOR;
local fullEventPrefixLength = string.len(eventPrefix .. "xxx/xxx|");
local eventPayloadCapacity = MAX_EVENT_STR_LENGTH - fullEventPrefixLength;
local eventPrefix = eventName .. MultiplayerCommunicator.__separator;
local fullEventPrefixLength = string.len(eventPrefix .. "xxx/xxx" .. MultiplayerCommunicator.__separator);
local eventPayloadCapacity = MultiplayerCommunicator.__max_str_len - fullEventPrefixLength;
local totalEventCount = math.ceil(tableStringLength / eventPayloadCapacity);
out("MultiplayerEvents: splitting " .. eventName .. " event into " .. tostring(totalEventCount) .. " events - full payload is " .. tableString);

for i=1, totalEventCount do
local splitEvent = eventPrefix .. createSplitEvent(i, totalEventCount, eventPayloadCapacity, tableString);
sendEvent(factionCqi, splitEvent);
local splitEvent = eventPrefix .. create_split_event_string(i, totalEventCount, eventPayloadCapacity, tableString);
send_event(factionCqi, splitEvent);
end
end

--v function(eventName: string, factionCqi: CA_CQI, table: map<string, string>)
function ClMultiplayerEvents.notifyEvent(eventName, factionCqi, table)
local tableString = GetTableSaveState(table)
local eventString = eventName .. SEPARATOR .. tableString
if string.len(eventString) > MAX_EVENT_STR_LENGTH then
splitEventAndNotify(eventName, factionCqi, tableString);
--- Triggers an instance of an event on all clients, passing the context of the table to each.
---@param event_name any
---@param faction_cqi any
---@param table any
function MultiplayerCommunicator:TriggerEvent(event_name, faction_cqi, table)
local table_str = cm:process_table_save(table)
local eventString = event_name .. self.__separator .. table_str
if string.len(eventString) > self.__max_str_len then
split_event_and_notify(event_name, faction_cqi, table_str);
else
sendEvent(factionCqi, eventString);
send_event(faction_cqi, eventString);
end
end

function ClMultiplayerEvents.notifyEventForCurrentFaction(eventName, table)
ClMultiplayerEvents.notifyEvent(eventName, cm:get_local_faction(true):command_queue_index(), table);
function MultiplayerCommunicator:TriggerEventForCurrentFaction(event_name, table)
MultiplayerCommunicator:TriggerEvent(event_name, cm:get_local_faction(true):command_queue_index(), table);
end
Loading

0 comments on commit e5e376e

Please sign in to comment.