diff --git a/Bridge/Bridge.csproj b/Bridge/Bridge.csproj
index 1ccf748..63f1953 100644
--- a/Bridge/Bridge.csproj
+++ b/Bridge/Bridge.csproj
@@ -71,7 +71,12 @@
+
+
+
+
+
Form
@@ -112,7 +117,7 @@
-
+
FormChat.cs
diff --git a/Bridge/BridgeCore.cs b/Bridge/BridgeCore.cs
new file mode 100644
index 0000000..61f917b
--- /dev/null
+++ b/Bridge/BridgeCore.cs
@@ -0,0 +1,570 @@
+using System;
+using System.IO;
+using System.Net;
+using System.Net.Sockets;
+using System.Windows.Forms;
+using System.Threading;
+using System.Drawing;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net.NetworkInformation;
+
+using Resources;
+using Resources.Packet;
+using Resources.Datagram;
+
+namespace Bridge {
+ public static partial class BridgeCore {
+ public static UdpClient udpToServer;
+ public static TcpClient tcpToServer, tcpToClient;
+ public static TcpListener tcpListener = new TcpListener(IPAddress.Parse("127.0.0.1"), 12345); //hardcoded because client port can't be changed
+ public static BinaryWriter swriter, cwriter;
+ public static BinaryReader sreader, creader;
+ public static ushort guid;
+ public static int mapseed;
+ public static FormMain form;
+ public static Dictionary dynamicEntities = new Dictionary();
+ public static Mutex outgoingMutex = new Mutex();
+ public static BridgeStatus status = BridgeStatus.Offline;
+
+ public static void Connect() {
+ form.Log("connecting...", Color.DarkGray);
+ string serverIP = Config.serverIP;
+ int serverPort = Config.serverPort;
+
+ try {
+ tcpToServer = new TcpClient();
+ tcpToServer.Connect(serverIP, serverPort);
+
+ udpToServer = new UdpClient(tcpToServer.Client.LocalEndPoint as IPEndPoint);
+ udpToServer.Connect(serverIP, serverPort);
+ SendUDP(new byte[1] { (byte)DatagramID.HolePunch });
+ }
+ catch (SocketException) {//connection refused
+ Disconnect();
+ form.Log("failed\n", Color.Red);
+ var result = MessageBox.Show("Unable to connect to Exceed. Please try again later.", "Connection Error", MessageBoxButtons.RetryCancel);
+ if (result == DialogResult.Retry) Connect();
+ return;
+ }
+ Stream stream = tcpToServer.GetStream();
+ swriter = new BinaryWriter(stream);
+ sreader = new BinaryReader(stream);
+ form.Log("connected\n", Color.Green);
+ new Thread(new ThreadStart(ListenFromServerTCP)).Start();
+ status = BridgeStatus.Connected;
+
+ form.Log("checking version...", Color.DarkGray);
+ swriter.Write((byte)0);//packetID
+ swriter.Write(Config.bridgeVersion);
+ }
+ public static void Disconnect() {
+ status = BridgeStatus.Offline;
+ form.Invoke(new Action(() => form.listBoxPlayers.Items.Clear()));
+ form.Invoke(new Action(() => form.OnLogout()));
+ LingerOption lingerOption = new LingerOption(true, 0);
+ try {
+ udpToServer.Close();
+ udpToServer = null;
+ }
+ catch { }
+ try {
+ tcpToServer.LingerState = lingerOption;
+ tcpToServer.Client.Close();
+ tcpToServer.Close();
+ udpToServer = null;
+ }
+ catch { }
+ try {
+ tcpToClient.LingerState = lingerOption;
+ tcpToClient.Client.Close();
+ tcpToClient.Close();
+ udpToServer = null;
+ }
+ catch { }
+ dynamicEntities.Clear();
+ }
+
+ public static void Login(string name, string password) {
+ form.Log("logging in...", Color.DarkGray);
+ swriter.Write((byte)ServerPacketID.Login);
+ swriter.Write(name);
+ swriter.Write(Hashing.Hash(password));
+ swriter.Write(NetworkInterface.GetAllNetworkInterfaces().Where(nic => nic.OperationalStatus == OperationalStatus.Up).Select(nic => nic.GetPhysicalAddress().ToString()).FirstOrDefault());
+ }
+ public static void Logout() {
+ swriter.Write((byte)ServerPacketID.Logout);
+ if (status == BridgeStatus.Playing) tcpToClient.Close();
+ status = BridgeStatus.Connected;
+ }
+ public static void Register(string name, string email, string password) {
+ swriter.Write((byte)ServerPacketID.Register);
+ swriter.Write(name);
+ swriter.Write(email);
+ swriter.Write(Hashing.Hash(password));
+ }
+
+ public static void ListenFromClientTCP() {
+ try {
+ tcpListener.Start();
+ }
+ catch (SocketException ex) {
+ var result = MessageBox.Show(ex.Message + "\n\nCan't start listening for the client, probably because the CubeWorld default port (12345) is already in use by another program. Do you have a CubeWorld server or another instance of the bridge already running on your computer?\n\nIf you don't know how to fix this, restarting your computer will likely help", "Error", MessageBoxButtons.RetryCancel);
+ if (result == DialogResult.Retry) ListenFromClientTCP();
+ return;
+ }
+ while (true) {
+ tcpToClient = tcpListener.AcceptTcpClient();
+ ClientConnected?.Invoke();
+ tcpToClient.ReceiveTimeout = 500;
+ tcpToClient.SendTimeout = 500;
+ tcpToClient.NoDelay = true;
+ var stream = tcpToClient.GetStream();
+ creader = new BinaryReader(stream);
+ cwriter = new BinaryWriter(stream);
+
+ if (status != BridgeStatus.LoggedIn) {
+ SendToClient(new ProtocolVersion() {
+ version = 42069,
+ });
+ form.Log("client rejected\n", Color.Red);
+ ClientDisconnected?.Invoke();
+ continue;
+ }
+ status = BridgeStatus.Playing;
+ try {
+ while (true) ProcessClientPacket(creader.ReadInt32());
+ }
+ catch (ObjectDisposedException) { }
+ catch (IOException) { }
+ tcpToClient.Close();
+ switch (status) {
+ case BridgeStatus.Offline://server crashed
+ break;
+ case BridgeStatus.Connected://player logged out
+ break;
+ case BridgeStatus.LoggedIn://kicked
+ goto default;
+ case BridgeStatus.Playing: //client disconnected himself
+ status = BridgeStatus.LoggedIn;
+ SendUDP(new RemoveDynamicEntity() { Guid = guid }.data);
+ break;
+ default:
+ //this shouldnt happen
+ break;
+ }
+ dynamicEntities.Remove(guid);
+ ClientDisconnected?.Invoke();
+ }
+ }
+ private static void ListenFromServerTCP() {
+ try {
+ while (true) ProcessServerPacket(sreader.ReadByte()); //we can use byte here because it doesn't contain vanilla packets
+ }
+ catch (IOException) {
+ form.Log("Connection to Server lost\n", Color.Red);
+ Disconnect();
+ Connect();
+ }
+ }
+ private static void ListenFromServerUDP() {
+ while (true) {
+ IPEndPoint source = null;
+ byte[] datagram = null;
+ try {
+ datagram = udpToServer.Receive(ref source);
+ }
+ catch (SocketException) {//when UDPclient is closed
+ return;
+ }
+ ProcessDatagram(datagram);
+ }
+ }
+
+ private static void ProcessDatagram(byte[] datagram) {
+ var serverUpdate = new ServerUpdate();
+ bool writeServerUpdate = false;
+ switch ((DatagramID)datagram[0]) {
+ case DatagramID.DynamicUpdate:
+ #region entityUpdate
+ var entityUpdate = new EntityUpdate(datagram);
+ EntityUpdateReceived?.Invoke(entityUpdate);
+ if (status == BridgeStatus.Playing) {
+ SendToClient(entityUpdate);
+ }
+ if (dynamicEntities.ContainsKey(entityUpdate.guid)) {
+ entityUpdate.Merge(dynamicEntities[entityUpdate.guid]);
+ }
+ else {
+ dynamicEntities.Add(entityUpdate.guid, entityUpdate);
+ }
+ break;
+ #endregion
+ case DatagramID.Attack:
+ #region attack
+ var attack = new Attack(datagram);
+ AttackReceived?.Invoke(attack);
+ var hit = new Hit() {
+ target = attack.Target,
+ damage = attack.Damage,
+ direction = attack.Direction,
+ critical = attack.Critical,
+ stuntime = attack.Stuntime,
+ position = dynamicEntities[attack.Target].position,
+ isYellow = attack.Skill,
+ type = attack.Type,
+ showlight = attack.ShowLight,
+ };
+ serverUpdate.hits.Add(hit);
+ writeServerUpdate = true;
+ break;
+ #endregion
+ case DatagramID.Projectile:
+ #region Projectile
+ var projectile = new Projectile(datagram);
+ ProjectileReceived?.Invoke(projectile);
+ var shoot = new Shoot() {
+ attacker = projectile.Source,
+ position = projectile.Position,
+ velocity = projectile.Velocity,
+ scale = projectile.Scale,
+ particles = projectile.Particles,
+ mana = projectile.Particles,
+ projectile = projectile.Type,
+ chunkX = (int)projectile.Position.x / 0x1000000,
+ chunkY = (int)projectile.Position.y / 0x1000000
+ };
+ var angle = Math.Atan2(shoot.velocity.y, shoot.velocity.x);
+ shoot.position.x += (long)(Math.Cos(angle) * 0x10000);
+ shoot.position.y += (long)(Math.Sin(angle) * 0x10000);
+
+ serverUpdate.shoots.Add(shoot);
+ writeServerUpdate = true;
+ break;
+ #endregion
+ case DatagramID.Proc:
+ #region proc
+ var proc = new Proc(datagram);
+ PassiveProcReceived?.Invoke(proc);
+ var passiveProc = new PassiveProc() {
+ target = proc.Target,
+ type = proc.Type,
+ modifier = proc.Modifier,
+ duration = proc.Duration
+ };
+ serverUpdate.passiveProcs.Add(passiveProc);
+ writeServerUpdate = true;
+ break;
+ #endregion
+ case DatagramID.Chat:
+ #region chat
+ var chat = new Chat(datagram);
+ ChatMessageReceived?.Invoke(chat);
+ if (status == BridgeStatus.Playing) {
+ var chatMessage = new ChatMessage() {
+ sender = chat.Sender,
+ message = chat.Text
+ };
+ SendToClient(chatMessage);
+ }
+ break;
+ #endregion
+ case DatagramID.Time:
+ #region time
+ var inGameTime = new InGameTime(datagram);
+ InGameTimeReceived?.Invoke(inGameTime);
+ if (status == BridgeStatus.Playing) {
+ var time = new Time() {
+ time = inGameTime.Milliseconds
+ };
+ SendToClient(time);
+ }
+ break;
+ #endregion
+ case DatagramID.Interaction:
+ #region interaction
+ var interaction = new Interaction(datagram);
+ InteractionReceived?.Invoke(interaction);
+ var entityAction = new EntityAction() {
+ chunkX = interaction.ChunkX,
+ chunkY = interaction.ChunkY,
+ index = interaction.Index,
+ type = ActionType.StaticInteraction
+ };
+ writeServerUpdate = true;
+ break;
+ #endregion
+ case DatagramID.StaticUpdate:
+ #region staticUpdate
+ var staticUpdate = new StaticUpdate(datagram);
+ StaticUpdateReceived?.Invoke(staticUpdate);
+ var staticEntity = new ServerUpdate.StaticEntity() {
+ chunkX = (int)(staticUpdate.Position.x / (65536 * 256)),
+ chunkY = (int)(staticUpdate.Position.y / (65536 * 256)),
+ id = staticUpdate.Id,
+ type = staticUpdate.Type,
+ position = staticUpdate.Position,
+ rotation = (int)staticUpdate.Direction,
+ size = staticUpdate.Size,
+ closed = staticUpdate.Closed,
+ time = staticUpdate.Time,
+ guid = staticUpdate.User
+ };
+ serverUpdate.statics.Add(staticEntity);
+ writeServerUpdate = true;
+ break;
+ #endregion
+ case DatagramID.Block:
+ //var block = new Block(datagram);
+ //TODO
+ break;
+ case DatagramID.Particle:
+ #region particle
+ var particleDatagram = new Particle(datagram);
+ ParticleReceived?.Invoke(particleDatagram);
+ var particleSubPacket = new ServerUpdate.Particle() {
+ position = particleDatagram.Position,
+ velocity = particleDatagram.Velocity,
+ color = new Resources.Utilities.FloatVector() {
+ x = particleDatagram.Color.R / 255,
+ y = particleDatagram.Color.G / 255,
+ z = particleDatagram.Color.B / 255
+ },
+ alpha = particleDatagram.Color.A / 255,
+ size = particleDatagram.Size,
+ count = particleDatagram.Count,
+ type = particleDatagram.Type,
+ spread = particleDatagram.Spread
+ };
+ serverUpdate.particles.Add(particleSubPacket);
+ writeServerUpdate = true;
+ break;
+ #endregion
+ case DatagramID.RemoveDynamicEntity:
+ #region RemoveDynamicEntity
+ var rde = new RemoveDynamicEntity(datagram);
+ DynamicEntityRemoved?.Invoke(rde);
+ entityUpdate = new EntityUpdate() {
+ guid = rde.Guid,
+ hostility = (Hostility)255, //workaround for DC because i dont like packet2
+ HP = 0
+ };
+ if (status == BridgeStatus.Playing) {
+ SendToClient(entityUpdate);
+ }
+ dynamicEntities.Remove(rde.Guid);
+ break;
+ #endregion
+ case DatagramID.SpecialMove:
+ #region speicalMove
+ var specialMove = new SpecialMove(datagram);
+ SpecialMoveReceived?.Invoke(specialMove);
+ break;
+ #endregion
+ default:
+ form.Log("unknown datagram ID: " + datagram[0], Color.Red);
+ break;
+ }
+ if (status == BridgeStatus.Playing && writeServerUpdate) SendToClient(serverUpdate);
+ }
+ private static void ProcessClientPacket(int packetID) {
+ switch ((PacketID)packetID) {
+ case PacketID.EntityUpdate:
+ #region entityUpdate
+ var entityUpdate = new EntityUpdate(creader);
+ EntityUpdateSent?.Invoke(entityUpdate);
+ if (dynamicEntities.ContainsKey(entityUpdate.guid)) {
+ entityUpdate.Filter(dynamicEntities[entityUpdate.guid]);
+ entityUpdate.Merge(dynamicEntities[entityUpdate.guid]);
+ }
+ else {
+ dynamicEntities.Add(entityUpdate.guid, entityUpdate);
+ }
+ if (!entityUpdate.IsEmpty) {
+ SendUDP(entityUpdate.CreateDatagram());
+ }
+ break;
+ #endregion
+ case PacketID.EntityAction:
+ #region entity action
+ var entityAction = new EntityAction(creader);
+ EntityActionSent?.Invoke(entityAction);
+ break;
+ #endregion
+ case PacketID.Hit:
+ #region hit
+ var hit = new Hit(creader);
+ HitSent?.Invoke(hit);
+ var attack = new Attack() {
+ Target = (ushort)hit.target,
+ Damage = hit.damage,
+ Direction = hit.direction,
+ Stuntime = hit.stuntime,
+ Skill = hit.isYellow,
+ Type = hit.type,
+ ShowLight = hit.showlight,
+ Critical = hit.critical
+ };
+ SendUDP(attack.data);
+ break;
+ #endregion
+ case PacketID.PassiveProc:
+ #region passiveProc
+ var passiveProc = new PassiveProc(creader);
+ PassiveProcSent?.Invoke(passiveProc);
+ var proc = new Proc() {
+ Target = (ushort)passiveProc.target,
+ Type = passiveProc.type,
+ Modifier = passiveProc.modifier,
+ Duration = passiveProc.duration
+ };
+ SendUDP(proc.data);
+ break;
+ #endregion
+ case PacketID.Shoot:
+ #region shoot
+ var shoot = new Shoot(creader);
+ ShotSent?.Invoke(shoot);
+ var projectile = new Projectile() {
+ Position = shoot.position,
+ Velocity = shoot.velocity,
+ Scale = shoot.scale,
+ Particles = shoot.particles,
+ Type = shoot.projectile,
+ Source = (ushort)shoot.attacker,
+ };
+ SendUDP(projectile.data);
+ break;
+ #endregion
+ case PacketID.Chat:
+ #region chat
+ var chatMessage = new ChatMessage(creader);
+ ChatMessageSent?.Invoke(chatMessage);
+ var chat = new Chat() {
+ Sender = guid,//client doesn't send this
+ Text = chatMessage.message
+ };
+ SendUDP(chat.data);
+ break;
+ #endregion
+ case PacketID.Chunk:
+ #region chunk
+ var chunk = new Chunk(creader);
+ ChunkDiscovered?.Invoke(chunk);
+ break;
+ #endregion
+ case PacketID.Sector:
+ #region sector
+ var sector = new Sector(creader);
+ SectorDiscovered?.Invoke(sector);
+ break;
+ #endregion
+ case PacketID.Version:
+ #region version
+ var version = new ProtocolVersion(creader);
+ VersionSent?.Invoke(version);
+ if (version.version != 3) {
+ version.version = 3;
+ SendToClient(version);
+ }
+ else {
+ SendToClient(new Join() {
+ guid = guid,
+ junk = new byte[0x1168]
+ });
+ SendToClient(new MapSeed() {
+ seed = mapseed
+ });
+ foreach (var dynamicEntity in dynamicEntities.Values.ToList()) {
+ SendToClient(dynamicEntity);
+ }
+ }
+ break;
+ #endregion
+ default:
+ form.Log("unknown client packet\n", Color.Magenta);
+ break;
+ }
+ }
+ private static void ProcessServerPacket(int packetID) {
+ switch ((ServerPacketID)packetID) {
+ case ServerPacketID.VersionCheck:
+ #region VersionCheck
+ if (!sreader.ReadBoolean()) {
+ form.Log("mismatch\n", Color.Red);
+ var b = MessageBox.Show("your bridge is outdated. Please download the newest version.\n\nGo to download page now?", "version mismatch", MessageBoxButtons.YesNo);
+ if (b == DialogResult.Yes) {
+ System.Diagnostics.Process.Start("https://github.com/LastExceed/Exceed/releases");
+ }
+ return;
+ }
+ form.Log("match\n", Color.Green);
+ form.Invoke(new Action(() => form.buttonLoginRegister.Enabled = true));
+ new Thread(new ThreadStart(ListenFromServerUDP)).Start();
+ break;
+ #endregion
+ case ServerPacketID.Login:
+ #region Login
+ var authResponse = (AuthResponse)sreader.ReadByte();
+ if (authResponse == AuthResponse.Success) {
+ status = BridgeStatus.LoggedIn;
+ guid = sreader.ReadUInt16();
+ mapseed = sreader.ReadInt32();
+ }
+ form.Invoke(new Action(() => form.register.OnLoginResponse(authResponse)));
+ break;
+ #endregion
+ case ServerPacketID.Register:
+ #region Register
+ switch ((RegisterResponse)sreader.ReadByte()) {
+ case RegisterResponse.Success:
+ form.Log("account registered\n", Color.DarkGray);
+ form.Invoke(new Action(() => form.register.SetLayout(false)));
+ break;
+ case RegisterResponse.UsernameTaken:
+ MessageBox.Show("this username is already in use");
+ form.Invoke(new Action(() => form.register.buttonRegister.Enabled = true));
+ break;
+ case RegisterResponse.EmailTaken:
+ MessageBox.Show("there is already an account associated to this email");
+ form.Invoke(new Action(() => form.register.buttonRegister.Enabled = true));
+ break;
+ }
+ break;
+ #endregion
+ case ServerPacketID.Kick:
+ #region Kick
+ status = BridgeStatus.LoggedIn;
+ tcpToClient.Close();
+ break;
+ #endregion
+ case ServerPacketID.BTFO:
+ #region BTFO
+ status = BridgeStatus.Connected;
+ form.Invoke(new Action(() => form.OnLogout()));
+ CwRam.memory.process.Kill();
+ var reason = sreader.ReadString();
+ MessageBox.Show(reason);
+ break;
+ #endregion
+ default:
+ MessageBox.Show("unknown server packet received");
+ break;
+ }
+ }
+
+ public static void SendUDP(byte[] data) {
+ udpToServer.Send(data, data.Length);
+ }
+ public static void SendToClient(Packet packet) {
+ outgoingMutex.WaitOne();
+ try {
+ packet.Write(cwriter);
+ }
+ catch (IOException) {
+ //handled in reading thread
+ }
+ outgoingMutex.ReleaseMutex();
+ }
+ }
+}
diff --git a/Bridge/BridgeCore_Events.cs b/Bridge/BridgeCore_Events.cs
new file mode 100644
index 0000000..b4c452a
--- /dev/null
+++ b/Bridge/BridgeCore_Events.cs
@@ -0,0 +1,57 @@
+using Resources.Datagram;
+using Resources.Packet;
+using System;
+
+namespace Bridge {
+ public delegate void EntityUpdateSentEventHandler(EntityUpdate entityUpdate);
+ public delegate void EntityActionSentEventHandler(EntityAction entityAction);
+ public delegate void HitSentHandler(Hit hit);
+ public delegate void PassiveProcSentEventHandler(PassiveProc passiveProc);
+ public delegate void ShotSentHandler(Shoot shoot);
+ public delegate void ChatMessageSentEventHandler(ChatMessage chatMessage);
+ public delegate void ChunkDiscoveredEventHandler(Chunk chunk);
+ public delegate void SectorDiscoveredEventHandler(Sector sector);
+ public delegate void VersionSentEventHandler(ProtocolVersion protocolVersion);
+
+ public delegate void EntityUpdateReceivedEventHandler(EntityUpdate entityUpdate);
+ public delegate void AttackReceivedEventHandler(Attack attack);
+ public delegate void ProjectileReceivedEventHandler(Projectile projectile);
+ public delegate void PassiveProcReceivedEventHandler(Proc proc);
+ public delegate void ChatMessageReceivedEventHandler(Chat chat);
+ public delegate void InGameTimeReceivedEventHandler(InGameTime inGameTime);
+ public delegate void InteractionReceivedEventHandler(Interaction interaction);
+ public delegate void StaticUpdateReceivedEventHandler(StaticUpdate staticUpdate);
+ public delegate void ParticleReceivedEventHandler(Particle particle);
+ public delegate void DynamicEntityRemovedEventHandler(RemoveDynamicEntity removeDynamicEntity);
+ public delegate void SpecialMoveReceiveddEventHandler(SpecialMove specialMove);
+
+ public delegate void ClientConnectedEventHandler();
+ public delegate void ClientDisconnectedEventHandler();
+
+ public static partial class BridgeCore {
+ public static event EntityUpdateSentEventHandler EntityUpdateSent;
+ public static event EntityActionSentEventHandler EntityActionSent;
+ public static event HitSentHandler HitSent;
+ public static event PassiveProcSentEventHandler PassiveProcSent;
+ public static event ShotSentHandler ShotSent;
+ public static event ChatMessageSentEventHandler ChatMessageSent;
+ public static event ChunkDiscoveredEventHandler ChunkDiscovered;
+ public static event SectorDiscoveredEventHandler SectorDiscovered;
+ public static event VersionSentEventHandler VersionSent;
+
+ public static event EntityUpdateReceivedEventHandler EntityUpdateReceived;
+ public static event AttackReceivedEventHandler AttackReceived;
+ public static event ProjectileReceivedEventHandler ProjectileReceived;
+ public static event PassiveProcReceivedEventHandler PassiveProcReceived;
+ public static event ChatMessageReceivedEventHandler ChatMessageReceived;
+ public static event InGameTimeReceivedEventHandler InGameTimeReceived;
+ public static event InteractionReceivedEventHandler InteractionReceived;
+ public static event StaticUpdateReceivedEventHandler StaticUpdateReceived;
+ public static event ParticleReceivedEventHandler ParticleReceived;
+ public static event DynamicEntityRemovedEventHandler DynamicEntityRemoved;
+ public static event SpecialMoveReceiveddEventHandler SpecialMoveReceived;
+
+ public static event ClientConnectedEventHandler ClientConnected;
+ public static event ClientDisconnectedEventHandler ClientDisconnected;
+ }
+}
diff --git a/Bridge/BridgeTCPUDP.cs b/Bridge/BridgeTCPUDP.cs
deleted file mode 100644
index f6ff3a1..0000000
--- a/Bridge/BridgeTCPUDP.cs
+++ /dev/null
@@ -1,1033 +0,0 @@
-using System;
-using System.IO;
-using System.Net;
-using System.Net.Sockets;
-using System.Windows.Forms;
-using System.Threading;
-using System.Drawing;
-using System.Collections.Generic;
-using System.Linq;
-using System.Net.NetworkInformation;
-
-using Resources;
-using Resources.Utilities;
-using Resources.Packet;
-using Resources.Datagram;
-
-namespace Bridge {
- static class BridgeTCPUDP {
- public static UdpClient udpToServer;
- public static TcpClient tcpToServer, tcpToClient;
- public static TcpListener tcpListener = new TcpListener(IPAddress.Parse("127.0.0.1"), 12345); //hardcoded because client port can't be changed
- public static BinaryWriter swriter, cwriter;
- public static BinaryReader sreader, creader;
- public static ushort guid;
- public static int mapseed;
- public static FormMain form;
- public static Dictionary dynamicEntities = new Dictionary();
- public static ushort lastTarget;
- public static Mutex outgoingMutex = new Mutex();
- public static BridgeStatus status = BridgeStatus.Offline;
-
- public static void Connect() {
- form.Log("connecting...", Color.DarkGray);
- string serverIP = Config.serverIP;
- int serverPort = Config.serverPort;
-
- try {
- tcpToServer = new TcpClient();
- tcpToServer.Connect(serverIP, serverPort);
-
- udpToServer = new UdpClient(tcpToServer.Client.LocalEndPoint as IPEndPoint);
- udpToServer.Connect(serverIP, serverPort);
- SendUDP(new byte[1] { (byte)DatagramID.HolePunch });
- }
- catch (SocketException) {//connection refused
- Disconnect();
- form.Log("failed\n", Color.Red);
- var result = MessageBox.Show("Unable to connect to Exceed. Please try again later.", "Connection Error", MessageBoxButtons.RetryCancel);
- if (result == DialogResult.Retry) Connect();
- return;
- }
- Stream stream = tcpToServer.GetStream();
- swriter = new BinaryWriter(stream);
- sreader = new BinaryReader(stream);
- form.Log("connected\n", Color.Green);
- new Thread(new ThreadStart(ListenFromServerTCP)).Start();
- status = BridgeStatus.Connected;
-
- form.Log("checking version...", Color.DarkGray);
- swriter.Write((byte)0);//packetID
- swriter.Write(Config.bridgeVersion);
- }
- public static void Disconnect() {
- status = BridgeStatus.Offline;
- form.Invoke(new Action(() => form.listBoxPlayers.Items.Clear()));
- form.Invoke(new Action(() => form.OnLogout()));
- LingerOption lingerOption = new LingerOption(true, 0);
- try {
- udpToServer.Close();
- udpToServer = null;
- }
- catch { }
- try {
- tcpToServer.LingerState = lingerOption;
- tcpToServer.Client.Close();
- tcpToServer.Close();
- udpToServer = null;
- }
- catch { }
- try {
- tcpToClient.LingerState = lingerOption;
- tcpToClient.Client.Close();
- tcpToClient.Close();
- udpToServer = null;
- }
- catch { }
- dynamicEntities.Clear();
- }
-
- public static void Login(string name, string password) {
- form.Log("logging in...", Color.DarkGray);
- swriter.Write((byte)ServerPacketID.Login);
- swriter.Write(name);
- swriter.Write(Hashing.Hash(password));
- swriter.Write(NetworkInterface.GetAllNetworkInterfaces().Where(nic => nic.OperationalStatus == OperationalStatus.Up).Select(nic => nic.GetPhysicalAddress().ToString()).FirstOrDefault());
- }
- public static void Logout() {
- swriter.Write((byte)ServerPacketID.Logout);
- if (status == BridgeStatus.Playing) tcpToClient.Close();
- status = BridgeStatus.Connected;
- }
- public static void Register(string name, string email, string password) {
- swriter.Write((byte)ServerPacketID.Register);
- swriter.Write(name);
- swriter.Write(email);
- swriter.Write(Hashing.Hash(password));
- }
-
- public static void ListenFromClientTCP() {
- try {
- tcpListener.Start();
- }
- catch (SocketException ex) {
- var result = MessageBox.Show(ex.Message + "\n\nCan't start listening for the client, probably because the CubeWorld default port (12345) is already in use by another program. Do you have a CubeWorld server or another instance of the bridge already running on your computer?\n\nIf you don't know how to fix this, restarting your computer will likely help", "Error", MessageBoxButtons.RetryCancel);
- if (result == DialogResult.Retry) ListenFromClientTCP();
- return;
- }
- while (true) {
- tcpToClient = tcpListener.AcceptTcpClient();
- form.Log("client connected\n", Color.Green);
- tcpToClient.ReceiveTimeout = 500;
- tcpToClient.SendTimeout = 500;
-
- tcpToClient.NoDelay = true;
- Stream stream = tcpToClient.GetStream();
- creader = new BinaryReader(stream);
- cwriter = new BinaryWriter(stream);
-
- if (status != BridgeStatus.LoggedIn) {
- SendToClient(new ProtocolVersion() {
- version = 42069,
- });
- form.Log("client rejected\n", Color.Red);
- continue;
- }
- status = BridgeStatus.Playing;
- try {
- while (true) ProcessClientPacket(creader.ReadInt32());
- }
- catch (ObjectDisposedException) { }
- catch (IOException) { }
- tcpToClient.Close();
- switch (status) {
- case BridgeStatus.Offline://server crashed
- break;
- case BridgeStatus.Connected://player logged out
- break;
- case BridgeStatus.LoggedIn://kicked
- goto default;
- case BridgeStatus.Playing: //client disconnected himself
- status = BridgeStatus.LoggedIn;
- SendUDP(new RemoveDynamicEntity() { Guid = guid }.data);
- break;
- default:
- //this shouldnt happen
- break;
- }
- dynamicEntities.Remove(guid);
- form.Log("client disconnected\n", Color.Red);
- RefreshPlayerlist();
- }
- }
- private static void ListenFromServerTCP() {
- try {
- while (true) ProcessServerPacket(sreader.ReadByte()); //we can use byte here because it doesn't contain vanilla packets
- }
- catch (IOException) {
- form.Log("Connection to Server lost\n", Color.Red);
- Disconnect();
- Connect();
- }
- }
- private static void ListenFromServerUDP() {
- while (true) {
- IPEndPoint source = null;
- byte[] datagram = null;
- try {
- datagram = udpToServer.Receive(ref source);
- }
- catch (SocketException) {//when UDPclient is closed
- return;
- }
- ProcessDatagram(datagram);
- }
- }
-
- private static void ProcessDatagram(byte[] datagram) {
- var serverUpdate = new ServerUpdate();
- bool writeServerUpdate = false;
- switch ((DatagramID)datagram[0]) {
- case DatagramID.DynamicUpdate:
- #region entityUpdate
- var entityUpdate = new EntityUpdate(datagram);
- if (status == BridgeStatus.Playing) {
- if (entityUpdate.guid == guid) {
- CwRam.Teleport(entityUpdate.position);
- break;
- }
- SendToClient(entityUpdate);
- }
-
- if (dynamicEntities.ContainsKey(entityUpdate.guid)) {
- entityUpdate.Merge(dynamicEntities[entityUpdate.guid]);
- }
- else {
- dynamicEntities.Add(entityUpdate.guid, entityUpdate);
- }
-
- if (entityUpdate.name != null) {
- RefreshPlayerlist();
- }
- break;
- #endregion
- case DatagramID.Attack:
- #region attack
- var attack = new Attack(datagram);
- CwRam.Knockback(attack.Direction);
- var hit = new Hit() {
- target = attack.Target,
- damage = attack.Damage,
- direction = attack.Direction,
- critical = attack.Critical,
- stuntime = attack.Stuntime,
- position = dynamicEntities[attack.Target].position,
- isYellow = attack.Skill,
- type = attack.Type,
- showlight = attack.ShowLight,
- };
- serverUpdate.hits.Add(hit);
- writeServerUpdate = true;
- break;
- #endregion
- case DatagramID.Projectile:
- #region Projectile
- var projectile = new Projectile(datagram);
-
- var shoot = new Shoot() {
- attacker = projectile.Source,
- position = projectile.Position,
- velocity = projectile.Velocity,
- scale = projectile.Scale,
- particles = projectile.Particles,
- mana = projectile.Particles,
- projectile = projectile.Type,
- chunkX = (int)projectile.Position.x / 0x1000000,
- chunkY = (int)projectile.Position.y / 0x1000000
- };
- var angle = Math.Atan2(shoot.velocity.y, shoot.velocity.x);
- shoot.position.x += (long)(Math.Cos(angle) * 0x10000);
- shoot.position.y += (long)(Math.Sin(angle) * 0x10000);
-
- serverUpdate.shoots.Add(shoot);
- writeServerUpdate = true;
- break;
- #endregion
- case DatagramID.Proc:
- #region proc
- var proc = new Proc(datagram);
- if (proc.Type == ProcType.Poison && proc.Target == guid) {
- var su = new ServerUpdate();
- su.hits.Add(new Hit() {
- damage = proc.Modifier,
- target = guid,
- position = dynamicEntities[guid].position,
- });
- bool tick() {
- bool f = status == BridgeStatus.Playing && dynamicEntities[guid].HP > 0;
- if (f) {
- SendToClient(su);
- }
- return !f;
- }
- Tools.DoLater(tick, 500, 7);
- }
- var passiveProc = new PassiveProc() {
- target = proc.Target,
- type = proc.Type,
- modifier = proc.Modifier,
- duration = proc.Duration
- };
- serverUpdate.passiveProcs.Add(passiveProc);
- writeServerUpdate = true;
- break;
- #endregion
- case DatagramID.Chat:
- #region chat
- var chat = new Chat(datagram);
- var chatMessage = new ChatMessage() {
- sender = chat.Sender,
- message = chat.Text
- };
- if (status == BridgeStatus.Playing) SendToClient(chatMessage);
- if (chat.Sender == 0) {
- form.Log(chat.Text + "\n", Color.Magenta);
- }
- else {
- form.Log(dynamicEntities[chat.Sender].name + ": ", Color.Cyan);
- form.Log(chat.Text + "\n", Color.White);
- }
- break;
- #endregion
- case DatagramID.Time:
- #region time
- var igt = new InGameTime(datagram);
-
- var time = new Time() {
- time = igt.Milliseconds
- };
- if (status == BridgeStatus.Playing) SendToClient(time);
- break;
- #endregion
- case DatagramID.Interaction:
- #region interaction
- var interaction = new Interaction(datagram);
- var entityAction = new EntityAction() {
- chunkX = interaction.ChunkX,
- chunkY = interaction.ChunkY,
- index = interaction.Index,
- type = ActionType.StaticInteraction
- };
- writeServerUpdate = true;
- break;
- #endregion
- case DatagramID.StaticUpdate:
- #region staticUpdate
- var staticUpdate = new StaticUpdate(datagram);
-
- var staticEntity = new ServerUpdate.StaticEntity() {
- chunkX = (int)(staticUpdate.Position.x / (65536 * 256)),
- chunkY = (int)(staticUpdate.Position.y / (65536 * 256)),
- id = staticUpdate.Id,
- type = staticUpdate.Type,
- position = staticUpdate.Position,
- rotation = (int)staticUpdate.Direction,
- size = staticUpdate.Size,
- closed = staticUpdate.Closed,
- time = staticUpdate.Time,
- guid = staticUpdate.User
- };
- serverUpdate.statics.Add(staticEntity);
- writeServerUpdate = true;
- break;
- #endregion
- case DatagramID.Block:
- //var block = new Block(datagram);
- //TODO
- break;
- case DatagramID.Particle:
- #region particle
- var particleDatagram = new Particle(datagram);
-
- var particleSubPacket = new ServerUpdate.Particle() {
- position = particleDatagram.Position,
- velocity = particleDatagram.Velocity,
- color = new Resources.Utilities.FloatVector() {
- x = particleDatagram.Color.R / 255,
- y = particleDatagram.Color.G / 255,
- z = particleDatagram.Color.B / 255
- },
- alpha = particleDatagram.Color.A / 255,
- size = particleDatagram.Size,
- count = particleDatagram.Count,
- type = particleDatagram.Type,
- spread = particleDatagram.Spread
- };
- serverUpdate.particles.Add(particleSubPacket);
- break;
- #endregion
- case DatagramID.RemoveDynamicEntity:
- #region RemoveDynamicEntity
- var rde = new RemoveDynamicEntity(datagram);
- entityUpdate = new EntityUpdate() {
- guid = rde.Guid,
- hostility = (Hostility)255, //workaround for DC because i dont like packet2
- HP = 0
- };
- if (status == BridgeStatus.Playing) SendToClient(entityUpdate);
- dynamicEntities.Remove(rde.Guid);
- RefreshPlayerlist();
- break;
- #endregion
- case DatagramID.SpecialMove:
- #region speicalMove
- var specialMove = new SpecialMove(datagram);
- switch (specialMove.Id) {
- case SpecialMoveID.Taunt:
- if (dynamicEntities.ContainsKey(specialMove.Guid)) {
- if (status == BridgeStatus.Playing) {
- CwRam.Teleport(dynamicEntities[specialMove.Guid].position);
- CwRam.Freeze(5000);
- }
- }
- break;
- case SpecialMoveID.CursedArrow:
- break;
- case SpecialMoveID.ArrowRain:
- break;
- case SpecialMoveID.Shrapnel:
- var su = new ServerUpdate();
- var blood_hit = new Hit() {
- damage = 5f,
- target = specialMove.Guid,
- };
- su.hits.Add(blood_hit);
- var blood_particles = new ServerUpdate.Particle() {
- count = 10,
- spread = 2f,
- type = ParticleType.Normal,
- size = 0.1f,
- velocity = new FloatVector() {
- z = 1f,
- },
- color = new FloatVector() {
- x = 1f,
- y = 0f,
- z = 0f
- },
- alpha = 1f,
- };
- su.particles.Add(blood_particles);
- bool tick() {
- bool f = status == BridgeStatus.Playing && dynamicEntities[specialMove.Guid].HP > 0;
- if (f) {
- blood_hit.position = blood_particles.position = dynamicEntities[specialMove.Guid].position;
- SendToClient(su);
- }
- return !f;
- }
- Tools.DoLater(tick, 50, 100);
- break;
- case SpecialMoveID.SmokeBomb:
- serverUpdate.particles.Add(new ServerUpdate.Particle() {
- count = 1000,
- spread = 5f,
- type = ParticleType.NoGravity,
- size = 5f,
- velocity = new Resources.Utilities.FloatVector(),
- color = new Resources.Utilities.FloatVector() {
- x = 1f,
- y = 1f,
- z = 1f
- },
- alpha = 1f,
- position = dynamicEntities[specialMove.Guid].position
- });
- writeServerUpdate = true;
- break;
- case SpecialMoveID.IceWave:
- if (specialMove.Guid != guid) {//distance small enough
- CwRam.Freeze(3000);
- }
- serverUpdate.particles.Add(new ServerUpdate.Particle() {
- count = 100,
- spread = 4f,
- type = ParticleType.NoGravity,
- size = 0.3f,
- velocity = new FloatVector(),
- color = new FloatVector() {
- x = 0f,
- y = 1f,
- z = 1f
- },
- alpha = 1f,
- position = dynamicEntities[specialMove.Guid].position
- });
- serverUpdate.particles.Add(new ServerUpdate.Particle() {
- count = 100,
- spread = 10f,
- type = ParticleType.NoGravity,
- size = 0.1f,
- velocity = new FloatVector(),
- color = new FloatVector() {
- x = 1f,
- y = 1f,
- z = 1f
- },
- alpha = 1f,
- position = dynamicEntities[specialMove.Guid].position
- });
- serverUpdate.particles.Add(new ServerUpdate.Particle() {
- count = 100,
- spread = 2f,
- type = ParticleType.NoGravity,
- size = 0.7f,
- velocity = new FloatVector(),
- color = new FloatVector() {
- x = 0f,
- y = 0f,
- z = 1f
- },
- alpha = 1f,
- position = dynamicEntities[specialMove.Guid].position
- });
- writeServerUpdate = true;
- break;
- case SpecialMoveID.Confusion:
- break;
- case SpecialMoveID.ShadowStep:
- break;
- default:
- break;
- }
- break;
- #endregion
- default:
- form.Log("unknown datagram ID: " + datagram[0], Color.Red);
- break;
- }
- if (status == BridgeStatus.Playing && writeServerUpdate) SendToClient(serverUpdate);
- }
- private static void ProcessClientPacket(int packetID) {
- switch ((PacketID)packetID) {
- case PacketID.EntityUpdate:
- #region entityUpdate
- var entityUpdate = new EntityUpdate(creader);
- if (dynamicEntities.ContainsKey(entityUpdate.guid)) {
- entityUpdate.Filter(dynamicEntities[entityUpdate.guid]);
- entityUpdate.Merge(dynamicEntities[entityUpdate.guid]);
- }
- else {
- dynamicEntities.Add(entityUpdate.guid, entityUpdate);
- }
- if (entityUpdate.name != null) {
- RefreshPlayerlist();
- }
- if (!entityUpdate.IsEmpty) {
- SendUDP(entityUpdate.CreateDatagram());
- }
- break;
- #endregion
- case PacketID.EntityAction:
- #region entity action
- EntityAction entityAction = new EntityAction(creader);
- switch (entityAction.type) {
- case ActionType.Talk:
- #region Talk
- break;
- #endregion
- case ActionType.StaticInteraction:
- #region StaticInteraction
- ChatMessage x = new ChatMessage() {
- message = "You can't use this, your hands are too small.",
- sender = 0
- };
- SendToClient(x);
- break;
- #endregion
- case ActionType.PickUp:
- #region PickUp
- break;
- #endregion
- case ActionType.Drop: //send item back to dropper because dropping is disabled to prevent chatspam
- #region Drop
- if (form.radioButtonDestroy.Checked) {
- SendToClient(new ChatMessage() {
- message = "item destroyed",
- sender = 0,
- });
- }
- else {
- var serverUpdate = new ServerUpdate();
- var pickup = new ServerUpdate.Pickup() {
- guid = guid,
- item = entityAction.item
- };
- serverUpdate.pickups.Add(pickup);
- if (form.radioButtonDuplicate.Checked) {
- serverUpdate.pickups.Add(pickup);
- }
- SendToClient(serverUpdate);
- }
- break;
- #endregion
- case ActionType.CallPet:
- #region CallPet
- break;
- #endregion
- default:
- //unknown type
- break;
- }
- break;
- #endregion
- case PacketID.Hit:
- #region hit
- var hit = new Hit(creader);
- var attack = new Attack() {
- Target = (ushort)hit.target,
- Damage = hit.damage,
- Direction = hit.direction,
- Stuntime = hit.stuntime,
- Skill = hit.isYellow,
- Type = hit.type,
- ShowLight = hit.showlight,
- Critical = hit.critical
- };
- SendUDP(attack.data);
- lastTarget = attack.Target;
- break;
- #endregion
- case PacketID.PassiveProc:
- #region passiveProc
- var passiveProc = new PassiveProc(creader);
- switch (passiveProc.type) {
- case ProcType.Bulwalk:
- SendToClient(new ChatMessage() {
- message = string.Format("bulwalk: {0}% dmg reduction", 1.0f - passiveProc.modifier),
- sender = 0,
- });
- break;
- case ProcType.WarFrenzy:
- CwRam.PlayerEntity.BossBuff = true;
- bool DisableBossBuff() {
- bool f = status == BridgeStatus.Playing && dynamicEntities[guid].HP > 0;
- if (f) {
- CwRam.PlayerEntity.BossBuff = false;
- }
- return !f;
- }
- Tools.DoLater(DisableBossBuff, passiveProc.duration, 1);
- break;
- case ProcType.Camouflage:
- break;
- case ProcType.Poison:
- break;
- case ProcType.UnknownA:
- break;
- case ProcType.ManaShield:
- SendToClient(new ChatMessage() {
- message = string.Format("manashield: {0}", passiveProc.modifier),
- sender = 0,
- });
- break;
- case ProcType.UnknownB:
- break;
- case ProcType.UnknownC:
- break;
- case ProcType.FireSpark:
- break;
- case ProcType.Intuition:
- break;
- case ProcType.Elusiveness:
- break;
- case ProcType.Swiftness:
- break;
- default:
- break;
- }
- var proc = new Proc() {
- Target = (ushort)passiveProc.target,
- Type = passiveProc.type,
- Modifier = passiveProc.modifier,
- Duration = passiveProc.duration
- };
- SendUDP(proc.data);
- break;
- #endregion
- case PacketID.Shoot:
- #region shoot
- var shoot = new Shoot(creader);
- var projectile = new Projectile() {
- Position = shoot.position,
- Velocity = shoot.velocity,
- Scale = shoot.scale,
- Particles = shoot.particles,
- Type = shoot.projectile,
- Source = (ushort)shoot.attacker,
- };
- SendUDP(projectile.data);
- break;
- #endregion
- case PacketID.Chat:
- #region chat
- var chatMessage = new ChatMessage(creader);
- if (chatMessage.message.ToLower() == @"/plane") {
- Console.Beep();
- var serverUpdate = new ServerUpdate() {
- blockDeltas = new Vox("model.vox").Parse(),
- };
- foreach (var block in serverUpdate.blockDeltas) {
- block.position.x += 0x802080;//(int)(dynamicEntities[guid].position.x / 0x10000);//8286946;
- block.position.y += 0x802080;//(int)(dynamicEntities[guid].position.y / 0x10000);//8344456;
- block.position.z += 150;// (int)(dynamicEntities[guid].position.z / 0x10000);//220;
- }
- SendToClient(serverUpdate);
- }
- if (chatMessage.message.ToLower() == @"/spawn") {
- CwRam.Teleport(new LongVector() {
- x = 0x8020800000,
- y = 0x8020800000,
- z = 0,
- });
- break;
- }
- var chat = new Chat() {
- Sender = guid,//client doesn't send this
- Text = chatMessage.message
- };
- SendUDP(chat.data);
- break;
- #endregion
- case PacketID.Chunk:
- #region chunk
- var chunk = new Chunk(creader);
- break;
- #endregion
- case PacketID.Sector:
- #region sector
- var sector = new Sector(creader);
- break;
- #endregion
- case PacketID.Version:
- #region version
- var version = new ProtocolVersion(creader);
- if (version.version != 3) {
- version.version = 3;
- SendToClient(version);
- }
- else {
- SendToClient(new Join() {
- guid = guid,
- junk = new byte[0x1168]
- });
- SendToClient(new MapSeed() {
- seed = mapseed
- });
- foreach (var dynamicEntity in dynamicEntities.Values.ToList()) {
- SendToClient(dynamicEntity);
- }
- }
- break;
- #endregion
- default:
- form.Log("unknown client packet\n", Color.Magenta);
- break;
- }
- }
- private static void ProcessServerPacket(int packetID) {
- switch ((ServerPacketID)packetID) {
- case ServerPacketID.VersionCheck:
- #region VersionCheck
- if (!sreader.ReadBoolean()) {
- form.Log("mismatch\n", Color.Red);
- var b = MessageBox.Show("your bridge is outdated. Please download the newest version.\n\nGo to download page now?", "version mismatch", MessageBoxButtons.YesNo);
- if (b == DialogResult.Yes) {
- System.Diagnostics.Process.Start("https://github.com/LastExceed/Exceed/releases");
- }
- return;
- }
- form.Log("match\n", Color.Green);
- form.Invoke(new Action(() => form.buttonLoginRegister.Enabled = true));
- new Thread(new ThreadStart(ListenFromServerUDP)).Start();
- break;
- #endregion
- case ServerPacketID.Login:
- #region Login
- var authResponse = (AuthResponse)sreader.ReadByte();
- if (authResponse == AuthResponse.Success) {
- status = BridgeStatus.LoggedIn;
- guid = sreader.ReadUInt16();
- mapseed = sreader.ReadInt32();
- }
- form.Invoke(new Action(() => form.register.OnLoginResponse(authResponse)));
- break;
- #endregion
- case ServerPacketID.Register:
- #region Register
- switch ((RegisterResponse)sreader.ReadByte()) {
- case RegisterResponse.Success:
- form.Log("account registered\n", Color.DarkGray);
- form.Invoke(new Action(() => form.register.SetLayout(false)));
- break;
- case RegisterResponse.UsernameTaken:
- MessageBox.Show("this username is already in use");
- form.Invoke(new Action(() => form.register.buttonRegister.Enabled = true));
- break;
- case RegisterResponse.EmailTaken:
- MessageBox.Show("there is already an account associated to this email");
- form.Invoke(new Action(() => form.register.buttonRegister.Enabled = true));
- break;
- }
- break;
- #endregion
- case ServerPacketID.Kick:
- #region Kick
- status = BridgeStatus.LoggedIn;
- tcpToClient.Close();
- break;
- #endregion
- case ServerPacketID.BTFO:
- #region BTFO
- status = BridgeStatus.Connected;
- form.Invoke(new Action(() => form.OnLogout()));
- CwRam.memory.process.Kill();
- var reason = sreader.ReadString();
- MessageBox.Show(reason);
- break;
- #endregion
- default:
- MessageBox.Show("unknown server packet received");
- break;
- }
- }
-
- public static void RefreshPlayerlist() {
- form.Invoke((Action)form.listBoxPlayers.Items.Clear);
- foreach (var dynamicEntity in dynamicEntities.Values.ToList()) {
- if (dynamicEntity.hostility == Hostility.Player) {
- form.Invoke(new Action(() => form.listBoxPlayers.Items.Add(dynamicEntity.name)));
- }
- }
- }
-
- public static void OnHotkey(HotkeyID hotkey) {
- if (CwRam.AnyInterfaceOpen) return;
- if (hotkey == HotkeyID.TeleportToTown) {
- CwRam.SetMode(Mode.Teleport_To_City, 0);
- return;
- }
- var notification = new ChatMessage() {
- sender = 0,
- };
- bool spec = dynamicEntities[guid].specialization == 1;
- switch (dynamicEntities[guid].entityClass) {
- case EntityClass.Rogue when spec:
- #region ninja
- if (hotkey == HotkeyID.CtrlSpace) {
- #region dash
- notification.message = "using [dash]";
- CwRam.SetMode(Mode.Spin_Run, 0);
- #endregion
- }
- else {
- #region blink
- notification.message = "using [blink]";
- if (dynamicEntities.ContainsKey(lastTarget)) {
- CwRam.Teleport(dynamicEntities[lastTarget].position);
- }
- #endregion
- }
- break;
- #endregion
- case EntityClass.Rogue:
- #region assassin
- if (hotkey == HotkeyID.CtrlSpace) {
- #region confusion
- notification.message = "TODO: [confusion]";
- var specialMove = new SpecialMove() {
- Guid = guid,
- Id = SpecialMoveID.Confusion,
- };
- SendUDP(specialMove.data);
- #endregion
- }
- else {
- #region shadow step
- notification.message = "TOD: [shadow step]";
- var specialMove = new SpecialMove() {
- Guid = guid,
- Id = SpecialMoveID.ShadowStep,
- };
- SendUDP(specialMove.data);
- #endregion
- }
- break;
- #endregion
- case EntityClass.Warrior when spec:
- #region guardian
- if (hotkey == HotkeyID.CtrlSpace) {
- #region taunt
- notification.message = "using [taunt]";
- var specialMove = new SpecialMove() {
- Guid = lastTarget,
- Id = SpecialMoveID.Taunt,
- };
- SendUDP(specialMove.data);
- #endregion
- }
- else {
- #region steel wall
- notification.message = "using [steel wall]";
- CwRam.SetMode(Mode.Boss_Skill_Block, 0);
- #endregion
- }
- break;
- #endregion
- case EntityClass.Warrior:
- #region berserk
- if (hotkey == HotkeyID.CtrlSpace) {
- #region boulder toss
- notification.message = "using [boulder toss]";
- CwRam.SetMode(Mode.Boulder_Toss, 0);
- #endregion
- }
- else {
- #region earth shatter
- notification.message = "using [earth shatter]";
- CwRam.SetMode(Mode.Earth_Shatter, 0);
- #endregion
- }
- break;
- #endregion
- case EntityClass.Mage when spec:
- #region watermage
- if (hotkey == HotkeyID.CtrlSpace) {
- #region splash
- notification.message = "using [splash]";
- CwRam.SetMode(Mode.Splash, 0);
- #endregion
- }
- else {
- #region ice wave
- notification.message = "using [ice wave]";
- var specialMove = new SpecialMove() {
- Guid = guid,
- Id = SpecialMoveID.IceWave,
- };
- SendUDP(specialMove.data);
- #endregion
- }
- break;
- #endregion
- case EntityClass.Mage:
- #region firemage
- if (hotkey == HotkeyID.CtrlSpace) {
- #region lava
- notification.message = "using [lava]";
- CwRam.SetMode(Mode.Lava, 0);
- #endregion
- }
- else {
- #region beam
- notification.message = "using [fire ray]";
- CwRam.SetMode(Mode.FireRay, 0);
- #endregion
- }
- break;
- #endregion
- case EntityClass.Ranger when spec:
- #region scout
- if (hotkey == HotkeyID.CtrlSpace) {
- #region shrapnel
- notification.message = "using [shrapnel] bleeding test";
- var specialMove = new SpecialMove() {
- Guid = guid,
- Id = SpecialMoveID.Shrapnel,
- };
- SendUDP(specialMove.data);
- #endregion
- }
- else {
- #region smoke bomb
- notification.message = "using [smoke bomb]";
- var specialMove = new SpecialMove() {
- Guid = guid,
- Id = SpecialMoveID.SmokeBomb,
- };
- SendUDP(specialMove.data);
-
- var fakeSmoke = new ServerUpdate();
- fakeSmoke.particles.Add(new ServerUpdate.Particle() {
- count = 1000,
- spread = 5f,
- type = ParticleType.NoGravity,
- size = 0.3f,
- velocity = new Resources.Utilities.FloatVector(),
- color = new Resources.Utilities.FloatVector() {
- x = 1f,
- y = 1f,
- z = 1f
- },
- alpha = 1f,
- position = dynamicEntities[specialMove.Guid].position
- });
- SendToClient(fakeSmoke);
- #endregion
- }
- break;
- #endregion
- case EntityClass.Ranger:
- #region sniper
- if (hotkey == HotkeyID.CtrlSpace) {
- #region cursed arrow
- //TODO
- notification.message = "TODO: [cursed arrow]";
- #endregion
- }
- else {
- #region arrow rain
- //TODO
- notification.message = "TODO: [arrow rain]";
- //const int rainSize = 7;
- //for (int x = 0; x < rainSize; x++) {
- // for (int y = 0; y < rainSize; y++) {
- // var projectile = new Projectile() {
- // Scale = 1f,
- // Type = ProjectileType.Arrow,
- // Source = guid,
- // Velocity = new FloatVector() { x = 0, y = 0, z = -1f },
- // Position = new LongVector() {
- // x = 0x8020800000,//dynamicEntities[guid].position.x + (long)((dynamicEntities[guid].rayHit.x + x - rainSize / 2) * 0x10000),
- // y = 0x8020800000,//dynamicEntities[guid].position.y + (long)((dynamicEntities[guid].rayHit.y + y - rainSize / 2) * 0x10000),
- // z = 0x01000000,//dynamicEntities[guid].position.z + (long)((dynamicEntities[guid].rayHit.z + 10) * 0x10000),
- // }
- // };
- // SendUDP(projectile.data);
- // ProcessDatagram(projectile.data);
- // }
- //}
- #endregion
- }
- break;
- #endregion
- }
- CwRam.memory.WriteInt(CwRam.EntityStart + 0x1164, 3);//mana cubes
- SendToClient(notification);
- }
-
- private static void SendUDP(byte[] data) {
- udpToServer.Send(data, data.Length);
- }
- private static void SendToClient(Packet packet) {
- outgoingMutex.WaitOne();
- try {
- packet.Write(cwriter);
- }
- catch (IOException) {
- //handled in reading thread
- }
- outgoingMutex.ReleaseMutex();
- }
- }
-}
diff --git a/Bridge/Extensions/ExtensionsCore.cs b/Bridge/Extensions/ExtensionsCore.cs
new file mode 100644
index 0000000..db7fda9
--- /dev/null
+++ b/Bridge/Extensions/ExtensionsCore.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Bridge.Extensions {
+ public static class ExtensionsCore {
+ public static void Init() {
+ Logging.Init();
+ NameSubjectToChange.Init();
+ SpecialMoves.Init();
+ }
+ }
+}
diff --git a/Bridge/Extensions/Logging.cs b/Bridge/Extensions/Logging.cs
new file mode 100644
index 0000000..ed91b3e
--- /dev/null
+++ b/Bridge/Extensions/Logging.cs
@@ -0,0 +1,57 @@
+using Resources;
+using Resources.Datagram;
+using Resources.Packet;
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Bridge.Extensions {
+ public static class Logging {
+ public static void Init() {
+ BridgeCore.ChatMessageReceived += ChatLog;
+ BridgeCore.ClientConnected += LogConnect;
+ BridgeCore.ClientDisconnected += LogDisconnect;
+
+ BridgeCore.EntityUpdateReceived += CheckForNewName;
+ BridgeCore.EntityUpdateSent += CheckForNewName;
+ BridgeCore.DynamicEntityRemoved += RemoveNameFromList;
+ BridgeCore.ClientDisconnected += RefreshPlayerlist;
+ }
+
+ private static void ChatLog(Chat chat) {
+ if (chat.Sender == 0) {
+ BridgeCore.form.Log(chat.Text + "\n", Color.Magenta);
+ }
+ else {
+ BridgeCore.form.Log(BridgeCore.dynamicEntities[chat.Sender].name + ": ", Color.Cyan);
+ BridgeCore.form.Log(chat.Text + "\n", Color.White);
+ }
+ }
+ private static void LogConnect() {
+ BridgeCore.form.Log("client connected\n", Color.Green);
+ }
+ private static void LogDisconnect() {
+ BridgeCore.form.Log("client disconnected\n", Color.Red);
+ }
+
+ private static void CheckForNewName(EntityUpdate entityUpdate) {
+ if (entityUpdate.name != null) {
+ RefreshPlayerlist();
+ }
+ }
+ private static void RemoveNameFromList(RemoveDynamicEntity rde) {
+ RefreshPlayerlist();
+ }
+ private static void RefreshPlayerlist() {
+ BridgeCore.form.Invoke((Action)BridgeCore.form.listBoxPlayers.Items.Clear);
+ foreach (var dynamicEntity in BridgeCore.dynamicEntities.Values.ToList()) {
+ if (dynamicEntity.hostility == Hostility.Player) {
+ BridgeCore.form.Invoke(new Action(() => BridgeCore.form.listBoxPlayers.Items.Add(dynamicEntity.name)));
+ }
+ }
+ }
+ }
+}
diff --git a/Bridge/Extensions/NameSubjectToChange.cs b/Bridge/Extensions/NameSubjectToChange.cs
new file mode 100644
index 0000000..daef5f1
--- /dev/null
+++ b/Bridge/Extensions/NameSubjectToChange.cs
@@ -0,0 +1,167 @@
+using Resources;
+using Resources.Datagram;
+using Resources.Packet;
+using Resources.Utilities;
+using System;
+using System.Drawing;
+using System.Linq;
+
+namespace Bridge.Extensions {
+ public static class NameSubjectToChange {
+ public static void Init() {
+ BridgeCore.EntityUpdateReceived += Teleport;
+ BridgeCore.AttackReceived += Knockback;
+ BridgeCore.PassiveProcReceived += Poison;
+ BridgeCore.EntityActionSent += HandleEntityAction;
+ }
+
+ private static void Teleport(EntityUpdate entityUpdate) {
+ if (BridgeCore.status == BridgeStatus.Playing) {
+ if (entityUpdate.guid == BridgeCore.guid) {
+ CwRam.Teleport(entityUpdate.position);
+ //todo: prevent packet from being sent to client
+ }
+ }
+ }
+ private static void Knockback(Attack attack) {
+ CwRam.Knockback(attack.Direction);
+ }
+ private static void Poison(Proc proc) {
+ if (proc.Type == ProcType.Poison && proc.Target == BridgeCore.guid) {
+ var su = new ServerUpdate();
+ su.hits.Add(new Hit() {
+ damage = proc.Modifier,
+ target = BridgeCore.guid,
+ position = BridgeCore.dynamicEntities[BridgeCore.guid].position,
+ });
+ bool tick() {
+ bool f = BridgeCore.status == BridgeStatus.Playing && BridgeCore.dynamicEntities[BridgeCore.guid].HP > 0;
+ if (f) {
+ BridgeCore.SendToClient(su);
+ }
+ return !f;
+ }
+ Tools.DoLater(tick, 500, 7);
+ }
+ }
+
+ private static void HandleEntityAction(EntityAction entityAction) {
+ switch (entityAction.type) {
+ case ActionType.Talk:
+ #region Talk
+ break;
+ #endregion
+ case ActionType.StaticInteraction:
+ #region StaticInteraction
+ ChatMessage x = new ChatMessage() {
+ message = "static interation is disabled",
+ sender = 0
+ };
+ BridgeCore.SendToClient(x);
+ break;
+ #endregion
+ case ActionType.PickUp:
+ #region PickUp
+ break;
+ #endregion
+ case ActionType.Drop: //send item back to dropper because dropping is disabled to prevent chatspam
+ #region Drop
+ if (BridgeCore.form.radioButtonDestroy.Checked) {
+ BridgeCore.SendToClient(new ChatMessage() {
+ message = "item destroyed",
+ sender = 0,
+ });
+ }
+ else {
+ var serverUpdate = new ServerUpdate();
+ var pickup = new ServerUpdate.Pickup() {
+ guid = BridgeCore.guid,
+ item = entityAction.item
+ };
+ serverUpdate.pickups.Add(pickup);
+ if (BridgeCore.form.radioButtonDuplicate.Checked) {
+ serverUpdate.pickups.Add(pickup);
+ }
+ BridgeCore.SendToClient(serverUpdate);
+ }
+ break;
+ #endregion
+ case ActionType.CallPet:
+ #region CallPet
+ break;
+ #endregion
+ default:
+ //unknown type
+ break;
+ }
+ }
+ private static void HandlePassiveProc(PassiveProc passiveProc) {
+ switch (passiveProc.type) {
+ case ProcType.Bulwalk:
+ BridgeCore.SendToClient(new ChatMessage() {
+ message = string.Format("bulwalk: {0}% dmg reduction", 1.0f - passiveProc.modifier),
+ sender = 0,
+ });
+ break;
+ case ProcType.WarFrenzy:
+ CwRam.PlayerEntity.BossBuff = true;
+ bool DisableBossBuff() {
+ bool f = BridgeCore.status == BridgeStatus.Playing && BridgeCore.dynamicEntities[BridgeCore.guid].HP > 0;
+ if (f) {
+ CwRam.PlayerEntity.BossBuff = false;
+ }
+ return !f;
+ }
+ Tools.DoLater(DisableBossBuff, passiveProc.duration, 1);
+ break;
+ case ProcType.Camouflage:
+ break;
+ case ProcType.Poison:
+ break;
+ case ProcType.UnknownA:
+ break;
+ case ProcType.ManaShield:
+ BridgeCore.SendToClient(new ChatMessage() {
+ message = string.Format("manashield: {0}", passiveProc.modifier),
+ sender = 0,
+ });
+ break;
+ case ProcType.UnknownB:
+ break;
+ case ProcType.UnknownC:
+ break;
+ case ProcType.FireSpark:
+ break;
+ case ProcType.Intuition:
+ break;
+ case ProcType.Elusiveness:
+ break;
+ case ProcType.Swiftness:
+ break;
+ default:
+ break;
+ }
+ }
+ private static void ClientSideChatCommands(ChatMessage chatMessage) {
+ if (chatMessage.message.ToLower() == @"/plane") {
+ Console.Beep();
+ var serverUpdate = new ServerUpdate() {
+ blockDeltas = new Vox("model.vox").Parse(),
+ };
+ foreach (var block in serverUpdate.blockDeltas) {
+ block.position.x += 0x802080;//(int)(dynamicEntities[guid].position.x / 0x10000);//8286946;
+ block.position.y += 0x802080;//(int)(dynamicEntities[guid].position.y / 0x10000);//8344456;
+ block.position.z += 150;// (int)(dynamicEntities[guid].position.z / 0x10000);//220;
+ }
+ BridgeCore.SendToClient(serverUpdate);
+ }
+ if (chatMessage.message.ToLower() == @"/spawn") {
+ CwRam.Teleport(new LongVector() {
+ x = 0x8020800000,
+ y = 0x8020800000,
+ z = 0,
+ });
+ }
+ }
+ }
+}
diff --git a/Bridge/Extensions/SpecialMoves.cs b/Bridge/Extensions/SpecialMoves.cs
new file mode 100644
index 0000000..0da06c8
--- /dev/null
+++ b/Bridge/Extensions/SpecialMoves.cs
@@ -0,0 +1,375 @@
+using Resources;
+using Resources.Datagram;
+using Resources.Packet;
+using Resources.Utilities;
+using System.Windows.Forms;
+
+namespace Bridge.Extensions {
+ public static class SpecialMoves {
+ private static ushort lastTarget;
+
+ public static void Init() {
+ BridgeCore.HitSent += RememberLastTarget;
+ BridgeCore.SpecialMoveReceived += HandleSpecialMoves;
+ BridgeCore.ClientConnected += EnableHotkeys;
+ BridgeCore.ClientDisconnected += DisableHotkeys;
+ }
+
+ private static void RememberLastTarget(Hit hit) {
+ lastTarget = (ushort)hit.target;
+ }
+ private static void HandleSpecialMoves(SpecialMove specialMove) {
+ switch (specialMove.Id) {
+ case SpecialMoveID.Taunt:
+ if (BridgeCore.dynamicEntities.ContainsKey(specialMove.Guid)) {
+ if (BridgeCore.status == BridgeStatus.Playing) {
+ CwRam.Teleport(BridgeCore.dynamicEntities[specialMove.Guid].position);
+ CwRam.Freeze(5000);
+ }
+ }
+ break;
+ case SpecialMoveID.CursedArrow:
+ break;
+ case SpecialMoveID.ArrowRain:
+ break;
+ case SpecialMoveID.Shrapnel:
+ var su = new ServerUpdate();
+ var blood_hit = new Hit() {
+ damage = 5f,
+ target = specialMove.Guid,
+ };
+ su.hits.Add(blood_hit);
+ var blood_particles = new ServerUpdate.Particle() {
+ count = 10,
+ spread = 2f,
+ type = ParticleType.Normal,
+ size = 0.1f,
+ velocity = new FloatVector() {
+ z = 1f,
+ },
+ color = new FloatVector() {
+ x = 1f,
+ y = 0f,
+ z = 0f
+ },
+ alpha = 1f,
+ };
+ su.particles.Add(blood_particles);
+ bool tick() {
+ bool f = BridgeCore.status == BridgeStatus.Playing && BridgeCore.dynamicEntities[specialMove.Guid].HP > 0;
+ if (f) {
+ blood_hit.position = blood_particles.position = BridgeCore.dynamicEntities[specialMove.Guid].position;
+ BridgeCore.SendToClient(su);
+ }
+ return !f;
+ }
+ Tools.DoLater(tick, 50, 100);
+ break;
+ case SpecialMoveID.SmokeBomb:
+ var serverUpdate = new ServerUpdate();
+ serverUpdate.particles.Add(new ServerUpdate.Particle() {
+ count = 1000,
+ spread = 5f,
+ type = ParticleType.NoGravity,
+ size = 5f,
+ velocity = new FloatVector(),
+ color = new FloatVector() {
+ x = 1f,
+ y = 1f,
+ z = 1f
+ },
+ alpha = 1f,
+ position = BridgeCore.dynamicEntities[specialMove.Guid].position
+ });
+ if (BridgeCore.status == BridgeStatus.Playing) {
+ BridgeCore.SendToClient(serverUpdate);
+ }
+ break;
+ case SpecialMoveID.IceWave:
+ if (specialMove.Guid != BridgeCore.guid) {//distance small enough
+ CwRam.Freeze(3000);
+ }
+ serverUpdate = new ServerUpdate();
+ serverUpdate.particles.Add(new ServerUpdate.Particle() {
+ count = 100,
+ spread = 4f,
+ type = ParticleType.NoGravity,
+ size = 0.3f,
+ velocity = new FloatVector(),
+ color = new FloatVector() {
+ x = 0f,
+ y = 1f,
+ z = 1f
+ },
+ alpha = 1f,
+ position = BridgeCore.dynamicEntities[specialMove.Guid].position
+ });
+ serverUpdate.particles.Add(new ServerUpdate.Particle() {
+ count = 100,
+ spread = 10f,
+ type = ParticleType.NoGravity,
+ size = 0.1f,
+ velocity = new FloatVector(),
+ color = new FloatVector() {
+ x = 1f,
+ y = 1f,
+ z = 1f
+ },
+ alpha = 1f,
+ position = BridgeCore.dynamicEntities[specialMove.Guid].position
+ });
+ serverUpdate.particles.Add(new ServerUpdate.Particle() {
+ count = 100,
+ spread = 2f,
+ type = ParticleType.NoGravity,
+ size = 0.7f,
+ velocity = new FloatVector(),
+ color = new FloatVector() {
+ x = 0f,
+ y = 0f,
+ z = 1f
+ },
+ alpha = 1f,
+ position = BridgeCore.dynamicEntities[specialMove.Guid].position
+ });
+ if (BridgeCore.status == BridgeStatus.Playing) {
+ BridgeCore.SendToClient(serverUpdate);
+ }
+ break;
+ case SpecialMoveID.Confusion:
+ break;
+ case SpecialMoveID.ShadowStep:
+ break;
+ default:
+ break;
+ }
+ }
+
+ private static void EnableHotkeys() {
+ BridgeCore.form.keyboardHook.KeyboardChanged += OnKeyboardChanged;
+ }
+ private static void DisableHotkeys() {
+ BridgeCore.form.keyboardHook.KeyboardChanged -= OnKeyboardChanged;
+ }
+ private static void OnKeyboardChanged(Keys key, bool isDown) {
+ switch (key) {
+ case Keys.D4 when isDown:
+ OnHotkey(HotkeyID.CtrlSpace);
+ break;
+ case Keys.D5 when isDown:
+ OnHotkey(HotkeyID.SpecialMove2);
+ break;
+ case Keys.D0 when isDown:
+ OnHotkey(HotkeyID.TeleportToTown);
+ break;
+ default:
+ break;
+ }
+ }
+ private static void OnHotkey(HotkeyID hotkey) {
+ if (CwRam.AnyInterfaceOpen) return;
+
+ if (hotkey == HotkeyID.TeleportToTown) {
+ CwRam.SetMode(Mode.Teleport_To_City, 0);
+ return;
+ }
+ var notification = new ChatMessage() {
+ sender = 0,
+ };
+ bool spec = BridgeCore.dynamicEntities[BridgeCore.guid].specialization == 1;
+ switch (BridgeCore.dynamicEntities[BridgeCore.guid].entityClass) {
+ case EntityClass.Rogue when spec:
+ #region ninja
+ if (hotkey == HotkeyID.CtrlSpace) {
+ #region dash
+ notification.message = "using [dash]";
+ CwRam.SetMode(Mode.Spin_Run, 0);
+ #endregion
+ }
+ else {
+ #region blink
+ notification.message = "using [blink]";
+ if (BridgeCore.dynamicEntities.ContainsKey(lastTarget)) {
+ CwRam.Teleport(BridgeCore.dynamicEntities[lastTarget].position);
+ }
+ #endregion
+ }
+ break;
+ #endregion
+ case EntityClass.Rogue:
+ #region assassin
+ if (hotkey == HotkeyID.CtrlSpace) {
+ #region confusion
+ notification.message = "TODO: [confusion]";
+ var specialMove = new SpecialMove() {
+ Guid = BridgeCore.guid,
+ Id = SpecialMoveID.Confusion,
+ };
+ BridgeCore.SendUDP(specialMove.data);
+ #endregion
+ }
+ else {
+ #region shadow step
+ notification.message = "TOD: [shadow step]";
+ var specialMove = new SpecialMove() {
+ Guid = BridgeCore.guid,
+ Id = SpecialMoveID.ShadowStep,
+ };
+ BridgeCore.SendUDP(specialMove.data);
+ #endregion
+ }
+ break;
+ #endregion
+ case EntityClass.Warrior when spec:
+ #region guardian
+ if (hotkey == HotkeyID.CtrlSpace) {
+ #region taunt
+ notification.message = "using [taunt]";
+ var specialMove = new SpecialMove() {
+ Guid = lastTarget,
+ Id = SpecialMoveID.Taunt,
+ };
+ BridgeCore.SendUDP(specialMove.data);
+ #endregion
+ }
+ else {
+ #region steel wall
+ notification.message = "using [steel wall]";
+ CwRam.SetMode(Mode.Boss_Skill_Block, 0);
+ #endregion
+ }
+ break;
+ #endregion
+ case EntityClass.Warrior:
+ #region berserk
+ if (hotkey == HotkeyID.CtrlSpace) {
+ #region boulder toss
+ notification.message = "using [boulder toss]";
+ CwRam.SetMode(Mode.Boulder_Toss, 0);
+ #endregion
+ }
+ else {
+ #region earth shatter
+ notification.message = "using [earth shatter]";
+ CwRam.SetMode(Mode.Earth_Shatter, 0);
+ #endregion
+ }
+ break;
+ #endregion
+ case EntityClass.Mage when spec:
+ #region watermage
+ if (hotkey == HotkeyID.CtrlSpace) {
+ #region splash
+ notification.message = "using [splash]";
+ CwRam.SetMode(Mode.Splash, 0);
+ #endregion
+ }
+ else {
+ #region ice wave
+ notification.message = "using [ice wave]";
+ var specialMove = new SpecialMove() {
+ Guid = BridgeCore.guid,
+ Id = SpecialMoveID.IceWave,
+ };
+ BridgeCore.SendUDP(specialMove.data);
+ #endregion
+ }
+ break;
+ #endregion
+ case EntityClass.Mage:
+ #region firemage
+ if (hotkey == HotkeyID.CtrlSpace) {
+ #region lava
+ notification.message = "using [lava]";
+ CwRam.SetMode(Mode.Lava, 0);
+ #endregion
+ }
+ else {
+ #region beam
+ notification.message = "using [fire ray]";
+ CwRam.SetMode(Mode.FireRay, 0);
+ #endregion
+ }
+ break;
+ #endregion
+ case EntityClass.Ranger when spec:
+ #region scout
+ if (hotkey == HotkeyID.CtrlSpace) {
+ #region shrapnel
+ notification.message = "using [shrapnel] bleeding test";
+ var specialMove = new SpecialMove() {
+ Guid = BridgeCore.guid,
+ Id = SpecialMoveID.Shrapnel,
+ };
+ BridgeCore.SendUDP(specialMove.data);
+ #endregion
+ }
+ else {
+ #region smoke bomb
+ notification.message = "using [smoke bomb]";
+ var specialMove = new SpecialMove() {
+ Guid = BridgeCore.guid,
+ Id = SpecialMoveID.SmokeBomb,
+ };
+ BridgeCore.SendUDP(specialMove.data);
+
+ var fakeSmoke = new ServerUpdate();
+ fakeSmoke.particles.Add(new ServerUpdate.Particle() {
+ count = 1000,
+ spread = 5f,
+ type = ParticleType.NoGravity,
+ size = 0.3f,
+ velocity = new Resources.Utilities.FloatVector(),
+ color = new Resources.Utilities.FloatVector() {
+ x = 1f,
+ y = 1f,
+ z = 1f
+ },
+ alpha = 1f,
+ position = BridgeCore.dynamicEntities[specialMove.Guid].position
+ });
+ BridgeCore.SendToClient(fakeSmoke);
+ #endregion
+ }
+ break;
+ #endregion
+ case EntityClass.Ranger:
+ #region sniper
+ if (hotkey == HotkeyID.CtrlSpace) {
+ #region cursed arrow
+ //TODO
+ notification.message = "TODO: [cursed arrow]";
+ #endregion
+ }
+ else {
+ #region arrow rain
+ //TODO
+ notification.message = "TODO: [arrow rain]";
+ //const int rainSize = 7;
+ //for (int x = 0; x < rainSize; x++) {
+ // for (int y = 0; y < rainSize; y++) {
+ // var projectile = new Projectile() {
+ // Scale = 1f,
+ // Type = ProjectileType.Arrow,
+ // Source = guid,
+ // Velocity = new FloatVector() { x = 0, y = 0, z = -1f },
+ // Position = new LongVector() {
+ // x = 0x8020800000,//dynamicEntities[guid].position.x + (long)((dynamicEntities[guid].rayHit.x + x - rainSize / 2) * 0x10000),
+ // y = 0x8020800000,//dynamicEntities[guid].position.y + (long)((dynamicEntities[guid].rayHit.y + y - rainSize / 2) * 0x10000),
+ // z = 0x01000000,//dynamicEntities[guid].position.z + (long)((dynamicEntities[guid].rayHit.z + 10) * 0x10000),
+ // }
+ // };
+ // SendUDP(projectile.data);
+ // ProcessDatagram(projectile.data);
+ // }
+ //}
+ #endregion
+ }
+ break;
+ #endregion
+ }
+ CwRam.memory.WriteInt(CwRam.EntityStart + 0x1164, 3);//mana cubes
+ BridgeCore.SendToClient(notification);
+ }
+ }
+}
diff --git a/Bridge/FormMain.cs b/Bridge/FormMain.cs
index f0d29c4..f2f611d 100644
--- a/Bridge/FormMain.cs
+++ b/Bridge/FormMain.cs
@@ -1,4 +1,5 @@
-using ReadWriteProcessMemory;
+using Bridge.Extensions;
+using ReadWriteProcessMemory;
using System;
using System.Diagnostics;
using System.Drawing;
@@ -12,8 +13,8 @@ public partial class FormMain : Form {
public FormRegister register = new FormRegister();
public FormChat chat = new FormChat();
public FormRankings rankings = new FormRankings();
+ public KeyboardHook keyboardHook;
private bool processAttached = false;
- private KeyboardHook keyboardHook;
public FormMain(string[]args) {
InitializeComponent();
@@ -22,11 +23,12 @@ private void FormMain_Shown(object sender, EventArgs e) {
chat.Show();
chat.Top = this.Top;
chat.Left = Left + Width;
- BridgeTCPUDP.form = this;
+ BridgeCore.form = this;
CwRam.formMain = this;
- new Thread(BridgeTCPUDP.ListenFromClientTCP).Start();
- new Thread(BridgeTCPUDP.Connect).Start();
keyboardHook = new KeyboardHook();
+ new Thread(BridgeCore.ListenFromClientTCP).Start();
+ new Thread(BridgeCore.Connect).Start();
+ ExtensionsCore.Init();
}
private void timerSearchProcess_Tick(object sender, EventArgs e) {
if (processAttached) {
@@ -39,7 +41,7 @@ private void timerSearchProcess_Tick(object sender, EventArgs e) {
}
else {
CwRam.RemoveFog();
- if (BridgeTCPUDP.status == Resources.BridgeStatus.Playing) CwRam.SetName(linkLabelUser.Text);
+ if (BridgeCore.status == Resources.BridgeStatus.Playing) CwRam.SetName(linkLabelUser.Text);
}
}
else {
@@ -108,7 +110,7 @@ private void buttonLoginRegister_Click(object sender, EventArgs e) {
}
private void contextMenuStripUser_ItemClicked(object sender, ToolStripItemClickedEventArgs e) {
- BridgeTCPUDP.Logout();
+ BridgeCore.Logout();
OnLogout();
}
public void OnLogout() {
diff --git a/Bridge/FormMap.cs b/Bridge/FormMap.cs
index 4ebc3ae..2b66665 100644
--- a/Bridge/FormMap.cs
+++ b/Bridge/FormMap.cs
@@ -13,7 +13,7 @@ public FormMap() {
private void Refreshtimer_Tick(object sender, EventArgs e) {
Controls.Clear();
playercollection.Clear();
- foreach (var entity in BridgeTCPUDP.dynamicEntities.Values.ToList()) {
+ foreach (var entity in BridgeCore.dynamicEntities.Values.ToList()) {
if (entity.hostility == Resources.Hostility.Player) {
Label playerlabel = new Label {
Left = (int)entity.position.x / 0x10000,
diff --git a/Bridge/FormRegister.cs b/Bridge/FormRegister.cs
index aaff13a..6ca0d7f 100644
--- a/Bridge/FormRegister.cs
+++ b/Bridge/FormRegister.cs
@@ -41,7 +41,7 @@ private void textBoxEmail_TextChanged(object sender, EventArgs e) {
private void buttonRegister_Click(object sender, EventArgs e) {
buttonRegister.Enabled = false;
- BridgeTCPUDP.Register(textBoxUsername.Text, textBoxEmail.Text, textBoxPassword.Text);
+ BridgeCore.Register(textBoxUsername.Text, textBoxEmail.Text, textBoxPassword.Text);
}
private void buttonCreate_Click(object sender, EventArgs e) {
@@ -77,39 +77,39 @@ private void buttonLogin_Click(object sender, EventArgs e) {
textBoxUsername.ReadOnly = true;
textBoxEmail.ReadOnly = true;
textBoxPassword.ReadOnly = true;
- BridgeTCPUDP.Login(textBoxUsername.Text, textBoxPassword.Text);
+ BridgeCore.Login(textBoxUsername.Text, textBoxPassword.Text);
}
public void OnLoginResponse(AuthResponse authResponse) {
switch (authResponse) {
case AuthResponse.Success:
- BridgeTCPUDP.form.Log("success\n", Color.Green);
- BridgeTCPUDP.form.buttonLoginRegister.Visible = false;
- BridgeTCPUDP.form.linkLabelUser.Text = textBoxUsername.Text;
- BridgeTCPUDP.form.linkLabelUser.Visible = true;
- BridgeTCPUDP.form.buttonClan.Enabled = true;
- BridgeTCPUDP.form.buttonClan.Visible = false;
- BridgeTCPUDP.form.linkLabelClan.Text = "Prisoners of Irreality";
- BridgeTCPUDP.form.linkLabelClan.Visible = true;
+ BridgeCore.form.Log("success\n", Color.Green);
+ BridgeCore.form.buttonLoginRegister.Visible = false;
+ BridgeCore.form.linkLabelUser.Text = textBoxUsername.Text;
+ BridgeCore.form.linkLabelUser.Visible = true;
+ BridgeCore.form.buttonClan.Enabled = true;
+ BridgeCore.form.buttonClan.Visible = false;
+ BridgeCore.form.linkLabelClan.Text = "Prisoners of Irreality";
+ BridgeCore.form.linkLabelClan.Visible = true;
this.Close();
break;
case AuthResponse.UnknownUser:
- BridgeTCPUDP.form.Log("username does not exist\n", Color.Red);
+ BridgeCore.form.Log("username does not exist\n", Color.Red);
goto default;
case AuthResponse.WrongPassword:
- BridgeTCPUDP.form.Log("wrong password\n", Color.Red);
+ BridgeCore.form.Log("wrong password\n", Color.Red);
goto default;
case AuthResponse.Banned:
- BridgeTCPUDP.form.Log("you are banned\n", Color.Red);
+ BridgeCore.form.Log("you are banned\n", Color.Red);
goto default;
case AuthResponse.AccountAlreadyActive:
- BridgeTCPUDP.form.Log("account already in use\n", Color.Red);
+ BridgeCore.form.Log("account already in use\n", Color.Red);
goto default;
case AuthResponse.Unverified:
- BridgeTCPUDP.form.Log("unverified (this shouldnt happen)\n", Color.Red);
+ BridgeCore.form.Log("unverified (this shouldnt happen)\n", Color.Red);
goto default;
case AuthResponse.UserAlreadyLoggedIn:
- BridgeTCPUDP.form.Log("you are already logged in (this shouldn't happen)\n", Color.Red);
+ BridgeCore.form.Log("you are already logged in (this shouldn't happen)\n", Color.Red);
goto default;
default:
buttonLogin.Enabled = true;
diff --git a/Bridge/KeyboardHook.cs b/Bridge/KeyboardHook.cs
index 7d3fcdb..c7b4d9c 100644
--- a/Bridge/KeyboardHook.cs
+++ b/Bridge/KeyboardHook.cs
@@ -1,8 +1,6 @@
using System;
using System.Collections.Generic;
-using System.Linq;
using System.Runtime.InteropServices;
-using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
@@ -92,25 +90,12 @@ private int KeybHookProc(int Code, int W, int L) {
private Dictionary keyboardState = new Dictionary();
private void OnKey(Keys key, bool isDown) {
if (GetForegroundWindow() != CwRam.memory.process.MainWindowHandle) return;
-
if (!keyboardState.ContainsKey(key)) keyboardState.Add(key, !isDown);
if (keyboardState[key] == isDown) return;
keyboardState[key] = isDown;
-
- if (!isDown) return;//temp fix to prevent activation on release
- switch (key) {
- case Keys.D4:
- BridgeTCPUDP.OnHotkey(Resources.HotkeyID.CtrlSpace);
- break;
- case Keys.D5:
- BridgeTCPUDP.OnHotkey(Resources.HotkeyID.SpecialMove2);
- break;
- case Keys.D0:
- BridgeTCPUDP.OnHotkey(Resources.HotkeyID.TeleportToTown);
- break;
- default:
- break;
- }
+ KeyboardChanged?.Invoke(key, isDown);
}
+ public delegate void KeyboardChangedEventHandler(Keys key, bool isDown);
+ public event KeyboardChangedEventHandler KeyboardChanged;
}
}
diff --git a/Resources/Config.cs b/Resources/Config.cs
index 3bc30fb..eb03c47 100644
--- a/Resources/Config.cs
+++ b/Resources/Config.cs
@@ -2,7 +2,7 @@
static public class Config {
public const int mapseed = 8710; //hardcoded for now
public const int bridgeVersion = 16;
- public const string serverIP = "pb97.ddns.net";//temp
+ public const string serverIP = "localhost";//temp
public const int serverPort = 12346;
}
}
diff --git a/Resources/CubeworldNetworking b/Resources/CubeworldNetworking
index b24405c..5323adb 160000
--- a/Resources/CubeworldNetworking
+++ b/Resources/CubeworldNetworking
@@ -1 +1 @@
-Subproject commit b24405c0d42a1384d3cce28cae56d7858fb568c5
+Subproject commit 5323adb8735c2b78d27fd2add8f4df54addbf546
diff --git a/Resources/Enums.cs b/Resources/Enums.cs
index 4fc5fc0..50a073c 100644
--- a/Resources/Enums.cs
+++ b/Resources/Enums.cs
@@ -58,4 +58,16 @@ public enum BridgeStatus : byte {
LoggedIn,
Playing,
}
+ public enum RoleID : byte
+ {
+ Default,
+ Vip,
+ Mod,
+ Admin
+ }
+ public enum PromoteResponse : byte
+ {
+ Success,
+ InvalidTarget
+ }
}
diff --git a/Resources/Player.cs b/Resources/Player.cs
index 40f9fae..c85b017 100644
--- a/Resources/Player.cs
+++ b/Resources/Player.cs
@@ -9,6 +9,7 @@ public class Player {
public BinaryWriter writer;
public BinaryReader reader;
public EntityUpdate entity;
+ public byte Permission;
public ushort? tomb;
public string MAC;
public ushort lastTarget;
diff --git a/Server/Addon/ZoxModel.cs b/Resources/ZoxModel.cs
similarity index 98%
rename from Server/Addon/ZoxModel.cs
rename to Resources/ZoxModel.cs
index b2c3f28..ed311ea 100644
--- a/Server/Addon/ZoxModel.cs
+++ b/Resources/ZoxModel.cs
@@ -3,7 +3,7 @@
using Resources.Utilities;
using Resources.Packet;
-namespace Server.Addon {
+namespace Resources {
class ZoxModel {
public string Creator { get; set; }
public byte Height { get; set; }
diff --git a/Server/Database/User.cs b/Server/Database/User.cs
index 27d7baf..6608939 100644
--- a/Server/Database/User.cs
+++ b/Server/Database/User.cs
@@ -23,6 +23,7 @@ public User(string username, string email, string password) {
this.Name = username;
this.Email = email;
this.PasswordHash = Hashing.Hash(password);
+ this.Permission = 0;
}
public bool VerifyPassword(string password) {
diff --git a/Server/Database/UserDatabase.cs b/Server/Database/UserDatabase.cs
index 44c887d..f09cd75 100644
--- a/Server/Database/UserDatabase.cs
+++ b/Server/Database/UserDatabase.cs
@@ -1,5 +1,6 @@
using Microsoft.EntityFrameworkCore;
using Resources;
+using System;
using System.Linq;
namespace Server.Database {
@@ -87,5 +88,24 @@ public void BanUser(string entityName, int ipAddress, string targetMac, string r
Bans.Add(ban);
SaveChanges();
}
+ public PromoteResponse PromoteUser(string entityName, RoleID roleId)
+ {
+ var user = Users.SingleOrDefault(x => x.Name == entityName);
+ if (user != null)
+ {
+ user.Permission = (byte)roleId;
+ SaveChanges();
+ return PromoteResponse.Success;
+ }
+ else
+ {
+ return PromoteResponse.InvalidTarget;
+ }
+ }
+ public byte getRoleId(string entityName)
+ {
+ var user = Users.SingleOrDefault(x => x.Name == entityName);
+ return user.Permission;
+ }
}
}
\ No newline at end of file
diff --git a/Server/Addon/AntiCheat.cs b/Server/Extensions/AntiCheat.cs
similarity index 68%
rename from Server/Addon/AntiCheat.cs
rename to Server/Extensions/AntiCheat.cs
index d3c707c..a09b08e 100644
--- a/Server/Addon/AntiCheat.cs
+++ b/Server/Extensions/AntiCheat.cs
@@ -1,17 +1,31 @@
using Resources;
using Resources.Packet;
+using System;
+using System.Collections.Generic;
+using System.Text;
-namespace Server.Addon {
- class AntiCheat {
- public static string Inspect(EntityUpdate current, EntityUpdate previous) {
+namespace Server.Extensions {
+ public static class AntiCheat {
+ public static void Init() {
+ ServerCore.EntityUpdated += That;
+ }
+
+ private static void That(EntityUpdate entityUpdate, Player player) {
+ string ACmessage = Inspect(entityUpdate, player.entity);
+ if (ACmessage != null) {
+ ServerCore.Kick(player, ACmessage);
+ }
+ }
+
+ private static string Inspect(EntityUpdate current, EntityUpdate previous) {
if (current.guid != previous.guid) {
return "guid";
}
if (current.hostility != null && current.hostility != 0) {
return "hostility";
}
- if(current.appearance != null) {
- if((current.appearance.character_size.x != 0.8000000119f && current.appearance.character_size.x != 0.9600000381f && current.appearance.character_size.x != 1.039999962f) ||
+ if (current.appearance != null) {
+ if ((current.appearance.character_size.x != 0.8000000119f && current.appearance.character_size.x != 0.9600000381f && current.appearance.character_size.x != 1.039999962f) ||
(current.appearance.character_size.y != 0.8000000119f && current.appearance.character_size.y != 0.9600000381f && current.appearance.character_size.y != 1.039999962f) ||
(current.appearance.character_size.z != 1.799999952f && current.appearance.character_size.z != 2.160000086f && current.appearance.character_size.z != 2.339999914f) ||
(current.appearance.head_size != 0.8999999762f && current.appearance.head_size != 1.00999999f) ||
@@ -26,13 +40,13 @@ public static string Inspect(EntityUpdate current, EntityUpdate previous) {
return "appearance";
}
}
- if(current.charge != null && current.charge > 1){ //(current.MP ?? previous.MP)) {
+ if (current.charge != null && current.charge > 1) { //(current.MP ?? previous.MP)) {
return "MP charge";
}
- if(current.HP != null && current.HP > 3333) {
+ if (current.HP != null && current.HP > 3333) {
return "HP";
}
- if(current.MP != null && current.MP > 1) {
+ if (current.MP != null && current.MP > 1) {
return "MP";
}
//if(current.multipliers != null) {
@@ -44,27 +58,27 @@ public static string Inspect(EntityUpdate current, EntityUpdate previous) {
// return "multipliers";
// }
//}
- if(current.level != null && current.level > 500) {
+ if (current.level != null && current.level > 500) {
return "level";
}
- if(current.consumable != null) {
- if(current.consumable.type == ItemType.Food &&
+ if (current.consumable != null) {
+ if (current.consumable.type == ItemType.Food &&
current.consumable.subtype == 1 ||
current.consumable.level > 647 ||
current.consumable.rarity != 0) {
//return "consumable";
}
}
- if(current.equipment != null) {
- foreach(Item item in current.equipment) {
- if(item.type != 0 &&
+ if (current.equipment != null) {
+ foreach (Item item in current.equipment) {
+ if (item.type != 0 &&
(item.level > 647 || (byte)item.rarity > 4)) {
return "equipment";
}
}
}
- if(current.skillDistribution != null) {
- if(current.skillDistribution.petmaster +
+ if (current.skillDistribution != null) {
+ if (current.skillDistribution.petmaster +
current.skillDistribution.petriding +
current.skillDistribution.sailing +
current.skillDistribution.climbing +
@@ -82,4 +96,3 @@ public static string Inspect(EntityUpdate current, EntityUpdate previous) {
}
}
}
-//block
diff --git a/Server/Extensions/Balancing.cs b/Server/Extensions/Balancing.cs
new file mode 100644
index 0000000..11a01be
--- /dev/null
+++ b/Server/Extensions/Balancing.cs
@@ -0,0 +1,34 @@
+using Resources;
+using Resources.Datagram;
+
+namespace Server.Extensions {
+ public static class Balancing {
+ public static void Init() {
+ ServerCore.EntityAttacked += CutDmgInHalf;
+ }
+
+ private static void CutDmgInHalf(Attack attack, Player player) {
+ if (attack.Damage > 0) {
+ attack.Damage *= 0.5f; //dmg
+ }
+ //else {
+ // attack.Damage *= 0.333333f; //heal
+ //}
+ var target = ServerCore.dynamicEntities[attack.Target];
+ var dmgReductionByShield = 0f;
+ if (target.equipment[(int)Equipment.LeftWeapon].subtype == (byte)ItemSubtypeWeapon.Shield) {
+ dmgReductionByShield += 0.25f;
+ }
+ if (target.equipment[(int)Equipment.RightWeapon].subtype == (byte)ItemSubtypeWeapon.Shield) {
+ dmgReductionByShield += 0.25f;
+ }
+ if (attack.Damage > 0) {
+ attack.Damage *= 1 - dmgReductionByShield;
+ }
+ Log.PrintLn(player.entity.name + " " + attack.Target + " " + attack.Damage + " " + attack.Critical, System.ConsoleColor.Magenta);
+ if (attack.Target == player.entity.guid) {//players can't damage themselves. this prevents double self heals since selfheal is already applied locally
+ attack.Damage = 0;
+ }
+ }
+ }
+}
diff --git a/Server/Extensions/ChatCommands.cs b/Server/Extensions/ChatCommands.cs
new file mode 100644
index 0000000..2ad170b
--- /dev/null
+++ b/Server/Extensions/ChatCommands.cs
@@ -0,0 +1,92 @@
+using Resources;
+using Resources.Datagram;
+using Resources.Packet;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Server.Extensions {
+ public static class ChatCommands {
+ public static void Init() {
+ ServerCore.ChatMessageReceived += ParseAsCommand;
+ }
+
+ private static void ParseAsCommand(string message, Player source) {
+ if (!message.StartsWith("/")) {
+ return;
+ }
+ var parameters = message.Substring(1).Split(" ");
+ var command = parameters[0].ToLower();
+ switch (command) {
+ case "kick":
+ break;
+ case "btfo":
+ break;
+ case "ban":
+ #region ban
+ if (!minimumPermission(source,3)) {
+ ServerCore.Notify(source, "no permission");
+ break;
+ }
+ if (parameters.Length == 1) {
+ ServerCore.Notify(source, string.Format("usage example: /kick blackrock"));
+ break;
+ }
+ var target = ServerCore.players.FirstOrDefault(x => x.entity.name.Contains(parameters[1]));
+ if (target == null) {
+ ServerCore.Notify(source, "invalid target");
+ break;
+ };
+ var reason = "no reason specified";
+ if (parameters.Length > 2) {
+ reason = parameters[2];
+ }
+ if (command == "kick") {
+ ServerCore.Kick(target, reason);
+ break;
+ }
+ target.writer.Write((byte)ServerPacketID.BTFO);
+ target.writer.Write(reason);
+ if (command == "ban") {
+ ServerCore.userDatabase.BanUser(target.entity.name, (int)target.IP.Address, target.MAC, reason);
+ }
+ ServerCore.RemovePlayerEntity(target, false);
+ break;
+ #endregion
+ case "bleeding":
+ break;
+ case "time":
+ #region time
+ if (parameters.Length == 1) {
+ ServerCore.Notify(source, string.Format("usage example: /time 12:00"));
+ break;
+ }
+ var clock = parameters[1].Split(":");
+ if (clock.Length < 2 ||
+ !int.TryParse(clock[0], out int hour) ||
+ !int.TryParse(clock[1], out int minute)) {
+ ServerCore.Notify(source, string.Format("invalid syntax"));
+ break;
+ }
+ var inGameTime = new InGameTime() {
+ Milliseconds = (hour * 60 + minute) * 60000,
+ };
+ ServerCore.SendUDP(inGameTime.data, source);
+ break;
+ #endregion
+ default:
+ ServerCore.Notify(source, string.Format("unknown command '{0}'", parameters[0]));
+ break;
+ }
+ }
+ private static Boolean minimumPermission(Player source,int roleId)
+ {
+ if(source.Permission >= roleId)
+ {
+ return true;
+ }
+ return false;
+ }
+ }
+}
diff --git a/Server/Extensions/ConsoleCommands.cs b/Server/Extensions/ConsoleCommands.cs
new file mode 100644
index 0000000..35d847e
--- /dev/null
+++ b/Server/Extensions/ConsoleCommands.cs
@@ -0,0 +1,53 @@
+using Resources;
+using Server.Database;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Server.Extensions
+{
+ static class ConsoleCommands
+ {
+ public static void analyzeCommand(string consoleCommands)
+ {
+ var parameters = consoleCommands.Split(" ");
+ switch (parameters[0])
+ {
+ case "promote":
+ if(parameters.Length == 3)
+ {
+ int role = int.Parse(parameters[2]);
+ if (0 <= role && role <= Enum.GetValues(typeof(RoleID)).Length - 1)
+ {
+ PromoteResponse response = ServerCore.userDatabase.PromoteUser(parameters[1], (RoleID)role);
+ if(response == PromoteResponse.Success)
+ {
+ var player = ServerCore.players.FirstOrDefault(x => x.entity?.name == parameters[1]);
+ if (player != null)
+ {
+ player.Permission = (byte)role;
+ }
+ Log.PrintLn(String.Format("User '{0}' promoted to role {1}!", parameters[1], parameters[2]));
+ }
+ else if(response == PromoteResponse.InvalidTarget)
+ {
+ Log.PrintLn(String.Format("Error : Invalid target"));
+ }
+ }
+ else
+ {
+ Log.PrintLn(String.Format("Error : RoleId out of Range"));
+ }
+ }
+ else
+ {
+ Log.PrintLn(String.Format("Syntax : promote [player] [roleId]"));
+ }
+ break;
+ case "default":
+ break;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Server/Extensions/Extensions.cs b/Server/Extensions/Extensions.cs
new file mode 100644
index 0000000..c14a3c6
--- /dev/null
+++ b/Server/Extensions/Extensions.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Server.Extensions {
+ public static class Extensions {
+ public static void Init() {
+ AntiCheat.Init();
+ Pvp.Init();
+ ChatCommands.Init();
+ SpecialMoves.Init();
+ Balancing.Init();
+ }
+ }
+}
diff --git a/Server/Extensions/Models.cs b/Server/Extensions/Models.cs
new file mode 100644
index 0000000..51e9357
--- /dev/null
+++ b/Server/Extensions/Models.cs
@@ -0,0 +1,83 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Server.Extensions {
+ public static class Models {
+ public static void Init() {
+
+ }
+
+ private static void Placeholder() {
+ #region models
+ //var rnd = new Random();
+ //for (int i = 8286946; i < 8286946 + 512; i++) {
+ // for (int j = 8344456; j < 8344456 + 512; j++) {
+ // var block = new ServerUpdate.BlockDelta() {
+ // color = new Resources.Utilities.ByteVector() {
+ // x = 0,
+ // y = 0,
+ // z = (byte)rnd.Next(0, 255),
+ // },
+ // type = BlockType.solid,
+ // position = new Resources.Utilities.IntVector() {
+ // x = i,
+ // y = j,
+ // z = 208,
+ // },
+ // };
+ // worldUpdate.blockDeltas.Add(block);
+ // }
+ //}
+ //x = 543093329157,
+ //y = 546862296355,
+ //z = 14423162
+ //ZoxModel model = JsonConvert.DeserializeObject(File.ReadAllText("models/Fulcnix_exceedspawn.zox"));
+ //model.Parse(worldUpdate, 8286883, 8344394, 200);
+ //model = JsonConvert.DeserializeObject(File.ReadAllText("models/Aster_Tavern2.zox"));
+ //model.Parse(worldUpdate, 8287010, 8344432, 200);
+ //model = JsonConvert.DeserializeObject(File.ReadAllText("models/Aster_Tavern1.zox"));
+ //model.Parse(worldUpdate, 8286919, 8344315, 212);
+ //model = JsonConvert.DeserializeObject(File.ReadAllText("models/arena/aster_arena.zox"));
+ //model.Parse(worldUpdate, 8286775, 8344392, 207);
+ //model = JsonConvert.DeserializeObject(File.ReadAllText("models/michael_project1.zox"));
+ //model.Parse(worldUpdate, 8286898, 8344375, 213);
+ //model = JsonConvert.DeserializeObject(File.ReadAllText("models/arena/fulcnix_hall.zox"));
+ //model.Parse(worldUpdate, 8286885, 8344505, 208);
+ //model = JsonConvert.DeserializeObject(File.ReadAllText("models/arena/fulcnix_hall.zox"));
+ //model.Parse(worldUpdate, 8286885, 8344629, 208);
+ //model = JsonConvert.DeserializeObject(File.ReadAllText("models/Tiecz_MountainArena.zox"));
+ //model.Parse(worldUpdate, 8286885, 8344759, 208);
+ ////8397006, 8396937, 127 //near spawn
+ //model = JsonConvert.DeserializeObject(File.ReadAllText("models/Aster_CloudyDay11.zox"));
+ //model.Parse(worldUpdate, 8286770, 8344262, 207);
+ //model = JsonConvert.DeserializeObject(File.ReadAllText("models/Aster_CloudyDay12.zox"));
+ //model.Parse(worldUpdate, 8286770, 8344136, 207);
+ //model = JsonConvert.DeserializeObject(File.ReadAllText("models/Aster_CloudyDay13.zox"));
+ //model.Parse(worldUpdate, 8286770, 8344010, 207);
+ //model = JsonConvert.DeserializeObject(File.ReadAllText("models/Aster_CloudyDay14.zox"));
+ //model.Parse(worldUpdate, 8286770, 8344010, 333);
+ //model = JsonConvert.DeserializeObject(File.ReadAllText("models/Aster_CloudyDay01.zox"));
+ //model.Parse(worldUpdate, 8286644, 8344010, 333);
+ //model = JsonConvert.DeserializeObject(File.ReadAllText("models/Aster_CloudyDay02.zox"));
+ //model.Parse(worldUpdate, 8286118, 8344010, 333);
+ //model = JsonConvert.DeserializeObject(File.ReadAllText("models/Aster_CloudyDay03.zox"));
+ //model.Parse(worldUpdate, 8285992, 8344010, 333);
+ //model = JsonConvert.DeserializeObject(File.ReadAllText("models/Aster_CloudyDay04.zox"));
+ //model.Parse(worldUpdate, 8285992, 8344136, 333);
+ //model = JsonConvert.DeserializeObject(File.ReadAllText("models/Aster_CloudyDay05.zox"));
+ //model.Parse(worldUpdate, 8285992, 8344262, 333);
+ //model = JsonConvert.DeserializeObject(File.ReadAllText("models/Aster_CloudyDay06.zox"));
+ //model.Parse(worldUpdate, 8286118, 8344262, 333);
+ //model = JsonConvert.DeserializeObject(File.ReadAllText("models/Aster_CloudyDay07.zox"));
+ //model.Parse(worldUpdate, 8286118, 8344136, 333);
+ //model = JsonConvert.DeserializeObject(File.ReadAllText("models/Aster_CloudyDay08.zox"));
+ //model.Parse(worldUpdate, 8286244, 8344136, 333);
+ //model = JsonConvert.DeserializeObject(File.ReadAllText("models/Aster_CloudyDay09.zox"));
+ //model.Parse(worldUpdate, 8286244, 8344262, 333);
+ //model = JsonConvert.DeserializeObject(File.ReadAllText("models/Aster_CloudyDay10.zox"));
+ //model.Parse(worldUpdate, 8286770, 8344262, 333);
+ #endregion
+ }
+ }
+}
diff --git a/Server/Extensions/Pvp.cs b/Server/Extensions/Pvp.cs
new file mode 100644
index 0000000..fe9a664
--- /dev/null
+++ b/Server/Extensions/Pvp.cs
@@ -0,0 +1,17 @@
+using Resources;
+using Resources.Packet;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Server.Extensions {
+ public static class Pvp {
+ public static void Init() {
+ ServerCore.EntityUpdated += EnableFriendlyFireFlag;
+ }
+
+ private static void EnableFriendlyFireFlag(EntityUpdate entityUpdate, Player player) {
+ entityUpdate.entityFlags |= 1 << 5;
+ }
+ }
+}
diff --git a/Server/Extensions/SpecialMoves.cs b/Server/Extensions/SpecialMoves.cs
new file mode 100644
index 0000000..b57e9de
--- /dev/null
+++ b/Server/Extensions/SpecialMoves.cs
@@ -0,0 +1,40 @@
+using Resources;
+using Resources.Datagram;
+using Resources.Packet;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Server.Extensions {
+ public static class SpecialMoves {
+ public static void Init() {
+ ServerCore.SpecialMoveUsed += OnSpecialMove;
+ }
+
+ private static void OnSpecialMove(SpecialMove specialMove, Player source) {
+ switch (specialMove.Id) {
+ case SpecialMoveID.Taunt:
+ var target = ServerCore.players.FirstOrDefault(p => p.entity.guid == specialMove.Guid);
+ if (target != null) {
+ specialMove.Guid = (ushort)source.entity.guid;
+ ServerCore.SendUDP(specialMove.data, target);
+ }
+ break;
+ case SpecialMoveID.SmokeBomb:
+ ServerCore.BroadcastUDP(specialMove.data, source);
+ break;
+ case SpecialMoveID.CursedArrow:
+ case SpecialMoveID.ArrowRain:
+ case SpecialMoveID.Shrapnel:
+ case SpecialMoveID.IceWave:
+ case SpecialMoveID.Confusion:
+ case SpecialMoveID.ShadowStep:
+ ServerCore.BroadcastUDP(specialMove.data);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+}
diff --git a/Server/Extensions/Tombstones.cs b/Server/Extensions/Tombstones.cs
new file mode 100644
index 0000000..bb7bad5
--- /dev/null
+++ b/Server/Extensions/Tombstones.cs
@@ -0,0 +1,45 @@
+using Resources;
+using Resources.Datagram;
+using Resources.Packet;
+using Resources.Utilities;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Server.Extensions {
+ public static class Tombstones {
+ public static void Init() {
+ ServerCore.EntityUpdated += SpawnTomb;
+ }
+
+ private static void SpawnTomb(EntityUpdate entityUpdate, Player player) {
+ if (entityUpdate.HP <= 0 && (player.entity.HP > 0 || player.entity.HP == null)) {
+ var tombstone = new EntityUpdate() {
+ guid = ServerCore.AssignGuid(),
+ position = entityUpdate.position ?? player.entity.position,
+ hostility = Hostility.Neutral,
+ entityType = EntityType.None,
+ appearance = new EntityUpdate.Appearance() {
+ character_size = new FloatVector() {
+ x = 1,
+ y = 1,
+ z = 1,
+ },
+ head_model = 2155,
+ head_size = 1
+ },
+ HP = 100,
+ name = "tombstone"
+ };
+ player.tomb = (ushort)tombstone.guid;
+ ServerCore.BroadcastUDP(tombstone.CreateDatagram());
+ }
+ else if (player.entity.HP <= 0 && entityUpdate.HP > 0 && player.tomb != null) {
+ var rde = new RemoveDynamicEntity() {
+ Guid = (ushort)player.tomb,
+ };
+ ServerCore.BroadcastUDP(rde.data);
+ }
+ }
+ }
+}
diff --git a/Server/Program.cs b/Server/Program.cs
index e4ee40f..8b951fd 100644
--- a/Server/Program.cs
+++ b/Server/Program.cs
@@ -1,11 +1,13 @@
-using System;
+using Server.Extensions;
+using System;
namespace Server {
class Program {
static void Main(string[] args) {
- Server.Start(12346);
+ ServerCore.Start(12346);
while (true) {
- Console.ReadLine();
+
+ ConsoleCommands.analyzeCommand(Console.ReadLine());
}
}
}
diff --git a/Server/Server.cs b/Server/Server.cs
deleted file mode 100644
index 92772fa..0000000
--- a/Server/Server.cs
+++ /dev/null
@@ -1,473 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
-using System.Net;
-using System.Net.Sockets;
-using System.IO;
-
-using Server.Addon;
-using Server.Database;
-
-using Microsoft.EntityFrameworkCore;
-
-using Resources;
-using Resources.Datagram;
-using Resources.Packet;
-using Resources.Utilities;
-
-namespace Server {
- public static class Server {
- public static UdpClient udpClient;
- public static TcpListener tcpListener;
- public static List players = new List();
- public static Dictionary dynamicEntities = new Dictionary();
- public static UserDatabase Database;
-
- public static void Start(int port) {
- Log.PrintLn("server starting...");
- Database = new UserDatabase();
- Database.Database.Migrate(); //Ensure database exists
-
- #region models
- //var rnd = new Random();
- //for (int i = 8286946; i < 8286946 + 512; i++) {
- // for (int j = 8344456; j < 8344456 + 512; j++) {
- // var block = new ServerUpdate.BlockDelta() {
- // color = new Resources.Utilities.ByteVector() {
- // x = 0,
- // y = 0,
- // z = (byte)rnd.Next(0, 255),
- // },
- // type = BlockType.solid,
- // position = new Resources.Utilities.IntVector() {
- // x = i,
- // y = j,
- // z = 208,
- // },
- // };
- // worldUpdate.blockDeltas.Add(block);
- // }
- //}
- //x = 543093329157,
- //y = 546862296355,
- //z = 14423162
- //ZoxModel model = JsonConvert.DeserializeObject(File.ReadAllText("models/Fulcnix_exceedspawn.zox"));
- //model.Parse(worldUpdate, 8286883, 8344394, 200);
- //model = JsonConvert.DeserializeObject(File.ReadAllText("models/Aster_Tavern2.zox"));
- //model.Parse(worldUpdate, 8287010, 8344432, 200);
- //model = JsonConvert.DeserializeObject(File.ReadAllText("models/Aster_Tavern1.zox"));
- //model.Parse(worldUpdate, 8286919, 8344315, 212);
- //model = JsonConvert.DeserializeObject(File.ReadAllText("models/arena/aster_arena.zox"));
- //model.Parse(worldUpdate, 8286775, 8344392, 207);
- //model = JsonConvert.DeserializeObject(File.ReadAllText("models/michael_project1.zox"));
- //model.Parse(worldUpdate, 8286898, 8344375, 213);
- //model = JsonConvert.DeserializeObject(File.ReadAllText("models/arena/fulcnix_hall.zox"));
- //model.Parse(worldUpdate, 8286885, 8344505, 208);
- //model = JsonConvert.DeserializeObject(File.ReadAllText("models/arena/fulcnix_hall.zox"));
- //model.Parse(worldUpdate, 8286885, 8344629, 208);
- //model = JsonConvert.DeserializeObject(File.ReadAllText("models/Tiecz_MountainArena.zox"));
- //model.Parse(worldUpdate, 8286885, 8344759, 208);
- ////8397006, 8396937, 127 //near spawn
- //model = JsonConvert.DeserializeObject(File.ReadAllText("models/Aster_CloudyDay11.zox"));
- //model.Parse(worldUpdate, 8286770, 8344262, 207);
- //model = JsonConvert.DeserializeObject(File.ReadAllText("models/Aster_CloudyDay12.zox"));
- //model.Parse(worldUpdate, 8286770, 8344136, 207);
- //model = JsonConvert.DeserializeObject(File.ReadAllText("models/Aster_CloudyDay13.zox"));
- //model.Parse(worldUpdate, 8286770, 8344010, 207);
- //model = JsonConvert.DeserializeObject(File.ReadAllText("models/Aster_CloudyDay14.zox"));
- //model.Parse(worldUpdate, 8286770, 8344010, 333);
- //model = JsonConvert.DeserializeObject(File.ReadAllText("models/Aster_CloudyDay01.zox"));
- //model.Parse(worldUpdate, 8286644, 8344010, 333);
- //model = JsonConvert.DeserializeObject(File.ReadAllText("models/Aster_CloudyDay02.zox"));
- //model.Parse(worldUpdate, 8286118, 8344010, 333);
- //model = JsonConvert.DeserializeObject(File.ReadAllText("models/Aster_CloudyDay03.zox"));
- //model.Parse(worldUpdate, 8285992, 8344010, 333);
- //model = JsonConvert.DeserializeObject(File.ReadAllText("models/Aster_CloudyDay04.zox"));
- //model.Parse(worldUpdate, 8285992, 8344136, 333);
- //model = JsonConvert.DeserializeObject(File.ReadAllText("models/Aster_CloudyDay05.zox"));
- //model.Parse(worldUpdate, 8285992, 8344262, 333);
- //model = JsonConvert.DeserializeObject(File.ReadAllText("models/Aster_CloudyDay06.zox"));
- //model.Parse(worldUpdate, 8286118, 8344262, 333);
- //model = JsonConvert.DeserializeObject(File.ReadAllText("models/Aster_CloudyDay07.zox"));
- //model.Parse(worldUpdate, 8286118, 8344136, 333);
- //model = JsonConvert.DeserializeObject(File.ReadAllText("models/Aster_CloudyDay08.zox"));
- //model.Parse(worldUpdate, 8286244, 8344136, 333);
- //model = JsonConvert.DeserializeObject(File.ReadAllText("models/Aster_CloudyDay09.zox"));
- //model.Parse(worldUpdate, 8286244, 8344262, 333);
- //model = JsonConvert.DeserializeObject(File.ReadAllText("models/Aster_CloudyDay10.zox"));
- //model.Parse(worldUpdate, 8286770, 8344262, 333);
- #endregion
-
- udpClient = new UdpClient(new IPEndPoint(IPAddress.Any, port));
- new Thread(new ThreadStart(ListenUDP)).Start();
- tcpListener = new TcpListener(IPAddress.Any, port);
- tcpListener.Start();
- new Thread(new ThreadStart(ListenTCP)).Start();
- Log.PrintLn("loading completed");
- }
-
- private static void ListenTCP() {
- var player = new Player(tcpListener.AcceptTcpClient());
- new Thread(new ThreadStart(ListenTCP)).Start();
- Log.PrintLn(player.IP + " connected", ConsoleColor.Blue);
- try {
- while (true) ProcessPacket(player.reader.ReadByte(), player);
- }
- catch (IOException) {
- if (player.entity != null) {
- RemovePlayerEntity(player, false);
- }
- players.Remove(player);
- Log.PrintLn(player.IP + " disconnected", ConsoleColor.Red);
- }
- }
- private static void ListenUDP() {
- IPEndPoint source = null;
- while (true) {
- byte[] datagram = udpClient.Receive(ref source);
- var player = players.FirstOrDefault(x => (x.RemoteEndPoint).Equals(source));
- if (player != null && player.entity != null) {
- try {
- ProcessDatagram(datagram, player);
- }
- catch (IndexOutOfRangeException) {
- Kick(player, "invalid data received");
- }
- }
- }
- }
-
- private static void SendUDP(byte[] data, Player target) {
- udpClient.Send(data, data.Length, target.RemoteEndPoint);
- }
- private static void BroadcastUDP(byte[] data, Player toSkip = null) {
- foreach (var player in players) {
- if (player != toSkip) {
- SendUDP(data, player);
- }
- }
- }
-
- private static void ProcessPacket(byte packetID, Player source) {
- switch ((ServerPacketID)packetID) {
- case ServerPacketID.VersionCheck:
- #region VersionCheck
- source.writer.Write((byte)ServerPacketID.VersionCheck);
- if (source.reader.ReadInt32() != Config.bridgeVersion) {
- source.writer.Write(false);
- //close connection
- break;
- }
- source.writer.Write(true);
- players.Add(source);
- foreach (EntityUpdate entity in dynamicEntities.Values) {
- SendUDP(entity.CreateDatagram(), source);
- }
- break;
- #endregion
- case ServerPacketID.Login://login
- #region login
- string username = source.reader.ReadString();
- string password = source.reader.ReadString();
- source.MAC = source.reader.ReadString();
-
- AuthResponse authResponse;
- if (!players.Contains(source)) {
- //musnt login without checking bridge version first
- authResponse = AuthResponse.Unverified;
- }
- else if (source.entity != null) {
- //already logged in into another account
- authResponse = AuthResponse.UserAlreadyLoggedIn;
- }
- else if (players.FirstOrDefault(x => x.entity?.name == username) != null) {
- //another user is already logged into this account
- authResponse = AuthResponse.AccountAlreadyActive;
- }
- else {
- authResponse = Database.AuthUser(username, password, (int)source.IP.Address, source.MAC);
- }
- source.writer.Write((byte)ServerPacketID.Login);
- source.writer.Write((byte)authResponse);
- if (authResponse != AuthResponse.Success) break;
-
- source.entity = new EntityUpdate() {
- guid = AssignGuid(),
- name = username,
- };
- source.writer.Write((ushort)source.entity.guid);
- source.writer.Write(Config.mapseed);
-
- dynamicEntities.Add((ushort)source.entity.guid, source.entity);
-
- Log.PrintLn(source.IP + " logged in as " + username, ConsoleColor.Green);
- break;
- #endregion
- case ServerPacketID.Logout:
- #region logout
- if (source.entity == null) break;//not logged in
- RemovePlayerEntity(source, false);
- Log.PrintLn(source.IP + " logged out", ConsoleColor.Yellow);
- break;
- #endregion
- case ServerPacketID.Register:
- #region Register
- username = source.reader.ReadString();
- var email = source.reader.ReadString();
- password = source.reader.ReadString();
-
- RegisterResponse registerResponse;
- if (!Tools.alphaNumericRegex.IsMatch(username) || !Tools.validEmailRegex.IsMatch(email)) {
- registerResponse = RegisterResponse.InvalidInput;
- }
- else {
- registerResponse = Database.RegisterUser(username, email, password);
- }
- source.writer.Write((byte)ServerPacketID.Register);
- source.writer.Write((byte)registerResponse);
- if (registerResponse == RegisterResponse.Success) {
- Log.PrintLn(source.IP + " registered as " + username, ConsoleColor.Cyan);
- }
- break;
- #endregion
- default:
- Log.PrintLn($"unknown packetID {packetID} received from {source.IP}", ConsoleColor.Magenta);
- source.tcpClient.Close();
- break;
- }
- }
- private static void ProcessDatagram(byte[] datagram, Player source) {
- switch ((DatagramID)datagram[0]) {
- case DatagramID.DynamicUpdate:
- #region entityUpdate
- var entityUpdate = new EntityUpdate(datagram);
- #region antiCheat
- string ACmessage = AntiCheat.Inspect(entityUpdate, source.entity);
- if (ACmessage != null) Kick(source, ACmessage);
- #endregion
- #region announce
- if (entityUpdate.name != null) {
- //Announce.Join(entityUpdate.name, player.entityData.name, players);
- }
- #endregion
- #region pvp
- entityUpdate.entityFlags |= 1 << 5; //enable friendly fire flag for pvp
- #endregion
- #region tombstone
- if (entityUpdate.HP <= 0 && (source.entity.HP > 0 || source.entity.HP == null)) {
- var tombstone = new EntityUpdate() {
- guid = AssignGuid(),
- position = entityUpdate.position ?? source.entity.position,
- hostility = Hostility.Neutral,
- entityType = EntityType.None,
- appearance = new EntityUpdate.Appearance() {
- character_size = new FloatVector() {
- x = 1,
- y = 1,
- z = 1,
- },
- head_model = 2155,
- head_size = 1
- },
- HP = 100,
- name = "tombstone"
- };
- source.tomb = (ushort)tombstone.guid;
- BroadcastUDP(tombstone.CreateDatagram());
- }
- else if (source.entity.HP <= 0 && entityUpdate.HP > 0 && source.tomb != null) {
- var rde = new RemoveDynamicEntity() {
- Guid = (ushort)source.tomb,
- };
- BroadcastUDP(rde.data);
- }
- #endregion
- entityUpdate.Merge(source.entity);
- BroadcastUDP(entityUpdate.CreateDatagram(), source);
- break;
- #endregion
- case DatagramID.Attack:
- #region attack
- var attack = new Attack(datagram);
- source.lastTarget = attack.Target;
- var target = players.FirstOrDefault(p => p.entity?.guid == attack.Target);
- if (target != null) SendUDP(attack.data, target);
- break;
- #endregion
- case DatagramID.Projectile:
- #region Projectile
- var projectile = new Projectile(datagram);
- BroadcastUDP(projectile.data, source); //pass to all players except source
- break;
- #endregion
- case DatagramID.Proc:
- #region proc
- var proc = new Proc(datagram);
- BroadcastUDP(proc.data, source); //pass to all players except source
- break;
- #endregion
- case DatagramID.Chat:
- #region chat
- var chat = new Chat(datagram);
-
- if (chat.Text.StartsWith("/")) {
- var parameters = chat.Text.Substring(1).Split(" ");
- var command = parameters[0].ToLower();
- switch (command) {
- case "kick":
- case "btfo":
- case "ban":
- #region ban
- if (source.entity.name != "BLACKROCK") {
- Notify(source, "no permission");
- break;
- }
- if (parameters.Length == 1) {
- Notify(source, string.Format("usage example: /kick blackrock"));
- break;
- }
- target = players.FirstOrDefault(x => x.entity.name.Contains(parameters[1]));
- if (target == null) {
- Notify(source, "invalid target");
- break;
- };
- var reason = "no reason specified";
- if (parameters.Length > 2) {
- reason = parameters[2];
- }
- if (command == "kick") {
- Kick(target, reason);
- break;
- }
- target.writer.Write((byte)ServerPacketID.BTFO);
- target.writer.Write(reason);
- if (command == "ban") {
- Database.BanUser(target.entity.name, (int)target.IP.Address, target.MAC, reason);
- }
- RemovePlayerEntity(target, false);
- break;
- #endregion
- case "bleeding":
-
- break;
- case "time":
- #region time
- if (parameters.Length == 1) {
- Notify(source, string.Format("usage example: /time 12:00"));
- break;
- }
- var clock = parameters[1].Split(":");
- if (clock.Length < 2 ||
- !int.TryParse(clock[0], out int hour) ||
- !int.TryParse(clock[1], out int minute)) {
- Notify(source, string.Format("invalid syntax"));
- break;
- }
- var inGameTime = new InGameTime() {
- Milliseconds = (hour * 60 + minute) * 60000,
- };
- SendUDP(inGameTime.data, source);
- break;
- #endregion
- default:
- Notify(source, string.Format("unknown command '{0}'", parameters[0]));
- break;
- }
- break;
- }
- Log.Print(dynamicEntities[chat.Sender].name + ": ", ConsoleColor.Cyan);
- Log.PrintLn(chat.Text, ConsoleColor.White, false);
-
- BroadcastUDP(chat.data, null); //pass to all players
- break;
- #endregion
- case DatagramID.Interaction:
- #region interaction
- var interaction = new Interaction(datagram);
- BroadcastUDP(interaction.data, source); //pass to all players except source
- break;
- #endregion
- case DatagramID.RemoveDynamicEntity:
- #region removeDynamicEntity
- var remove = new RemoveDynamicEntity(datagram);
- RemovePlayerEntity(source, true);
- break;
- #endregion
- case DatagramID.SpecialMove:
- #region specialMove
- var specialMove = new SpecialMove(datagram);
- switch (specialMove.Id) {
- case SpecialMoveID.Taunt:
- target = players.FirstOrDefault(p => p.entity.guid == specialMove.Guid);
- if (target != null) {
- specialMove.Guid = (ushort)source.entity.guid;
- SendUDP(specialMove.data, target);
- }
- break;
- case SpecialMoveID.SmokeBomb:
- BroadcastUDP(specialMove.data, source);
- break;
- case SpecialMoveID.CursedArrow:
- case SpecialMoveID.ArrowRain:
- case SpecialMoveID.Shrapnel:
- case SpecialMoveID.IceWave:
- case SpecialMoveID.Confusion:
- case SpecialMoveID.ShadowStep:
- BroadcastUDP(specialMove.data);
- break;
- default:
- break;
- }
- break;
- #endregion
- case DatagramID.HolePunch:
- break;
- default:
- Log.PrintLn($"unknown DatagramID {datagram[0]} received from {source.IP}", ConsoleColor.Magenta);
- Kick(source, "invalid data received");
- break;
- }
- }
-
- public static void RemovePlayerEntity(Player player, bool createNewEntity) {
- var rde = new RemoveDynamicEntity() {
- Guid = (ushort)player.entity.guid,
- };
- BroadcastUDP(rde.data, player);
- if (player.tomb != null) {
- rde.Guid = (ushort)player.tomb;
- BroadcastUDP(rde.data);
- player.tomb = null;
- }
- if (createNewEntity) {
- player.entity = new EntityUpdate() {
- guid = player.entity.guid
- };
- }
- else {
- dynamicEntities.Remove((ushort)player.entity.guid);
- player.entity = null;
- }
- }
- public static void Kick(Player target, string reason) {
- Notify(target, "you got kicked: " + reason);
- target.writer.Write((byte)ServerPacketID.Kick);
- RemovePlayerEntity(target, true);
- }
-
- public static ushort AssignGuid() {
- ushort newGuid = 1;
- while (dynamicEntities.ContainsKey(newGuid)) newGuid++;
- return newGuid;
- }
-
- public static void Notify(Player target, string message) {
- var chat = new Chat() {
- Sender = 0,
- Text = message,
- };
- SendUDP(chat.data, target);
- }
- }
-}
diff --git a/Server/ServerCore.cs b/Server/ServerCore.cs
new file mode 100644
index 0000000..ada329e
--- /dev/null
+++ b/Server/ServerCore.cs
@@ -0,0 +1,281 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Net;
+using System.Net.Sockets;
+using System.IO;
+using Server.Database;
+
+using Microsoft.EntityFrameworkCore;
+
+using Resources;
+using Resources.Datagram;
+using Resources.Packet;
+
+namespace Server {
+ public static partial class ServerCore {
+ public static UdpClient udpClient;
+ public static TcpListener tcpListener;
+ public static List players = new List();
+ public static Dictionary dynamicEntities = new Dictionary();
+ public static UserDatabase userDatabase;
+
+ public static void Start(int port) {
+ Log.PrintLn("server starting...");
+ userDatabase = new UserDatabase();
+ userDatabase.Database.Migrate(); //Ensure database exists
+
+ udpClient = new UdpClient(new IPEndPoint(IPAddress.Any, port));
+ new Thread(new ThreadStart(ListenUDP)).Start();
+ tcpListener = new TcpListener(IPAddress.Any, port);
+ tcpListener.Start();
+ new Thread(new ThreadStart(ListenTCP)).Start();
+
+ Extensions.Extensions.Init();
+ Log.PrintLn("loading completed");
+ }
+
+ private static void ListenTCP() {
+ var player = new Player(tcpListener.AcceptTcpClient());
+ new Thread(new ThreadStart(ListenTCP)).Start();
+ Log.PrintLn(player.IP + " connected", ConsoleColor.Blue);
+ try {
+ while (true) ProcessPacket(player.reader.ReadByte(), player);
+ }
+ catch (IOException) {
+ if (player.entity != null) {
+ RemovePlayerEntity(player, false);
+ }
+ players.Remove(player);
+ Log.PrintLn(player.IP + " disconnected", ConsoleColor.Red);
+ }
+ }
+ private static void ListenUDP() {
+ IPEndPoint source = null;
+ while (true) {
+ byte[] datagram = udpClient.Receive(ref source);
+ var player = players.FirstOrDefault(x => (x.RemoteEndPoint).Equals(source));
+ if (player != null && player.entity != null) {
+ try {
+ ProcessDatagram(datagram, player);
+ }
+ catch (IndexOutOfRangeException) {
+ Kick(player, "invalid data received");
+ }
+ }
+ }
+ }
+
+ public static void SendUDP(byte[] data, Player target) {
+ udpClient.Send(data, data.Length, target.RemoteEndPoint);
+ }
+ public static void BroadcastUDP(byte[] data, Player toSkip = null) {
+ foreach (var player in players.ToList()) {
+ if (player != toSkip) {
+ SendUDP(data, player);
+ }
+ }
+ }
+
+ private static void ProcessPacket(byte packetID, Player source) {
+ switch ((ServerPacketID)packetID) {
+ case ServerPacketID.VersionCheck:
+ #region VersionCheck
+ source.writer.Write((byte)ServerPacketID.VersionCheck);
+ if (source.reader.ReadInt32() != Config.bridgeVersion) {
+ source.writer.Write(false);
+ //close connection
+ break;
+ }
+ source.writer.Write(true);
+ players.Add(source);
+ foreach (EntityUpdate entity in dynamicEntities.Values) {
+ SendUDP(entity.CreateDatagram(), source);
+ }
+ break;
+ #endregion
+ case ServerPacketID.Login://login
+ #region login
+ string username = source.reader.ReadString();
+ string password = source.reader.ReadString();
+ source.MAC = source.reader.ReadString();
+
+ AuthResponse authResponse;
+ if (!players.Contains(source)) {
+ //musnt login without checking bridge version first
+ authResponse = AuthResponse.Unverified;
+ }
+ else if (source.entity != null) {
+ //already logged in into another account
+ authResponse = AuthResponse.UserAlreadyLoggedIn;
+ }
+ else if (players.FirstOrDefault(x => x.entity?.name == username) != null) {
+ //another user is already logged into this account
+ authResponse = AuthResponse.AccountAlreadyActive;
+ }
+ else {
+ authResponse = userDatabase.AuthUser(username, password, (int)source.IP.Address, source.MAC);
+ }
+ source.writer.Write((byte)ServerPacketID.Login);
+ source.writer.Write((byte)authResponse);
+ if (authResponse != AuthResponse.Success) break;
+
+ source.entity = new EntityUpdate() {
+ guid = AssignGuid(),
+ name = username,
+ };
+ source.Permission = userDatabase.getRoleId(username);
+ source.writer.Write((ushort)source.entity.guid);
+ source.writer.Write(Config.mapseed);
+
+ dynamicEntities.Add((ushort)source.entity.guid, source.entity);
+
+ Log.PrintLn(source.IP + " logged in as " + username, ConsoleColor.Green);
+ break;
+ #endregion
+ case ServerPacketID.Logout:
+ #region logout
+ if (source.entity == null) break;//not logged in
+ RemovePlayerEntity(source, false);
+ Log.PrintLn(source.IP + " logged out", ConsoleColor.Yellow);
+ break;
+ #endregion
+ case ServerPacketID.Register:
+ #region Register
+ username = source.reader.ReadString();
+ var email = source.reader.ReadString();
+ password = source.reader.ReadString();
+
+ RegisterResponse registerResponse;
+ if (!Tools.alphaNumericRegex.IsMatch(username) || !Tools.validEmailRegex.IsMatch(email)) {
+ registerResponse = RegisterResponse.InvalidInput;
+ }
+ else {
+ registerResponse = userDatabase.RegisterUser(username, email, password);
+ }
+ source.writer.Write((byte)ServerPacketID.Register);
+ source.writer.Write((byte)registerResponse);
+ if (registerResponse == RegisterResponse.Success) {
+ Log.PrintLn(source.IP + " registered as " + username, ConsoleColor.Cyan);
+ }
+ break;
+ #endregion
+ default:
+ Log.PrintLn($"unknown packetID {packetID} received from {source.IP}", ConsoleColor.Magenta);
+ source.tcpClient.Close();
+ break;
+ }
+ }
+ private static void ProcessDatagram(byte[] datagram, Player source) {
+ switch ((DatagramID)datagram[0]) {
+ case DatagramID.DynamicUpdate:
+ #region entityUpdate
+ var entityUpdate = new EntityUpdate(datagram);
+ EntityUpdated?.Invoke(entityUpdate, source);
+ entityUpdate.Merge(source.entity);
+ BroadcastUDP(entityUpdate.CreateDatagram(), source);
+ break;
+ #endregion
+ case DatagramID.Attack:
+ #region attack
+ var attack = new Attack(datagram);
+ EntityAttacked?.Invoke(attack, source);
+ source.lastTarget = attack.Target;
+ var target = players.FirstOrDefault(p => p.entity?.guid == attack.Target);
+ if (target != null) SendUDP(attack.data, target);
+ break;
+ #endregion
+ case DatagramID.Projectile:
+ #region Projectile
+ var projectile = new Projectile(datagram);
+ ProjectileCreated?.Invoke(projectile, source);
+ BroadcastUDP(projectile.data, source); //pass to all players except source
+ break;
+ #endregion
+ case DatagramID.Proc:
+ #region proc
+ var proc = new Proc(datagram);
+ PassiveProcced?.Invoke(proc, source);
+ BroadcastUDP(proc.data, source); //pass to all players except source
+ break;
+ #endregion
+ case DatagramID.Chat:
+ #region chat
+ var chat = new Chat(datagram);
+ ChatMessageReceived?.Invoke(chat.Text, source);
+ Log.Print(dynamicEntities[chat.Sender].name + ": ", ConsoleColor.Cyan);
+ Log.PrintLn(chat.Text, ConsoleColor.White, false);
+ BroadcastUDP(chat.data, null); //pass to all players
+ break;
+ #endregion
+ case DatagramID.Interaction:
+ #region interaction
+ var interaction = new Interaction(datagram);
+ EntityInteracted?.Invoke(interaction, source);
+ BroadcastUDP(interaction.data, source); //pass to all players except source
+ break;
+ #endregion
+ case DatagramID.RemoveDynamicEntity:
+ #region removeDynamicEntity
+ var remove = new RemoveDynamicEntity(datagram);
+ EntityRemoved?.Invoke(remove, source);
+ RemovePlayerEntity(source, true);
+ break;
+ #endregion
+ case DatagramID.SpecialMove:
+ #region specialMove
+ var specialMove = new SpecialMove(datagram);
+ SpecialMoveUsed?.Invoke(specialMove, source);
+ break;
+ #endregion
+ case DatagramID.HolePunch:
+ break;
+ default:
+ Log.PrintLn($"unknown DatagramID {datagram[0]} received from {source.IP}", ConsoleColor.Magenta);
+ Kick(source, "invalid data received");
+ break;
+ }
+ }
+
+ public static void RemovePlayerEntity(Player player, bool createNewEntity) {
+ var rde = new RemoveDynamicEntity() {
+ Guid = (ushort)player.entity.guid,
+ };
+ BroadcastUDP(rde.data, player);
+ if (player.tomb != null) {
+ rde.Guid = (ushort)player.tomb;
+ BroadcastUDP(rde.data);
+ player.tomb = null;
+ }
+ if (createNewEntity) {
+ player.entity = new EntityUpdate() {
+ guid = player.entity.guid
+ };
+ }
+ else {
+ dynamicEntities.Remove((ushort)player.entity.guid);
+ player.entity = null;
+ }
+ }
+ public static void Kick(Player target, string reason) {
+ Notify(target, "you got kicked: " + reason);
+ target.writer.Write((byte)ServerPacketID.Kick);
+ RemovePlayerEntity(target, true);
+ }
+
+ public static ushort AssignGuid() {
+ ushort newGuid = 1;
+ while (dynamicEntities.ContainsKey(newGuid)) newGuid++;
+ return newGuid;
+ }
+
+ public static void Notify(Player target, string message) {
+ var chat = new Chat() {
+ Sender = 0,
+ Text = message,
+ };
+ SendUDP(chat.data, target);
+ }
+ }
+}
diff --git a/Server/ServerCore_Events.cs b/Server/ServerCore_Events.cs
new file mode 100644
index 0000000..f79930e
--- /dev/null
+++ b/Server/ServerCore_Events.cs
@@ -0,0 +1,28 @@
+using Resources;
+using Resources.Datagram;
+using Resources.Packet;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Server {
+ public delegate void EntityUpdatedEventHandler(EntityUpdate entityUpdate, Player source);
+ public delegate void EntityAttackedEventHandler(Attack datagram, Player source);
+ public delegate void ProjectileCreatedEventHandler(Projectile datagram, Player source);
+ public delegate void PassiveProccedEventHandler(Proc datagram, Player source);
+ public delegate void ChatMessageReceivedEventHandler(string message, Player source);
+ public delegate void EntityInteractedEventHandler(Interaction datagram, Player source);
+ public delegate void EntityRemovedEventHandler(RemoveDynamicEntity datagram, Player source);
+ public delegate void SpecialMoveUsedEventHandler(SpecialMove datagram, Player source);
+
+ public static partial class ServerCore {
+ public static event EntityUpdatedEventHandler EntityUpdated;
+ public static event EntityAttackedEventHandler EntityAttacked;
+ public static event ProjectileCreatedEventHandler ProjectileCreated;
+ public static event PassiveProccedEventHandler PassiveProcced;
+ public static event ChatMessageReceivedEventHandler ChatMessageReceived;
+ public static event EntityInteractedEventHandler EntityInteracted;
+ public static event EntityRemovedEventHandler EntityRemoved;
+ public static event SpecialMoveUsedEventHandler SpecialMoveUsed;
+ }
+}