This commit is contained in:
Regalis
2015-07-31 21:05:55 +03:00
parent 23d847a4ac
commit 85b0cda4ca
181 changed files with 4455 additions and 4073 deletions

View File

@@ -0,0 +1,563 @@
using System;
using System.Diagnostics;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using System.Collections.Generic;
namespace Subsurface.Networking
{
class GameClient : NetworkMember
{
private NetClient client;
private Character myCharacter;
private CharacterInfo characterInfo;
private GUIMessageBox reconnectBox;
private bool connected;
private int myID;
private List<Client> otherClients;
private string serverIP;
public Character Character
{
get { return myCharacter; }
set { myCharacter = value; }
}
public CharacterInfo CharacterInfo
{
get { return characterInfo; }
}
public int ID
{
get { return myID; }
}
public GameClient(string newName)
{
name = newName;
characterInfo = new CharacterInfo(Character.HumanConfigFile, name);
otherClients = new List<Client>();
}
public void ConnectToServer(string hostIP)
{
string[] address = hostIP.Split(':');
if (address.Length==1)
{
serverIP = hostIP;
Port = DefaultPort;
}
else
{
serverIP = address[0];
if (!int.TryParse(address[1], out Port))
{
DebugConsole.ThrowError("Invalid port: address[1]!");
Port = DefaultPort;
}
}
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.25f;
// Create new client, with previously created configs
client = new NetClient(Config);
NetOutgoingMessage outmsg = client.CreateMessage();
client.Start();
outmsg.Write((byte)PacketTypes.Login);
outmsg.Write(Game1.Version.ToString());
outmsg.Write(Game1.SelectedPackage.Name);
outmsg.Write(Game1.SelectedPackage.MD5hash.Hash);
outmsg.Write(name);
// Connect client, to ip previously requested from user
try
{
client.Connect(serverIP, Port, outmsg);
}
catch (ArgumentNullException e)
{
DebugConsole.ThrowError("Couldn't connect to "+hostIP+". Error message: "+e.Message);
return;
}
// Create timespan of 30ms
updateInterval = new TimeSpan(0, 0, 0, 0, 200);
// Set timer to tick every 50ms
//update = new System.Timers.Timer(50);
// When time has elapsed ( 50ms in this case ), call "update_Elapsed" funtion
//update.Elapsed += new System.Timers.ElapsedEventHandler(Update);
// Funtion that waits for connection approval info from server
reconnectBox = new GUIMessageBox("CONNECTING", "Connecting to " + serverIP, new string[0]);
CoroutineManager.StartCoroutine(WaitForStartingInfo());
// Start the timer
//update.Start();
}
private bool RetryConnection(GUIButton button, object obj)
{
ConnectToServer(serverIP);
return true;
}
private bool SelectMainMenu(GUIButton button, object obj)
{
Disconnect();
Game1.NetworkMember = null;
Game1.MainMenuScreen.Select();
return true;
}
// Before main looping starts, we loop here and wait for approval message
private IEnumerable<Status> WaitForStartingInfo()
{
// When this is set to true, we are approved and ready to go
bool CanStart = false;
DateTime timeOut = DateTime.Now + new TimeSpan(0,0,5);
// Loop untill we are approved
while (!CanStart)
{
yield return Status.Running;
if (DateTime.Now > timeOut) break;
NetIncomingMessage inc;
// If new messages arrived
if ((inc = client.ReadMessage()) == null) continue;
// Switch based on the message types
switch (inc.MessageType)
{
// All manually sent messages are type of "Data"
case NetIncomingMessageType.Data:
byte packetType = inc.ReadByte();
if (packetType == (byte)PacketTypes.LoggedIn)
{
myID = inc.ReadInt32();
if (inc.ReadBoolean())
{
new GUIMessageBox("Please wait", "A round is already running. You will have to wait for a new round to start.");
}
Game1.NetLobbyScreen.ClearPlayers();
//add the names of other connected clients to the lobby screen
int existingClients = inc.ReadInt32();
for (int i = 1; i <= existingClients; i++)
{
Client otherClient = new Client(inc.ReadString(), inc.ReadInt32());
Game1.NetLobbyScreen.AddPlayer(otherClient);
}
//add the name of own client to the lobby screen
Game1.NetLobbyScreen.AddPlayer(new Client(name, myID));
CanStart = true;
}
else if (packetType == (byte)PacketTypes.KickedOut)
{
string msg = inc.ReadString();
DebugConsole.ThrowError(msg);
Game1.MainMenuScreen.Select();
}
break;
case NetIncomingMessageType.StatusChanged:
NetConnectionStatus connectionStatus = (NetConnectionStatus)inc.ReadByte();
Debug.WriteLine(connectionStatus);
if (connectionStatus != NetConnectionStatus.Connected)
{
string denyMessage = inc.ReadString();
DebugConsole.ThrowError(denyMessage);
}
break;
default:
Console.WriteLine(inc.ReadString() + " Strange message");
break;
}
}
if (reconnectBox != null)
{
reconnectBox.Close(null, null);
reconnectBox = null;
}
if (client.ConnectionStatus != NetConnectionStatus.Connected)
{
var reconnect = new GUIMessageBox("CONNECTION FAILED", "Failed to connect to server.", new string[] { "Retry", "Cancel" });
reconnect.Buttons[0].OnClicked += RetryConnection;
reconnect.Buttons[0].OnClicked += reconnect.Close;
reconnect.Buttons[1].OnClicked += SelectMainMenu;
reconnect.Buttons[1].OnClicked += reconnect.Close;
}
else
{
if (Screen.Selected == Game1.MainMenuScreen) Game1.NetLobbyScreen.Select();
connected = true;
}
yield return Status.Success;
}
public override void Update()
{
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//if (PlayerInput.KeyDown(Microsoft.Xna.Framework.Input.Keys.K))
//{
// SendRandomData();
//}
//if (PlayerInput.KeyDown(Microsoft.Xna.Framework.Input.Keys.L))
//{
// ConnectToServer(serverIP);
//}
if (gameStarted) inGameHUD.Update((float)Physics.step);
if (!connected || updateTimer > DateTime.Now) return;
if (client.ConnectionStatus == NetConnectionStatus.Disconnected && 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)
{
reconnectBox.Close(null,null);
reconnectBox = null;
}
if (myCharacter != null)
{
if (myCharacter.IsDead)
{
Character.Controlled = null;
Game1.GameScreen.Cam.TargetPos = Vector2.Zero;
Game1.GameScreen.Cam.Zoom = 1.0f;
}
else
{
if (gameStarted) new NetworkEvent(myCharacter.ID, true);
}
}
foreach (NetworkEvent networkEvent in NetworkEvent.events)
{
NetOutgoingMessage message = client.CreateMessage();
message.Write((byte)PacketTypes.NetworkEvent);
if (networkEvent.FillData(message))
{
client.SendMessage(message,
(networkEvent.IsImportant) ? NetDeliveryMethod.ReliableUnordered : NetDeliveryMethod.Unreliable);
}
}
NetworkEvent.events.Clear();
try
{
CheckServerMessages();
}
catch { }
// Update current time
updateTimer = DateTime.Now + updateInterval;
}
/// <summary>
/// Check for new incoming messages from server
/// </summary>
private void CheckServerMessages()
{
// Create new incoming message holder
NetIncomingMessage inc;
while ((inc = client.ReadMessage()) != null)
{
if (inc.MessageType != NetIncomingMessageType.Data) continue;
switch (inc.ReadByte())
{
case (byte)PacketTypes.StartGame:
if (gameStarted) continue;
if (this.Character != null) Character.Remove();
int seed = inc.ReadInt32();
Rand.SetSyncedSeed(seed);
string levelSeed = inc.ReadString();
string mapName = inc.ReadString();
string mapHash = inc.ReadString();
Game1.NetLobbyScreen.TrySelectMap(mapName, mapHash);
double durationMinutes = inc.ReadDouble();
TimeSpan duration = new TimeSpan(0,(int)durationMinutes,0);
//int gameModeIndex = inc.ReadInt32();
Game1.GameSession = new GameSession(Submarine.Loaded, "", Game1.NetLobbyScreen.SelectedMode);
Game1.GameSession.StartShift(duration, levelSeed);
//myCharacter = ReadCharacterData(inc);
//Character.Controlled = myCharacter;
int count = inc.ReadInt32();
for (int n = 0; n < count; n++)
{
int id = inc.ReadInt32();
Character newCharacter = ReadCharacterData(inc);
if (id == myID)
{
myCharacter = newCharacter;
Character.Controlled = myCharacter;
}
}
gameStarted = true;
Game1.GameScreen.Select();
AddChatMessage("Press TAB to chat", ChatMessageType.Server);
break;
case (byte)PacketTypes.EndGame:
string endMessage = inc.ReadString();
EndGame(endMessage);
break;
case (byte)PacketTypes.PlayerJoined:
Client otherClient = new Client(inc.ReadString(), inc.ReadInt32());
Game1.NetLobbyScreen.AddPlayer(otherClient);
AddChatMessage(otherClient.name + " has joined the server", ChatMessageType.Server);
break;
case (byte)PacketTypes.PlayerLeft:
int leavingID = inc.ReadInt32();
AddChatMessage(inc.ReadString(), ChatMessageType.Server);
Game1.NetLobbyScreen.RemovePlayer(otherClients.Find(c => c.ID==leavingID));
break;
case (byte)PacketTypes.KickedOut:
string msg = inc.ReadString();
new GUIMessageBox("KICKED", msg);
Game1.MainMenuScreen.Select();
break;
case (byte)PacketTypes.Chatmessage:
ChatMessageType messageType = (ChatMessageType)inc.ReadByte();
AddChatMessage(inc.ReadString(), messageType);
break;
case (byte)PacketTypes.NetworkEvent:
//read the data from the message and update client state accordingly
if (!gameStarted) break;
NetworkEvent.ReadData(inc);
break;
case (byte)PacketTypes.UpdateNetLobby:
if (gameStarted) continue;
Game1.NetLobbyScreen.ReadData(inc);
break;
case (byte)PacketTypes.Traitor:
string targetName = inc.ReadString();
new GUIMessageBox("You are the Traitor!", "Your secret task is to assassinate " + targetName + "!");
break;
}
}
}
public void EndGame(string endMessage)
{
Submarine.Unload();
Game1.NetLobbyScreen.Select();
if (Game1.GameSession!=null) Game1.GameSession.EndShift("");
new GUIMessageBox("The round has ended", endMessage);
myCharacter = null;
gameStarted = false;
}
public override void Disconnect()
{
NetOutgoingMessage msg = client.CreateMessage();
msg.Write((byte)PacketTypes.PlayerLeft);
client.SendMessage(msg, NetDeliveryMethod.ReliableUnordered);
client.Shutdown("");
}
public void SendCharacterData()
{
if (characterInfo == null) return;
NetOutgoingMessage msg = client.CreateMessage();
msg.Write((byte)PacketTypes.CharacterInfo);
msg.Write(characterInfo.Name);
msg.Write(characterInfo.Gender == Gender.Male);
msg.Write(characterInfo.HeadSpriteId);
var jobPreferences = Game1.NetLobbyScreen.JobPreferences;
int count = Math.Min(jobPreferences.Count, 3);
msg.Write(count);
for (int i = 0; i < count; i++ )
{
msg.Write(jobPreferences[i].Name);
}
client.SendMessage(msg, NetDeliveryMethod.ReliableUnordered);
}
private Character ReadCharacterData(NetIncomingMessage inc)
{
string newName = inc.ReadString();
int ID = inc.ReadInt32();
bool isFemale = inc.ReadBoolean();
int inventoryID = inc.ReadInt32();
int headSpriteID = inc.ReadInt32();
Vector2 position = new Vector2(inc.ReadFloat(), inc.ReadFloat());
string jobName = inc.ReadString();
JobPrefab jobPrefab = JobPrefab.List.Find(jp => jp.Name == jobName);
if (inc.Position > inc.LengthBits)
{
return null;
}
CharacterInfo ch = new CharacterInfo(Character.HumanConfigFile, newName, isFemale ? Gender.Female : Gender.Male, jobPrefab);
ch.HeadSpriteId = headSpriteID;
WayPoint closestWaypoint = null;
float closestDist = 0.0f;
foreach (WayPoint wp in WayPoint.WayPointList)
{
float dist = Vector2.Distance(wp.SimPosition, position);
if (closestWaypoint != null && dist > closestDist) continue;
closestWaypoint = wp;
closestDist = dist;
continue;
}
Character character = (closestWaypoint == null) ?
new Character(ch, position) :
new Character(ch, closestWaypoint);
character.ID = ID;
character.Inventory.ID = inventoryID;
character.GiveJobItems(closestWaypoint);
return character;
}
public override void SendChatMessage(string message, ChatMessageType type = ChatMessageType.Default)
{
//AddChatMessage(message);
type = (gameStarted && myCharacter != null && myCharacter.IsDead) ? ChatMessageType.Dead : ChatMessageType.Default;
NetOutgoingMessage msg = client.CreateMessage();
msg.Write((byte)PacketTypes.Chatmessage);
msg.Write((byte)type);
msg.Write(message);
client.SendMessage(msg, NetDeliveryMethod.ReliableUnordered);
}
/// <summary>
/// sends some random data to the server (can be a networkevent or just something completely random)
/// use for debugging purposes
/// </summary>
public void SendRandomData()
{
NetOutgoingMessage msg = client.CreateMessage();
switch (Rand.Int(5))
{
case 0:
msg.Write((byte)PacketTypes.NetworkEvent);
msg.Write((byte)NetworkEventType.UpdateEntity);
msg.Write(Rand.Int(MapEntity.mapEntityList.Count));
break;
case 1:
msg.Write((byte)PacketTypes.NetworkEvent);
msg.Write((byte)Enum.GetNames(typeof(NetworkEventType)).Length);
msg.Write(Rand.Int(MapEntity.mapEntityList.Count));
break;
case 2:
msg.Write((byte)PacketTypes.NetworkEvent);
msg.Write((byte)NetworkEventType.UpdateComponent);
msg.Write((int)Item.itemList[Rand.Int(Item.itemList.Count)].ID);
msg.Write(Rand.Int(8));
break;
case 3:
msg.Write((byte)Enum.GetNames(typeof(PacketTypes)).Length);
break;
}
int bitCount = Rand.Int(100);
for (int i = 0; i<bitCount; i++)
{
msg.Write((Rand.Int(2)==0) ? true : false);
}
client.SendMessage(msg, (Rand.Int(2)==0) ? NetDeliveryMethod.ReliableOrdered : NetDeliveryMethod.Unreliable);
}
}
}

