the perplexed constable in old scroll obsidian
h
This commit is contained in:
parent
e96d341cb6
commit
8230796a1b
7 changed files with 187 additions and 23 deletions
86
protocol.md
86
protocol.md
|
@ -87,6 +87,49 @@ Communication between the master server and clients will be done over a WebSocke
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
<table style="margin-right: 8px; margin-bottom: 8px;">
|
||||||
|
<thead>
|
||||||
|
<th colspan="100" class="center">
|
||||||
|
ID 2: Positive ACK<br />
|
||||||
|
Responder
|
||||||
|
</th>
|
||||||
|
</thead>
|
||||||
|
<thead>
|
||||||
|
<th>#</th>
|
||||||
|
<th>Region</th>
|
||||||
|
<th>Type</th>
|
||||||
|
</thead>
|
||||||
|
<tr>
|
||||||
|
<td>1</td>
|
||||||
|
<td>Request Packet ID</td>
|
||||||
|
<td>Byte</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<table style="margin-right: 8px; margin-bottom: 8px;">
|
||||||
|
<thead>
|
||||||
|
<th colspan="100" class="center">
|
||||||
|
ID 3: Negative ACK<br />
|
||||||
|
Responder
|
||||||
|
</th>
|
||||||
|
</thead>
|
||||||
|
<thead>
|
||||||
|
<th>#</th>
|
||||||
|
<th>Region</th>
|
||||||
|
<th>Type</th>
|
||||||
|
</thead>
|
||||||
|
<tr>
|
||||||
|
<td>1</td>
|
||||||
|
<td>Request Packet ID</td>
|
||||||
|
<td>Byte</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>2</td>
|
||||||
|
<td>Error Message</td>
|
||||||
|
<td>String</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
#### Slave to Master
|
#### Slave to Master
|
||||||
|
|
||||||
<table style="margin-right: 8px; margin-bottom: 8px;">
|
<table style="margin-right: 8px; margin-bottom: 8px;">
|
||||||
|
@ -127,6 +170,49 @@ Communication between the master server and clients will be done over a WebSocke
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
<table style="margin-right: 8px; margin-bottom: 8px;">
|
||||||
|
<thead>
|
||||||
|
<th colspan="100" class="center">
|
||||||
|
ID 2: Status Update<br />
|
||||||
|
Blind Requester
|
||||||
|
</th>
|
||||||
|
</thead>
|
||||||
|
<thead>
|
||||||
|
<th># (<i>r</i>)</th>
|
||||||
|
<th>Region</th>
|
||||||
|
<th>Type</th>
|
||||||
|
</thead>
|
||||||
|
<tr>
|
||||||
|
<td class="center">1</td>
|
||||||
|
<td>Server Count (<i>n</i>)</td>
|
||||||
|
<td>Unsigned Byte</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><i>r</i> > 1</td>
|
||||||
|
<td colspan="2">Iterated over <i>n</i> (0 ≤ <i>i</i> ≤ <i>n - 1</i>)</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="center">2 + 4<i>i</i></td>
|
||||||
|
<td>Server Id</td>
|
||||||
|
<td>Packed Unsigned Short</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="center">3 + 4<i>i</i></td>
|
||||||
|
<td>User Count</td>
|
||||||
|
<td>Packed Unsigned Short</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="center">4 + 4<i>i</i></td>
|
||||||
|
<td>IPv4 Address</td>
|
||||||
|
<td>Bytes (4)</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="center">5 + 4<i>i</i></td>
|
||||||
|
<td>Port</td>
|
||||||
|
<td>Packed Unsigned Short</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
### Master/Client Packet IDs
|
### Master/Client Packet IDs
|
||||||
|
|
||||||
#### Master to Client
|
#### Master to Client
|
||||||
|
|
|
@ -13,15 +13,17 @@ using Kneesocks;
|
||||||
using MySql.Data.Entity;
|
using MySql.Data.Entity;
|
||||||
|
|
||||||
namespace SockScape {
|
namespace SockScape {
|
||||||
|
static class ServerContext {
|
||||||
|
public static Dictionary<int, Server<PlayerConnection>> Servers { get; }
|
||||||
|
= new Dictionary<int, Server<PlayerConnection>>();
|
||||||
|
public static Dictionary<int, Pool<PlayerConnection>> Pools { get; }
|
||||||
|
= new Dictionary<int, Pool<PlayerConnection>>();
|
||||||
|
}
|
||||||
|
|
||||||
class Entrypoint {
|
class Entrypoint {
|
||||||
static void Main(string[] args) {
|
static void Main(string[] args) {
|
||||||
var db = new DAL.ScapeDb();
|
var db = new DAL.ScapeDb();
|
||||||
|
|
||||||
Dictionary<int, Server> servers
|
|
||||||
= new Dictionary<int, Server>();
|
|
||||||
Dictionary<int, Pool<PlayerConnection>> pools
|
|
||||||
= new Dictionary<int, Pool<PlayerConnection>>();
|
|
||||||
|
|
||||||
foreach(var server in Configuration.Servers) {
|
foreach(var server in Configuration.Servers) {
|
||||||
var pool = new Pool<PlayerConnection> {
|
var pool = new Pool<PlayerConnection> {
|
||||||
InitialCount = 3,
|
InitialCount = 3,
|
||||||
|
@ -33,8 +35,8 @@ namespace SockScape {
|
||||||
|
|
||||||
var serverHandle = new Server<PlayerConnection>((ushort)server["Port"], pool, server);
|
var serverHandle = new Server<PlayerConnection>((ushort)server["Port"], pool, server);
|
||||||
|
|
||||||
pools.Add(server["Id"], pool);
|
ServerContext.Pools.Add(server["Id"], pool);
|
||||||
servers.Add(server["Id"], serverHandle);
|
ServerContext.Servers.Add(server["Id"], serverHandle);
|
||||||
serverHandle.Start();
|
serverHandle.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,10 +6,26 @@ using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace SockScape {
|
namespace SockScape {
|
||||||
static class ServerList {
|
class ServerList {
|
||||||
public static Dictionary<int, IPEndPoint> Servers { get; private set; }
|
public static Dictionary<int, Server> Servers { get; }
|
||||||
= new Dictionary<int, IPEndPoint>();
|
= new Dictionary<int, Server>();
|
||||||
|
|
||||||
|
public Server this[int i] {
|
||||||
|
get => Servers.ContainsKey(i) ? Servers[i] : null;
|
||||||
|
set => Servers[i] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool HasId(int id)
|
||||||
|
=> Servers.ContainsKey(id);
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
=> Servers.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
class Server {
|
||||||
|
public ushort Id { get; set; }
|
||||||
|
public ushort UserCount { get; set; }
|
||||||
|
public IPEndPoint Address { get; set; }
|
||||||
|
public IPEndPoint Owner { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,12 +16,13 @@ namespace SockScape {
|
||||||
private static UdpClient Sock;
|
private static UdpClient Sock;
|
||||||
private static Thread ListeningThread;
|
private static Thread ListeningThread;
|
||||||
private static bool IsOpen;
|
private static bool IsOpen;
|
||||||
|
private static DateTime LastMessage;
|
||||||
|
|
||||||
public static void Initialize() {
|
public static void Initialize() {
|
||||||
if(IsOpen || ListeningThread != null)
|
if(IsOpen || ListeningThread != null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
short port = (short)Configuration.General["Master Port"];
|
ushort port = (ushort)Configuration.General["Master Port"];
|
||||||
Sock = new UdpClient(Configuration.General["Master Addr"], port);
|
Sock = new UdpClient(Configuration.General["Master Addr"], port);
|
||||||
|
|
||||||
Key = new Key();
|
Key = new Key();
|
||||||
|
@ -41,6 +42,11 @@ namespace SockScape {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void Send(byte[] message) {
|
||||||
|
Sock.Send(message, message.Length);
|
||||||
|
LastMessage = DateTime.Now;
|
||||||
|
}
|
||||||
|
|
||||||
public static void Close() {
|
public static void Close() {
|
||||||
IsOpen = false;
|
IsOpen = false;
|
||||||
ListeningThread.Join();
|
ListeningThread.Join();
|
||||||
|
|
|
@ -13,6 +13,7 @@ namespace SockScape {
|
||||||
static class MasterUdpServer {
|
static class MasterUdpServer {
|
||||||
private static Dictionary<string, Client> Prospects;
|
private static Dictionary<string, Client> Prospects;
|
||||||
private static Dictionary<string, Client> Clients;
|
private static Dictionary<string, Client> Clients;
|
||||||
|
private static ServerList Servers;
|
||||||
|
|
||||||
private static UdpClient Sock;
|
private static UdpClient Sock;
|
||||||
private static Thread ListeningThread;
|
private static Thread ListeningThread;
|
||||||
|
@ -22,8 +23,11 @@ namespace SockScape {
|
||||||
if(IsOpen || ListeningThread != null)
|
if(IsOpen || ListeningThread != null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
Servers = new ServerList();
|
||||||
|
Prospects = new Dictionary<string, Client>();
|
||||||
Clients = new Dictionary<string, Client>();
|
Clients = new Dictionary<string, Client>();
|
||||||
short port = (short)Configuration.General["Master Port"];
|
|
||||||
|
ushort port = (ushort)Configuration.General["Master Port"];
|
||||||
Sock = new UdpClient(new IPEndPoint(IPAddress.Any, port));
|
Sock = new UdpClient(new IPEndPoint(IPAddress.Any, port));
|
||||||
|
|
||||||
IsOpen = true;
|
IsOpen = true;
|
||||||
|
@ -31,10 +35,6 @@ namespace SockScape {
|
||||||
ListeningThread.Start();
|
ListeningThread.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsClientConnected(string client) {
|
|
||||||
return Clients.ContainsKey(client);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Listener() {
|
public static void Listener() {
|
||||||
while(IsOpen) {
|
while(IsOpen) {
|
||||||
IPEndPoint endPoint = new IPEndPoint(IPAddress.Any, 0);
|
IPEndPoint endPoint = new IPEndPoint(IPAddress.Any, 0);
|
||||||
|
@ -42,29 +42,59 @@ namespace SockScape {
|
||||||
var data = Sock.Receive(ref endPoint);
|
var data = Sock.Receive(ref endPoint);
|
||||||
var client = endPoint.ToString();
|
var client = endPoint.ToString();
|
||||||
var encryptor = IsClientConnected(client) ? Clients[client].Encryptor : null;
|
var encryptor = IsClientConnected(client) ? Clients[client].Encryptor : null;
|
||||||
|
if(data.Take(Packet.MagicNumber.Length).SequenceEqual(Packet.MagicNumber))
|
||||||
|
encryptor = null;
|
||||||
|
|
||||||
Packet packet =
|
Packet packet =
|
||||||
encryptor == null ? Packet.FromBytes(data)
|
encryptor == null ? Packet.FromBytes(data)
|
||||||
: Packet.FromBytes(encryptor.Parse(data));
|
: Packet.FromBytes(encryptor.Parse(data));
|
||||||
|
|
||||||
|
if(packet == null)
|
||||||
|
break;
|
||||||
|
|
||||||
|
byte[] sendBuffer;
|
||||||
switch((kIntraMasterId)packet.Id) {
|
switch((kIntraMasterId)packet.Id) {
|
||||||
case kIntraMasterId.InitiationAttempt:
|
case kIntraMasterId.InitiationAttempt:
|
||||||
if(packet.RegionCount != 1)
|
if(packet.RegionCount != 1)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if(packet[0] == Configuration.General["Master Secret"]) {
|
if(packet[0] == Configuration.General["Master Secret"]) {
|
||||||
var request =
|
var key = new Key();
|
||||||
|
Prospects[client] = new Client {
|
||||||
var request = Key.GenerateRequestPacket().GetBytes();
|
LastReceive = DateTime.Now,
|
||||||
Sock.Send(request, request.Length, endPoint);
|
Address = endPoint,
|
||||||
|
Key = key
|
||||||
|
};
|
||||||
|
|
||||||
|
sendBuffer = key.GenerateRequestPacket().GetBytes();
|
||||||
|
Sock.Send(sendBuffer, sendBuffer.Length, endPoint);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case kIntraMasterId.KeyExchange:
|
case kIntraMasterId.KeyExchange:
|
||||||
|
if(!IsProspectConnected(client))
|
||||||
|
break;
|
||||||
|
|
||||||
|
var privateKey = Prospects[client].Key.ParseResponsePacket(packet);
|
||||||
|
if(privateKey != -1) {
|
||||||
|
Prospects[client].Encryptor = new Cipher(privateKey);
|
||||||
|
Clients[client] = Prospects[client];
|
||||||
|
Prospects.Remove(client);
|
||||||
|
} else
|
||||||
|
Prospects.Remove(client);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case kIntraMasterId.StatusUpdate:
|
||||||
|
if(!IsClientConnected(client) || packet.RegionCount < 1)
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Thread.Sleep(1);
|
Thread.Sleep(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -75,8 +105,25 @@ namespace SockScape {
|
||||||
ListeningThread = null;
|
ListeningThread = null;
|
||||||
Sock.Dispose();
|
Sock.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool IsProspectConnected(string client) {
|
||||||
|
return Prospects.ContainsKey(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsClientConnected(string client) {
|
||||||
|
return Clients.ContainsKey(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Packet PositiveAck(byte id) {
|
||||||
|
return new Packet((int)kIntraMasterAckId.PositiveAck, new { id });
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Packet NegativeAck(byte id, string message) {
|
||||||
|
return new Packet((int)kIntraMasterAckId.NegativeAck, new { id, message });
|
||||||
|
}
|
||||||
|
|
||||||
class Client {
|
class Client {
|
||||||
|
public IPEndPoint Address { get; set; }
|
||||||
public DateTime LastReceive { get; set; }
|
public DateTime LastReceive { get; set; }
|
||||||
public Cipher Encryptor { get; set; }
|
public Cipher Encryptor { get; set; }
|
||||||
public Key Key { get; set; }
|
public Key Key { get; set; }
|
||||||
|
|
|
@ -7,6 +7,12 @@ using System.Threading.Tasks;
|
||||||
namespace SockScape {
|
namespace SockScape {
|
||||||
public enum kIntraMasterId {
|
public enum kIntraMasterId {
|
||||||
InitiationAttempt = 0,
|
InitiationAttempt = 0,
|
||||||
KeyExchange
|
KeyExchange,
|
||||||
|
StatusUpdate
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum kIntraMasterAckId {
|
||||||
|
PositiveAck = 1,
|
||||||
|
NegativeAck
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,8 @@ using Glove;
|
||||||
|
|
||||||
namespace SockScape {
|
namespace SockScape {
|
||||||
class Packet {
|
class Packet {
|
||||||
private static readonly byte[] MagicNumber = { 0xF0, 0x9F, 0xA6, 0x91 };
|
// (squid)
|
||||||
|
public static readonly byte[] MagicNumber = { 0xF0, 0x9F, 0xA6, 0x91 };
|
||||||
|
|
||||||
public static Packet FromBytes(byte[] raw) {
|
public static Packet FromBytes(byte[] raw) {
|
||||||
if(raw.Length < 7)
|
if(raw.Length < 7)
|
||||||
|
|
Loading…
Reference in a new issue