diff --git a/Subsurface/Source/Characters/AI/AIController.cs b/Subsurface/Source/Characters/AI/AIController.cs index f399f170f..53aed1da8 100644 --- a/Subsurface/Source/Characters/AI/AIController.cs +++ b/Subsurface/Source/Characters/AI/AIController.cs @@ -43,7 +43,7 @@ namespace Subsurface steeringManager = new SteeringManager(this); } - public virtual void SelectTarget(IDamageable target) { } + public virtual void SelectTarget(AITarget target) { } public virtual void Update(float deltaTime) { } diff --git a/Subsurface/Source/Characters/AI/EnemyAIController.cs b/Subsurface/Source/Characters/AI/EnemyAIController.cs index d0604970f..4eda0150f 100644 --- a/Subsurface/Source/Characters/AI/EnemyAIController.cs +++ b/Subsurface/Source/Characters/AI/EnemyAIController.cs @@ -82,11 +82,10 @@ namespace Subsurface state = AiState.None; } - public override void SelectTarget(IDamageable target) + public override void SelectTarget(AITarget target) { - targetEntity = target; - selectedAiTarget = target.AiTarget; - selectedTargetMemory = FindTargetMemory(target.AiTarget); + selectedAiTarget = target; + selectedTargetMemory = FindTargetMemory(target); targetValue = 100.0f; } diff --git a/Subsurface/Source/Characters/AnimController.cs b/Subsurface/Source/Characters/AnimController.cs index 2dc202f8d..3ed46bc4e 100644 --- a/Subsurface/Source/Characters/AnimController.cs +++ b/Subsurface/Source/Characters/AnimController.cs @@ -31,7 +31,11 @@ namespace Subsurface public float StunTimer { get { return stunTimer; } - set { stunTimer = value; } + set + { + if (float.IsNaN(value) || float.IsInfinity(value)) return; + stunTimer = value; + } } public AnimController(Character character, XElement element) diff --git a/Subsurface/Source/Characters/Character.cs b/Subsurface/Source/Characters/Character.cs index 37462d66e..0760b87c1 100644 --- a/Subsurface/Source/Characters/Character.cs +++ b/Subsurface/Source/Characters/Character.cs @@ -1024,6 +1024,10 @@ namespace Subsurface { return; } + else if (type== NetworkEventType.NotMoving) + { + return; + } //if (type == Networking.NetworkEventType.KeyHit) @@ -1057,11 +1061,11 @@ namespace Subsurface message.Write(limb.body.Position.X); message.Write(limb.body.Position.Y); - message.Write(limb.body.LinearVelocity.X); - message.Write(limb.body.LinearVelocity.Y); + //message.Write(limb.body.LinearVelocity.X); + //message.Write(limb.body.LinearVelocity.Y); message.Write(limb.body.Rotation); - message.Write(limb.body.AngularVelocity); + //message.WriteRangedSingle(MathHelper.Clamp(limb.body.AngularVelocity, -10.0f, 10.0f), -10.0f, 10.0f, 8); i++; } @@ -1118,6 +1122,13 @@ namespace Subsurface } return; } + else if (type == NetworkEventType.NotMoving) + { + AnimController.TargetMovement = Vector2.Zero; + actionKeyDown.State = false; + secondaryKeyDown.State = false; + return; + } bool actionKeyState = false; bool secondaryKeyState = false; @@ -1176,11 +1187,11 @@ namespace Subsurface pos.X = message.ReadFloat(); pos.Y = message.ReadFloat(); - vel.X = message.ReadFloat(); - vel.Y = message.ReadFloat(); + //vel.X = message.ReadFloat(); + //vel.Y = message.ReadFloat(); rotation = message.ReadFloat(); - angularVel = message.ReadFloat(); + //angularVel = message.ReadFloat(); } catch { @@ -1189,10 +1200,10 @@ namespace Subsurface if (limb.body != null) { - limb.body.TargetVelocity = vel; + limb.body.TargetVelocity = limb.body.LinearVelocity; limb.body.TargetPosition = pos;// +vel * (float)(deltaTime / 60.0); limb.body.TargetRotation = rotation;// +angularVel * (float)(deltaTime / 60.0); - limb.body.TargetAngularVelocity = angularVel; + limb.body.TargetAngularVelocity = limb.body.AngularVelocity; } } diff --git a/Subsurface/Source/Characters/HumanoidAnimController.cs b/Subsurface/Source/Characters/HumanoidAnimController.cs index a887f35e3..93f618a1c 100644 --- a/Subsurface/Source/Characters/HumanoidAnimController.cs +++ b/Subsurface/Source/Characters/HumanoidAnimController.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Xml.Linq; using FarseerPhysics; using Microsoft.Xna.Framework; +using Subsurface.Items.Components; namespace Subsurface { @@ -542,7 +543,7 @@ namespace Subsurface void UpdateClimbing() { - if (character.SelectedConstruction == null) + if (character.SelectedConstruction == null || character.SelectedConstruction.GetComponent()==null) { Anim = Animation.None; return; @@ -623,7 +624,12 @@ namespace Subsurface torso.body.ApplyForce(climbForce * 40.0f * torso.Mass); head.body.SmoothRotate(0.0f); - Rectangle trigger = character.SelectedConstruction.Prefab.Triggers.First(); + Rectangle trigger = character.SelectedConstruction.Prefab.Triggers.FirstOrDefault(); + if (trigger == null) + { + character.SelectedConstruction = null; + return; + } trigger = character.SelectedConstruction.TransformTrigger(trigger); //stop climbing if: diff --git a/Subsurface/Source/Characters/Ragdoll.cs b/Subsurface/Source/Characters/Ragdoll.cs index 0efc2b6b3..3187ed605 100644 --- a/Subsurface/Source/Characters/Ragdoll.cs +++ b/Subsurface/Source/Characters/Ragdoll.cs @@ -472,7 +472,7 @@ namespace Subsurface inWater = false; headInWater = false; - if (currentHull.Volume>currentHull.FullVolume*0.95f || ConvertUnits.ToSimUnits(currentHull.Surface)-floorY> HeadPosition*0.95f) + if (currentHull.Volume > currentHull.FullVolume * 0.95f || ConvertUnits.ToSimUnits(currentHull.Surface) - floorY > HeadPosition * 0.95f) inWater = true; } @@ -562,7 +562,7 @@ namespace Subsurface private void UpdateNetplayerPosition() { Limb refLimb = GetLimb(LimbType.Torso); - if (refLimb== null) refLimb = GetLimb(LimbType.Head); + if (refLimb == null) refLimb = GetLimb(LimbType.Head); if (refLimb.body.TargetPosition == Vector2.Zero) return; @@ -603,7 +603,7 @@ namespace Subsurface if (resetAll) { - System.Diagnostics.Debug.WriteLine("resetall"); + System.Diagnostics.Debug.WriteLine("reset ragdoll limb positions"); foreach (Limb limb in limbs) { diff --git a/Subsurface/Source/GameSession/GameModes/TutorialMode.cs b/Subsurface/Source/GameSession/GameModes/TutorialMode.cs index 7a83faa6f..dd82ff8d9 100644 --- a/Subsurface/Source/GameSession/GameModes/TutorialMode.cs +++ b/Subsurface/Source/GameSession/GameModes/TutorialMode.cs @@ -22,6 +22,8 @@ namespace Subsurface Game1.GameSession.StartShift(TimeSpan.Zero, "tutorial"); + Game1.GameSession.taskManager.Tasks.Clear(); + Game1.GameScreen.Select(); } @@ -184,7 +186,7 @@ namespace Subsurface + " going into the to the power connection - that's why the monitor isn't working." + " You should find a piece of wire to connect it. Try searching some of the cabinets scattered around the sub."); - while (Character.Controlled.Inventory.items.FirstOrDefault(i => i!=null && i.GetComponent()!=null)==null) + while (!HasItem("Wire")) { yield return Status.Running; } @@ -280,32 +282,159 @@ namespace Subsurface var moloch = new Character("Content/Characters/Moloch/moloch.xml", steering.Item.SimPosition + Vector2.UnitX * 15.0f); moloch.PlaySound(AIController.AiState.Attack); - //moloch.AIController. - infoBox = CreateInfoFrame("Uh-oh... Something enormous just appeared on the radar."); - Structure window = null; + List windows = new List(); foreach (Structure s in Structure.wallList) { - if (s.CastShadow) continue; + if (s.CastShadow || !s.HasBody) continue; - if (window == null || s.Rect.Right > window.Rect.Right) window = s; + if (s.Rect.Right > steering.Item.Position.X) windows.Add(s); } bool broken = false; do { - moloch.AIController.SelectTarget(steering.Item); - for (int i = 0; i < window.SectionCount; i++) + moloch.AIController.SelectTarget(steering.Item.CurrentHull.AiTarget); + Vector2 steeringDir = windows[0].Position - moloch.Position; + if (steeringDir != Vector2.Zero) steeringDir = Vector2.Normalize(steeringDir); + + foreach (Limb limb in moloch.AnimController.limbs) { - if (!window.SectionHasHole(i)) continue; - broken = true; - break; + limb.body.LinearVelocity = new Vector2(limb.LinearVelocity.X, limb.LinearVelocity.Y + steeringDir.Y*0.01f); } + moloch.AIController.Steering = steeringDir; + + foreach (Structure window in windows) + { + for (int i = 0; i < window.SectionCount; i++) + { + if (!window.SectionHasHole(i)) continue; + broken = true; + break; + } + if (broken) break; + } + + yield return new WaitForSeconds(1.0f); } while (!broken); + yield return new WaitForSeconds(1.0f); + + + var capacitor1 = Item.itemList.Find(i => i.HasTag("capacitor1")).GetComponent(); + var capacitor2 = Item.itemList.Find(i => i.HasTag("capacitor1")).GetComponent(); + CoroutineManager.StartCoroutine(KeepEnemyAway(moloch, new PowerContainer[] { capacitor1, capacitor2 })); + + + infoBox = CreateInfoFrame("The hull has been breached! Close all the doors to the command room to stop the water from flooding the entire sub!"); + + + Door commandDoor1 = Item.itemList.Find(i => i.HasTag("commanddoor1")).GetComponent(); + Door commandDoor2 = Item.itemList.Find(i => i.HasTag("commanddoor2")).GetComponent(); + Door commandDoor3 = Item.itemList.Find(i => i.HasTag("commanddoor3")).GetComponent(); + + while (commandDoor1.IsOpen && (commandDoor2.IsOpen || commandDoor3.IsOpen)) + { + yield return Status.Running; + } + + infoBox = CreateInfoFrame("Great! You should find yourself an diving mask or a diving suit, in case the creature causes more damage. "+ + "There are some in the room next to the airlock."); + + while (!HasItem("Diving Mask") && !HasItem("Diving Suit")) + { + yield return Status.Running; + } + + if (HasItem("Diving Mask")) + { + infoBox = CreateInfoFrame("The diving mask will let you breathe underwater, but it won't protect from the water pressure outside the sub. "+ + "It should be fine for the situation at hand, but you still need to find an oxygen tank and drag it into the same slot as the mask." + + "You should grab one or two."); + } + else if (HasItem("Diving Suit")) + { + infoBox = CreateInfoFrame("In addition to letting you breathe underwater, the suit will protect you from the water pressure outside the sub " + + "(unlike the diving mask). However, you still need to drag an oxygen tank into the same slot as the suit to supply oxygen. "+ + "You should grab one or two."); + } + + while (!HasItem("Oxygen Tank")) + { + yield return Status.Running; + } + + yield return new WaitForSeconds(5.0f); + + infoBox = CreateInfoFrame("Now it's time to stop the creature attacking the submarine. Head to the railgun room at the upper right corner of the sub."); + + var railGun = Item.itemList.Find(i => i.GetComponent()!=null); + + while (Vector2.Distance(Character.Controlled.Position, railGun.Position)>500) + { + yield return new WaitForSeconds(1.0f); + } + + infoBox = CreateInfoFrame("The railgun requires a large power surge to fire. The reactor can't provide a surge large enough, so we need to use the " + +" supercapacitors in the railgun room. The capacitors need to be charged first; select them and crank up the recharge rate."); + + while (capacitor1.RechargeSpeed<0.5f && capacitor2.RechargeSpeed<0.5f) + { + yield return new WaitForSeconds(1.0f); + } + + infoBox = CreateInfoFrame("The capacitors consume large amounts of power when they're being charged at a high rate. "+ + "Be cautious to overload the electrical grid or the reactor. They also take some time to recharge, so now is a good "+ + "time to head to the room below to load some shells into the railgun."); + + + var loader = Item.itemList.Find(i => i.Name == "Railgun Loader").GetComponent(); + + while (Math.Abs(Character.Controlled.Position.Y - loader.Item.Position.Y)>50) + { + yield return Status.Running; + } + + infoBox = CreateInfoFrame("Grab one of the shells. You can load it by selecting the railgun loader and dragging the shell to. " + +"one of the free slots."); + + while (loader.Item.ContainedItems.FirstOrDefault(i => i != null) != null) + { + capacitor1.Charge += 1.0f; + capacitor2.Charge += 1.0f; + yield return Status.Running; + } + + + yield return Status.Success; + } + + private bool HasItem(string itemName) + { + if (Character.Controlled == null) return false; + return Character.Controlled.Inventory.items.FirstOrDefault(i => i != null && i.Name == itemName)!=null; + } + + /// + /// keeps the enemy away from the sub until the capacitors are loaded + /// + private IEnumerable KeepEnemyAway(Character enemy, PowerContainer[] capacitors) + { + do + { + Vector2 targetPos = Character.Controlled.Position + new Vector2(0.0f, 3000.0f); + + Vector2 steering = targetPos - enemy.Position; + if (steering != Vector2.Zero) steering = Vector2.Normalize(steering); + + enemy.AIController.Steering = steering*2.0f; + + yield return Status.Running; + } while (capacitors.FirstOrDefault(c => c.Charge > 0.4f) == null); + yield return Status.Success; } diff --git a/Subsurface/Source/Items/Components/Label.cs b/Subsurface/Source/Items/Components/Label.cs index 411fbdeee..dbc63f08b 100644 --- a/Subsurface/Source/Items/Components/Label.cs +++ b/Subsurface/Source/Items/Components/Label.cs @@ -92,8 +92,11 @@ namespace Subsurface.Items.Components newText = message.ReadString(); } - catch + catch (Exception e) { +#if DEBUG + DebugConsole.ThrowError("invalid network message", e); +#endif return; } diff --git a/Subsurface/Source/Items/Components/Machines/Pump.cs b/Subsurface/Source/Items/Components/Machines/Pump.cs index d162e1e42..c320f0944 100644 --- a/Subsurface/Source/Items/Components/Machines/Pump.cs +++ b/Subsurface/Source/Items/Components/Machines/Pump.cs @@ -22,8 +22,9 @@ namespace Subsurface.Items.Components get { return flowPercentage; } set { - if (float.IsNaN(flowPercentage)) return; - flowPercentage = MathHelper.Clamp(value,-100.0f,100.0f); + if (!MathUtils.IsValid(flowPercentage)) return; + flowPercentage = MathHelper.Clamp(value,-100.0f,100.0f); + flowPercentage = MathUtils.Round(flowPercentage, 1.0f); } } @@ -117,14 +118,14 @@ namespace Subsurface.Items.Components spriteBatch.DrawString(GUI.Font, "Flow percentage: " + (int)flowPercentage + " %", new Vector2(x + 20, y + 80), Color.White); - if (GUI.DrawButton(spriteBatch, new Rectangle(x + 200, y + 70, 40, 40), "+", true)) + if (GUI.DrawButton(spriteBatch, new Rectangle(x + 200, y + 70, 40, 40), "+", false)) { - FlowPercentage += 1.0f; + FlowPercentage += 10.0f; item.NewComponentEvent(this, true); } - if (GUI.DrawButton(spriteBatch, new Rectangle(x + 250, y + 70, 40, 40), "-", true)) + if (GUI.DrawButton(spriteBatch, new Rectangle(x + 250, y + 70, 40, 40), "-", false)) { - FlowPercentage -= 1.0f; + FlowPercentage -= 10.0f; item.NewComponentEvent(this, true); } @@ -166,7 +167,7 @@ namespace Subsurface.Items.Components public override void FillNetworkData(Networking.NetworkEventType type, Lidgren.Network.NetOutgoingMessage message) { - message.Write(flowPercentage); + message.Write(Convert.ToByte(flowPercentage+100)); message.Write(isActive); } @@ -177,11 +178,17 @@ namespace Subsurface.Items.Components try { - newFlow = message.ReadFloat(); + newFlow = (float)(message.ReadByte()-100); newActive = message.ReadBoolean(); } - catch { return; } + catch (Exception e) + { +#if DEBUG + DebugConsole.ThrowError("invalid network message", e); +#endif + return; + } FlowPercentage = newFlow; isActive = newActive; diff --git a/Subsurface/Source/Items/Components/Machines/Reactor.cs b/Subsurface/Source/Items/Components/Machines/Reactor.cs index a2351be9d..b5bc0a300 100644 --- a/Subsurface/Source/Items/Components/Machines/Reactor.cs +++ b/Subsurface/Source/Items/Components/Machines/Reactor.cs @@ -68,19 +68,31 @@ namespace Subsurface.Items.Components public float FissionRate { get { return fissionRate; } - set { fissionRate = MathHelper.Clamp(value, 0.0f, 100.0f); } + set + { + if (!MathUtils.IsValid(value)) return; + fissionRate = MathHelper.Clamp(value, 0.0f, 100.0f); + } } public float CoolingRate { get { return coolingRate; } - set { coolingRate = MathHelper.Clamp(value, 0.0f, 100.0f); } + set + { + if (!MathUtils.IsValid(value)) return; + coolingRate = MathHelper.Clamp(value, 0.0f, 100.0f); + } } public float Temperature { get { return temperature; } - set { temperature = MathHelper.Clamp(value, 0.0f, 10000.0f); } + set + { + if (!MathUtils.IsValid(value)) return; + temperature = MathHelper.Clamp(value, 0.0f, 10000.0f); + } } public bool IsRunning() @@ -100,6 +112,7 @@ namespace Subsurface.Items.Components public float ShutDownTemp { get { return shutDownTemp; } + private set { shutDownTemp = MathHelper.Clamp(value, 0.0f, 10000.0f); } } public Reactor(Item item, XElement element) @@ -334,12 +347,12 @@ namespace Subsurface.Items.Components if (GUI.DrawButton(spriteBatch, new Rectangle(x + 400, y + 180, 40, 40), "+", true)) { valueChanged = true; - shutDownTemp += 100.0f; + ShutDownTemp += 100.0f; } if (GUI.DrawButton(spriteBatch, new Rectangle(x + 450, y + 180, 40, 40), "-", true)) { valueChanged = true; - shutDownTemp -= 100.0f; + ShutDownTemp -= 100.0f; } if (valueChanged) @@ -392,11 +405,11 @@ namespace Subsurface.Items.Components public override void FillNetworkData(NetworkEventType type, NetOutgoingMessage message) { message.Write(autoTemp); - message.Write(temperature); - message.Write(shutDownTemp); + message.WriteRangedSingle(temperature, 0.0f, 10000.0f, 16); + message.WriteRangedSingle(shutDownTemp, 0.0f, 10000.0f, 16); - message.Write(coolingRate); - message.Write(fissionRate); + message.WriteRangedSingle(coolingRate, 0.0f, 100.0f, 8); + message.WriteRangedSingle(fissionRate, 0.0f, 100.0f, 8); } public override void ReadNetworkData(NetworkEventType type, NetIncomingMessage message) @@ -408,18 +421,24 @@ namespace Subsurface.Items.Components try { newAutoTemp = message.ReadBoolean(); - newTemperature = message.ReadFloat(); - newShutDownTemp = message.ReadFloat(); + newTemperature = message.ReadRangedSingle(0.0f, 10000.0f, 16); + newShutDownTemp = message.ReadRangedSingle(0.0f, 10000.0f, 16); - newCoolingRate = message.ReadFloat(); - newFissionRate = message.ReadFloat(); + newCoolingRate = message.ReadRangedSingle(0.0f, 100.0f, 8); + newFissionRate = message.ReadRangedSingle(0.0f, 100.0f, 8); } - catch { return; } + catch (Exception e) + { +#if DEBUG + DebugConsole.ThrowError("invalid network message", e); +#endif + return; + } autoTemp = newAutoTemp; Temperature = newTemperature; - shutDownTemp = newShutDownTemp; + ShutDownTemp = newShutDownTemp; CoolingRate = newCoolingRate; FissionRate = newFissionRate; diff --git a/Subsurface/Source/Items/Components/Machines/Steering.cs b/Subsurface/Source/Items/Components/Machines/Steering.cs index 0caf77329..5bb97e7c8 100644 --- a/Subsurface/Source/Items/Components/Machines/Steering.cs +++ b/Subsurface/Source/Items/Components/Machines/Steering.cs @@ -48,10 +48,7 @@ namespace Subsurface.Items.Components get { return targetVelocity;} set { - if (float.IsNaN(value.X) || float.IsNaN(value.Y)) - { - return; - } + if (!MathUtils.IsValid(value)) return; targetVelocity.X = MathHelper.Clamp(value.X, -100.0f, 100.0f); targetVelocity.Y = MathHelper.Clamp(value.Y, -100.0f, 100.0f); } diff --git a/Subsurface/Source/Map/Entity.cs b/Subsurface/Source/Map/Entity.cs index bd2dc940d..375f796fd 100644 --- a/Subsurface/Source/Map/Entity.cs +++ b/Subsurface/Source/Map/Entity.cs @@ -45,6 +45,11 @@ namespace Subsurface get { return Vector2.Zero; } } + public AITarget AiTarget + { + get { return aiTarget; } + } + public Entity() { //give an unique ID diff --git a/Subsurface/Source/Map/Submarine.cs b/Subsurface/Source/Map/Submarine.cs index 4d09ddcb1..e520bc943 100644 --- a/Subsurface/Source/Map/Submarine.cs +++ b/Subsurface/Source/Map/Submarine.cs @@ -505,7 +505,7 @@ namespace Subsurface private void Translate(Vector2 amount) { - if (amount == Vector2.Zero || ! amount.IsValid()) return; + if (amount == Vector2.Zero || !amount.IsValid()) return; Level.Loaded.Move(-amount); } @@ -587,8 +587,11 @@ namespace Subsurface newSpeed = new Vector2(message.ReadFloat(), message.ReadFloat()); } - catch + catch (Exception e) { +#if DEBUG + DebugConsole.ThrowError("invalid network message", e); +#endif return; } diff --git a/Subsurface/Source/Networking/GameClient.cs b/Subsurface/Source/Networking/GameClient.cs index 7e9b882ce..b1f1d0936 100644 --- a/Subsurface/Source/Networking/GameClient.cs +++ b/Subsurface/Source/Networking/GameClient.cs @@ -72,13 +72,18 @@ namespace Subsurface.Networking myCharacter = Character.Controlled; // Create new instance of configs. Parameter is "application Id". It has to be same on client and server. - NetPeerConfiguration Config = new NetPeerConfiguration("subsurface"); - - //Config.SimulatedLoss = 0.2f; - //Config.SimulatedMinimumLatency = 0.5f; + NetPeerConfiguration config = new NetPeerConfiguration("subsurface"); + +#if DEBUG + config.SimulatedLoss = 0.2f; + config.SimulatedMinimumLatency = 0.3f; +#endif + + config.DisableMessageType(NetIncomingMessageType.DebugMessage | NetIncomingMessageType.WarningMessage | NetIncomingMessageType.Receipt + | NetIncomingMessageType.ErrorMessage | NetIncomingMessageType.Error); // Create new client, with previously created configs - client = new NetClient(Config); + client = new NetClient(config); NetOutgoingMessage outmsg = client.CreateMessage(); client.Start(); @@ -267,9 +272,19 @@ namespace Subsurface.Networking Character.Controlled = null; Game1.GameScreen.Cam.TargetPos = Vector2.Zero; } - else + else if (gameStarted) { - if (gameStarted) new NetworkEvent(myCharacter.ID, true); + Vector2 charMovement = myCharacter.AnimController.TargetMovement; + if ((charMovement==Vector2.Zero || charMovement.Length()<0.001f) && + !myCharacter.ActionKeyDown.State && !myCharacter.SecondaryKeyDown.State) + { + new NetworkEvent(NetworkEventType.NotMoving, myCharacter.ID, true); + + } + else + { + new NetworkEvent(myCharacter.ID, true); + } } } diff --git a/Subsurface/Source/Networking/GameServer.cs b/Subsurface/Source/Networking/GameServer.cs index 4472cbf9a..65d548fd3 100644 --- a/Subsurface/Source/Networking/GameServer.cs +++ b/Subsurface/Source/Networking/GameServer.cs @@ -30,7 +30,7 @@ namespace Subsurface.Networking private Client myClient; - public GameServer(string name, int port, bool isPublic = false, string password="", bool attemptUPnP = false, int maxPlayers = 10) + public GameServer(string name, int port, bool isPublic = false, string password = "", bool attemptUPnP = false, int maxPlayers = 10) { var endRoundButton = new GUIButton(new Rectangle(Game1.GraphicsWidth - 290, 20, 150, 25), "End round", Alignment.TopLeft, GUI.style, inGameHUD); endRoundButton.OnClicked = EndButtonHit; @@ -41,8 +41,10 @@ namespace Subsurface.Networking config = new NetPeerConfiguration("subsurface"); - //config.SimulatedLoss = 0.2f; - //config.SimulatedMinimumLatency = 0.5f; +#if DEBUG + config.SimulatedLoss = 0.2f; + config.SimulatedMinimumLatency = 0.3f; +#endif config.Port = port; Port = port; @@ -53,6 +55,9 @@ namespace Subsurface.Networking } config.MaximumConnections = maxPlayers; + + config.DisableMessageType(NetIncomingMessageType.DebugMessage | NetIncomingMessageType.WarningMessage | NetIncomingMessageType.Receipt + | NetIncomingMessageType.ErrorMessage | NetIncomingMessageType.Error); config.EnableMessageType(NetIncomingMessageType.ConnectionApproval); @@ -457,6 +462,31 @@ namespace Subsurface.Networking { //System.Diagnostics.Debug.WriteLine("networkevent "+networkEvent.ID); + List recipients = new List(); + + if (!networkEvent.IsImportant) + { + Entity e = Entity.FindEntityByID(networkEvent.ID); + foreach (Client c in connectedClients) + { + if (c.character==null) continue; + if (Vector2.Distance(e.SimPosition, c.character.SimPosition) > 2000.0f) continue; + + recipients.Add(c.Connection); + } + } + else + { + foreach (Client c in connectedClients) + { + if (c.character == null) continue; + + recipients.Add(c.Connection); + } + } + + if (recipients.Count == 0) return; + NetOutgoingMessage message = server.CreateMessage(); message.Write((byte)PacketTypes.NetworkEvent); //if (!networkEvent.IsClient) continue; @@ -467,7 +497,7 @@ namespace Subsurface.Networking if (server.ConnectionsCount>0) { - server.SendMessage(message, server.Connections, + server.SendMessage(message, recipients, (networkEvent.IsImportant) ? NetDeliveryMethod.Unreliable : NetDeliveryMethod.ReliableUnordered, 0); } @@ -703,8 +733,7 @@ namespace Subsurface.Networking spriteBatch.DrawString(GUI.SmallFont, "Sent bytes: " + server.Statistics.SentBytes, new Vector2(x + 10, y + 75), Color.White); spriteBatch.DrawString(GUI.SmallFont, "Sent packets: " + server.Statistics.SentPackets, new Vector2(x + 10, y + 90), Color.White); - - + y += 110; foreach (Client c in connectedClients) { diff --git a/Subsurface/Source/Networking/NetworkEvent.cs b/Subsurface/Source/Networking/NetworkEvent.cs index 47daaed52..37e69e849 100644 --- a/Subsurface/Source/Networking/NetworkEvent.cs +++ b/Subsurface/Source/Networking/NetworkEvent.cs @@ -11,14 +11,15 @@ namespace Subsurface.Networking DropItem = 3, InventoryUpdate = 4, PickItem = 5, - UpdateProperty = 6 + UpdateProperty = 6, + NotMoving = 7 } class NetworkEvent { public static List events = new List(); - private static bool[] isImportant = { false, true, false, true, true, true }; + private static bool[] isImportant = { false, true, false, true, true, true, true, false }; private int id; diff --git a/Subsurface/Source/Physics/PhysicsBody.cs b/Subsurface/Source/Physics/PhysicsBody.cs index 8af21e7f0..562397974 100644 --- a/Subsurface/Source/Physics/PhysicsBody.cs +++ b/Subsurface/Source/Physics/PhysicsBody.cs @@ -7,6 +7,7 @@ using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Subsurface.Networking; using System.Collections.Generic; +using System; namespace Subsurface { @@ -48,6 +49,7 @@ namespace Subsurface get { return targetPosition; } set { + if (float.IsNaN(value.X) || float.IsNaN(value.Y)) return; targetPosition.X = MathHelper.Clamp(value.X, -10000.0f, 10000.0f); targetPosition.Y = MathHelper.Clamp(value.Y, -10000.0f, 10000.0f); } @@ -57,7 +59,8 @@ namespace Subsurface { get { return targetVelocity; } set - { + { + if (float.IsNaN(value.X) || float.IsNaN(value.Y)) return; targetVelocity.X = MathHelper.Clamp(value.X, -100.0f, 100.0f); targetVelocity.Y = MathHelper.Clamp(value.Y, -100.0f, 100.0f); } @@ -68,7 +71,7 @@ namespace Subsurface get { return targetRotation; } set { - if (float.IsNaN(value) || float.IsInfinity(value) || float.IsNegativeInfinity(value)) return; + if (float.IsNaN(value) || float.IsInfinity(value)) return; targetRotation = value; } } @@ -76,7 +79,11 @@ namespace Subsurface public float TargetAngularVelocity { get { return targetAngularVelocity; } - set { targetAngularVelocity = value; } + set + { + if (float.IsNaN(value) || float.IsInfinity(value)) return; + targetAngularVelocity = value; + } } public Vector2 DrawPosition @@ -356,13 +363,37 @@ namespace Subsurface public void ReadNetworkData(NetworkEventType type, NetIncomingMessage message) { - targetPosition.X = message.ReadFloat(); - targetPosition.Y = message.ReadFloat(); - targetVelocity.X = message.ReadFloat(); - targetVelocity.Y = message.ReadFloat(); + Vector2 newTargetPos = Vector2.Zero; + Vector2 newTargetVel = Vector2.Zero; + + float newTargetRotation = 0.0f, newTargetAngularVel = 0.0f; + try + { + newTargetPos = new Vector2(message.ReadFloat(),message.ReadFloat()); + newTargetVel = new Vector2(message.ReadFloat(),message.ReadFloat()); + + newTargetRotation = message.ReadFloat(); + newTargetAngularVel = message.ReadFloat(); + } + + catch (Exception e) + { +#if DEBUG + DebugConsole.ThrowError("invalid network message", e); +#endif + return; + } + + if (!MathUtils.IsValid(newTargetPos) || !MathUtils.IsValid(newTargetVel) || + !MathUtils.IsValid(newTargetRotation) || !MathUtils.IsValid(newTargetAngularVel)) return; + + targetPosition = newTargetPos; + targetVelocity = newTargetVel; + + targetRotation = newTargetRotation; + targetAngularVelocity = newTargetAngularVel; + - targetRotation = message.ReadFloat(); - targetAngularVelocity = message.ReadFloat(); } } } diff --git a/Subsurface/Source/Utils/MathUtils.cs b/Subsurface/Source/Utils/MathUtils.cs index 1a0d088e5..e16b13542 100644 --- a/Subsurface/Source/Utils/MathUtils.cs +++ b/Subsurface/Source/Utils/MathUtils.cs @@ -27,6 +27,18 @@ namespace Subsurface return (float)Math.Atan2(vector.Y, vector.X); } + public static bool IsValid(float value) + { + return (!float.IsInfinity(value) && !float.IsInfinity(value)); + } + + public static bool IsValid(Vector2 vector) + { + return (!float.IsInfinity(vector.X) && !float.IsInfinity(vector.Y) && !float.IsNaN(vector.X) && !float.IsNaN(vector.Y)); + } + + + public static float CurveAngle(float from, float to, float step) { diff --git a/Subsurface_Solution.v12.suo b/Subsurface_Solution.v12.suo index 12c9edcda..24e07d133 100644 Binary files a/Subsurface_Solution.v12.suo and b/Subsurface_Solution.v12.suo differ