View File

@@ -0,0 +1,778 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using Lidgren.Network;
using Microsoft.Xna.Framework;
namespace Subsurface.Networking
{
class GameServer : NetworkMember
{
public List<Client> connectedClients = new List<Client>();
private NetServer server;
private NetPeerConfiguration config;
private TimeSpan SparseUpdateInterval = new TimeSpan(0, 0, 0, 1);
private DateTime sparseUpdateTimer;
private Client myClient;
public GameServer(string name, int port)
{
var endRoundButton = new GUIButton(new Rectangle(Game1.GraphicsWidth - 290, 20, 150, 25), "End round", Alignment.TopLeft, GUI.style, inGameHUD);
endRoundButton.OnClicked = EndButtonHit;
this.name = name;
config = new NetPeerConfiguration("subsurface");
config.Port = port;
config.EnableUPnP = true;
config.MaximumConnections = 10;
config.EnableMessageType(NetIncomingMessageType.ConnectionApproval);
try
{
server = new NetServer(config);
server.Start();
// attempt to forward port
server.UPnP.ForwardPort(port, "subsurface");
}
catch (Exception e)
{
DebugConsole.ThrowError("Couldn't start the server", e);
}
updateInterval = new TimeSpan(0, 0, 0, 0, 30);
DebugConsole.NewMessage("Server started", Color.Green);
}
public override void Update()
{
//if (PlayerInput.KeyDown(Microsoft.Xna.Framework.Input.Keys.K))
//{
// SendRandomData();
//}
if (gameStarted) inGameHUD.Update((float)Physics.step);
NetIncomingMessage inc = server.ReadMessage();
if (inc != null)
{
try
{
ReadMessage(inc);
}
catch
{
}
}
// if 30ms has passed
if (updateTimer < DateTime.Now)
{
if (server.ConnectionsCount > 0)
{
if (sparseUpdateTimer < DateTime.Now) SparseUpdate();
SendNetworkEvents();
}
updateTimer = DateTime.Now + updateInterval;
}
}
private void SparseUpdate()
{
foreach (Character c in Character.CharacterList)
{
bool isClient = false;
foreach (Client client in connectedClients)
{
if (client.character != c) continue;
isClient = true;
break;
}
if (!isClient)
{
c.LargeUpdateTimer = 0;
new NetworkEvent(c.ID, false);
}
}
if (gameStarted) new NetworkEvent(Submarine.Loaded.ID, false);
sparseUpdateTimer = DateTime.Now + SparseUpdateInterval;
}
private void ReadMessage(NetIncomingMessage inc)
{
NetOutgoingMessage outmsg;
switch (inc.MessageType)
{
case NetIncomingMessageType.ConnectionApproval:
if (inc.ReadByte() != (byte)PacketTypes.Login) break;
DebugConsole.NewMessage("New player has joined the server", Color.White);
Client existingClient = connectedClients.Find(c=> c.Connection == inc.SenderConnection);
if (existingClient==null)
{
string version = "", packageName="", packageHash="", name = "";
try
{
version = inc.ReadString();
packageName = inc.ReadString();
packageHash = inc.ReadString();
name = inc.ReadString();
}
catch
{
inc.SenderConnection.Deny("Connection error - server failed to read your ConnectionApproval message");
break;
}
if (version != Game1.Version.ToString())
{
inc.SenderConnection.Deny("Subsurface version " + Game1.Version + " required to connect to the server (Your version: " + version + ")");
break;
}
else if (packageName != Game1.SelectedPackage.Name)
{
inc.SenderConnection.Deny("Your content package ("+packageName+") doesn't match the server's version (" + Game1.SelectedPackage.Name + ")");
break;
}
else if (packageHash != Game1.SelectedPackage.MD5hash.Hash)
{
inc.SenderConnection.Deny("Your content package (MD5: " + packageHash + ") doesn't match the server's version (MD5: " + Game1.SelectedPackage.MD5hash.Hash + ")");
break;
}
else if (connectedClients.Find(c => c.name.ToLower() == name.ToLower())!=null)
{
inc.SenderConnection.Deny("The name ''" + name + "'' is already in use. Please choose another name.");
break;
}
int id = 1;
while (connectedClients.Find(c=>c.ID==id)!=null)
{
id++;
}
Client newClient = new Client(name, id);
newClient.Connection = inc.SenderConnection;
newClient.version = version;
connectedClients.Add(newClient);
inc.SenderConnection.Approve();
}
else
{
inc.SenderConnection.Deny();
}
//Character ch = new Character("Content/Characters/Human/human.xml");
break;
case NetIncomingMessageType.StatusChanged:
Debug.WriteLine(inc.SenderConnection + " status changed. " + (NetConnectionStatus)inc.SenderConnection.Status);
if (inc.SenderConnection.Status == NetConnectionStatus.Connected)
{
Client sender = connectedClients.Find(x => x.Connection == inc.SenderConnection);
if (sender == null) break;
if (sender.version != Game1.Version.ToString())
{
DisconnectClient(sender, sender.name+" was unable to connect to the server (nonmatching game version)",
"Subsurface version " + Game1.Version + " required to connect to the server (Your version: " + sender.version + ")");
}
else if (connectedClients.Find(x => x.name == sender.name && x != sender)!=null)
{
DisconnectClient(sender, sender.name + " was unable to connect to the server (name already in use)",
"The name ''"+sender.name+"'' is already in use. Please choose another name.");
}
else
{
AssignJobs();
Game1.NetLobbyScreen.AddPlayer(sender);
// Notify the client that they have logged in
outmsg = server.CreateMessage();
outmsg.Write((byte)PacketTypes.LoggedIn);
outmsg.Write(sender.ID);
outmsg.Write(gameStarted);
//notify the client about other clients already logged in
outmsg.Write((myClient == null) ? connectedClients.Count - 1 : connectedClients.Count);
foreach (Client c in connectedClients)
{
if (c.Connection == inc.SenderConnection) continue;
outmsg.Write(c.name);
outmsg.Write(c.ID);
}
if (myClient != null) outmsg.Write(myClient.name);
server.SendMessage(outmsg, inc.SenderConnection, NetDeliveryMethod.ReliableUnordered, 0);
//notify other clients about the new client
outmsg = server.CreateMessage();
outmsg.Write((byte)PacketTypes.PlayerJoined);
outmsg.Write(sender.name);
outmsg.Write(sender.ID);
//send the message to everyone except the client who just logged in
SendMessage(outmsg, NetDeliveryMethod.ReliableUnordered, inc.SenderConnection);
AddChatMessage(sender.name + " has joined the server", ChatMessageType.Server);
UpdateNetLobby(null);
}
}
else if (inc.SenderConnection.Status == NetConnectionStatus.Disconnected)
{
DisconnectClient(inc.SenderConnection);
}
break;
case NetIncomingMessageType.Data:
switch (inc.ReadByte())
{
case (byte)PacketTypes.NetworkEvent:
if (!gameStarted) break;
if (!NetworkEvent.ReadData(inc)) break;
outmsg = server.CreateMessage();
outmsg.Write(inc);
List<NetConnection> recipients = new List<NetConnection>();
foreach (Client client in connectedClients)
{
if (client.Connection == inc.SenderConnection) continue;
if (!client.inGame) continue;
recipients.Add(client.Connection);
}
if (recipients.Count == 0) break;
server.SendMessage(outmsg, recipients, inc.DeliveryMethod, 0);
break;
case (byte)PacketTypes.Chatmessage:
ChatMessageType messageType = (ChatMessageType)inc.ReadByte();
string message = inc.ReadString();
SendChatMessage(message, messageType);
break;
case (byte)PacketTypes.PlayerLeft:
DisconnectClient(inc.SenderConnection);
break;
case (byte)PacketTypes.CharacterInfo:
ReadCharacterData(inc);
break;
}
break;
case NetIncomingMessageType.WarningMessage:
Debug.WriteLine(inc.ReadString());
break;
}
}
private void SendMessage(NetOutgoingMessage msg, NetDeliveryMethod deliveryMethod, NetConnection excludedConnection)
{
List<NetConnection> recipients = new List<NetConnection>();
foreach (Client client in connectedClients)
{
if (client.Connection != excludedConnection) recipients.Add(client.Connection);
}
if (recipients.Count == 0) return;
server.SendMessage(msg, recipients, deliveryMethod, 0);
}
private void SendNetworkEvents()
{
if (NetworkEvent.events.Count == 0) return;
//System.Diagnostics.Debug.WriteLine("*************************");
foreach (NetworkEvent networkEvent in NetworkEvent.events)
{
//System.Diagnostics.Debug.WriteLine("networkevent "+networkEvent.ID);
NetOutgoingMessage message = server.CreateMessage();
message.Write((byte)PacketTypes.NetworkEvent);
//if (!networkEvent.IsClient) continue;
networkEvent.FillData(message);
if (server.ConnectionsCount>0)
{
server.SendMessage(message, server.Connections,
(networkEvent.IsImportant) ? NetDeliveryMethod.Unreliable : NetDeliveryMethod.ReliableUnordered, 0);
}
}
NetworkEvent.events.Clear();
}
public bool StartGame(GUIButton button, object obj)
{
int seed = DateTime.Now.Millisecond;
Rand.SetSyncedSeed(seed);
Submarine selectedMap = Game1.NetLobbyScreen.SelectedMap as Submarine;
//selectedMap.Load();
Game1.GameSession = new GameSession(selectedMap, "", Game1.NetLobbyScreen.SelectedMode);
Game1.GameSession.StartShift(Game1.NetLobbyScreen.GameDuration, Game1.NetLobbyScreen.LevelSeed);
//EventManager.SelectEvent(Game1.netLobbyScreen.SelectedEvent);
List<CharacterInfo> characterInfos = new List<CharacterInfo>();
foreach (Client client in connectedClients)
{
client.inGame = true;
WayPoint spawnPoint = WayPoint.GetRandom(SpawnType.Human);
if (client.characterInfo==null)
{
client.characterInfo = new CharacterInfo(Character.HumanConfigFile, client.name);
}
characterInfos.Add(client.characterInfo);
//client.character = new Character(client.characterInfo, (spawnPoint == null) ? Vector2.Zero : spawnPoint.SimPosition, true);
}
WayPoint[] assignedWayPoints = WayPoint.SelectCrewSpawnPoints(characterInfos);
for (int i = 0; i < connectedClients.Count; i++ )
{
connectedClients[i].character = new Character(
connectedClients[i].characterInfo, assignedWayPoints[i], true);
connectedClients[i].character.GiveJobItems(assignedWayPoints[i]);
}
//todo: fix
if (myClient != null)
{
WayPoint spawnPoint = WayPoint.GetRandom(SpawnType.Human);
CharacterInfo ch = new CharacterInfo(Character.HumanConfigFile, myClient.name);
myClient.character = new Character(ch, (spawnPoint == null) ? Vector2.Zero : spawnPoint.SimPosition);
}
//foreach (Client client in connectedClients)
//{
NetOutgoingMessage msg = server.CreateMessage();
msg.Write((byte)PacketTypes.StartGame);
msg.Write(seed);
msg.Write(Game1.NetLobbyScreen.LevelSeed);
msg.Write(Game1.NetLobbyScreen.SelectedMap.Name);
msg.Write(Game1.NetLobbyScreen.SelectedMap.Hash.Hash);
msg.Write(Game1.NetLobbyScreen.GameDuration.TotalMinutes);
//WriteCharacterData(msg, client.name, client.character);
msg.Write((myClient == null) ? connectedClients.Count : connectedClients.Count+1);
foreach (Client client in connectedClients)
{
//if (otherClient == client) continue;
msg.Write(client.ID);
WriteCharacterData(msg, client.name, client.character);
}
if (myClient!=null)
{
WriteCharacterData(msg, myClient.name, myClient.character);
}
SendMessage(msg, NetDeliveryMethod.ReliableUnordered, null);
//}
gameStarted = true;
Game1.GameScreen.Cam.TargetPos = Vector2.Zero;
Game1.GameScreen.Select();
return true;
}
private bool EndButtonHit(GUIButton button, object obj)
{
Game1.GameSession.gameMode.End("Server admin has ended the round");
return true;
}
public void EndGame(string endMessage)
{
Submarine.Unload();
gameStarted = false;
if (connectedClients.Count>0)
{
NetOutgoingMessage msg = server.CreateMessage();
msg.Write((byte)PacketTypes.EndGame);
msg.Write(endMessage);
if (server.ConnectionsCount > 0)
{
server.SendMessage(msg, server.Connections, NetDeliveryMethod.ReliableOrdered, 0);
}
foreach (Client client in connectedClients)
{
client.character = null;
client.inGame = false;
}
}
Game1.NetLobbyScreen.Select();
DebugConsole.ThrowError(endMessage);
}
private void DisconnectClient(NetConnection senderConnection)
{
Client client = connectedClients.Find(x => x.Connection == senderConnection);
if (client != null) DisconnectClient(client);
}
private void DisconnectClient(Client client, string msg = "", string targetmsg = "")
{
if (client == null) return;
if (gameStarted && client.character != null) client.character.Kill(true);
if (msg == "") msg = client.name + " has left the server";
if (targetmsg == "") targetmsg = "You have left the server";
NetOutgoingMessage outmsg = server.CreateMessage();
outmsg.Write((byte)PacketTypes.KickedOut);
outmsg.Write(targetmsg);
server.SendMessage(outmsg, client.Connection, NetDeliveryMethod.ReliableUnordered, 0);
connectedClients.Remove(client);
outmsg = server.CreateMessage();
outmsg.Write((byte)PacketTypes.PlayerLeft);
outmsg.Write(client.ID);
outmsg.Write(msg);
Game1.NetLobbyScreen.RemovePlayer(client);
if (server.Connections.Count > 0)
{
server.SendMessage(outmsg, server.Connections, NetDeliveryMethod.ReliableUnordered, 0);
}
AddChatMessage(msg, ChatMessageType.Server);
}
public void KickPlayer(string playerName)
{
playerName = playerName.ToLower();
foreach (Client c in connectedClients)
{
if (c.name.ToLower() == playerName) KickClient(c);
break;
}
}
private void KickClient(Client client)
{
if (client == null) return;
DisconnectClient(client, client.name + " has been kicked from the server", "You have been kicked from the server");
}
public void NewTraitor(Client traitor, Client target)
{
NetOutgoingMessage msg = server.CreateMessage();
msg.Write((byte)PacketTypes.Traitor);
msg.Write(target.name);
if (server.Connections.Count > 0)
{
server.SendMessage(msg, traitor.Connection, NetDeliveryMethod.ReliableUnordered, 0);
}
}
public bool UpdateNetLobby(object obj)
{
NetOutgoingMessage msg = server.CreateMessage();
msg.Write((byte)PacketTypes.UpdateNetLobby);
Game1.NetLobbyScreen.WriteData(msg);
if (server.Connections.Count > 0)
{
server.SendMessage(msg, server.Connections, NetDeliveryMethod.ReliableUnordered, 0);
}
return true;
}
public override void SendChatMessage(string message, ChatMessageType type = ChatMessageType.Server)
{
AddChatMessage(message, type);
if (server.Connections.Count == 0) return;
NetOutgoingMessage msg = server.CreateMessage();
msg.Write((byte)PacketTypes.Chatmessage);
msg.Write((byte)type);
msg.Write(message);
if (type==ChatMessageType.Dead)
{
List<NetConnection> recipients = new List<NetConnection>();
foreach (Client c in connectedClients)
{
if (c.character != null && c.character.IsDead) recipients.Add(c.Connection);
}
if (recipients.Count>0)
{
server.SendMessage(msg, recipients, NetDeliveryMethod.ReliableUnordered, 0);
}
}
else
{
server.SendMessage(msg, server.Connections, NetDeliveryMethod.ReliableUnordered, 0);
}
}
private void ReadCharacterData(NetIncomingMessage message)
{
string name = "";
Gender gender = Gender.Male;
int headSpriteId = 0;
try
{
name = message.ReadString();
gender = message.ReadBoolean() ? Gender.Male : Gender.Female;
headSpriteId = message.ReadInt32();
}
catch
{
name = "";
gender = Gender.Male;
headSpriteId = 0;
}
List<JobPrefab> jobPreferences = new List<JobPrefab>();
int count = message.ReadInt32();
for (int i = 0; i < count; i++)
{
string jobName = message.ReadString();
JobPrefab jobPrefab = JobPrefab.List.Find(jp => jp.Name == jobName);
if (jobPrefab != null) jobPreferences.Add(jobPrefab);
}
foreach (Client c in connectedClients)
{
if (c.Connection != message.SenderConnection) continue;
c.characterInfo = new CharacterInfo(Character.HumanConfigFile, name, gender);
c.characterInfo.HeadSpriteId = headSpriteId;
c.jobPreferences = jobPreferences;
break;
}
}
private void WriteCharacterData(NetOutgoingMessage message, string name, Character character)
{
message.Write(name);
message.Write(character.ID);
message.Write(character.Info.Gender == Gender.Female);
message.Write(character.Inventory.ID);
message.Write(character.Info.HeadSpriteId);
message.Write(character.SimPosition.X);
message.Write(character.SimPosition.Y);
message.Write(character.Info.Job.Name);
}
private void AssignJobs()
{
List<Client> unassigned = new List<Client>(connectedClients);
int[] assignedClientCount = new int[JobPrefab.List.Count];
//if any of the players has chosen a job that is Always Allowed, give them that job
for (int i = unassigned.Count - 1; i >= 0; i--)
{
if (!unassigned[i].jobPreferences[0].AllowAlways) continue;
unassigned[i].assignedJob = unassigned[i].jobPreferences[0];
unassigned.RemoveAt(i);
}
//go throught the jobs whose MinNumber>0 (i.e. at least one crew member has to have the job)
bool unassignedJobsFound = true;
while (unassignedJobsFound && unassigned.Count > 0)
{
unassignedJobsFound = false;
for (int i = 0; i < JobPrefab.List.Count; i++)
{
if (unassigned.Count == 0) break;
if (JobPrefab.List[i].MinNumber < 1 || assignedClientCount[i] >= JobPrefab.List[i].MinNumber) continue;
//find the client that wants the job the most, or force it to random client if none of them want it
Client assignedClient = FindClientWithJobPreference(unassigned, JobPrefab.List[i], true);
assignedClient.assignedJob = JobPrefab.List[i];
assignedClientCount[i]++;
unassigned.Remove(assignedClient);
//the job still needs more crew members, set unassignedJobsFound to true to keep the while loop running
if (assignedClientCount[i] < JobPrefab.List[i].MinNumber) unassignedJobsFound = true;
}
}
for (int preferenceIndex = 0; preferenceIndex < 3; preferenceIndex++)
{
for (int i = unassigned.Count - 1; i >= 0; i--)
{
int jobIndex = JobPrefab.List.FindIndex(jp => jp == unassigned[i].jobPreferences[preferenceIndex]);
//if there's enough crew members assigned to the job already, continue
if (assignedClientCount[jobIndex] >= JobPrefab.List[jobIndex].MaxNumber) continue;
unassigned[i].assignedJob = JobPrefab.List[jobIndex];
assignedClientCount[jobIndex]++;
unassigned.RemoveAt(i);
}
}
UpdateNetLobby(null);
}
private Client FindClientWithJobPreference(List<Client> clients, JobPrefab job, bool forceAssign = false)
{
int bestPreference = 0;
Client preferredClient = null;
foreach (Client c in clients)
{
int index = c.jobPreferences.FindIndex(jp => jp == job);
if (preferredClient == null || index < bestPreference)
{
bestPreference = index;
preferredClient = c;
}
}
//none of the clients wants the job
if (forceAssign && preferredClient == null)
{
preferredClient = clients[Rand.Int(clients.Count)];
}
return preferredClient;
}
/// <summary>
/// sends some random data to the clients
/// use for debugging purposes
/// </summary>
public void SendRandomData()
{
NetOutgoingMessage msg = server.CreateMessage();
switch (Rand.Int(5))
{
case 0:
msg.Write((byte)PacketTypes.NetworkEvent);
msg.Write((byte)Enum.GetNames(typeof(NetworkEventType)).Length);
msg.Write(Rand.Int(MapEntity.mapEntityList.Count));
break;
case 1:
msg.Write((byte)PacketTypes.NetworkEvent);
msg.Write((byte)NetworkEventType.UpdateComponent);
msg.Write((int)Item.itemList[Rand.Int(Item.itemList.Count)].ID);
msg.Write(Rand.Int(8));
break;
case 2:
msg.Write((byte)Enum.GetNames(typeof(PacketTypes)).Length);
break;
case 3:
msg.Write((byte)PacketTypes.UpdateNetLobby);
break;
}
int bitCount = Rand.Int(100);
for (int i = 0; i < bitCount; i++)
{
msg.Write((Rand.Int(2) == 0) ? true : false);
}
SendMessage(msg, (Rand.Int(2) == 0) ? NetDeliveryMethod.ReliableOrdered : NetDeliveryMethod.Unreliable, null);
}
public override void Disconnect()
{
server.Shutdown("");
}
}
class Client
{
public string name;
public int ID;
public Character character;
public CharacterInfo characterInfo;
public NetConnection Connection { get; set; }
public string version;
public bool inGame;
public List<JobPrefab> jobPreferences;
public JobPrefab assignedJob;
public Client(string name, int ID)
{
this.name = name;
this.ID = ID;
jobPreferences = new List<JobPrefab>(JobPrefab.List.GetRange(0,3));
}
}
}

