missionserver.c 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776
  1. //! int time of the logout end
  2. //: string uid of the player
  3. typedef Param2<int, string> LogoutInfo;
  4. class MissionServer extends MissionBase
  5. {
  6. ref array<Man> m_Players;
  7. ref array<ref CorpseData> m_DeadPlayersArray;
  8. ref map<PlayerBase, ref LogoutInfo> m_LogoutPlayers;
  9. ref map<PlayerBase, ref LogoutInfo> m_NewLogoutPlayers;
  10. ref RainProcurementHandler m_RainProcHandler;
  11. const int SCHEDULER_PLAYERS_PER_TICK = 5;
  12. int m_currentPlayer;
  13. int m_RespawnMode;
  14. // -----------------------
  15. // ARTILLERY SOUNDS SETUP
  16. // -----------------------
  17. private float m_ArtyBarrageTimer = 0; // This is not to be edited in Init.c this is just to increment time
  18. // Variables to be modified in Init.c
  19. protected bool m_PlayArty = false; // Toggle if Off map artillery sounds are played
  20. protected float m_ArtyDelay = 0; // Set how much time there is between two barrages (in seconds)
  21. protected int m_MinSimultaneousStrikes = 0; // The MIN of simultaneous shots on the map (Will be clamped between 1 and max shots)
  22. protected int m_MaxSimultaneousStrikes = 0; // The MAX of simultaneous shots on the map (Will be clamped between 1 and max amount of coords)
  23. protected ref array<vector> m_FiringPos; // Where we should fire from. On Init set the relevant data
  24. //All Chernarus firing coordinates
  25. protected const ref array<vector> CHERNARUS_STRIKE_POS =
  26. {
  27. "-500.00 165.00 5231.69",
  28. "-500.00 300.00 9934.41",
  29. "10406.86 192.00 15860.00",
  30. "4811.75 370.00 15860.00",
  31. "-500.00 453.00 15860.00"
  32. };
  33. //All livonia firing coordinates
  34. protected const ref array<vector> LIVONIA_STRIKE_POS =
  35. {
  36. "7440.00 417.00 -500.00",
  37. "-500.00 276.00 5473.00",
  38. "-500.00 265.00 9852.00",
  39. "4953.00 240.00 13300.00",
  40. "9620.00 188.00 13300.00",
  41. "13300.00 204.00 10322.00",
  42. "13300.00 288.00 6204.00",
  43. "13300.00 296.00 -500.00"
  44. };
  45. // -----------------------
  46. // END OF ARTILLERY SETUP
  47. // -----------------------
  48. PlayerBase m_player;
  49. MissionBase m_mission;
  50. void MissionServer()
  51. {
  52. GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(this.UpdatePlayersStats, 30000, true);
  53. m_DeadPlayersArray = new array<ref CorpseData>;
  54. UpdatePlayersStats();
  55. m_Players = new array<Man>;
  56. m_LogoutPlayers = new map<PlayerBase, ref LogoutInfo>;
  57. m_NewLogoutPlayers = new map<PlayerBase, ref LogoutInfo>;
  58. m_RainProcHandler = new RainProcurementHandler(this);
  59. }
  60. void ~MissionServer()
  61. {
  62. GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).Remove(this.UpdatePlayersStats);
  63. }
  64. override void OnInit()
  65. {
  66. super.OnInit();
  67. CfgGameplayHandler.LoadData();
  68. PlayerSpawnHandler.LoadData();
  69. CfgPlayerRestrictedAreaHandler.LoadData();
  70. UndergroundAreaLoader.SpawnAllTriggerCarriers();
  71. //Either pass consts in Init.c or insert all desired coords (or do both ;))
  72. m_FiringPos = new array<vector>();
  73. }
  74. override void OnMissionStart()
  75. {
  76. super.OnMissionStart();
  77. // We will load the Effect areas on Default mission start
  78. EffectAreaLoader.CreateZones();
  79. }
  80. override void OnUpdate(float timeslice)
  81. {
  82. UpdateDummyScheduler();
  83. TickScheduler(timeslice);
  84. UpdateLogoutPlayers();
  85. m_WorldData.UpdateBaseEnvTemperature(timeslice); // re-calculate base enviro temperature
  86. m_RainProcHandler.Update(timeslice);
  87. RandomArtillery(timeslice);
  88. super.OnUpdate(timeslice);
  89. }
  90. override void OnGameplayDataHandlerLoad()
  91. {
  92. m_RespawnMode = CfgGameplayHandler.GetDisableRespawnDialog();
  93. GetGame().SetDebugMonitorEnabled(GetGame().ServerConfigGetInt("enableDebugMonitor"));
  94. InitialiseWorldData();
  95. }
  96. void RandomArtillery(float deltaTime)
  97. {
  98. // ARTY barrage
  99. if (m_PlayArty)
  100. {
  101. // We only perform timer checks and increments if we enabled the artillery barrage
  102. if (m_ArtyBarrageTimer > m_ArtyDelay)
  103. {
  104. //We clamp to guarantee 1 and never have multiple shots on same pos, even in case of entry error
  105. m_MaxSimultaneousStrikes = Math.Clamp(m_MaxSimultaneousStrikes, 1, m_FiringPos.Count());
  106. m_MinSimultaneousStrikes = Math.Clamp(m_MinSimultaneousStrikes, 1, m_MaxSimultaneousStrikes);
  107. // Variables to be used in this scope
  108. int randPos; // Select random position
  109. Param1<vector> pos; // The value to be sent through RPC
  110. array<ref Param> params; // The RPC params
  111. if (m_MaxSimultaneousStrikes == 1)
  112. {
  113. // We only have one set of coordinates to send
  114. randPos = Math.RandomIntInclusive(0, m_FiringPos.Count() - 1);
  115. pos = new Param1<vector>(m_FiringPos[randPos]);
  116. params = new array<ref Param>;
  117. params.Insert(pos);
  118. GetGame().RPC(null, ERPCs.RPC_SOUND_ARTILLERY, params, true);
  119. }
  120. else
  121. {
  122. //We will do some extra steps to
  123. /*
  124. 1. Send multiple coords (Send one RPC per coord set)
  125. 2. Ensure we don't have duplicates
  126. */
  127. array<int> usedIndices = new array<int>; // Will store all previusly fired upon indices
  128. // We determine how many positions fire between MIN and MAX
  129. int randFireNb = Math.RandomIntInclusive(m_MinSimultaneousStrikes, m_MaxSimultaneousStrikes);
  130. for (int i = 0; i < randFireNb; i++)
  131. {
  132. randPos = Math.RandomIntInclusive(0, m_FiringPos.Count() - 1);
  133. if (usedIndices.Count() <= 0 || usedIndices.Find(randPos) < 0) //We do not find the index or array is empty
  134. {
  135. // We prepare to send the message
  136. pos = new Param1<vector>(m_FiringPos[randPos]);
  137. params = new array<ref Param>;
  138. // We send the message with this set of coords
  139. params.Insert(pos);
  140. GetGame().RPC(null, ERPCs.RPC_SOUND_ARTILLERY, params, true);
  141. // We store the last used value
  142. usedIndices.Insert(randPos);
  143. }
  144. }
  145. }
  146. // Reset timer for new loop
  147. m_ArtyBarrageTimer = 0.0;
  148. }
  149. m_ArtyBarrageTimer += deltaTime;
  150. }
  151. }
  152. override bool IsServer()
  153. {
  154. return true;
  155. }
  156. override bool IsPlayerDisconnecting(Man player)
  157. {
  158. return (m_LogoutPlayers && m_LogoutPlayers.Contains(PlayerBase.Cast(player))) || (m_NewLogoutPlayers && m_NewLogoutPlayers.Contains(PlayerBase.Cast(player)));
  159. }
  160. void UpdatePlayersStats()
  161. {
  162. PluginLifespan moduleLifespan;
  163. Class.CastTo(moduleLifespan, GetPlugin(PluginLifespan));
  164. array<Man> players = new array<Man>();
  165. GetGame().GetPlayers(players);
  166. foreach (Man man : players)
  167. {
  168. PlayerBase player;
  169. if (Class.CastTo(player, man))
  170. {
  171. player.StatUpdateByTime(AnalyticsManagerServer.STAT_PLAYTIME);
  172. player.StatUpdateByPosition(AnalyticsManagerServer.STAT_DISTANCE);
  173. moduleLifespan.UpdateLifespan(player);
  174. }
  175. }
  176. UpdateCorpseStatesServer();
  177. }
  178. protected void AddNewPlayerLogout(PlayerBase player, notnull LogoutInfo info)
  179. {
  180. m_LogoutPlayers.Insert(player, info);
  181. m_NewLogoutPlayers.Remove(player);
  182. }
  183. // check if logout finished for some players
  184. void UpdateLogoutPlayers()
  185. {
  186. for (int i = 0; i < m_LogoutPlayers.Count();)
  187. {
  188. LogoutInfo info = m_LogoutPlayers.GetElement(i);
  189. if (GetGame().GetTime() >= info.param1)
  190. {
  191. PlayerIdentity identity;
  192. PlayerBase player = m_LogoutPlayers.GetKey(i);
  193. if (player)
  194. {
  195. identity = player.GetIdentity();
  196. m_LogoutPlayers.Remove(player);
  197. }
  198. else
  199. {
  200. m_LogoutPlayers.RemoveElement(i);
  201. }
  202. // disable reconnecting to old char
  203. // GetGame().RemoveFromReconnectCache(info.param2);
  204. PlayerDisconnected(player, identity, info.param2);
  205. }
  206. else
  207. {
  208. ++i;
  209. }
  210. }
  211. }
  212. override void OnEvent(EventType eventTypeId, Param params)
  213. {
  214. PlayerIdentity identity;
  215. PlayerBase player;
  216. int counter = 0;
  217. switch (eventTypeId)
  218. {
  219. case ClientPrepareEventTypeID:
  220. ClientPrepareEventParams clientPrepareParams;
  221. Class.CastTo(clientPrepareParams, params);
  222. CfgGameplayHandler.SyncDataSendEx(clientPrepareParams.param1);
  223. UndergroundAreaLoader.SyncDataSend(clientPrepareParams.param1);
  224. CfgPlayerRestrictedAreaHandler.SyncDataSend(clientPrepareParams.param1);
  225. OnClientPrepareEvent(clientPrepareParams.param1, clientPrepareParams.param2, clientPrepareParams.param3, clientPrepareParams.param4, clientPrepareParams.param5);
  226. break;
  227. case ClientNewEventTypeID:
  228. ClientNewEventParams newParams;
  229. Class.CastTo(newParams, params);
  230. player = OnClientNewEvent(newParams.param1, newParams.param2, newParams.param3);
  231. if (!player)
  232. {
  233. Debug.Log("ClientNewEvent: Player is empty");
  234. return;
  235. }
  236. identity = newParams.param1;
  237. InvokeOnConnect(player,identity);
  238. SyncEvents.SendPlayerList();
  239. ControlPersonalLight(player);
  240. SyncGlobalLighting(player);
  241. break;
  242. case ClientReadyEventTypeID:
  243. ClientReadyEventParams readyParams;
  244. Class.CastTo(readyParams, params);
  245. identity = readyParams.param1;
  246. Class.CastTo(player, readyParams.param2);
  247. if (!player)
  248. {
  249. Debug.Log("ClientReadyEvent: Player is empty");
  250. return;
  251. }
  252. OnClientReadyEvent(identity, player);
  253. InvokeOnConnect(player, identity);
  254. // Send list of players at all clients
  255. SyncEvents.SendPlayerList();
  256. ControlPersonalLight(player);
  257. SyncGlobalLighting(player);
  258. break;
  259. case ClientRespawnEventTypeID:
  260. ClientRespawnEventParams respawnParams;
  261. Class.CastTo(respawnParams, params);
  262. identity = respawnParams.param1;
  263. Class.CastTo(player, respawnParams.param2);
  264. if (!player)
  265. {
  266. Debug.Log("ClientRespawnEvent: Player is empty");
  267. return;
  268. }
  269. OnClientRespawnEvent(identity, player);
  270. break;
  271. case ClientReconnectEventTypeID:
  272. ClientReconnectEventParams reconnectParams;
  273. Class.CastTo(reconnectParams, params);
  274. identity = reconnectParams.param1;
  275. Class.CastTo(player, reconnectParams.param2);
  276. if (!player)
  277. {
  278. Debug.Log("ClientReconnectEvent: Player is empty");
  279. return;
  280. }
  281. OnClientReconnectEvent(identity, player);
  282. break;
  283. case ClientDisconnectedEventTypeID:
  284. ClientDisconnectedEventParams discoParams;
  285. Class.CastTo(discoParams, params);
  286. identity = discoParams.param1;
  287. Class.CastTo(player, discoParams.param2);
  288. int logoutTime = discoParams.param3;
  289. bool authFailed = discoParams.param4;
  290. if (!player)
  291. {
  292. Debug.Log("ClientDisconnectenEvent: Player is empty");
  293. return;
  294. }
  295. OnClientDisconnectedEvent(identity, player, logoutTime, authFailed);
  296. break;
  297. case LogoutCancelEventTypeID:
  298. LogoutCancelEventParams logoutCancelParams;
  299. Class.CastTo(logoutCancelParams, params);
  300. Class.CastTo(player, logoutCancelParams.param1);
  301. identity = player.GetIdentity();
  302. if (identity)
  303. {
  304. // disable reconnecting to old char
  305. // GetGame().RemoveFromReconnectCache(identity.GetId());
  306. Print("[Logout]: Player " + identity.GetId() + " cancelled");
  307. }
  308. else
  309. {
  310. Print("[Logout]: Player cancelled");
  311. }
  312. m_LogoutPlayers.Remove(player);
  313. m_NewLogoutPlayers.Remove(player);
  314. break;
  315. }
  316. }
  317. void InvokeOnConnect(PlayerBase player, PlayerIdentity identity)
  318. {
  319. Debug.Log("InvokeOnConnect:"+this.ToString(),"Connect");
  320. if (player)
  321. player.OnConnect();
  322. }
  323. void InvokeOnDisconnect(PlayerBase player)
  324. {
  325. Debug.Log("InvokeOnDisconnect:"+this.ToString(),"Connect");
  326. if (player)
  327. player.OnDisconnect();
  328. }
  329. void OnClientPrepareEvent(PlayerIdentity identity, out bool useDB, out vector pos, out float yaw, out int preloadTimeout)
  330. {
  331. if (GetHive())
  332. {
  333. // use character from database
  334. useDB = true;
  335. }
  336. else
  337. {
  338. // use following data without database
  339. useDB = false;
  340. pos = "1189.3 0.0 5392.48";
  341. yaw = 0;
  342. }
  343. }
  344. // Enables/Disables personal light on the given player.
  345. void ControlPersonalLight(PlayerBase player)
  346. {
  347. if (player)
  348. {
  349. bool is_personal_light = ! GetGame().ServerConfigGetInt("disablePersonalLight");
  350. Param1<bool> personal_light_toggle = new Param1<bool>(is_personal_light);
  351. GetGame().RPCSingleParam(player, ERPCs.RPC_TOGGLE_PERSONAL_LIGHT, personal_light_toggle, true, player.GetIdentity());
  352. }
  353. else
  354. {
  355. Error("Error! Player was not initialized at the right time. Thus cannot send RPC command to enable or disable personal light!");
  356. }
  357. }
  358. // syncs global lighting setup from the server (lightingConfig server config parameter)
  359. void SyncGlobalLighting(PlayerBase player)
  360. {
  361. if (player)
  362. {
  363. int lightingID = GetGame().ServerConfigGetInt("lightingConfig");
  364. Param1<int> lightID = new Param1<int>(lightingID);
  365. GetGame().RPCSingleParam(player, ERPCs.RPC_SEND_LIGHTING_SETUP, lightID, true, player.GetIdentity());
  366. }
  367. }
  368. //! returns whether received data is valid, ctx can be filled on client in StoreLoginData()
  369. bool ProcessLoginData(ParamsReadContext ctx)
  370. {
  371. //creates temporary server-side structure for handling default character spawn
  372. return GetGame().GetMenuDefaultCharacterData(false).DeserializeCharacterData(ctx);
  373. }
  374. //
  375. PlayerBase CreateCharacter(PlayerIdentity identity, vector pos, ParamsReadContext ctx, string characterName)
  376. {
  377. Entity playerEnt;
  378. playerEnt = GetGame().CreatePlayer(identity, characterName, pos, 0, "NONE");//Creates random player
  379. Class.CastTo(m_player, playerEnt);
  380. GetGame().SelectPlayer(identity, m_player);
  381. return m_player;
  382. }
  383. //! Spawns character equip from received data. Checks validity against config, randomizes if invalid value and config array not empty.
  384. void EquipCharacter(MenuDefaultCharacterData char_data)
  385. {
  386. int slot_ID;
  387. string attachment_type;
  388. for (int i = 0; i < DefaultCharacterCreationMethods.GetAttachmentSlotsArray().Count(); i++)
  389. {
  390. slot_ID = DefaultCharacterCreationMethods.GetAttachmentSlotsArray().Get(i);
  391. attachment_type = "";
  392. if (m_RespawnMode != GameConstants.RESPAWN_MODE_CUSTOM || !char_data.GetAttachmentMap().Find(slot_ID,attachment_type) || !VerifyAttachmentType(slot_ID,attachment_type)) //todo insert verification fn here
  393. {
  394. //randomize
  395. if (DefaultCharacterCreationMethods.GetConfigArrayCountFromSlotID(slot_ID) > 0)
  396. {
  397. attachment_type = DefaultCharacterCreationMethods.GetConfigAttachmentTypes(slot_ID).GetRandomElement();
  398. }
  399. else //undefined, moving on
  400. continue;
  401. }
  402. if (attachment_type != "")
  403. {
  404. m_player.GetInventory().CreateAttachmentEx(attachment_type,slot_ID);
  405. }
  406. }
  407. StartingEquipSetup(m_player, true);
  408. }
  409. //! can be overriden to manually set up starting equip. 'clothesChosen' is legacy parameter, does nothing.
  410. void StartingEquipSetup(PlayerBase player, bool clothesChosen)
  411. {
  412. }
  413. bool VerifyAttachmentType(int slot_ID, string attachment_type)
  414. {
  415. return DefaultCharacterCreationMethods.GetConfigAttachmentTypes(slot_ID).Find(attachment_type) > -1;
  416. }
  417. PlayerBase OnClientNewEvent(PlayerIdentity identity, vector pos, ParamsReadContext ctx)
  418. {
  419. string characterType = GetGame().CreateRandomPlayer();
  420. bool generateRandomEquip = false;
  421. // get login data for new character
  422. if (ProcessLoginData(ctx) && (m_RespawnMode == GameConstants.RESPAWN_MODE_CUSTOM) && !GetGame().GetMenuDefaultCharacterData(false).IsRandomCharacterForced())
  423. {
  424. if (GetGame().ListAvailableCharacters().Find(GetGame().GetMenuDefaultCharacterData().GetCharacterType()) > -1)
  425. characterType = GetGame().GetMenuDefaultCharacterData().GetCharacterType();
  426. }
  427. else
  428. {
  429. generateRandomEquip = true;
  430. }
  431. if (PlayerSpawnHandler.IsInitialized())
  432. {
  433. PlayerSpawnPreset presetData = PlayerSpawnHandler.GetRandomCharacterPreset();
  434. if (presetData && presetData.IsValid())
  435. {
  436. string presetCharType = presetData.GetRandomCharacterType();
  437. if (presetCharType == string.Empty)
  438. presetCharType = characterType;
  439. if (CreateCharacter(identity, pos, ctx, presetCharType) != null)
  440. {
  441. PlayerSpawnHandler.ProcessEquipmentData(m_player,presetData);
  442. return m_player;
  443. }
  444. else
  445. {
  446. ErrorEx("Failed to create character from type: " + presetCharType + ", using default spawning method");
  447. }
  448. }
  449. else
  450. {
  451. ErrorEx("Failed to load PlayerSpawnPreset data properly, using default spawning method");
  452. }
  453. }
  454. if (CreateCharacter(identity, pos, ctx, characterType))
  455. {
  456. if (generateRandomEquip)
  457. GetGame().GetMenuDefaultCharacterData().GenerateRandomEquip();
  458. EquipCharacter(GetGame().GetMenuDefaultCharacterData());
  459. }
  460. return m_player;
  461. }
  462. void OnClientReadyEvent(PlayerIdentity identity, PlayerBase player)
  463. {
  464. GetGame().SelectPlayer(identity, player);
  465. #ifdef DIAG_DEVELOPER
  466. if (FeatureTimeAccel.m_CurrentTimeAccel)
  467. {
  468. GetGame().RPCSingleParam(player, ERPCs.DIAG_TIMEACCEL_CLIENT_SYNC, FeatureTimeAccel.m_CurrentTimeAccel, true, identity);
  469. }
  470. #endif
  471. }
  472. void OnClientRespawnEvent(PlayerIdentity identity, PlayerBase player)
  473. {
  474. if (player)
  475. {
  476. if (player.IsUnconscious() || player.IsRestrained())
  477. {
  478. // kill character
  479. player.SetHealth("", "", 0.0);
  480. }
  481. }
  482. #ifdef DIAG_DEVELOPER
  483. if (FeatureTimeAccel.m_CurrentTimeAccel)
  484. {
  485. GetGame().RPCSingleParam(player, ERPCs.DIAG_TIMEACCEL_CLIENT_SYNC, FeatureTimeAccel.m_CurrentTimeAccel, true, identity);
  486. }
  487. #endif
  488. }
  489. void OnClientReconnectEvent(PlayerIdentity identity, PlayerBase player)
  490. {
  491. if (player)
  492. {
  493. player.OnReconnect();
  494. }
  495. }
  496. void OnClientDisconnectedEvent(PlayerIdentity identity, PlayerBase player, int logoutTime, bool authFailed)
  497. {
  498. bool disconnectNow = true;
  499. // TODO: get out of vehicle
  500. // using database and no saving if authorization failed
  501. if (GetHive() && !authFailed)
  502. {
  503. if (player.IsAlive())
  504. {
  505. if (!m_LogoutPlayers.Contains(player) && !m_NewLogoutPlayers.Contains(player))
  506. {
  507. Print("[Logout]: New player " + identity.GetId() + " with logout time " + logoutTime.ToString());
  508. // send statistics to client
  509. player.StatSyncToClient();
  510. // inform client about logout time
  511. GetGame().SendLogoutTime(player, logoutTime);
  512. // wait for some time before logout and save
  513. LogoutInfo params = new LogoutInfo(GetGame().GetTime() + logoutTime * 1000, identity.GetId());
  514. m_NewLogoutPlayers.Insert(player, params);
  515. GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(AddNewPlayerLogout, 0, false, player, params);
  516. // allow reconnecting to old char only if not in cars, od ladders etc. as they cannot be properly synchronized for reconnect
  517. //if (!player.GetCommand_Vehicle() && !player.GetCommand_Ladder())
  518. //{
  519. // GetGame().AddToReconnectCache(identity);
  520. //}
  521. // wait until logout timer runs out
  522. disconnectNow = false;
  523. }
  524. return;
  525. }
  526. }
  527. if (disconnectNow)
  528. {
  529. Print("[Logout]: New player " + identity.GetId() + " with instant logout");
  530. // inform client about instant logout
  531. GetGame().SendLogoutTime(player, 0);
  532. PlayerDisconnected(player, identity, identity.GetId());
  533. }
  534. }
  535. void PlayerDisconnected(PlayerBase player, PlayerIdentity identity, string uid)
  536. {
  537. // Note: At this point, identity can be already deleted
  538. if (!player)
  539. {
  540. Print("[Logout]: Skipping player " + uid + ", already removed");
  541. return;
  542. }
  543. // disable reconnecting to old char
  544. //GetGame().RemoveFromReconnectCache(uid);
  545. // now player can't cancel logout anymore, so call everything needed upon disconnect
  546. InvokeOnDisconnect(player);
  547. Print("[Logout]: Player " + uid + " finished");
  548. if (GetHive())
  549. {
  550. // save player
  551. player.Save();
  552. // unlock player in DB
  553. GetHive().CharacterExit(player);
  554. }
  555. // handle player's existing char in the world
  556. player.ReleaseNetworkControls();
  557. HandleBody(player);
  558. // remove player from server
  559. GetGame().DisconnectPlayer(identity, uid);
  560. // Send list of players at all clients
  561. GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(SyncEvents.SendPlayerList, 1000);
  562. }
  563. bool ShouldPlayerBeKilled(PlayerBase player)
  564. {
  565. if (player.IsUnconscious() || player.IsRestrained())
  566. {
  567. switch (player.GetKickOffReason())
  568. {
  569. case EClientKicked.SERVER_EXIT:
  570. return false;
  571. case EClientKicked.KICK_ALL_ADMIN:
  572. return false;
  573. case EClientKicked.KICK_ALL_SERVER:
  574. return false;
  575. case EClientKicked.SERVER_SHUTDOWN:
  576. return false;
  577. default:
  578. return true;
  579. }
  580. }
  581. return false;
  582. }
  583. void HandleBody(PlayerBase player)
  584. {
  585. if (player.IsAlive())
  586. {
  587. if (ShouldPlayerBeKilled(player))
  588. {
  589. player.SetHealth("", "", 0.0);//kill
  590. }
  591. else
  592. {
  593. player.Delete();// remove the body
  594. }
  595. }
  596. }
  597. void TickScheduler(float timeslice)
  598. {
  599. GetGame().GetWorld().GetPlayerList(m_Players);
  600. int players_count = m_Players.Count();
  601. int tick_count_max = Math.Min(players_count, SCHEDULER_PLAYERS_PER_TICK);
  602. for (int i = 0; i < tick_count_max; i++)
  603. {
  604. if (m_currentPlayer >= players_count)
  605. {
  606. m_currentPlayer = 0;
  607. }
  608. PlayerBase currentPlayer = PlayerBase.Cast(m_Players.Get(m_currentPlayer));
  609. if (currentPlayer)
  610. currentPlayer.OnTick();
  611. m_currentPlayer++;
  612. }
  613. }
  614. //--------------------------------------------------
  615. override bool InsertCorpse(Man player)
  616. {
  617. CorpseData corpse_data = new CorpseData(PlayerBase.Cast(player),GetGame().GetTime());
  618. return m_DeadPlayersArray.Insert(corpse_data) >= 0;
  619. }
  620. void UpdateCorpseStatesServer()
  621. {
  622. if (m_DeadPlayersArray.Count() == 0)//nothing to process, abort
  623. return;
  624. int current_time = GetGame().GetTime();
  625. array<int> invalid_corpses = new array<int>;
  626. CorpseData corpse_data;
  627. for (int i = 0; i < m_DeadPlayersArray.Count(); i++)
  628. {
  629. corpse_data = m_DeadPlayersArray.Get(i);
  630. if (!corpse_data || (corpse_data && (!corpse_data.m_Player || !corpse_data.m_bUpdate)))
  631. {
  632. invalid_corpses.Insert(i);
  633. }
  634. else if (corpse_data.m_bUpdate && current_time - corpse_data.m_iLastUpdateTime >= 30000)
  635. {
  636. corpse_data.UpdateCorpseState();
  637. corpse_data.m_iLastUpdateTime = current_time;
  638. }
  639. }
  640. //cleanup
  641. if (invalid_corpses.Count() > 0)
  642. {
  643. for (i = invalid_corpses.Count() - 1; i > -1; i--)
  644. {
  645. m_DeadPlayersArray.Remove(invalid_corpses.Get(i));
  646. }
  647. }
  648. }
  649. //--------------------------------------------------
  650. override void SyncRespawnModeInfo(PlayerIdentity identity)
  651. {
  652. ScriptRPC rpc = new ScriptRPC();
  653. rpc.Write(m_RespawnMode);
  654. rpc.Send(null, ERPCs.RPC_SERVER_RESPAWN_MODE, true, identity);
  655. }
  656. override RainProcurementHandler GetRainProcurementHandler()
  657. {
  658. return m_RainProcHandler;
  659. }
  660. //! DEPRECATED
  661. PluginAdditionalInfo m_moduleDefaultCharacter;
  662. }