diff --git a/Subsurface/Content/Jobs.xml b/Subsurface/Content/Jobs.xml index a9fc829e4..8316e2e55 100644 --- a/Subsurface/Content/Jobs.xml +++ b/Subsurface/Content/Jobs.xml @@ -13,7 +13,7 @@ - + @@ -24,7 +24,7 @@ - + diff --git a/Subsurface/Properties/AssemblyInfo.cs b/Subsurface/Properties/AssemblyInfo.cs index 511733048..2feb084e1 100644 --- a/Subsurface/Properties/AssemblyInfo.cs +++ b/Subsurface/Properties/AssemblyInfo.cs @@ -31,5 +31,5 @@ using System.Runtime.InteropServices; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("0.1.3.1")] -[assembly: AssemblyFileVersion("0.1.3.1")] +[assembly: AssemblyVersion("0.1.3.2")] +[assembly: AssemblyFileVersion("0.1.3.2")] diff --git a/Subsurface/Source/Characters/AI/EnemyAIController.cs b/Subsurface/Source/Characters/AI/EnemyAIController.cs index ffdba403f..d0604970f 100644 --- a/Subsurface/Source/Characters/AI/EnemyAIController.cs +++ b/Subsurface/Source/Characters/AI/EnemyAIController.cs @@ -482,9 +482,9 @@ namespace Subsurface } message.Write(MathUtils.AngleToByte(steeringManager.WanderAngle)); - message.WriteRangedSingle(Math.Max(updateTargetsTimer,0.0f), 0.0f, UpdateTargetsInterval, 8); - message.WriteRangedSingle(Math.Max(raycastTimer,0.0f), 0.0f, RaycastInterval, 8); - message.WriteRangedSingle(Math.Max(coolDownTimer, 0.0f), 0.0f, attackCoolDown*2.0f, 8); + message.WriteRangedSingle(MathHelper.Clamp(updateTargetsTimer,0.0f, UpdateTargetsInterval), 0.0f, UpdateTargetsInterval, 8); + message.WriteRangedSingle(MathHelper.Clamp(raycastTimer, 0.0f, RaycastInterval), 0.0f, RaycastInterval, 8); + message.WriteRangedSingle(MathHelper.Clamp(coolDownTimer, 0.0f, attackCoolDown * 2.0f), 0.0f, attackCoolDown * 2.0f, 8); message.Write(targetEntity==null ? -1 : (targetEntity as Entity).ID); } diff --git a/Subsurface/Source/Characters/Character.cs b/Subsurface/Source/Characters/Character.cs index 85b126a30..37462d66e 100644 --- a/Subsurface/Source/Characters/Character.cs +++ b/Subsurface/Source/Characters/Character.cs @@ -634,7 +634,6 @@ namespace Subsurface } } - if (AnimController.onGround && !AnimController.InWater && AnimController.Anim != AnimController.Animation.UsingConstruction) @@ -1037,15 +1036,15 @@ namespace Subsurface message.Write(NetTime.Now); // Write byte = move direction - message.WriteRangedSingle(AnimController.TargetMovement.X, -10.0f, 10.0f, 8); - message.WriteRangedSingle(AnimController.TargetMovement.Y, -10.0f, 10.0f, 8); + message.WriteRangedSingle(MathHelper.Clamp(AnimController.TargetMovement.X, -10.0f, 10.0f), -10.0f, 10.0f, 8); + message.WriteRangedSingle(MathHelper.Clamp(AnimController.TargetMovement.Y, -10.0f, 10.0f), -10.0f, 10.0f, 8); message.Write(AnimController.TargetDir==Direction.Right); - if (aiController!=null) + if (aiController==null) { - message.WriteRangedSingle(cursorPosition.X, -1000.0f, 1000.0f, 16); - message.WriteRangedSingle(cursorPosition.Y, -1000.0f, 1000.0f, 16); + message.Write(cursorPosition.X); + message.Write(cursorPosition.Y); } message.Write(LargeUpdateTimer <= 0); @@ -1061,12 +1060,12 @@ namespace Subsurface message.Write(limb.body.LinearVelocity.X); message.Write(limb.body.LinearVelocity.Y); - message.Write(MathUtils.AngleToByte(limb.body.Rotation)); + message.Write(limb.body.Rotation); message.Write(limb.body.AngularVelocity); i++; } - message.WriteRangedSingle(AnimController.StunTimer, 0.0f, 60.0f, 8); + message.WriteRangedSingle(MathHelper.Clamp(AnimController.StunTimer,0.0f,60.0f), 0.0f, 60.0f, 8); message.Write((byte)health); if (aiController != null) aiController.FillNetworkData(message); @@ -1140,13 +1139,12 @@ namespace Subsurface targetDir = message.ReadBoolean(); - if (aiController!=null) + if (aiController==null) { cursorPos = new Vector2( - message.ReadRangedSingle(-1000.0f, 1000.0f, 16), - message.ReadRangedSingle(-1000.0f, 1000.0f, 16)); + message.ReadFloat(), + message.ReadFloat()); } - } catch @@ -1181,7 +1179,7 @@ namespace Subsurface vel.X = message.ReadFloat(); vel.Y = message.ReadFloat(); - rotation = MathUtils.ByteToAngle(message.ReadByte()); + rotation = message.ReadFloat(); angularVel = message.ReadFloat(); } catch diff --git a/Subsurface/Source/GUI/GUITextBlock.cs b/Subsurface/Source/GUI/GUITextBlock.cs index 3cc5f0da1..3edce2fd4 100644 --- a/Subsurface/Source/GUI/GUITextBlock.cs +++ b/Subsurface/Source/GUI/GUITextBlock.cs @@ -80,6 +80,8 @@ namespace Subsurface : this (rect, text, null, null, alignment, textAlignment, style, parent, wrap) { this.Font = font == null ? GUI.Font : font; + + SetTextPos(); } public GUITextBlock(Rectangle rect, string text, Color? color, Color? textColor, Alignment textAlignment = Alignment.Left, GUIStyle style = null, GUIComponent parent = null, bool wrap = false) diff --git a/Subsurface/Source/GUI/GUITickBox.cs b/Subsurface/Source/GUI/GUITickBox.cs index 1294417ea..a36d02855 100644 --- a/Subsurface/Source/GUI/GUITickBox.cs +++ b/Subsurface/Source/GUI/GUITickBox.cs @@ -41,19 +41,26 @@ namespace Subsurface box.HoverColor = Color.Gray; box.SelectedColor = Color.DarkGray; - text = new GUITextBlock(new Rectangle(rect.X + 40, rect.Y, 200, 30), label, Color.Transparent, Color.White, Alignment.TopLeft, null, this); + text = new GUITextBlock(new Rectangle(rect.X + 40, rect.Y, 200, rect.Height), label, Color.Transparent, Color.White, Alignment.TopLeft, null, this); Enabled = true; } public override void Update(float deltaTime) { - base.Update(deltaTime); + if (rect.Width ==420) + { + int asd = 1; + } + //base.Update(deltaTime); if (!Enabled) return; if (box.Rect.Contains(PlayerInput.GetMouseState.Position)) { + + + box.State = ComponentState.Hover; if (PlayerInput.GetMouseState.LeftButton == ButtonState.Pressed) @@ -77,12 +84,16 @@ namespace Subsurface public override void Draw(SpriteBatch spriteBatch) { + if (rect.Width == 420) + { + int asd = 1; + } + DrawChildren(spriteBatch); - if (Selected) - { - GUI.DrawRectangle(spriteBatch, new Rectangle(box.Rect.X + 2, box.Rect.Y + 2, box.Rect.Width - 4, box.Rect.Height - 4), Color.Green * 0.8f, true); - } + GUI.DrawRectangle(spriteBatch, new Rectangle(box.Rect.X + 2, box.Rect.Y + 2, box.Rect.Width - 4, box.Rect.Height - 4), + selected ? Color.Green * 0.8f : Color.Black, true); + } } } diff --git a/Subsurface/Source/Networking/GameClient.cs b/Subsurface/Source/Networking/GameClient.cs index 47fcbd36a..7e9b882ce 100644 --- a/Subsurface/Source/Networking/GameClient.cs +++ b/Subsurface/Source/Networking/GameClient.cs @@ -74,8 +74,8 @@ namespace Subsurface.Networking // 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; + //Config.SimulatedLoss = 0.2f; + //Config.SimulatedMinimumLatency = 0.5f; // Create new client, with previously created configs client = new NetClient(Config); @@ -111,8 +111,11 @@ namespace Subsurface.Networking //update.Elapsed += new System.Timers.ElapsedEventHandler(Update); // Funtion that waits for connection approval info from server + if (reconnectBox==null) + { + reconnectBox = new GUIMessageBox("CONNECTING", "Connecting to " + serverIP, new string[0]); + } - reconnectBox = new GUIMessageBox("CONNECTING", "Connecting to " + serverIP, new string[0]); CoroutineManager.StartCoroutine(WaitForStartingInfo()); // Start the timer @@ -239,14 +242,19 @@ namespace Subsurface.Networking if (!connected || updateTimer > DateTime.Now) return; - if (client.ConnectionStatus == NetConnectionStatus.Disconnected && reconnectBox==null) + if (client.ConnectionStatus == NetConnectionStatus.Disconnected) { - reconnectBox = new GUIMessageBox("CONNECTION LOST", "You have been disconnected from the server. Reconnecting...", new string[0]); - connected = false; - ConnectToServer(serverIP); + if (reconnectBox==null) + { + reconnectBox = new GUIMessageBox("CONNECTION LOST", "You have been disconnected from the server. Reconnecting...", new string[0]); + connected = false; + ConnectToServer(serverIP); + } + return; } - else if (reconnectBox!=null) + + if (reconnectBox!=null) { reconnectBox.Close(null,null); reconnectBox = null; diff --git a/Subsurface/Source/Networking/GameServer.cs b/Subsurface/Source/Networking/GameServer.cs index 95ba6e8ad..4472cbf9a 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="") + 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,26 +41,30 @@ namespace Subsurface.Networking config = new NetPeerConfiguration("subsurface"); - config.SimulatedLoss = 0.2f; - config.SimulatedMinimumLatency = 0.5f; + //config.SimulatedLoss = 0.2f; + //config.SimulatedMinimumLatency = 0.5f; config.Port = port; Port = port; - config.EnableUPnP = true; + if (attemptUPnP) + { + config.EnableUPnP = true; + } - config.MaximumConnections = 10; - + config.MaximumConnections = maxPlayers; + config.EnableMessageType(NetIncomingMessageType.ConnectionApproval); try { server = new NetServer(config); server.Start(); - - // attempt to forward port - server.UPnP.ForwardPort(port, "subsurface"); + if (attemptUPnP) + { + server.UPnP.ForwardPort(port, "subsurface"); + } } catch (Exception e) @@ -126,7 +130,7 @@ namespace Subsurface.Networking masterServerResponded = false; var restRequestHandle = client.ExecuteAsync(request, response => MasterServerCallBack(response)); - DateTime timeOut = DateTime.Now + new TimeSpan(0, 0, 10); + DateTime timeOut = DateTime.Now + new TimeSpan(0, 0, 15); while (!masterServerResponded) { if (DateTime.Now > timeOut) @@ -220,9 +224,9 @@ namespace Subsurface.Networking break; } - if (!isClient) + if (!isClient && (c.SimPosition==Vector2.Zero || c.SimPosition.Length() < 300.0f)) { - //c.LargeUpdateTimer = 0; + c.LargeUpdateTimer -= 2; new NetworkEvent(c.ID, false); } } diff --git a/Subsurface/Source/Screens/MainMenu.cs b/Subsurface/Source/Screens/MainMenu.cs index 781fcfea7..237d7b75a 100644 --- a/Subsurface/Source/Screens/MainMenu.cs +++ b/Subsurface/Source/Screens/MainMenu.cs @@ -18,8 +18,8 @@ namespace Subsurface private GUITextBox saveNameBox, seedBox; - private GUITextBox serverNameBox, portBox, passwordBox; - private GUITickBox isPublicBox; + private GUITextBox serverNameBox, portBox, passwordBox, maxPlayersBox; + private GUITickBox isPublicBox, useUpnpBox; private Game1 game; @@ -113,18 +113,41 @@ namespace Subsurface new GUITextBlock(new Rectangle(0, -25, 0, 30), "Host Server", GUI.style, Alignment.CenterX, Alignment.CenterX, menuTabs[(int)Tabs.HostServer], false, GUI.LargeFont); - new GUITextBlock(new Rectangle(0, 30, 0, 30), "Server Name:", GUI.style, Alignment.CenterX, Alignment.CenterX, menuTabs[(int)Tabs.HostServer]); - serverNameBox = new GUITextBox(new Rectangle(0, 60, 200, 30), null, null, Alignment.CenterX, Alignment.CenterX, GUI.style, menuTabs[(int)Tabs.HostServer]); + new GUITextBlock(new Rectangle(0, 50, 0, 30), "Server Name:", GUI.style, Alignment.TopLeft, Alignment.Left, menuTabs[(int)Tabs.HostServer]); + serverNameBox = new GUITextBox(new Rectangle(160, 50, 200, 30), null, null, Alignment.TopLeft, Alignment.Left, GUI.style, menuTabs[(int)Tabs.HostServer]); - new GUITextBlock(new Rectangle(0, 100, 0, 30), "Server port:", GUI.style, Alignment.CenterX, Alignment.CenterX, menuTabs[(int)Tabs.HostServer]); - portBox = new GUITextBox(new Rectangle(0, 130, 200, 30), null, null, Alignment.CenterX, Alignment.CenterX, GUI.style, menuTabs[(int)Tabs.HostServer]); + new GUITextBlock(new Rectangle(0, 100, 0, 30), "Server port:", GUI.style, Alignment.TopLeft, Alignment.Left, menuTabs[(int)Tabs.HostServer]); + portBox = new GUITextBox(new Rectangle(160, 100, 200, 30), null, null, Alignment.TopLeft, Alignment.Left, GUI.style, menuTabs[(int)Tabs.HostServer]); portBox.Text = NetworkMember.DefaultPort.ToString(); portBox.ToolTip = "Server port"; - isPublicBox = new GUITickBox(new Rectangle(portBox.Rect.X - menuTabs[(int)Tabs.HostServer].Rect.X, 200, 20, 20), "Public server", Alignment.TopLeft, menuTabs[(int)Tabs.HostServer]); + new GUITextBlock(new Rectangle(0, 150, 100, 30), "Max players:", GUI.style, Alignment.TopLeft, Alignment.Left, menuTabs[(int)Tabs.HostServer]); + maxPlayersBox = new GUITextBox(new Rectangle(195, 150, 30, 30), null, null, Alignment.TopLeft, Alignment.Center, GUI.style, menuTabs[(int)Tabs.HostServer]); + maxPlayersBox.Text = "8"; + maxPlayersBox.Enabled = false; + + var plusPlayersBox = new GUIButton(new Rectangle(230, 150, 30, 30), "+", GUI.style, menuTabs[(int)Tabs.HostServer]); + plusPlayersBox.UserData = 1; + plusPlayersBox.OnClicked = ChangeMaxPlayers; + + var minusPlayersBox = new GUIButton(new Rectangle(160, 150, 30, 30), "-", GUI.style, menuTabs[(int)Tabs.HostServer]); + minusPlayersBox.UserData = -1; + minusPlayersBox.OnClicked = ChangeMaxPlayers; + + new GUITextBlock(new Rectangle(0, 200, 0, 30), "Password (optional):", GUI.style, Alignment.TopLeft, Alignment.Left, menuTabs[(int)Tabs.HostServer]); + passwordBox = new GUITextBox(new Rectangle(160, 200, 200, 30), null, null, Alignment.TopLeft, Alignment.Left, GUI.style, menuTabs[(int)Tabs.HostServer]); + + + isPublicBox = new GUITickBox(new Rectangle(10, 250, 20, 20), "Public server", Alignment.TopLeft, menuTabs[(int)Tabs.HostServer]); + + useUpnpBox = new GUITickBox(new Rectangle(10, 300, 20, 20), "Attempt UPnP port forwarding", Alignment.TopLeft, menuTabs[(int)Tabs.HostServer]); + new GUITextBlock(new Rectangle(0, 330, 0, 30), + "UPnP can be used for forwarding ports on your router to allow players join the server." + + " However, UPnP isn't supported by all routers, so you may need to setup port forwards manually" + +" if players are unable to join the server (see the readme for instructions).", + GUI.style, Alignment.TopLeft, Alignment.TopLeft, menuTabs[(int)Tabs.HostServer], true, GUI.SmallFont); + - new GUITextBlock(new Rectangle(0, 240, 0, 30), "Password (optional):", GUI.style, Alignment.CenterX, Alignment.CenterX, menuTabs[(int)Tabs.HostServer]); - passwordBox = new GUITextBox(new Rectangle(0, 270, 200, 30), null, null, Alignment.CenterX, Alignment.CenterX, GUI.style, menuTabs[(int)Tabs.HostServer]); GUIButton hostButton = new GUIButton(new Rectangle(0, 0, 200, 30), "Start", Alignment.BottomCenter, GUI.style, menuTabs[(int)Tabs.HostServer]); hostButton.OnClicked = HostServerClicked; @@ -163,6 +186,18 @@ namespace Subsurface return true; } + private bool ChangeMaxPlayers(GUIButton button, object obj) + { + int currMaxPlayers = 10; + + int.TryParse(maxPlayersBox.Text, out currMaxPlayers); + currMaxPlayers = MathHelper.Clamp(currMaxPlayers+(int)button.UserData, 1, 10); + + maxPlayersBox.Text = currMaxPlayers.ToString(); + + return true; + } + private bool HostServerClicked(GUIButton button, object obj) { string name = serverNameBox.Text; @@ -181,7 +216,7 @@ namespace Subsurface return false; } - Game1.NetworkMember = new GameServer(name, port, isPublicBox.Selected, passwordBox.Text); + Game1.NetworkMember = new GameServer(name, port, isPublicBox.Selected, passwordBox.Text, useUpnpBox.Selected, int.Parse(maxPlayersBox.Text)); Game1.NetLobbyScreen.IsServer = true; Game1.NetLobbyScreen.Select(); diff --git a/Subsurface/Source/Screens/NetLobbyScreen.cs b/Subsurface/Source/Screens/NetLobbyScreen.cs index 9cbcae64f..dbcf83bde 100644 --- a/Subsurface/Source/Screens/NetLobbyScreen.cs +++ b/Subsurface/Source/Screens/NetLobbyScreen.cs @@ -309,9 +309,11 @@ namespace Subsurface jobList = new GUIListBox(new Rectangle(0, 180, 180, 0), GUI.style, playerFrame); jobList.Enabled = false; + + int i = 1; foreach (JobPrefab job in JobPrefab.List) { - GUITextBlock jobText = new GUITextBlock(new Rectangle(0,0,0,20), job.Name, GUI.style, Alignment.Left, Alignment.Right, jobList); + GUITextBlock jobText = new GUITextBlock(new Rectangle(0,0,0,20), i+". "+job.Name, GUI.style, Alignment.Left, Alignment.Right, jobList); jobText.UserData = job; GUIButton upButton = new GUIButton(new Rectangle(0, 0, 15, 15), "u", GUI.style, jobText); @@ -560,13 +562,17 @@ namespace Subsurface private void UpdateJobPreferences(GUIListBox listBox) { listBox.Deselect(); - for (int i = 1; i < listBox.children.Count; i++) + for (int i = 0; i < listBox.children.Count; i++) { float a = (float)(i - 1) / 3.0f; a = Math.Min(a, 3); Color color = new Color(1.0f - a, (1.0f - a) * 0.6f, 0.0f, 0.3f); listBox.children[i].Color = color; + listBox.children[i].HoverColor = color; + listBox.children[i].SelectedColor = color; + + (listBox.children[i] as GUITextBlock).Text = (i+1) + ". " + (listBox.children[i].UserData as JobPrefab).Name; } Game1.Client.SendCharacterData(); diff --git a/Subsurface/changelog.txt b/Subsurface/changelog.txt index e4c559a3a..28d44748c 100644 --- a/Subsurface/changelog.txt +++ b/Subsurface/changelog.txt @@ -1,4 +1,18 @@ +--------------------------------------------------------------------------------------------------------- +v0.1.3.2 +--------------------------------------------------------------------------------------------------------- + +Multiplayer: + - some major opimization to networked messages (less lag) + - option to disable UPnP port forwarding (which may have prevented some from hosting a server) + - a new round can't be started if a submarine hasn't been selected (which used to crash the game) + - maximum number of players can be changed + - fixed a bug in the net lobby screen that disabled the start button when the chat box was scrolled + to a specific position + - a window that displays some network statistics when hosting a server (can be activated by entering + "debugview" to the debug console) + --------------------------------------------------------------------------------------------------------- v0.1.3.1 --------------------------------------------------------------------------------------------------------- diff --git a/Subsurface/readme.txt b/Subsurface/readme.txt index 58e9c8186..d3d4f6d62 100644 --- a/Subsurface/readme.txt +++ b/Subsurface/readme.txt @@ -17,6 +17,31 @@ http://subsurface.gamepedia.com ------------------------------------------------------------------------ +Port forwarding: +You may try to forward ports on your router using UPnP (Universal Plug and +Play) port forwarding by selecting "Attempt UPnP port forwarding" in the +"Host Server" menu. + +However, UPnP isn't supported by all routers, so you may need to setup port +forwards manually. The exact steps for forwarding a port depend on your +router's model, but you may you may be able to find a port forwarding +guide for your particular router/application on portforward.com or by +practicing your google-fu skills. + +These are the values that you should use when forwarding a port to your +Subsurface server: + +Service/Application: subsurface +External Port: The port you have selected for your server (14242 by default) +Internal Port: The port you have selected for your server (14242 by default) +Protocol: UDP + + +------------------------------------------------------------------------ +------------------------------------------------------------------------ +Credits: +------------------------------------------------------------------------ + Programming, graphics, sounds, game design - Joonas Rikkonen ("Regalis") Graphics - James Bear ("Moonsaber99") diff --git a/Subsurface_Solution.v12.suo b/Subsurface_Solution.v12.suo index 081fd5b1c..12c9edcda 100644 Binary files a/Subsurface_Solution.v12.suo and b/Subsurface_Solution.v12.suo differ