View File

@@ -0,0 +1,123 @@
using System.Collections.Generic;
using Lidgren.Network;
namespace Subsurface.Networking
{
enum NetworkEventType
{
UpdateEntity = 0,
KillCharacter = 1,
UpdateComponent = 2,
DropItem = 3,
InventoryUpdate = 4,
PickItem = 5,
UpdateProperty = 6
}
class NetworkEvent
{
public static List<NetworkEvent> events = new List<NetworkEvent>();
private static bool[] isImportant = { false, true, true, true, true, true };
private int id;
private NetworkEventType eventType;
private bool isClientEvent;
private object data;
//private NetOutgoingMessage message;
public int ID
{
get { return id; }
}
public bool IsClient
{
get { return isClientEvent; }
}
public bool IsImportant
{
get { return isImportant[(int)eventType]; }
}
public NetworkEvent(int id, bool isClient)
: this(NetworkEventType.UpdateEntity, id, isClient)
{
}
public NetworkEvent(NetworkEventType type, int id, bool isClient, object data = null)
{
if (isClient)
{
if (Game1.Server != null) return;
}
else
{
if (Game1.Server == null) return;
}
eventType = type;
foreach (NetworkEvent e in events)
{
if (!isImportant[(int)type] && e.id == id && e.eventType == type) return;
}
this.id = id;
isClientEvent = isClient;
this.data = data;
events.Add(this);
}
public bool FillData(NetOutgoingMessage message)
{
message.Write((byte)eventType);
Entity e = Entity.FindEntityByID(id);
if (e == null) return false;
message.Write(id);
e.FillNetworkData(eventType, message, data);
return true;
}
public static bool ReadData(NetIncomingMessage message)
{
NetworkEventType eventType;
int id;
try
{
eventType = (NetworkEventType)message.ReadByte();
id = message.ReadInt32();
}
catch
{
DebugConsole.ThrowError("Received invalid network message");
return false;
}
Entity e = Entity.FindEntityByID(id);
if (e == null)
{
//DebugConsole.ThrowError("Couldn't find an entity matching the ID ''" + id + "''");
return false;
}
//System.Diagnostics.Debug.WriteLine("new message: " + eventType +" - "+e);
e.ReadNetworkData(eventType, message);
return true;
}
}
}

