mirror of
https://gitlab.com/Syroot/Worms.git
synced 2025-01-27 14:18:00 +03:00
Finish Worms 2 server (so far).
This commit is contained in:
parent
0fee2e1e13
commit
02a644abe9
@ -32,7 +32,7 @@ Implementation of formats listed above as unsupported is planned for a later dat
|
||||
|
||||
* `Syroot.Worms.Mgame.Launcher`: Creates a fake launch configuration to start OW or WWPA clients with.
|
||||
* `Syroot.Worms.Mgame.GameServer`: Simulates OW or WWPA networking to allow playing games.
|
||||
* `Syroot.Worms.Worms2.GameServer`: Simulates W2 networking (WIP).
|
||||
* `Syroot.Worms.Worms2.GameServer`: Simulates a Worms 2 server.
|
||||
|
||||
## Availability
|
||||
|
||||
|
@ -140,17 +140,17 @@ namespace Syroot.Worms.Worms2.GameServer
|
||||
int dataLength = 0;
|
||||
Code = (PacketCode)stream.ReadInt32();
|
||||
Flags flags = (Flags)stream.ReadInt32();
|
||||
if (flags.HasFlag(Flags.HasValue0)) Value0 = stream.ReadInt32();
|
||||
if (flags.HasFlag(Flags.HasValue1)) Value1 = stream.ReadInt32();
|
||||
if (flags.HasFlag(Flags.HasValue2)) Value2 = stream.ReadInt32();
|
||||
if (flags.HasFlag(Flags.HasValue3)) Value3 = stream.ReadInt32();
|
||||
if (flags.HasFlag(Flags.HasValue4)) Value4 = stream.ReadInt32();
|
||||
if (flags.HasFlag(Flags.HasValue10)) Value10 = stream.ReadInt32();
|
||||
if (flags.HasFlag(Flags.HasDataLength)) dataLength = stream.ReadInt32();
|
||||
if (flags.HasFlag(Flags.HasData) && dataLength != 0) Data = stream.ReadFixedString(dataLength, Encodings.Windows1252);
|
||||
if (flags.HasFlag(Flags.HasError)) Error = stream.ReadInt32();
|
||||
if (flags.HasFlag(Flags.HasName)) Name = stream.ReadFixedString(20, Encodings.Windows1252);
|
||||
if (flags.HasFlag(Flags.HasSession)) Session = stream.ReadStruct<SessionInfo>();
|
||||
if (flags.HasFlag(Flags.Value0)) Value0 = stream.ReadInt32();
|
||||
if (flags.HasFlag(Flags.Value1)) Value1 = stream.ReadInt32();
|
||||
if (flags.HasFlag(Flags.Value2)) Value2 = stream.ReadInt32();
|
||||
if (flags.HasFlag(Flags.Value3)) Value3 = stream.ReadInt32();
|
||||
if (flags.HasFlag(Flags.Value4)) Value4 = stream.ReadInt32();
|
||||
if (flags.HasFlag(Flags.Value10)) Value10 = stream.ReadInt32();
|
||||
if (flags.HasFlag(Flags.DataLength)) dataLength = stream.ReadInt32();
|
||||
if (flags.HasFlag(Flags.Data) && dataLength != 0) Data = stream.ReadFixedString(dataLength, Encodings.Windows1252);
|
||||
if (flags.HasFlag(Flags.Error)) Error = stream.ReadInt32();
|
||||
if (flags.HasFlag(Flags.Name)) Name = stream.ReadFixedString(20, Encodings.Windows1252);
|
||||
if (flags.HasFlag(Flags.Session)) Session = stream.ReadStruct<SessionInfo>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -182,20 +182,20 @@ namespace Syroot.Worms.Worms2.GameServer
|
||||
private Flags GetFlags()
|
||||
{
|
||||
Flags flags = Flags.None;
|
||||
if (Value0.HasValue) flags |= Flags.HasValue0;
|
||||
if (Value1.HasValue) flags |= Flags.HasValue1;
|
||||
if (Value2.HasValue) flags |= Flags.HasValue2;
|
||||
if (Value3.HasValue) flags |= Flags.HasValue3;
|
||||
if (Value4.HasValue) flags |= Flags.HasValue4;
|
||||
if (Value10.HasValue) flags |= Flags.HasValue10;
|
||||
if (Value0.HasValue) flags |= Flags.Value0;
|
||||
if (Value1.HasValue) flags |= Flags.Value1;
|
||||
if (Value2.HasValue) flags |= Flags.Value2;
|
||||
if (Value3.HasValue) flags |= Flags.Value3;
|
||||
if (Value4.HasValue) flags |= Flags.Value4;
|
||||
if (Value10.HasValue) flags |= Flags.Value10;
|
||||
if (Data != null)
|
||||
{
|
||||
flags |= Flags.HasDataLength;
|
||||
flags |= Flags.HasData;
|
||||
flags |= Flags.DataLength;
|
||||
flags |= Flags.Data;
|
||||
}
|
||||
if (Error.HasValue) flags |= Flags.HasError;
|
||||
if (Name != null) flags |= Flags.HasName;
|
||||
if (Session.HasValue) flags |= Flags.HasSession;
|
||||
if (Error.HasValue) flags |= Flags.Error;
|
||||
if (Name != null) flags |= Flags.Name;
|
||||
if (Session.HasValue) flags |= Flags.Session;
|
||||
return flags;
|
||||
}
|
||||
|
||||
@ -205,17 +205,17 @@ namespace Syroot.Worms.Worms2.GameServer
|
||||
private enum Flags : int
|
||||
{
|
||||
None,
|
||||
HasValue0 = 1 << 0,
|
||||
HasValue1 = 1 << 1,
|
||||
HasValue2 = 1 << 2,
|
||||
HasValue3 = 1 << 3,
|
||||
HasValue4 = 1 << 4,
|
||||
HasValue10 = 1 << 10,
|
||||
HasDataLength = 1 << 5,
|
||||
HasData = 1 << 6,
|
||||
HasError = 1 << 7,
|
||||
HasName = 1 << 8,
|
||||
HasSession = 1 << 9
|
||||
Value0 = 1 << 0,
|
||||
Value1 = 1 << 1,
|
||||
Value2 = 1 << 2,
|
||||
Value3 = 1 << 3,
|
||||
Value4 = 1 << 4,
|
||||
Value10 = 1 << 10,
|
||||
DataLength = 1 << 5,
|
||||
Data = 1 << 6,
|
||||
Error = 1 << 7,
|
||||
Name = 1 << 8,
|
||||
Session = 1 << 9
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,11 +17,11 @@
|
||||
CreateRoomReply = 701,
|
||||
Join = 800,
|
||||
JoinReply = 801,
|
||||
LeaveRoom = 900,
|
||||
LeaveRoomReply = 901,
|
||||
Leave = 900,
|
||||
LeaveReply = 901,
|
||||
DisconnectUser = 1000,
|
||||
CloseRoom = 1100,
|
||||
CloseRoomReply = 1101,
|
||||
Close = 1100,
|
||||
CloseReply = 1101,
|
||||
CreateGame = 1200,
|
||||
CreateGameReply = 1201,
|
||||
ChatRoom = 1300,
|
||||
|
@ -36,9 +36,9 @@ namespace Syroot.Worms.Worms2.GameServer
|
||||
[PacketCode.ListGames] = OnListGames,
|
||||
[PacketCode.Login] = OnLogin,
|
||||
[PacketCode.CreateRoom] = OnCreateRoom,
|
||||
[PacketCode.Join] = OnJoinRoom,
|
||||
[PacketCode.LeaveRoom] = OnLeaveRoom,
|
||||
[PacketCode.CloseRoom] = OnCloseRoom,
|
||||
[PacketCode.Join] = OnJoin,
|
||||
[PacketCode.Leave] = OnLeave,
|
||||
[PacketCode.Close] = OnClose,
|
||||
[PacketCode.CreateGame] = OnCreateGame,
|
||||
[PacketCode.ChatRoom] = OnChatRoom,
|
||||
[PacketCode.ConnectGame] = OnConnectGame,
|
||||
@ -128,10 +128,65 @@ namespace Syroot.Worms.Worms2.GameServer
|
||||
}
|
||||
}
|
||||
|
||||
private void LeaveGame(User fromUser)
|
||||
{
|
||||
// Close an abandoned game.
|
||||
Game? game = _games.SingleOrDefault(x => x.Name == fromUser.Name);
|
||||
if (game == null)
|
||||
return;
|
||||
|
||||
_games.Remove(game);
|
||||
|
||||
// Notify other users.
|
||||
foreach (User user in _users.Where(x => x != fromUser))
|
||||
{
|
||||
SendPacket(user.Connection, new Packet(PacketCode.Leave,
|
||||
value2: game.ID,
|
||||
value10: fromUser.ID));
|
||||
SendPacket(user.Connection, new Packet(PacketCode.Close,
|
||||
value10: game.ID));
|
||||
}
|
||||
}
|
||||
|
||||
private void LeaveRoom(User fromUser)
|
||||
{
|
||||
// Close an abandoned room.
|
||||
bool roomClosed = fromUser.RoomID != 0
|
||||
&& !_users.Any(x => x != fromUser && x.RoomID == fromUser.RoomID)
|
||||
&& !_games.Any(x => x.RoomID == fromUser.RoomID);
|
||||
if (roomClosed)
|
||||
_rooms.Remove(_rooms.Single(x => x.ID == fromUser.RoomID));
|
||||
|
||||
// Notify other users.
|
||||
foreach (User user in _users.Where(x => x != fromUser))
|
||||
{
|
||||
// Notify room leave, if any.
|
||||
if (fromUser.RoomID != 0)
|
||||
{
|
||||
SendPacket(user.Connection, new Packet(PacketCode.Leave,
|
||||
value2: fromUser.RoomID,
|
||||
value10: fromUser.ID));
|
||||
}
|
||||
// Notify room close, if any.
|
||||
if (roomClosed)
|
||||
{
|
||||
SendPacket(user.Connection, new Packet(PacketCode.Close,
|
||||
value10: fromUser.RoomID));
|
||||
}
|
||||
}
|
||||
|
||||
// Update user room.
|
||||
fromUser.RoomID = 0;
|
||||
}
|
||||
|
||||
// ---- Handlers ----
|
||||
|
||||
private void OnListRooms(PacketConnection connection, Packet packet)
|
||||
{
|
||||
User? fromUser = GetUser(connection);
|
||||
if (fromUser == null || packet.Value4 != 0)
|
||||
return;
|
||||
|
||||
foreach (Room room in _rooms)
|
||||
{
|
||||
SendPacket(connection, new Packet(PacketCode.ListItem,
|
||||
@ -146,7 +201,7 @@ namespace Syroot.Worms.Worms2.GameServer
|
||||
private void OnListUsers(PacketConnection connection, Packet packet)
|
||||
{
|
||||
User? fromUser = GetUser(connection);
|
||||
if (fromUser == null)
|
||||
if (fromUser == null || packet.Value2 != fromUser.RoomID || packet.Value4 != 0)
|
||||
return;
|
||||
|
||||
foreach (User user in _users.Where(x => x.RoomID == fromUser.RoomID)) // notably includes the user itself
|
||||
@ -162,7 +217,7 @@ namespace Syroot.Worms.Worms2.GameServer
|
||||
private void OnListGames(PacketConnection connection, Packet packet)
|
||||
{
|
||||
User? fromUser = GetUser(connection);
|
||||
if (fromUser == null)
|
||||
if (fromUser == null || packet.Value2 != fromUser.RoomID || packet.Value4 != 0)
|
||||
return;
|
||||
|
||||
foreach (Game game in _games.Where(x => x.RoomID == fromUser.RoomID))
|
||||
@ -178,10 +233,15 @@ namespace Syroot.Worms.Worms2.GameServer
|
||||
|
||||
private void OnLogin(PacketConnection connection, Packet packet)
|
||||
{
|
||||
if (packet.Value1 == null || packet.Value4 == null || packet.Name == null || packet.Session == null)
|
||||
return;
|
||||
|
||||
// Check if user name is valid and not already taken.
|
||||
if (!String.IsNullOrWhiteSpace(packet.Name)
|
||||
&& packet.Session.HasValue
|
||||
&& !_users.Any(x => x.Name.Equals(packet.Name, StringComparison.InvariantCultureIgnoreCase)))
|
||||
if (_users.Any(x => x.Name.Equals(packet.Name, StringComparison.InvariantCultureIgnoreCase)))
|
||||
{
|
||||
SendPacket(connection, new Packet(PacketCode.LoginReply, value1: 0, error: 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
User newUser = new User(connection, ++_lastID, packet.Name, packet.Session.Value.Nation);
|
||||
|
||||
@ -197,28 +257,19 @@ namespace Syroot.Worms.Worms2.GameServer
|
||||
|
||||
// Register new user and send reply to him.
|
||||
_users.Add(newUser);
|
||||
SendPacket(connection, new Packet(PacketCode.LoginReply,
|
||||
value1: newUser.ID,
|
||||
error: 0));
|
||||
}
|
||||
else
|
||||
{
|
||||
SendPacket(connection, new Packet(PacketCode.LoginReply,
|
||||
value1: 0,
|
||||
error: 1));
|
||||
SendPacket(connection, new Packet(PacketCode.LoginReply, value1: newUser.ID, error: 0));
|
||||
}
|
||||
}
|
||||
|
||||
private void OnCreateRoom(PacketConnection connection, Packet packet)
|
||||
{
|
||||
User? fromUser = GetUser(connection);
|
||||
if (fromUser == null)
|
||||
if (fromUser == null || packet.Value1 != 0 || packet.Value4 != 0 || packet.Data == null
|
||||
|| packet.Name == null || packet.Session == null)
|
||||
return;
|
||||
|
||||
// Check if room name is valid is not already taken.
|
||||
if (!String.IsNullOrWhiteSpace(packet.Name)
|
||||
&& packet.Session.HasValue
|
||||
&& !_rooms.Any(x => x.Name.Equals(packet.Name, StringComparison.InvariantCultureIgnoreCase)))
|
||||
if (!_rooms.Any(x => x.Name.Equals(packet.Name, StringComparison.InvariantCultureIgnoreCase)))
|
||||
{
|
||||
Room newRoom = new Room(++_lastID, packet.Name, packet.Session.Value.Nation,
|
||||
connection.RemoteEndPoint.Address);
|
||||
@ -243,18 +294,19 @@ namespace Syroot.Worms.Worms2.GameServer
|
||||
else
|
||||
{
|
||||
SendPacket(connection, new Packet(PacketCode.CreateRoomReply,
|
||||
value1: 0,
|
||||
error: 1));
|
||||
}
|
||||
}
|
||||
|
||||
private void OnJoinRoom(PacketConnection connection, Packet packet)
|
||||
private void OnJoin(PacketConnection connection, Packet packet)
|
||||
{
|
||||
User? fromUser = GetUser(connection);
|
||||
if (fromUser == null)
|
||||
if (fromUser == null || packet.Value2 == null || packet.Value10 != fromUser.ID)
|
||||
return;
|
||||
|
||||
// Check if room ID is valid.
|
||||
if (packet.Value2.HasValue && _rooms.Any(x => x.ID == packet.Value2))
|
||||
// Require valid room or game ID.
|
||||
if (_rooms.Any(x => x.ID == packet.Value2))
|
||||
{
|
||||
fromUser.RoomID = packet.Value2.Value;
|
||||
|
||||
@ -267,66 +319,47 @@ namespace Syroot.Worms.Worms2.GameServer
|
||||
}
|
||||
|
||||
// Send reply to joiner.
|
||||
SendPacket(connection, new Packet(PacketCode.JoinReply,
|
||||
error: 0));
|
||||
SendPacket(connection, new Packet(PacketCode.JoinReply, error: 0));
|
||||
}
|
||||
else
|
||||
else if (_games.Any(x => x.ID == packet.Value2 && x.RoomID == fromUser.RoomID))
|
||||
{
|
||||
SendPacket(connection, new Packet(PacketCode.JoinReply,
|
||||
error: 1));
|
||||
}
|
||||
}
|
||||
|
||||
private void OnLeaveRoom(PacketConnection connection, Packet packet)
|
||||
{
|
||||
User? fromUser = GetUser(connection);
|
||||
if (fromUser == null)
|
||||
return;
|
||||
|
||||
// Require left room ID and user ID.
|
||||
if (packet.Value2.HasValue && packet.Value2 == fromUser.RoomID
|
||||
&& packet.Value10.HasValue && packet.Value10 == fromUser.ID)
|
||||
{
|
||||
LeaveRoom(fromUser);
|
||||
// Reply to leaver.
|
||||
SendPacket(connection, new Packet(PacketCode.LeaveRoomReply,
|
||||
error: 0));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Reply to leaver.
|
||||
SendPacket(connection, new Packet(PacketCode.LeaveRoomReply,
|
||||
error: 1));
|
||||
}
|
||||
}
|
||||
|
||||
private void LeaveRoom(User fromUser)
|
||||
{
|
||||
// Close an abandoned room.
|
||||
bool roomClosed = fromUser.RoomID != 0 && !_users.Any(x => x != fromUser && x.RoomID == fromUser.RoomID);
|
||||
if (roomClosed)
|
||||
_rooms.Remove(_rooms.Single(x => x.ID == fromUser.RoomID));
|
||||
|
||||
// Notify other users.
|
||||
foreach (User user in _users.Where(x => x != fromUser))
|
||||
{
|
||||
// Notify room leave, if any.
|
||||
if (fromUser.RoomID != 0)
|
||||
// Notify other users about the join.
|
||||
foreach (User user in _users.Where(x => x != fromUser))
|
||||
{
|
||||
SendPacket(user.Connection, new Packet(PacketCode.LeaveRoom,
|
||||
SendPacket(user.Connection, new Packet(PacketCode.Join,
|
||||
value2: fromUser.RoomID,
|
||||
value10: fromUser.ID));
|
||||
}
|
||||
// Notify room close, if any.
|
||||
if (roomClosed)
|
||||
{
|
||||
SendPacket(user.Connection, new Packet(PacketCode.CloseRoom,
|
||||
value10: fromUser.RoomID));
|
||||
}
|
||||
}
|
||||
|
||||
// Update user room.
|
||||
fromUser.RoomID = 0;
|
||||
// Send reply to joiner.
|
||||
SendPacket(connection, new Packet(PacketCode.JoinReply, error: 0));
|
||||
}
|
||||
else
|
||||
{
|
||||
SendPacket(connection, new Packet(PacketCode.JoinReply, error: 1));
|
||||
}
|
||||
}
|
||||
|
||||
private void OnLeave(PacketConnection connection, Packet packet)
|
||||
{
|
||||
User? fromUser = GetUser(connection);
|
||||
if (fromUser == null || packet.Value2 == null || packet.Value10 != fromUser.ID)
|
||||
return;
|
||||
|
||||
// Require valid room ID (never sent for games, users disconnect if leaving a game).
|
||||
if (packet.Value2 == fromUser.RoomID)
|
||||
{
|
||||
LeaveRoom(fromUser);
|
||||
// Reply to leaver.
|
||||
SendPacket(connection, new Packet(PacketCode.LeaveReply,
|
||||
error: 0));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Reply to leaver.
|
||||
SendPacket(connection, new Packet(PacketCode.LeaveReply,
|
||||
error: 1));
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDisconnectUser(PacketConnection connection)
|
||||
@ -337,6 +370,7 @@ namespace Syroot.Worms.Worms2.GameServer
|
||||
|
||||
_users.Remove(fromUser);
|
||||
|
||||
LeaveGame(fromUser);
|
||||
LeaveRoom(fromUser);
|
||||
|
||||
// Notify user disconnect.
|
||||
@ -347,31 +381,59 @@ namespace Syroot.Worms.Worms2.GameServer
|
||||
}
|
||||
}
|
||||
|
||||
private void OnCloseRoom(PacketConnection connection, Packet packet)
|
||||
private void OnClose(PacketConnection connection, Packet packet)
|
||||
{
|
||||
User? fromUser = GetUser(connection);
|
||||
if (fromUser == null)
|
||||
if (fromUser == null || packet.Value10 == null)
|
||||
return;
|
||||
|
||||
// Never sent for games, users disconnect if leaving a game.
|
||||
// Simply reply success to client, the server decides when to actually close rooms.
|
||||
SendPacket(connection, new Packet(PacketCode.CloseRoomReply, error: 0));
|
||||
SendPacket(connection, new Packet(PacketCode.CloseReply, error: 0));
|
||||
}
|
||||
|
||||
private void OnCreateGame(PacketConnection connection, Packet packet)
|
||||
{
|
||||
SendPacket(connection, new Packet(PacketCode.CreateGameReply,
|
||||
value1: 123456,
|
||||
error: 0));
|
||||
User? fromUser = GetUser(connection);
|
||||
if (fromUser == null || packet.Value2 == null || packet.Data == null || packet.Session == null)
|
||||
return;
|
||||
|
||||
// Require valid room ID.
|
||||
if (packet.Value2 == fromUser.RoomID)
|
||||
{
|
||||
Game newGame = new Game(++_lastID, fromUser.Name, fromUser.Session.Nation, fromUser.RoomID,
|
||||
connection.RemoteEndPoint.Address, // do not use bad NAT IP reported by users here
|
||||
packet.Session.Value.Access);
|
||||
_games.Add(newGame);
|
||||
|
||||
// Notify other users about new game, even those in other rooms.
|
||||
foreach (User user in _users.Where(x => x != fromUser))
|
||||
{
|
||||
SendPacket(user.Connection, new Packet(PacketCode.CreateGame,
|
||||
value2: newGame.RoomID,
|
||||
value4: newGame.ID,
|
||||
data: newGame.IPAddress.ToString(),
|
||||
name: newGame.Name,
|
||||
session: newGame.Session));
|
||||
}
|
||||
|
||||
// Send reply to host.
|
||||
SendPacket(connection, new Packet(PacketCode.CreateGameReply,
|
||||
value1: newGame.ID,
|
||||
error: 0));
|
||||
}
|
||||
else
|
||||
{
|
||||
SendPacket(connection, new Packet(PacketCode.CreateGameReply,
|
||||
value1: 0,
|
||||
error: 1));
|
||||
}
|
||||
}
|
||||
|
||||
private void OnChatRoom(PacketConnection connection, Packet packet)
|
||||
{
|
||||
User? fromUser = GetUser(connection);
|
||||
if (fromUser == null)
|
||||
return;
|
||||
|
||||
// Requires user ID, target ID, and message data.
|
||||
if (!packet.Value0.HasValue || !packet.Value3.HasValue || packet.Data == null)
|
||||
if (fromUser == null || packet.Value0 != fromUser.ID || packet.Value3 == null || packet.Data == null)
|
||||
return;
|
||||
|
||||
int targetID = packet.Value3.Value;
|
||||
@ -422,8 +484,24 @@ namespace Syroot.Worms.Worms2.GameServer
|
||||
|
||||
private void OnConnectGame(PacketConnection connection, Packet packet)
|
||||
{
|
||||
SendPacket(connection, new Packet(PacketCode.ConnectGameReply,
|
||||
data: "12.34.45.56"));
|
||||
User? fromUser = GetUser(connection);
|
||||
if (fromUser == null || packet.Value0 == null)
|
||||
return;
|
||||
|
||||
// Require valid game ID and user to be in appropriate room.
|
||||
Game? game = _games.FirstOrDefault(x => x.ID == packet.Value0 && x.RoomID == fromUser.RoomID);
|
||||
if (game == null)
|
||||
{
|
||||
SendPacket(connection, new Packet(PacketCode.ConnectGameReply,
|
||||
data: String.Empty,
|
||||
error: 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
SendPacket(connection, new Packet(PacketCode.ConnectGameReply,
|
||||
data: game.IPAddress.ToString(),
|
||||
error: 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user