/// <summary> /// Creates a new subspace and sets its message queue from a past subspace. /// Must be called AFTER the subspace is created in the warp context. /// </summary> public static void CreateNewSubspace(int subspaceId) { //If the new subspace is the most advanced in time skip all this method if (!WarpSystem.GetFutureSubspaces(subspaceId).Any()) { return; } var pastSubspaces = WarpSystem.GetPastSubspaces(subspaceId); if (pastSubspaces.Any()) { //Here we get the PAST subspace that is closest in time to the one we got as parameter var closestPastSubspace = WarpContext.Subspaces .Where(s => pastSubspaces.Contains(s.Key)) .OrderByDescending(s => s.Value) .Select(s => s.Key) .First(); //Now we are going to get all the messages from that PAST subspace and remove the ones that are too old for our new subspace. //Te idea is that we are going to use a queue with a lot of messages that came from future and use for our new subspace //But in that queue there might be messages that are too old so we can remove them to save space var subspaceTime = WarpSystem.GetCurrentSubspaceTime(subspaceId); var messageQueue = DbCollection.Find(v => v.SubspaceId == closestPastSubspace && v.Msg.SentTime < subspaceTime).ToList(); //Once we've got the queue clean of old messages we add it do the db //so the future subspaces fill up our queue. messageQueue.ForEach(m => m.SubspaceId = subspaceId); DbCollection.InsertBulk(messageQueue); DbCollection.EnsureIndex(x => x.Id); DbCollection.EnsureIndex(x => x.SubspaceId); DbCollection.EnsureIndex(x => x.GameTime); } }
/// <summary> /// Creates a new subspace and sets its message queue from a past subspace. /// Must be called AFTER the subspace is created in the warp context. /// </summary> public static void CreateNewSubspace(int subspaceId) { //If the new subspace is the most advanced in time skip all this method if (!WarpSystem.GetFutureSubspaces(subspaceId).Any()) { return; } var pastSubspaces = WarpSystem.GetPastSubspaces(subspaceId); if (pastSubspaces.Any()) { //Here we get the PAST subspace that is closest in time to the one we've got as a parameter var closestPastSubspace = WarpContext.Subspaces .Where(s => pastSubspaces.Contains(s.Key)) .OrderByDescending(s => s.Value) .Select(s => s.Key) .First(); //Now we clone it's message queue if (OldSubspaceVesselMessages.TryGetValue(closestPastSubspace, out var vesselsAndQueues)) { foreach (var vesselQueue in vesselsAndQueues) { var messageQueue = vesselQueue.Value.CloneConcurrentQueue(); //As we cloned a queue from a PAST subspace, we may have many messages //that are TOO OLD as we are in a future subspace. Therefore, we remove the old //messages for this subspace var subspaceTime = WarpSystem.GetCurrentSubspaceTime(subspaceId); while (messageQueue.TryDequeue(out var relayItem)) { if (relayItem.Msg.SentTime >= subspaceTime) { break; } } //Once we've got the queue clean of old messages we add it do the dictionary //so the future subspaces fill up our queue. OldSubspaceVesselMessages.TryAdd(subspaceId, new ConcurrentDictionary <Guid, ConcurrentQueue <VesselRelayItem> > { [vesselQueue.Key] = messageQueue }); } } } }
/// <summary> /// This method relays a message to the other clients in the same subspace. /// In case there are other players in OLDER subspaces it stores it in their queue for further processing /// </summary> public static void HandleVesselMessage(ClientStructure client, dynamic msg) { if (client.Subspace == -1) { return; } var vesselId = (Guid)msg.VesselId; var gameTime = (double)msg.GameTime; MessageQueuer.RelayMessageToSubspace <VesselSrvMsg>(client, msg); if (GeneralSettings.SettingsStore.ShowVesselsInThePast) { //Here we send this update to all the players in the FUTURE foreach (var subspace in WarpSystem.GetFutureSubspaces(client.Subspace)) { MessageQueuer.RelayMessageToSubspace <VesselSrvMsg>(client, msg, subspace); } } if (!VesselRelaySystem.ShouldStoreMessage(vesselId)) { return; } //In case the client is running in the future here we adjust the real sent time of the message msg.SentTime += WarpSystem.GetSubspaceTimeDifference(client.Subspace); foreach (var subspace in WarpSystem.GetPastSubspaces(client.Subspace)) { var vesselsAndQueues = OldSubspaceVesselMessages.GetOrAdd(subspace, new ConcurrentDictionary <Guid, ConcurrentQueue <VesselRelayItem> >()); var vesselQueue = vesselsAndQueues.GetOrAdd(vesselId, new ConcurrentQueue <VesselRelayItem>()); //This is the case when a user reverted (the message received has a game time LOWER than the last message received). //To handle this, we remove all the messages that we received UNTIL this revert. if (vesselQueue.LastOrDefault()?.GameTime > gameTime) { while (vesselQueue.TryPeek(out var peekValue) && peekValue.GameTime > gameTime) { vesselQueue.TryDequeue(out _); } } vesselQueue.Enqueue(new VesselRelayItem(subspace, vesselId, gameTime, msg)); } }
/// <summary> /// This method relays a message to the other clients in the same subspace. /// In case there are other players in OLDER subspaces it stores it in their queue for further processing /// </summary> public static void HandleVesselMessage(ClientStructure client, dynamic msg) { if (client.Subspace == -1) { return; } var vesselId = (Guid)msg.VesselId; var gameTime = (double)msg.GameTime; MessageQueuer.RelayMessageToSubspace <VesselSrvMsg>(client, msg); if (GeneralSettings.SettingsStore.ShowVesselsInThePast) { foreach (var subspace in WarpSystem.GetFutureSubspaces(client.Subspace)) { MessageQueuer.RelayMessageToSubspace <VesselSrvMsg>(client, msg, subspace); } } if (!VesselRelaySystem.ShouldStoreMessage(vesselId)) { return; } //The client is running in the future so here we adjust the real sent time of the message msg.SentTime += WarpSystem.GetSubspaceTimeDifference(client.Subspace); foreach (var subspace in WarpSystem.GetPastSubspaces(client.Subspace)) { //This is the case when a user reverted (the message received has a game time LOWER than the last message received). //To handle this, we remove all the messages that we received UNTIL this revert. if (DbCollection.Exists(x => x.VesselId == vesselId && x.GameTime > gameTime)) { DbCollection.Delete(x => x.VesselId == vesselId && x.GameTime > gameTime); DataBase.Shrink(); } DbCollection.Insert(new VesselRelayDbItem(subspace, vesselId, gameTime, msg)); } DbCollection.EnsureIndex(x => x.Id); DbCollection.EnsureIndex(x => x.SubspaceId); DbCollection.EnsureIndex(x => x.VesselId); DbCollection.EnsureIndex(x => x.GameTime); }