diff --git a/Dynamic Asteroids/Data/Scripts/DynamicAsteroids/AsteroidEntities/AsteroidEntity.cs b/Dynamic Asteroids/Data/Scripts/DynamicAsteroids/AsteroidEntities/AsteroidEntity.cs index c04daa86..750806e7 100644 --- a/Dynamic Asteroids/Data/Scripts/DynamicAsteroids/AsteroidEntities/AsteroidEntity.cs +++ b/Dynamic Asteroids/Data/Scripts/DynamicAsteroids/AsteroidEntities/AsteroidEntity.cs @@ -93,7 +93,7 @@ public void SplitAsteroid() splits = (int)Math.Ceiling(Size); float newSize = Size / splits; - MyAPIGateway.Utilities.ShowNotification($"NS: {newSize}"); + // MyAPIGateway.Utilities.ShowNotification($"NS: {newSize}"); // Commented out for less frequent logging if (newSize <= 1) { @@ -103,6 +103,12 @@ public void SplitAsteroid() { MyFloatingObjects.Spawn(new MyPhysicalInventoryItem(1000, newObject), PositionComp.GetPosition() + RandVector() * Size, Vector3D.Forward, Vector3D.Up, Physics); } + + // Send a removal message before closing + var removalMessage1 = new AsteroidNetworkMessage(PositionComp.GetPosition(), Size, Vector3D.Zero, Vector3D.Zero, Type, false, EntityId, true, false); + var removalMessageBytes1 = MyAPIGateway.Utilities.SerializeToBinary(removalMessage1); + MyAPIGateway.Multiplayer.SendMessageToOthers(1337, removalMessageBytes1); + Close(); return; } @@ -112,9 +118,22 @@ public void SplitAsteroid() Vector3D newPos = PositionComp.GetPosition() + RandVector() * Size; Vector3D newVelocity = RandVector() * AsteroidSettings.GetRandomSubChunkVelocity(MainSession.I.Rand); Vector3D newAngularVelocity = RandVector() * AsteroidSettings.GetRandomSubChunkAngularVelocity(MainSession.I.Rand); + + // Create the sub-chunk asteroid on the server var subChunk = CreateAsteroid(newPos, newSize, newVelocity, Type); subChunk.Physics.AngularVelocity = newAngularVelocity; + + // Send a network message to clients + var message = new AsteroidNetworkMessage(newPos, newSize, newVelocity, newAngularVelocity, Type, true, subChunk.EntityId, false, true); + var messageBytes = MyAPIGateway.Utilities.SerializeToBinary(message); + MyAPIGateway.Multiplayer.SendMessageToOthers(1337, messageBytes); } + + // Send a removal message before closing + var removalMessage2 = new AsteroidNetworkMessage(PositionComp.GetPosition(), Size, Vector3D.Zero, Vector3D.Zero, Type, false, EntityId, true, false); + var removalMessageBytes2 = MyAPIGateway.Utilities.SerializeToBinary(removalMessage2); + MyAPIGateway.Multiplayer.SendMessageToOthers(1337, removalMessageBytes2); + Close(); } @@ -206,6 +225,16 @@ private void Init(Vector3D position, float size, Vector3D initialVelocity, Aster Physics.AngularVelocity = RandVector() * AsteroidSettings.GetRandomAngularVelocity(MainSession.I.Rand); // Set initial angular velocity Log.Info($"Asteroid model {ModelString} loaded successfully with initial angular velocity: {Physics.AngularVelocity}"); + + // Ensure the entity is added to the physics world + if (MyAPIGateway.Session.IsServer) + { + SyncFlag = true; + } + else + { + CreatePhysics(); + } } catch (Exception ex) { diff --git a/Dynamic Asteroids/Data/Scripts/DynamicAsteroids/AsteroidEntities/AsteroidSpawner.cs b/Dynamic Asteroids/Data/Scripts/DynamicAsteroids/AsteroidEntities/AsteroidSpawner.cs index f5ddd35d..488a63d3 100644 --- a/Dynamic Asteroids/Data/Scripts/DynamicAsteroids/AsteroidEntities/AsteroidSpawner.cs +++ b/Dynamic Asteroids/Data/Scripts/DynamicAsteroids/AsteroidEntities/AsteroidSpawner.cs @@ -5,6 +5,7 @@ using VRage.Game.ModAPI; using VRage.ModAPI; using VRageMath; +using ProtoBuf; namespace DynamicAsteroids.AsteroidEntities { @@ -28,9 +29,8 @@ public void Close() return; Log.Info("Closing AsteroidSpawner"); - _asteroids.Clear(); + _asteroids?.Clear(); } - public void UpdateTick() { if (!MyAPIGateway.Session.IsServer) @@ -38,48 +38,71 @@ public void UpdateTick() try { - Vector3D playerPosition = MyAPIGateway.Session?.Player?.GetPosition() ?? Vector3D.MaxValue; - - if (playerPosition == Vector3D.MaxValue || !AsteroidSettings.PlayerCanSeeRings(playerPosition)) - return; + // Get all players on the server + List players = new List(); + MyAPIGateway.Players.GetPlayers(players); - foreach (var asteroid in _asteroids.ToArray()) + foreach (var player in players) { - if (Vector3D.DistanceSquared(asteroid.PositionComp.GetPosition(), playerPosition) > - AsteroidSettings.AsteroidSpawnRadius * AsteroidSettings.AsteroidSpawnRadius * 1.1) + Vector3D playerPosition = player.GetPosition(); + + if (!AsteroidSettings.PlayerCanSeeRings(playerPosition)) { - _asteroids.Remove(asteroid); - asteroid.Close(); continue; } - } - int asteroidsSpawned = 0; - while (_asteroids.Count < AsteroidSettings.MaxAsteroidCount && asteroidsSpawned < 10) - { - Vector3D newPosition = playerPosition + RandVector() * AsteroidSettings.AsteroidSpawnRadius; - Vector3D newVelocity; - if (!AsteroidSettings.CanSpawnAsteroidAtPoint(newPosition, out newVelocity)) - continue; + foreach (var asteroid in _asteroids.ToArray()) + { + if (Vector3D.DistanceSquared(asteroid.PositionComp.GetPosition(), playerPosition) > + AsteroidSettings.AsteroidSpawnRadius * AsteroidSettings.AsteroidSpawnRadius * 1.1) + { + Log.Info($"Removing asteroid at {asteroid.PositionComp.GetPosition()} due to distance from player"); + _asteroids.Remove(asteroid); + + // Send a network message to clients for removal + var removalMessage = new AsteroidNetworkMessage(asteroid.PositionComp.GetPosition(), asteroid.Size, Vector3D.Zero, Vector3D.Zero, asteroid.Type, false, asteroid.EntityId, true, false); + var removalMessageBytes = MyAPIGateway.Utilities.SerializeToBinary(removalMessage); + MyAPIGateway.Multiplayer.SendMessageToOthers(1337, removalMessageBytes); + + asteroid.Close(); + continue; + } + } - if (IsNearVanillaAsteroid(newPosition)) + int asteroidsSpawned = 0; + while (_asteroids.Count < AsteroidSettings.MaxAsteroidCount && asteroidsSpawned < 10) { - Log.Info("Skipped spawning asteroid due to proximity to vanilla asteroid."); - continue; + Vector3D newPosition = playerPosition + RandVector() * AsteroidSettings.AsteroidSpawnRadius; + Vector3D newVelocity; + if (!AsteroidSettings.CanSpawnAsteroidAtPoint(newPosition, out newVelocity)) + continue; + + if (IsNearVanillaAsteroid(newPosition)) + { + Log.Info("Skipped spawning asteroid due to proximity to vanilla asteroid."); + continue; + } + + // Determine asteroid type to spawn + AsteroidType type = AsteroidSettings.GetRandomAsteroidType(MainSession.I.Rand); + + Log.Info($"Spawning asteroid at {newPosition} with velocity {newVelocity} of type {type}"); + var asteroid = AsteroidEntity.CreateAsteroid(newPosition, AsteroidSettings.GetRandomAsteroidSize(MainSession.I.Rand), newVelocity, type); + _asteroids.Add(asteroid); + asteroidsSpawned++; + + // Send a network message to clients + var message = new AsteroidNetworkMessage(newPosition, asteroid.Size, newVelocity, Vector3D.Zero, type, false, asteroid.EntityId, false, true); + var messageBytes = MyAPIGateway.Utilities.SerializeToBinary(message); + MyAPIGateway.Multiplayer.SendMessageToOthers(1337, messageBytes); } - // Determine asteroid type to spawn - AsteroidType type = AsteroidSettings.GetRandomAsteroidType(MainSession.I.Rand); + // Show a notification with the number of active asteroids + MyAPIGateway.Utilities.ShowNotification($"Active Asteroids: {_asteroids.Count}", 1000 / 60); - _asteroids.Add(AsteroidEntity.CreateAsteroid(newPosition, RandAsteroidSize, newVelocity, type)); - asteroidsSpawned++; + // Log the number of active asteroids for debugging purposes + //Log.Info($"Active Asteroids: {_asteroids.Count}"); } - - // Show a notification with the number of active asteroids - MyAPIGateway.Utilities.ShowNotification($"Active Asteroids: {_asteroids.Count}", 1000 / 60); - - // Log the number of active asteroids for debugging purposes - Log.Info($"Active Asteroids: {_asteroids.Count}"); } catch (Exception ex) { @@ -96,6 +119,7 @@ private bool IsNearVanillaAsteroid(Vector3D position) { if (Vector3D.DistanceSquared(position, voxelMap.GetPosition()) < MinDistanceFromVanillaAsteroids * MinDistanceFromVanillaAsteroids) { + Log.Info($"Position {position} is near vanilla asteroid {voxelMap.StorageName}"); return true; } } diff --git a/Dynamic Asteroids/Data/Scripts/DynamicAsteroids/AsteroidNetworkMessage.cs b/Dynamic Asteroids/Data/Scripts/DynamicAsteroids/AsteroidNetworkMessage.cs new file mode 100644 index 00000000..21ccfb0c --- /dev/null +++ b/Dynamic Asteroids/Data/Scripts/DynamicAsteroids/AsteroidNetworkMessage.cs @@ -0,0 +1,50 @@ +using DynamicAsteroids.AsteroidEntities; +using VRageMath; +using ProtoBuf; + +namespace DynamicAsteroids +{ + [ProtoContract] + public struct AsteroidNetworkMessage + { + [ProtoMember(1)] + public Vector3D Position; + + [ProtoMember(2)] + public float Size; + + [ProtoMember(3)] + public Vector3D InitialVelocity; + + [ProtoMember(4)] + public Vector3D AngularVelocity; + + [ProtoMember(5)] + public AsteroidType Type; + + [ProtoMember(6)] + public bool IsSubChunk; + + [ProtoMember(7)] + public long EntityId; + + [ProtoMember(8)] + public bool IsRemoval; + + [ProtoMember(9)] + public bool IsInitialCreation; + + public AsteroidNetworkMessage(Vector3D position, float size, Vector3D initialVelocity, Vector3D angularVelocity, AsteroidType type, bool isSubChunk, long entityId, bool isRemoval, bool isInitialCreation) + { + Position = position; + Size = size; + InitialVelocity = initialVelocity; + AngularVelocity = angularVelocity; + Type = type; + IsSubChunk = isSubChunk; + EntityId = entityId; + IsRemoval = isRemoval; + IsInitialCreation = isInitialCreation; + } + } +} \ No newline at end of file diff --git a/Dynamic Asteroids/Data/Scripts/DynamicAsteroids/MainSession.cs b/Dynamic Asteroids/Data/Scripts/DynamicAsteroids/MainSession.cs index 99d7b388..74366aa6 100644 --- a/Dynamic Asteroids/Data/Scripts/DynamicAsteroids/MainSession.cs +++ b/Dynamic Asteroids/Data/Scripts/DynamicAsteroids/MainSession.cs @@ -7,6 +7,8 @@ using VRage.Game.Components; using VRage.Input; using VRageMath; +using ProtoBuf; +using Sandbox.Game.Entities; namespace DynamicAsteroids { @@ -33,6 +35,8 @@ public override void LoadData() { _spawner.Init(); } + + MyAPIGateway.Multiplayer.RegisterMessageHandler(1337, OnMessageReceived); } catch (Exception ex) { @@ -49,6 +53,8 @@ protected override void UnloadData() { _spawner.Close(); } + + MyAPIGateway.Multiplayer.UnregisterMessageHandler(1337, OnMessageReceived); } catch (Exception ex) { @@ -68,7 +74,7 @@ public override void UpdateAfterSimulation() _spawner.UpdateTick(); } - if (MyAPIGateway.Session?.Player?.Character != null) + if (MyAPIGateway.Session?.Player?.Character != null && _spawner._asteroids != null) { Vector3D characterPosition = MyAPIGateway.Session.Player.Character.PositionComp.GetPosition(); AsteroidEntity nearestAsteroid = FindNearestAsteroid(characterPosition); @@ -97,9 +103,55 @@ public override void UpdateAfterSimulation() Log.Exception(ex, typeof(MainSession)); } } + private void OnMessageReceived(byte[] message) + { + try + { + var asteroidMessage = MyAPIGateway.Utilities.SerializeFromBinary(message); + Log.Info($"Client: Received message to create/remove asteroid at {asteroidMessage.Position} with velocity {asteroidMessage.InitialVelocity} of type {asteroidMessage.Type}"); + + if (asteroidMessage.IsRemoval) + { + // Find and remove the asteroid with the given EntityId + var asteroid = MyEntities.GetEntityById(asteroidMessage.EntityId) as AsteroidEntity; + if (asteroid != null) + { + asteroid.Close(); + } + } + else if (asteroidMessage.IsInitialCreation) + { + var asteroid = AsteroidEntity.CreateAsteroid(asteroidMessage.Position, asteroidMessage.Size, asteroidMessage.InitialVelocity, asteroidMessage.Type); + asteroid.Physics.AngularVelocity = asteroidMessage.AngularVelocity; + MyEntities.Add(asteroid); + } + else + { + if (asteroidMessage.IsSubChunk) + { + // Create the sub-chunk asteroid on the client + var subChunk = AsteroidEntity.CreateAsteroid(asteroidMessage.Position, asteroidMessage.Size, asteroidMessage.InitialVelocity, asteroidMessage.Type); + subChunk.Physics.AngularVelocity = asteroidMessage.AngularVelocity; + } + else + { + // Create the regular asteroid on the client + var asteroid = AsteroidEntity.CreateAsteroid(asteroidMessage.Position, asteroidMessage.Size, asteroidMessage.InitialVelocity, asteroidMessage.Type); + asteroid.Physics.AngularVelocity = asteroidMessage.AngularVelocity; + MyEntities.Add(asteroid); + } + } + } + catch (Exception ex) + { + Log.Exception(ex, typeof(MainSession)); + } + } private AsteroidEntity FindNearestAsteroid(Vector3D characterPosition) { + if (_spawner._asteroids == null) return null; + AsteroidEntity nearestAsteroid = null; double minDistance = double.MaxValue; @@ -119,8 +171,6 @@ private AsteroidEntity FindNearestAsteroid(Vector3D characterPosition) // This function determines the type of asteroid to spawn private AsteroidType DetermineAsteroidType() { - // Here you can add logic to determine the type of asteroid. - // For example, randomly selecting a type or using some other logic. int randValue = Rand.Next(0, 2); // Adjust as needed for more types return (AsteroidType)randValue; } diff --git a/Dynamic Asteroids/Invalid_copy to DS and client.bat b/Dynamic Asteroids/Invalid_copy to DS and client.bat new file mode 100644 index 00000000..aa2c4f77 --- /dev/null +++ b/Dynamic Asteroids/Invalid_copy to DS and client.bat @@ -0,0 +1,31 @@ +@echo off +rem Testing mods in DS by yourself can be done without the need to re-publish every time. +rem You can simply update the files that are on your machine! +rem This will only work for you, anyone else joining the server will of course download the mod from the workshop. + +rem To use: +rem 1. Copy this .bat file in the ROOT folder of your local mod (e.g. %appdata%/SpaceEngineers/Mods/YourLocalMod/) + +rem 2. Edit this variable if applicable (do not add quotes or end with backslash). +set STEAM_PATH=C:\Program Files\Steam + +rem 3. Edit this with your mod's workshop id. +set WORKSHOP_ID=3262577158 + +rem Now you can run it every time you want to update the mod on DS and client. + + + +rem Don't edit the below unless you really need different paths. +rem NOTE: don't add quotes and don't end with a backslash! + +set CLIENT_PATH=O:\SteamLibrary\steamapps\workshop\content\244850\%WORKSHOP_ID% +set DS_PATH=%APPDATA%\SpaceEngineersDedicated\content\244850\%WORKSHOP_ID% + +rmdir "%CLIENT_PATH%" /S /Q +rmdir "%DS_PATH%" /S /Q + +robocopy.exe .\ "%DS_PATH%" *.* /S /xd .git bin obj .vs ignored /xf *.lnk *.git* *.bat *.zip *.7z *.blend* *.md *.log *.sln *.csproj *.csproj.user *.ruleset modinfo.sbmi +robocopy.exe "%DS_PATH%" "%CLIENT_PATH%" *.* /S + +pause \ No newline at end of file