11using HarmonyLib ;
2+ using Il2CppMicrosoft . AspNetCore . SignalR . Client ;
3+ using PolyMod . ViewModels ;
24using Polytopia . Data ;
5+ using PolytopiaBackendBase ;
6+ using PolytopiaBackendBase . Common ;
7+ using PolytopiaBackendBase . Game ;
8+ using PolytopiaBackendBase . Game . BindingModels ;
9+ using UnityEngine ;
10+ using Newtonsoft . Json ;
311
412namespace PolyMod . Managers ;
513
614public static class Multiplayer
715{
816 internal const string DEFAULT_SERVER_URL = "https://dev.polydystopia.xyz" ;
17+ internal const string LOCAL_SERVER_URL = "http://localhost:5051/" ;
918 private const string GldMarker = "##GLD:" ;
1019 internal static bool allowGldMods = false ;
1120
@@ -18,10 +27,10 @@ internal static void Init()
1827 Harmony . CreateAndPatchAll ( typeof ( Multiplayer ) ) ;
1928 BuildConfig buildConfig = BuildConfigHelper . GetSelectedBuildConfig ( ) ;
2029 buildConfig . buildServerURL = BuildServerURL . Custom ;
21- buildConfig . customServerURL = Plugin . config . backendUrl ;
30+ buildConfig . customServerURL = LOCAL_SERVER_URL ;
2231
23- Plugin . logger . LogInfo ( $ "Polydystopia> Server URL set to: { Plugin . config . backendUrl } ") ;
24- Plugin . logger . LogInfo ( "Polydystopia> GLD patches applied" ) ;
32+ Plugin . logger . LogInfo ( $ "Server URL set to: { Plugin . config . backendUrl } ") ;
33+ Plugin . logger . LogInfo ( "GLD patches applied" ) ;
2534 }
2635
2736 [ HarmonyPostfix ]
@@ -39,6 +48,17 @@ private static void StartScreen_Start(StartScreen __instance)
3948 __instance . weeklyChallengesButton . gameObject . SetActive ( false ) ;
4049 }
4150
51+ [ HarmonyPostfix ]
52+ [ HarmonyPatch ( typeof ( SystemInfo ) , nameof ( SystemInfo . deviceUniqueIdentifier ) , MethodType . Getter ) ]
53+ public static void SteamClient_get_SteamId ( ref string __result )
54+ {
55+ if ( Plugin . config . overrideDeviceId != string . Empty )
56+ {
57+ __result = Plugin . config . overrideDeviceId ;
58+ }
59+ }
60+
61+
4262 /// <summary>
4363 /// After GameState deserialization, check for trailing GLD version ID and set mockedGameLogicData.
4464 /// The server appends "##GLD:" + modGldVersion (int) after the normal serialized data.
@@ -164,4 +184,196 @@ private static void Deserialize_Postfix(GameState __instance, BinaryReader __0)
164184 }
165185 return null ;
166186 }
187+
188+ [ HarmonyPrefix ]
189+ [ HarmonyPatch ( typeof ( BackendAdapter ) , nameof ( BackendAdapter . StartLobbyGame ) ) ]
190+ private static bool BackendAdapter_StartLobbyGame (
191+ ref Il2CppSystem . Threading . Tasks . Task < ServerResponse < LobbyGameViewModel > > __result ,
192+ BackendAdapter __instance ,
193+ StartLobbyBindingModel model )
194+ {
195+ Plugin . logger . LogInfo ( "BackendAdapter_StartLobbyGame" ) ;
196+ _ = HandleStartLobbyGameAsync ( __instance , model ) ;
197+ return true ;
198+ }
199+
200+ private static async Task HandleStartLobbyGameAsync ( BackendAdapter instance , StartLobbyBindingModel model )
201+ {
202+ try
203+ {
204+ var lobbyResponse = await PolytopiaBackendAdapter . Instance . GetLobby ( new GetLobbyBindingModel
205+ {
206+ LobbyId = model . LobbyId
207+ } ) ;
208+ Plugin . logger . LogInfo ( $ "Lobby processed { lobbyResponse . Success } ") ;
209+ LobbyGameViewModel lobbyGameViewModel = lobbyResponse . Data ;
210+ Plugin . logger . LogInfo ( "Lobby received" ) ;
211+
212+ ( byte [ ] serializedGameState , string gameSettingsJson ) = CreateMultiplayerGame (
213+ lobbyGameViewModel ,
214+ VersionManager . GameVersion ,
215+ VersionManager . GameLogicDataVersion
216+ ) ;
217+
218+ Plugin . logger . LogInfo ( "Game data created" ) ;
219+
220+ var setupGameDataViewModel = new SetupGameDataViewModel
221+ {
222+ lobbyId = lobbyGameViewModel . Id . ToString ( ) ,
223+ serializedGameState = serializedGameState ,
224+ gameSettingsJson = gameSettingsJson
225+ } ;
226+
227+ var setupData = System . Text . Json . JsonSerializer . Serialize ( setupGameDataViewModel ) ;
228+
229+ var serverResponse = await instance . HubConnection . InvokeAsync < ServerResponse < BoolResponseViewModel > > (
230+ "SetupGameData" ,
231+ setupData ,
232+ Il2CppSystem . Threading . CancellationToken . None
233+ ) ;
234+
235+ Plugin . logger . LogInfo ( "Setup complete: " + serverResponse . Success ) ;
236+ }
237+ catch ( Exception ex )
238+ {
239+ Plugin . logger . LogInfo ( "Error: " + ex . Message ) ;
240+ }
241+ }
242+
243+ public static ( byte [ ] serializedGameState , string gameSettingsJson ) CreateMultiplayerGame ( LobbyGameViewModel lobby ,
244+ int gameVersion , int gameLogicVersion )
245+ {
246+ Console . Write ( 1 ) ;
247+ Console . Write ( lobby == null ) ;
248+ var lobbyMapSize = lobby . MapSize ;
249+ Console . Write ( 11 ) ;
250+ var settings = new GameSettings ( ) ;
251+ Console . Write ( 111 ) ;
252+ settings . ApplyLobbySettings ( lobby ) ;
253+ Console . Write ( 111 ) ;
254+ if ( settings . LiveGamePreset )
255+ {
256+ settings . SetLiveModePreset ( ) ;
257+ }
258+ Console . Write ( 3 ) ;
259+ foreach ( var participatorViewModel in lobby . Participators )
260+ {
261+ Console . Write ( 4 ) ;
262+ if ( participatorViewModel . SelectedTribe == 0 ) participatorViewModel . SelectedTribe = 2 ; //TODO: Remove later
263+
264+ var humanPlayer = new PlayerData
265+ {
266+ type = PlayerDataType . LocalUser ,
267+ state = PlayerDataFriendshipState . Accepted ,
268+ knownTribe = true ,
269+ tribe = ( TribeType ) participatorViewModel . SelectedTribe ,
270+ tribeMix = ( TribeType ) participatorViewModel . SelectedTribe , //?
271+ skinType = ( SkinType ) participatorViewModel . SelectedTribeSkin ,
272+ defaultName = participatorViewModel . GetNameInternal ( )
273+ } ;
274+ humanPlayer . profile . id = participatorViewModel . UserId ;
275+ humanPlayer . profile . SetName ( participatorViewModel . GetNameInternal ( ) ) ;
276+ SerializationHelpers . FromByteArray < AvatarState > ( participatorViewModel . AvatarStateData , out var avatarState ) ;
277+ humanPlayer . profile . avatarState = avatarState ;
278+
279+ settings . AddPlayer ( humanPlayer ) ;
280+ Console . Write ( 5 ) ;
281+ }
282+ Console . Write ( 6 ) ;
283+ foreach ( var botDifficulty in lobby . Bots )
284+ {
285+ var botGuid = Il2CppSystem . Guid . NewGuid ( ) ;
286+
287+ var botPlayer = new PlayerData
288+ {
289+ type = PlayerDataType . Bot ,
290+ state = PlayerDataFriendshipState . Accepted ,
291+ knownTribe = true ,
292+ tribe = Enum . GetValues < TribeType > ( ) . Where ( t => t != TribeType . None )
293+ . OrderBy ( x => Il2CppSystem . Guid . NewGuid ( ) ) . First ( )
294+ } ;
295+ ;
296+ botPlayer . botDifficulty = ( BotDifficulty ) botDifficulty ;
297+ botPlayer . skinType = SkinType . Default ; //TODO
298+ botPlayer . defaultName = "Bot" + botGuid ;
299+ botPlayer . profile . id = botGuid ;
300+
301+ settings . AddPlayer ( botPlayer ) ;
302+ }
303+
304+ GameState gameState = new GameState ( )
305+ {
306+ Version = gameVersion ,
307+ Settings = settings ,
308+ PlayerStates = new Il2CppSystem . Collections . Generic . List < PlayerState > ( )
309+ } ;
310+
311+ for ( int index = 0 ; index < settings . GetPlayerCount ( ) ; ++ index )
312+ {
313+ PlayerData player = settings . GetPlayer ( index ) ;
314+ if ( player . type != PlayerDataType . Bot )
315+ {
316+ Il2CppSystem . Nullable < Il2CppSystem . Guid > nullableGuid = new ( )
317+ {
318+ value = player . profile . id
319+ } ;
320+ PlayerState playerState = new PlayerState ( )
321+ {
322+ Id = ( byte ) ( index + 1 ) ,
323+ AccountId = nullableGuid ,
324+ AutoPlay = player . type == PlayerDataType . Bot ,
325+ UserName = player . GetNameInternal ( ) ,
326+ tribe = player . tribe ,
327+ tribeMix = player . tribeMix ,
328+ hasChosenTribe = true ,
329+ skinType = player . skinType
330+ } ;
331+ gameState . PlayerStates . Add ( playerState ) ;
332+ Plugin . logger . LogInfo ( $ "Created player: { playerState } ") ;
333+ }
334+ else
335+ {
336+ GameStateUtils . AddAIOpponent ( gameState , GameStateUtils . GetRandomPickableTribe ( gameState ) ,
337+ GameSettings . HandicapFromDifficulty ( player . botDifficulty ) , player . skinType ) ;
338+ }
339+ }
340+
341+ GameStateUtils . SetPlayerColors ( gameState ) ;
342+ GameStateUtils . AddNaturePlayer ( gameState ) ;
343+ Plugin . logger . LogInfo ( "Creating world..." ) ;
344+ ushort num = ( ushort ) Math . Max ( lobbyMapSize ,
345+ ( int ) MapDataExtensions . GetMinimumMapSize ( gameState . PlayerCount ) ) ;
346+ gameState . Map = new MapData ( num , num ) ;
347+ MapGeneratorSettings generatorSettings = settings . GetMapGeneratorSettings ( ) ;
348+ new MapGenerator ( ) . Generate ( gameState , generatorSettings ) ;
349+ Plugin . logger . LogInfo ( $ "Creating initial state for { gameState . PlayerCount } players...") ;
350+
351+ foreach ( PlayerState playerState3 in gameState . PlayerStates )
352+ {
353+ foreach ( PlayerState playerState4 in gameState . PlayerStates )
354+ playerState3 . aggressions [ playerState4 . Id ] = 0 ;
355+ if ( playerState3 . Id != byte . MaxValue )
356+ {
357+ playerState3 . Currency = 55 ;
358+ TribeData data3 ;
359+ UnitData data4 ;
360+ if ( gameState . GameLogicData . TryGetData ( playerState3 . tribe , out data3 ) &&
361+ gameState . GameLogicData . TryGetData ( data3 . startingUnit . type , out data4 ) )
362+ {
363+ TileData tile = gameState . Map . GetTile ( playerState3 . startTile ) ;
364+ UnitState unitState = ActionUtils . TrainUnitScored ( gameState , playerState3 , tile , data4 ) ;
365+ unitState . attacked = false ;
366+ unitState . moved = false ;
367+ }
368+ }
369+ }
370+
371+ Plugin . logger . LogInfo ( "Session created successfully" ) ;
372+ gameState . CommandStack . Add ( ( CommandBase ) new StartMatchCommand ( ( byte ) 1 ) ) ;
373+
374+ var serializedGameState = SerializationHelpers . ToByteArray ( gameState , gameState . Version ) ;
375+
376+ return ( serializedGameState ,
377+ JsonConvert . SerializeObject ( gameState . Settings ) ) ;
378+ }
167379}
0 commit comments