View File

@@ -0,0 +1,142 @@
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Subsurface.Networking
{
enum PacketTypes
{
Login,
LoggedIn,
LogOut,
PlayerJoined,
PlayerLeft,
KickedOut,
StartGame,
EndGame,
CharacterInfo,
Chatmessage,
UpdateNetLobby,
NetworkEvent,
Traitor
}
class NetworkMember
{
public const int DefaultPort = 14242;
protected static Color[] messageColor = { Color.White, Color.Red, Color.LightBlue, Color.LightGreen };
protected string name;
protected TimeSpan updateInterval;
protected DateTime updateTimer;
protected GUIFrame inGameHUD;
protected GUIListBox chatBox;
public int Port;
protected bool gameStarted;
public string Name
{
get { return name; }
set
{
if (string.IsNullOrEmpty(name)) return;
name = value;
}
}
public GUIFrame InGameHUD
{
get { return inGameHUD; }
}
public NetworkMember()
{
inGameHUD = new GUIFrame(new Rectangle(0,0,0,0), null, null);
inGameHUD.CanBeFocused = false;
int width = 350, height = 100;
chatBox = new GUIListBox(new Rectangle(
Game1.GraphicsWidth - 20 - width,
Game1.GraphicsHeight - 40 - 25 - height,
width, height),
Color.White * 0.5f, GUI.style, inGameHUD);
var textBox = new GUITextBox(
new Rectangle(chatBox.Rect.X, chatBox.Rect.Y + chatBox.Rect.Height + 20, chatBox.Rect.Width, 25),
Color.White * 0.5f, Color.Black, Alignment.TopLeft, Alignment.Left, GUI.style, inGameHUD);
textBox.Font = GUI.SmallFont;
textBox.OnEnter = EnterChatMessage;
}
public bool EnterChatMessage(GUITextBox textBox, string message)
{
if (string.IsNullOrWhiteSpace(message)) return false;
SendChatMessage(Game1.NetworkMember.Name + ": " + message);
textBox.Deselect();
return true;
}
public void AddChatMessage(string message, ChatMessageType messageType)
{
Game1.NetLobbyScreen.NewChatMessage(message, messageColor[(int)messageType]);
while (chatBox.CountChildren > 20)
{
chatBox.RemoveChild(chatBox.children[1]);
}
GUITextBlock msg = new GUITextBlock(new Rectangle(0, 0, 0, 20), message,
((chatBox.CountChildren % 2) == 0) ? Color.Transparent : Color.Black * 0.1f, messageColor[(int)messageType],
Alignment.Left, null, null, true);
msg.Font = GUI.SmallFont;
msg.Padding = new Vector4(20.0f, 0, 0, 0);
//float prevScroll = chatBox.BarScroll;
float prevSize = chatBox.BarSize;
float oldScroll = chatBox.BarScroll;
msg.Padding = new Vector4(20, 0, 0, 0);
chatBox.AddChild(msg);
if ((prevSize == 1.0f && chatBox.BarScroll == 0.0f) || (prevSize < 1.0f && chatBox.BarScroll == 1.0f)) chatBox.BarScroll = 1.0f;
GUI.PlayMessageSound();
}
public virtual void SendChatMessage(string message, ChatMessageType type = ChatMessageType.Server) { }
public virtual void Update() { }
public virtual void Draw(Microsoft.Xna.Framework.Graphics.SpriteBatch spriteBatch)
{
if (!gameStarted) return;
inGameHUD.Draw(spriteBatch);
}
public virtual void Disconnect() { }
}
enum ChatMessageType
{
Default, Admin, Dead, Server
}
}