mirror of
https://github.com/ClassiCube/ClassiCube.git
synced 2025-09-17 19:45:23 -04:00
Mostly port NetworkProcessor to C
This commit is contained in:
parent
b5f2c1f9f3
commit
1eca5206f2
@ -243,7 +243,6 @@
|
||||
<Compile Include="Network\Enums.cs" />
|
||||
<Compile Include="Network\IServerConnection.cs" />
|
||||
<Compile Include="Network\NetworkProcessor.cs" />
|
||||
<Compile Include="Network\NetworkProcessor.Helpers.cs" />
|
||||
<Compile Include="Network\Utils\AsyncDownloader.cs" />
|
||||
<Compile Include="Network\Utils\NetReader.cs" />
|
||||
<Compile Include="Network\Utils\FixedBufferStream.cs" />
|
||||
|
@ -1,135 +0,0 @@
|
||||
// Copyright 2014-2017 ClassicalSharp | Licensed under BSD-3
|
||||
using System;
|
||||
using ClassicalSharp.Entities;
|
||||
using OpenTK;
|
||||
using OpenTK.Input;
|
||||
|
||||
namespace ClassicalSharp.Network {
|
||||
|
||||
public partial class NetworkProcessor : IServerConnection {
|
||||
|
||||
internal bool addEntityHack = true;
|
||||
internal byte[] needRemoveNames = new byte[256 >> 3];
|
||||
|
||||
public override void SendChat(string text) {
|
||||
if (String.IsNullOrEmpty(text)) return;
|
||||
|
||||
while (text.Length > Utils.StringLength) {
|
||||
classic.WriteChat(text.Substring(0, Utils.StringLength), true);
|
||||
SendPacket();
|
||||
text = text.Substring(Utils.StringLength);
|
||||
}
|
||||
classic.WriteChat(text, false);
|
||||
SendPacket();
|
||||
}
|
||||
|
||||
public override void SendPosition(Vector3 pos, float rotY, float headX) {
|
||||
classic.WritePosition(pos, rotY, headX);
|
||||
SendPacket();
|
||||
}
|
||||
|
||||
public override void SendPlayerClick(MouseButton button, bool buttonDown, byte targetId, PickedPos pos) {
|
||||
cpe.WritePlayerClick(button, buttonDown, targetId, pos);
|
||||
SendPacket();
|
||||
}
|
||||
|
||||
|
||||
internal void CheckName(byte id, ref string displayName, ref string skinName) {
|
||||
displayName = Utils.RemoveEndPlus(displayName);
|
||||
skinName = Utils.RemoveEndPlus(skinName);
|
||||
skinName = Utils.StripColours(skinName);
|
||||
|
||||
// Server is only allowed to change our own name colours.
|
||||
if (id != EntityList.SelfID) return;
|
||||
if (Utils.StripColours(displayName) != game.Username)
|
||||
displayName = game.Username;
|
||||
if (skinName == "")
|
||||
skinName = game.Username;
|
||||
}
|
||||
|
||||
internal void AddEntity(byte id, string displayName, string skinName, bool readPosition) {
|
||||
if (id != EntityList.SelfID) {
|
||||
Entity oldEntity = game.Entities.List[id];
|
||||
if (oldEntity != null) game.Entities.RemoveEntity(id);
|
||||
|
||||
game.Entities.List[id] = new NetPlayer(displayName, skinName, game);
|
||||
game.EntityEvents.RaiseAdded(id);
|
||||
} else {
|
||||
game.LocalPlayer.Despawn();
|
||||
// Always reset the texture here, in case other network players are using the same skin as us.
|
||||
// In that case, we don't want the fetching of new skin for us to delete the texture used by them.
|
||||
game.LocalPlayer.ResetSkin();
|
||||
game.LocalPlayer.fetchedSkin = false;
|
||||
|
||||
game.LocalPlayer.DisplayName = displayName;
|
||||
game.LocalPlayer.SkinName = skinName;
|
||||
game.LocalPlayer.UpdateName();
|
||||
}
|
||||
|
||||
if (!readPosition) return;
|
||||
classic.ReadAbsoluteLocation(id, false);
|
||||
if (id != EntityList.SelfID) return;
|
||||
|
||||
LocalPlayer p = game.LocalPlayer;
|
||||
p.Spawn = p.Position;
|
||||
p.SpawnRotY = p.HeadY;
|
||||
p.SpawnHeadX = p.HeadX;
|
||||
}
|
||||
|
||||
internal void RemoveEntity(byte id) {
|
||||
Entity entity = game.Entities.List[id];
|
||||
if (entity == null) return;
|
||||
if (id != EntityList.SelfID) game.Entities.RemoveEntity(id);
|
||||
|
||||
// See comment about some servers in HandleAddEntity
|
||||
int mask = id >> 3, bit = 1 << (id & 0x7);
|
||||
if (!addEntityHack || (needRemoveNames[mask] & bit) == 0) return;
|
||||
|
||||
RemoveTablistEntry(id);
|
||||
needRemoveNames[mask] &= (byte)~bit;
|
||||
}
|
||||
|
||||
internal void UpdateLocation(byte playerId, LocationUpdate update, bool interpolate) {
|
||||
Entity entity = game.Entities.List[playerId];
|
||||
if (entity != null) {
|
||||
entity.SetLocation(update, interpolate);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal void AddTablistEntry(byte id, string playerName, string listName,
|
||||
string groupName, byte groupRank) {
|
||||
TabListEntry oldInfo = TabList.Entries[id];
|
||||
TabListEntry info = new TabListEntry(playerName, listName, groupName, groupRank);
|
||||
TabList.Entries[id] = info;
|
||||
|
||||
if (oldInfo != null) {
|
||||
// Only redraw the tab list if something changed.
|
||||
if (info.PlayerName != oldInfo.PlayerName || info.ListName != oldInfo.ListName ||
|
||||
info.Group != oldInfo.Group || info.GroupRank != oldInfo.GroupRank) {
|
||||
game.EntityEvents.RaiseTabListEntryChanged(id);
|
||||
}
|
||||
} else {
|
||||
game.EntityEvents.RaiseTabEntryAdded(id);
|
||||
}
|
||||
}
|
||||
|
||||
internal void RemoveTablistEntry(byte id) {
|
||||
game.EntityEvents.RaiseTabEntryRemoved(id);
|
||||
TabList.Entries[id] = null;
|
||||
}
|
||||
|
||||
internal void DisableAddEntityHack() {
|
||||
if (!addEntityHack) return;
|
||||
addEntityHack = false;
|
||||
|
||||
for (int id = 0; id < EntityList.MaxCount; id++) {
|
||||
int mask = id >> 3, bit = 1 << (id & 0x7);
|
||||
if ((needRemoveNames[mask] & bit) == 0) continue;
|
||||
|
||||
RemoveTablistEntry((byte)id);
|
||||
needRemoveNames[mask] &= (byte)~bit;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -11,10 +11,12 @@ using ClassicalSharp.Network;
|
||||
using ClassicalSharp.Textures;
|
||||
using ClassicalSharp.Network.Protocols;
|
||||
using BlockID = System.UInt16;
|
||||
using OpenTK;
|
||||
using OpenTK.Input;
|
||||
|
||||
namespace ClassicalSharp.Network {
|
||||
|
||||
public partial class NetworkProcessor : IServerConnection {
|
||||
public sealed class NetworkProcessor : IServerConnection {
|
||||
|
||||
public NetworkProcessor(Game window) {
|
||||
game = window;
|
||||
@ -64,6 +66,28 @@ namespace ClassicalSharp.Network {
|
||||
lastPacket = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
public override void SendChat(string text) {
|
||||
if (String.IsNullOrEmpty(text)) return;
|
||||
|
||||
while (text.Length > Utils.StringLength) {
|
||||
classic.WriteChat(text.Substring(0, Utils.StringLength), true);
|
||||
SendPacket();
|
||||
text = text.Substring(Utils.StringLength);
|
||||
}
|
||||
classic.WriteChat(text, false);
|
||||
SendPacket();
|
||||
}
|
||||
|
||||
public override void SendPosition(Vector3 pos, float rotY, float headX) {
|
||||
classic.WritePosition(pos, rotY, headX);
|
||||
SendPacket();
|
||||
}
|
||||
|
||||
public override void SendPlayerClick(MouseButton button, bool buttonDown, byte targetId, PickedPos pos) {
|
||||
cpe.WritePlayerClick(button, buttonDown, targetId, pos);
|
||||
SendPacket();
|
||||
}
|
||||
|
||||
public override void Dispose() {
|
||||
if (Disconnected) return;
|
||||
game.UserEvents.BlockChanged -= BlockChanged;
|
||||
@ -159,7 +183,7 @@ namespace ClassicalSharp.Network {
|
||||
UsingPlayerClick = false;
|
||||
SupportsPartialMessages = false;
|
||||
SupportsFullCP437 = false;
|
||||
addEntityHack = true;
|
||||
IProtocol.addEntityHack = true;
|
||||
|
||||
for (int i = 0; i < handlers.Length; i++) {
|
||||
handlers[i] = null;
|
||||
@ -201,8 +225,9 @@ namespace ClassicalSharp.Network {
|
||||
|
||||
public override void OnNewMap(Game game) {
|
||||
// wipe all existing entity states
|
||||
for (int i = 0; i < EntityList.MaxCount; i++)
|
||||
RemoveEntity((byte)i);
|
||||
for (int i = 0; i < EntityList.MaxCount; i++) {
|
||||
classic.RemoveEntity((byte)i);
|
||||
}
|
||||
}
|
||||
|
||||
double testAcc = 0;
|
||||
|
@ -135,21 +135,21 @@ namespace ClassicalSharp.Network.Protocols {
|
||||
|
||||
// Some server software will declare they support ExtPlayerList, but send AddEntity then AddPlayerName
|
||||
// we need to workaround this case by removing all the tab names we added for the AddEntity packets
|
||||
net.DisableAddEntityHack();
|
||||
net.AddTablistEntry((byte)id, playerName, listName, groupName, groupRank);
|
||||
DisableAddEntityHack();
|
||||
AddTablistEntry((byte)id, playerName, listName, groupName, groupRank);
|
||||
}
|
||||
|
||||
void HandleExtAddEntity() {
|
||||
byte id = reader.ReadUInt8();
|
||||
string displayName = reader.ReadString();
|
||||
string skinName = reader.ReadString();
|
||||
net.CheckName(id, ref displayName, ref skinName);
|
||||
net.AddEntity(id, displayName, skinName, false);
|
||||
CheckName(id, ref displayName, ref skinName);
|
||||
AddEntity(id, displayName, skinName, false);
|
||||
}
|
||||
|
||||
void HandleExtRemovePlayerName() {
|
||||
int id = reader.ReadInt16() & 0xFF;
|
||||
net.RemoveTablistEntry((byte)id);
|
||||
RemoveTablistEntry((byte)id);
|
||||
}
|
||||
|
||||
void HandleMakeSelection() {
|
||||
@ -257,8 +257,8 @@ namespace ClassicalSharp.Network.Protocols {
|
||||
byte id = reader.ReadUInt8();
|
||||
string displayName = reader.ReadString();
|
||||
string skinName = reader.ReadString();
|
||||
net.CheckName(id, ref displayName, ref skinName);
|
||||
net.AddEntity(id, displayName, skinName, true);
|
||||
CheckName(id, ref displayName, ref skinName);
|
||||
AddEntity(id, displayName, skinName, true);
|
||||
}
|
||||
|
||||
const int bulkCount = 256;
|
||||
@ -444,24 +444,24 @@ namespace ClassicalSharp.Network.Protocols {
|
||||
writer.WriteUInt8((byte)pos.Face);
|
||||
}
|
||||
|
||||
internal void WriteExtInfo(string appName, int extensionsCount) {
|
||||
void WriteExtInfo(string appName, int extensionsCount) {
|
||||
writer.WriteUInt8((byte)Opcode.CpeExtInfo);
|
||||
writer.WriteString(appName);
|
||||
writer.WriteInt16((short)extensionsCount);
|
||||
}
|
||||
|
||||
internal void WriteExtEntry(string extensionName, int extensionVersion) {
|
||||
void WriteExtEntry(string extensionName, int extensionVersion) {
|
||||
writer.WriteUInt8((byte)Opcode.CpeExtEntry);
|
||||
writer.WriteString(extensionName);
|
||||
writer.WriteInt32(extensionVersion);
|
||||
}
|
||||
|
||||
internal void WriteCustomBlockSupportLevel(byte version) {
|
||||
void WriteCustomBlockSupportLevel(byte version) {
|
||||
writer.WriteUInt8((byte)Opcode.CpeCustomBlockSupportLevel);
|
||||
writer.WriteUInt8(version);
|
||||
}
|
||||
|
||||
internal void WriteTwoWayPing(bool serverToClient, ushort data) {
|
||||
void WriteTwoWayPing(bool serverToClient, ushort data) {
|
||||
writer.WriteUInt8((byte)Opcode.CpeTwoWayPing);
|
||||
writer.WriteUInt8((byte)(serverToClient ? 1 : 0));
|
||||
writer.WriteInt16((short)data);
|
||||
|
@ -225,14 +225,14 @@ namespace ClassicalSharp.Network.Protocols {
|
||||
byte id = reader.ReadUInt8();
|
||||
string name = reader.ReadString();
|
||||
string skin = name;
|
||||
net.CheckName(id, ref name, ref skin);
|
||||
net.AddEntity(id, name, skin, true);
|
||||
CheckName(id, ref name, ref skin);
|
||||
AddEntity(id, name, skin, true);
|
||||
|
||||
if (!net.addEntityHack) return;
|
||||
if (!addEntityHack) return;
|
||||
// Workaround for some servers that declare they support ExtPlayerList,
|
||||
// but doesn't send ExtAddPlayerName packets.
|
||||
net.AddTablistEntry(id, name, name, "Players", 0);
|
||||
net.needRemoveNames[id >> 3] |= (byte)(1 << (id & 0x7));
|
||||
AddTablistEntry(id, name, name, "Players", 0);
|
||||
needRemoveNames[id >> 3] |= (byte)(1 << (id & 0x7));
|
||||
}
|
||||
|
||||
void HandleEntityTeleport() {
|
||||
@ -250,7 +250,7 @@ namespace ClassicalSharp.Network.Protocols {
|
||||
float rotY = (float)Utils.PackedToDegrees(reader.ReadUInt8());
|
||||
float headX = (float)Utils.PackedToDegrees(reader.ReadUInt8());
|
||||
LocationUpdate update = LocationUpdate.MakePosAndOri(v, rotY, headX, true);
|
||||
net.UpdateLocation(id, update, true);
|
||||
UpdateLocation(id, update, true);
|
||||
}
|
||||
|
||||
void HandleRelPositionUpdate() {
|
||||
@ -261,7 +261,7 @@ namespace ClassicalSharp.Network.Protocols {
|
||||
v.Z = reader.ReadInt8() / 32f;
|
||||
|
||||
LocationUpdate update = LocationUpdate.MakePos(v, true);
|
||||
net.UpdateLocation(id, update, true);
|
||||
UpdateLocation(id, update, true);
|
||||
}
|
||||
|
||||
void HandleOrientationUpdate() {
|
||||
@ -270,12 +270,12 @@ namespace ClassicalSharp.Network.Protocols {
|
||||
float headX = (float)Utils.PackedToDegrees(reader.ReadUInt8());
|
||||
|
||||
LocationUpdate update = LocationUpdate.MakeOri(rotY, headX);
|
||||
net.UpdateLocation(id, update, true);
|
||||
UpdateLocation(id, update, true);
|
||||
}
|
||||
|
||||
void HandleRemoveEntity() {
|
||||
byte id = reader.ReadUInt8();
|
||||
net.RemoveEntity(id);
|
||||
RemoveEntity(id);
|
||||
}
|
||||
|
||||
void HandleMessage() {
|
||||
@ -309,7 +309,7 @@ namespace ClassicalSharp.Network.Protocols {
|
||||
|
||||
if (id == EntityList.SelfID) receivedFirstPosition = true;
|
||||
LocationUpdate update = LocationUpdate.MakePosAndOri(P, rotY, headX, false);
|
||||
net.UpdateLocation(id, update, interpolate);
|
||||
UpdateLocation(id, update, interpolate);
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
// Copyright 2014-2017 ClassicalSharp | Licensed under BSD-3
|
||||
using System;
|
||||
using ClassicalSharp.Entities;
|
||||
|
||||
namespace ClassicalSharp.Network.Protocols {
|
||||
|
||||
@ -18,5 +19,106 @@ namespace ClassicalSharp.Network.Protocols {
|
||||
|
||||
public abstract void Reset();
|
||||
public abstract void Tick();
|
||||
|
||||
protected internal static bool addEntityHack = true;
|
||||
protected static byte[] needRemoveNames = new byte[256 >> 3];
|
||||
|
||||
protected void CheckName(byte id, ref string displayName, ref string skinName) {
|
||||
displayName = Utils.RemoveEndPlus(displayName);
|
||||
skinName = Utils.RemoveEndPlus(skinName);
|
||||
skinName = Utils.StripColours(skinName);
|
||||
|
||||
// Server is only allowed to change our own name colours.
|
||||
if (id != EntityList.SelfID) return;
|
||||
if (Utils.StripColours(displayName) != game.Username)
|
||||
displayName = game.Username;
|
||||
if (skinName == "")
|
||||
skinName = game.Username;
|
||||
}
|
||||
|
||||
protected void AddEntity(byte id, string displayName, string skinName, bool readPosition) {
|
||||
if (id != EntityList.SelfID) {
|
||||
Entity oldEntity = game.Entities.List[id];
|
||||
if (oldEntity != null) game.Entities.RemoveEntity(id);
|
||||
|
||||
game.Entities.List[id] = new NetPlayer(displayName, skinName, game);
|
||||
game.EntityEvents.RaiseAdded(id);
|
||||
} else {
|
||||
game.LocalPlayer.Despawn();
|
||||
// Always reset the texture here, in case other network players are using the same skin as us.
|
||||
// In that case, we don't want the fetching of new skin for us to delete the texture used by them.
|
||||
game.LocalPlayer.ResetSkin();
|
||||
game.LocalPlayer.fetchedSkin = false;
|
||||
|
||||
game.LocalPlayer.DisplayName = displayName;
|
||||
game.LocalPlayer.SkinName = skinName;
|
||||
game.LocalPlayer.UpdateName();
|
||||
}
|
||||
|
||||
if (!readPosition) return;
|
||||
net.classic.ReadAbsoluteLocation(id, false);
|
||||
if (id != EntityList.SelfID) return;
|
||||
|
||||
LocalPlayer p = game.LocalPlayer;
|
||||
p.Spawn = p.Position;
|
||||
p.SpawnRotY = p.HeadY;
|
||||
p.SpawnHeadX = p.HeadX;
|
||||
}
|
||||
|
||||
internal void RemoveEntity(byte id) {
|
||||
Entity entity = game.Entities.List[id];
|
||||
if (entity == null) return;
|
||||
if (id != EntityList.SelfID) game.Entities.RemoveEntity(id);
|
||||
|
||||
// See comment about some servers in HandleAddEntity
|
||||
int mask = id >> 3, bit = 1 << (id & 0x7);
|
||||
if (!addEntityHack || (needRemoveNames[mask] & bit) == 0) return;
|
||||
|
||||
RemoveTablistEntry(id);
|
||||
needRemoveNames[mask] &= (byte)~bit;
|
||||
}
|
||||
|
||||
protected void UpdateLocation(byte playerId, LocationUpdate update, bool interpolate) {
|
||||
Entity entity = game.Entities.List[playerId];
|
||||
if (entity != null) {
|
||||
entity.SetLocation(update, interpolate);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected void AddTablistEntry(byte id, string playerName, string listName,
|
||||
string groupName, byte groupRank) {
|
||||
TabListEntry oldInfo = TabList.Entries[id];
|
||||
TabListEntry info = new TabListEntry(playerName, listName, groupName, groupRank);
|
||||
TabList.Entries[id] = info;
|
||||
|
||||
if (oldInfo != null) {
|
||||
// Only redraw the tab list if something changed.
|
||||
if (info.PlayerName != oldInfo.PlayerName || info.ListName != oldInfo.ListName ||
|
||||
info.Group != oldInfo.Group || info.GroupRank != oldInfo.GroupRank) {
|
||||
game.EntityEvents.RaiseTabListEntryChanged(id);
|
||||
}
|
||||
} else {
|
||||
game.EntityEvents.RaiseTabEntryAdded(id);
|
||||
}
|
||||
}
|
||||
|
||||
protected void RemoveTablistEntry(byte id) {
|
||||
game.EntityEvents.RaiseTabEntryRemoved(id);
|
||||
TabList.Entries[id] = null;
|
||||
}
|
||||
|
||||
protected void DisableAddEntityHack() {
|
||||
if (!addEntityHack) return;
|
||||
addEntityHack = false;
|
||||
|
||||
for (int id = 0; id < EntityList.MaxCount; id++) {
|
||||
int mask = id >> 3, bit = 1 << (id & 0x7);
|
||||
if ((needRemoveNames[mask] & bit) == 0) continue;
|
||||
|
||||
RemoveTablistEntry((byte)id);
|
||||
needRemoveNames[mask] &= (byte)~bit;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -209,6 +209,7 @@
|
||||
<ClInclude Include="Input.h" />
|
||||
<ClInclude Include="InputHandler.h" />
|
||||
<ClInclude Include="Menus.h" />
|
||||
<ClInclude Include="PacketHandlers.h" />
|
||||
<ClInclude Include="Physics.h" />
|
||||
<ClInclude Include="Inventory.h" />
|
||||
<ClInclude Include="IsometricDrawer.h" />
|
||||
|
@ -366,6 +366,9 @@
|
||||
<ClInclude Include="Audio.h">
|
||||
<Filter>Header Files\Game</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="PacketHandlers.h">
|
||||
<Filter>Header Files\Network</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="Noise.c">
|
||||
|
@ -1,3 +1,4 @@
|
||||
#include "PacketHandlers.h"
|
||||
#include "Deflate.h"
|
||||
#include "Utils.h"
|
||||
#include "ServerConnection.h"
|
||||
@ -617,7 +618,7 @@ void CPE_CustomBlockLevel(Stream* stream) {
|
||||
UInt8 supportLevel = Stream_ReadU8(stream);
|
||||
stream = ServerConnection_WriteStream();
|
||||
CPE_WriteCustomBlockLevel(stream, 1);
|
||||
ServerConnection_SendPacket();
|
||||
Net_SendPacket();
|
||||
Game_UseCPEBlocks = true;
|
||||
Event_RaiseVoid(&BlockEvents_PermissionsChanged);
|
||||
}
|
||||
@ -951,7 +952,7 @@ void CPE_TwoWayPing(Stream* stream) {
|
||||
|
||||
stream = ServerConnection_WriteStream();
|
||||
CPE_WriteTwoWayPing(stream, true, data); /* server to client reply */
|
||||
ServerConnection_SendPacket();
|
||||
Net_SendPacket();
|
||||
}
|
||||
|
||||
void CPE_SetInventoryOrder(Stream* stream) {
|
||||
@ -965,7 +966,7 @@ void CPE_SetInventoryOrder(Stream* stream) {
|
||||
}
|
||||
|
||||
#define Ext_Deg2Packed(x) ((Int16)((x) * 65536.0f / 360.0f))
|
||||
void CPE_WritePlayerClick(Stream* stream, MouseButton button, bool buttonDown, UInt8 targetId, PickedPos pos) {
|
||||
void CPE_WritePlayerClick(Stream* stream, MouseButton button, bool buttonDown, UInt8 targetId, PickedPos* pos) {
|
||||
Entity* p = &LocalPlayer_Instance.Base;
|
||||
Stream_WriteU8(stream, OPCODE_CPE_PLAYER_CLICK);
|
||||
Stream_WriteU8(stream, button);
|
||||
@ -974,13 +975,13 @@ void CPE_WritePlayerClick(Stream* stream, MouseButton button, bool buttonDown, U
|
||||
Stream_WriteI16_BE(stream, Ext_Deg2Packed(p->HeadX));
|
||||
|
||||
Stream_WriteU8(stream, targetId);
|
||||
Stream_WriteI16_BE(stream, pos.BlockPos.X);
|
||||
Stream_WriteI16_BE(stream, pos.BlockPos.Y);
|
||||
Stream_WriteI16_BE(stream, pos.BlockPos.Z);
|
||||
Stream_WriteI16_BE(stream, pos->BlockPos.X);
|
||||
Stream_WriteI16_BE(stream, pos->BlockPos.Y);
|
||||
Stream_WriteI16_BE(stream, pos->BlockPos.Z);
|
||||
|
||||
UInt8 face = 255;
|
||||
/* Our own face values differ from CPE block face */
|
||||
switch (pos.ClosestFace) {
|
||||
switch (pos->ClosestFace) {
|
||||
case FACE_XMAX: face = 0; break;
|
||||
case FACE_XMIN: face = 1; break;
|
||||
case FACE_YMAX: face = 2; break;
|
||||
@ -1021,7 +1022,7 @@ void CPE_SendCpeExtInfoReply(void) {
|
||||
Stream* stream = ServerConnection_WriteStream();
|
||||
|
||||
CPE_WriteExtInfo(stream, &ServerConnection_AppName, count);
|
||||
ServerConnection_SendPacket();
|
||||
Net_SendPacket();
|
||||
Int32 i, ver;
|
||||
|
||||
for (i = 0; i < Array_Elems(cpe_clientExtensions); i++) {
|
||||
@ -1037,7 +1038,7 @@ void CPE_SendCpeExtInfoReply(void) {
|
||||
}
|
||||
|
||||
CPE_WriteExtEntry(stream, &name, ver);
|
||||
ServerConnection_SendPacket();
|
||||
Net_SendPacket();
|
||||
}
|
||||
}
|
||||
|
||||
|
21
src/Client/PacketHandlers.h
Normal file
21
src/Client/PacketHandlers.h
Normal file
@ -0,0 +1,21 @@
|
||||
#ifndef CC_PACKETHANDLERS_H
|
||||
#define CC_PACKETHANDLERS_H
|
||||
#include "Input.h"
|
||||
#include "String.h"
|
||||
#include "Vectors.h"
|
||||
/* Implements network protocol handlers for original classic, CPE, and WoM textures.
|
||||
Copyright 2014-2017 ClassicalSharp | Licensed under BSD-3
|
||||
*/
|
||||
|
||||
typedef struct PickedPos_ PickedPos;
|
||||
typedef struct Stream_ Stream;
|
||||
void Handlers_Reset(void);
|
||||
void Handlers_Tick(void);
|
||||
|
||||
void Handlers_RemoveEntity(EntityID id);
|
||||
void Classic_WriteChat(Stream* stream, STRING_PURE String* text, bool partial);
|
||||
void Classic_WritePosition(Stream* stream, Vector3 pos, Real32 rotY, Real32 headX);
|
||||
void Classic_WriteSetBlock(Stream* stream, Int32 x, Int32 y, Int32 z, bool place, BlockID block);
|
||||
void Classic_WriteLogin(Stream* stream, STRING_PURE String* username, STRING_PURE String* verKey);
|
||||
void CPE_WritePlayerClick(Stream* stream, MouseButton button, bool buttonDown, UInt8 targetId, PickedPos* pos);
|
||||
#endif
|
@ -65,4 +65,11 @@ void Platform_SetBitmap(Bitmap* bmp);
|
||||
void Platform_ReleaseBitmap(void);
|
||||
Size2D Platform_MeasureText(DrawTextArgs* args);
|
||||
void Platform_DrawText(DrawTextArgs* args, Int32 x, Int32 y);
|
||||
|
||||
void Platform_SocketCreate(void** socket);
|
||||
ReturnCode Platform_SocketConnect(void* socket, STRING_PURE String* ip, Int32 port);
|
||||
ReturnCode Platform_SocketRead(void* socket, UInt8* buffer, UInt32 count, UInt32* modified);
|
||||
ReturnCode Platform_SocketWrite(void* socket, UInt8* buffer, UInt32 count, UInt32* modified);
|
||||
ReturnCode Platform_SocketClose(void* socket);
|
||||
ReturnCode Platform_SocketAvailable(void* socket, UInt32* available);
|
||||
#endif
|
@ -16,7 +16,13 @@
|
||||
#include "Camera.h"
|
||||
#include "TexturePack.h"
|
||||
#include "Menus.h"
|
||||
#include "ErrorHandler.h"
|
||||
#include "PacketHandlers.h"
|
||||
#include "Inventory.h"
|
||||
|
||||
/*########################################################################################################################*
|
||||
*-----------------------------------------------------Common handlers-----------------------------------------------------*
|
||||
*#########################################################################################################################*/
|
||||
UInt8 ServerConnection_ServerNameBuffer[String_BufferSize(STRING_SIZE)];
|
||||
String ServerConnection_ServerName = String_FromEmptyArray(ServerConnection_ServerNameBuffer);
|
||||
UInt8 ServerConnection_ServerMOTDBuffer[String_BufferSize(STRING_SIZE)];
|
||||
@ -119,6 +125,9 @@ void ServerConnection_EndGeneration(void) {
|
||||
}
|
||||
|
||||
|
||||
/*########################################################################################################################*
|
||||
*--------------------------------------------------------PingList---------------------------------------------------------*
|
||||
*#########################################################################################################################*/
|
||||
typedef struct PingEntry_ {
|
||||
Int64 TimeSent, TimeReceived;
|
||||
UInt16 Data;
|
||||
@ -176,6 +185,9 @@ Int32 PingList_AveragePingMs(void) {
|
||||
}
|
||||
|
||||
|
||||
/*########################################################################################################################*
|
||||
*-------------------------------------------------Singleplayer connection-------------------------------------------------*
|
||||
*#########################################################################################################################*/
|
||||
void SPConnection_Connect(STRING_PURE String* ip, Int32 port) {
|
||||
String logName = String_FromConst("Singleplayer");
|
||||
Chat_SetLogName(&logName);
|
||||
@ -257,27 +269,237 @@ void ServerConnection_InitSingleplayer(void) {
|
||||
|
||||
ServerConnection_ReadStream = NULL;
|
||||
ServerConnection_WriteStream = NULL;
|
||||
ServerConnection_SendPacket = NULL;
|
||||
}
|
||||
|
||||
|
||||
void Net_Set(UInt8 opcode, Net_Handler handler, UInt16 size) {
|
||||
/*########################################################################################################################*
|
||||
*--------------------------------------------------Multiplayer connection-------------------------------------------------*
|
||||
*#########################################################################################################################*/
|
||||
void* net_socket;
|
||||
DateTime net_lastPacket;
|
||||
UInt8 net_lastOpcode;
|
||||
Stream net_readStream;
|
||||
Stream net_writeStream;
|
||||
UInt8 net_readBuffer[4096 * 5];
|
||||
UInt8 net_writeBuffer[131];
|
||||
Int32 net_maxHandledPacket;
|
||||
bool net_writeFailed;
|
||||
Int32 net_ticks;
|
||||
|
||||
void MPConnection_Connect(STRING_PURE String* ip, Int32 port) {
|
||||
Platform_SocketCreate(&net_socket);
|
||||
Event_RegisterBlock(&UserEvents_BlockChanged, NULL, MPConnection_BlockChanged);
|
||||
ServerConnection_Disconnected = false;
|
||||
|
||||
ReturnCode result = Platform_SocketConnect(net_socket, &Game_IPAddress, Game_Port);
|
||||
if (result != 0) {
|
||||
ErrorHandler.LogError("connecting to server", ex);
|
||||
game.Disconnect("Failed to connect to " + address + ":" + port,
|
||||
"You failed to connect to the server. It's probably down!");
|
||||
ServerConnection_Free();
|
||||
return;
|
||||
}
|
||||
|
||||
String streamName = String_FromConst("network socket");
|
||||
Stream_ReadonlyMemory(&net_readStream, net_readBuffer, sizeof(net_readBuffer), &streamName);
|
||||
Stream_WriteonlyMemory(&net_writeStream, net_writeBuffer, sizeof(net_writeBuffer), &streamName);
|
||||
|
||||
Handlers_Reset();
|
||||
Classic_WriteLogin(&net_writeStream, &Game_Username, &Game_Mppass);
|
||||
Net_SendPacket();
|
||||
Platform_CurrentUTCTime(&net_lastPacket);
|
||||
}
|
||||
|
||||
void MPConnection_SendChat(STRING_PURE String* text) {
|
||||
if (text->length == 0) return;
|
||||
String remaining = *text;
|
||||
|
||||
while (remaining.length > STRING_SIZE) {
|
||||
String portion = String_UNSAFE_Substring(&remaining, 0, STRING_SIZE);
|
||||
Classic_WriteChat(&net_writeStream, &portion, true);
|
||||
Net_SendPacket();
|
||||
remaining = String_UNSAFE_SubstringAt(&remaining, STRING_SIZE);
|
||||
}
|
||||
|
||||
Classic_WriteChat(&net_writeStream, &remaining, false);
|
||||
Net_SendPacket();
|
||||
}
|
||||
|
||||
void MPConnection_SendPosition(Vector3 pos, Real32 rotY, Real32 headX) {
|
||||
Classic_WritePosition(&net_writeStream, pos, rotY, headX);
|
||||
Net_SendPacket();
|
||||
}
|
||||
|
||||
void MPConnection_SendPlayerClick(MouseButton button, bool buttonDown, EntityID targetId, PickedPos* pos) {
|
||||
CPE_WritePlayerClick(&net_writeStream, button, buttonDown, targetId, pos);
|
||||
Net_SendPacket();
|
||||
}
|
||||
|
||||
double testAcc = 0;
|
||||
void CheckDisconnection(double delta) {
|
||||
testAcc += delta;
|
||||
if (testAcc < 1) return;
|
||||
testAcc = 0;
|
||||
|
||||
if (net_writeFailed || (socket.Poll(1000, SelectMode.SelectRead) && socket.Available == 0)) {
|
||||
String title = String_FromConst("Disconnected!");
|
||||
String reason = String_FromConst("You've lost connection to the server");
|
||||
Game_Disconnect(&title, &reason);
|
||||
}
|
||||
}
|
||||
|
||||
void MPConnection_Tick(ScheduledTask* task) {
|
||||
if (ServerConnection_Disconnected) return;
|
||||
DateTime now; Platform_CurrentUTCTime(&now);
|
||||
|
||||
if (DateTime_MsBetween(&net_lastPacket, &now) >= 30 * 1000) {
|
||||
CheckDisconnection(task.Interval);
|
||||
}
|
||||
if (ServerConnection_Disconnected) return;
|
||||
|
||||
UInt32 modified = 0;
|
||||
ReturnCode recvResult = Platform_SocketAvailable(net_socket, &modified);
|
||||
if (recvResult == 0 && modified > 0) {
|
||||
/* NOTE: Always using a read call that is a multiple of 4096 (appears to?) improve read performance */
|
||||
UInt8* src = net_readBuffer + net_readStream.Meta_Mem_Count;
|
||||
recvResult = Platform_SocketRead(net_socket, src, 4096 * 4, &modified);
|
||||
net_readStream.Meta_Mem_Count += modified;
|
||||
}
|
||||
if (recvResult != 0) {
|
||||
ErrorHandler.LogError("reading packets", ex);
|
||||
game.Disconnect("&eLost connection to the server", "I/O error when reading packets");
|
||||
return;
|
||||
}
|
||||
|
||||
while (net_readStream.Meta_Mem_Count > 0) {
|
||||
UInt8 opcode = net_readStream.Meta_Mem_Buffer[0];
|
||||
/* Workaround for older D3 servers which wrote one byte too many for HackControl packets */
|
||||
if (cpeData.needD3Fix && net_lastOpcode == OPCODE_CPE_HACK_CONTROL && (opcode == 0x00 || opcode == 0xFF)) {
|
||||
Platform_LogConst("Skipping invalid HackControl byte from D3 server");
|
||||
Stream_Skip(&net_readStream, 1);
|
||||
|
||||
LocalPlayer* p = &LocalPlayer_Instance;
|
||||
p->Physics.JumpVel = 0.42f; /* assume default jump height */
|
||||
p->Physics.ServerJumpVel = p->Physics.JumpVel;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (opcode > net_maxHandledPacket) {
|
||||
throw new InvalidOperationException("Invalid opcode: " + opcode);
|
||||
}
|
||||
if (net_readStream.Meta_Mem_Count < Net_PacketSizes[opcode]) break;
|
||||
|
||||
Stream_Skip(&net_readStream, 1); /* remove opcode */
|
||||
net_lastOpcode = opcode;
|
||||
Net_Handler handler = Net_Handlers[opcode];
|
||||
Platform_CurrentUTCTime(&net_lastPacket);
|
||||
|
||||
if (handler == NULL) {
|
||||
throw new InvalidOperationException("Unsupported opcode: " + opcode);
|
||||
}
|
||||
handler(&net_readStream);
|
||||
}
|
||||
|
||||
reader.RemoveProcessed();
|
||||
|
||||
/* Network is ticked 60 times a second. We only send position updates 20 times a second */
|
||||
if ((net_ticks % 3) == 0) {
|
||||
ServerConnection_CheckAsyncResources();
|
||||
Handlers_Tick();
|
||||
/* Have any packets been written? */
|
||||
if (net_writeStream.Meta_Mem_Buffer != net_writeBuffer) Net_SendPacket();
|
||||
}
|
||||
net_ticks++;
|
||||
}
|
||||
|
||||
void Net_Set(UInt8 opcode, Net_Handler handler, UInt16 packetSize) {
|
||||
Net_Handlers[opcode] = handler;
|
||||
Net_PacketSizes[opcode] = size;
|
||||
maxHandledPacket = Math.Max(opcode, maxHandledPacket);
|
||||
Net_PacketSizes[opcode] = packetSize;
|
||||
net_maxHandledPacket = max(opcode, net_maxHandledPacket);
|
||||
}
|
||||
|
||||
void Net_SendPacket(void) {
|
||||
if (!ServerConnection_Disconnected) {
|
||||
/* NOTE: Not immediately disconnecting here, as otherwise we sometimes miss out on kick messages */
|
||||
UInt32 count = (UInt32)(net_writeStream.Meta_Mem_Buffer - net_writeBuffer), modified = 0;
|
||||
|
||||
while (count > 0) {
|
||||
ReturnCode result = Platform_SocketWrite(net_socket, net_writeBuffer, count, &modified);
|
||||
if (result != 0 || modified == 0) { net_writeFailed = true; break; }
|
||||
count -= modified;
|
||||
}
|
||||
}
|
||||
|
||||
net_writeStream.Meta_Mem_Buffer = net_writeBuffer;
|
||||
net_writeStream.Meta_Mem_Count = sizeof(net_writeBuffer);
|
||||
}
|
||||
|
||||
void MPConnection_BlockChanged(void* obj, Vector3I coords, BlockID oldBlock, BlockID block) {
|
||||
Vector3I p = coords;
|
||||
if (block == BLOCK_AIR) {
|
||||
block = Inventory_SelectedBlock;
|
||||
Classic_WriteSetBlock(&net_writeStream, p.X, p.Y, p.Z, false, block);
|
||||
} else {
|
||||
Classic_WriteSetBlock(&net_writeStream, p.X, p.Y, p.Z, true, block);
|
||||
}
|
||||
Net_SendPacket();
|
||||
}
|
||||
|
||||
Stream* MPConnection_ReadStream(void) { return &net_readStream; }
|
||||
Stream* MPConnection_WriteStream(void) { return &net_writeStream; }
|
||||
void ServerConnection_InitMultiplayer(void) {
|
||||
ServerConnection_ResetState();
|
||||
ServerConnection_IsSinglePlayer = false;
|
||||
|
||||
ServerConnection_Connect = MPConnection_Connect;
|
||||
ServerConnection_SendChat = MPConnection_SendChat;
|
||||
ServerConnection_SendPosition = MPConnection_SendPosition;
|
||||
ServerConnection_SendPlayerClick = MPConnection_SendPlayerClick;
|
||||
ServerConnection_Tick = MPConnection_Tick;
|
||||
|
||||
ServerConnection_ReadStream = MPConnection_ReadStream;
|
||||
ServerConnection_WriteStream = MPConnection_WriteStream;
|
||||
}
|
||||
|
||||
|
||||
void MPConnection_OnNewMap(void) {
|
||||
if (!ServerConnection_IsSinglePlayer) return;
|
||||
/* wipe all existing entity states */
|
||||
Int32 i;
|
||||
for (i = 0; i < ENTITIES_MAX_COUNT; i++) {
|
||||
classic.RemoveEntity((byte)i);
|
||||
}
|
||||
}
|
||||
|
||||
void MPConnection_Reset(void) {
|
||||
if (!ServerConnection_IsSinglePlayer) return;
|
||||
Int32 i;
|
||||
for (i = 0; i < OPCODE_COUNT; i++) {
|
||||
Net_Handlers[i] = NULL;
|
||||
Net_PacketSizes[i] = 0;
|
||||
}
|
||||
|
||||
net_writeFailed = false;
|
||||
net_maxHandledPacket = 0;
|
||||
Handlers_Reset();
|
||||
ServerConnection_Free();
|
||||
}
|
||||
|
||||
void ServerConnection_Free(void) {
|
||||
if (ServerConnection_IsSinglePlayer) {
|
||||
Physics_Free();
|
||||
} else {
|
||||
|
||||
if (ServerConnection_Disconnected) return;
|
||||
Event_UnregisterBlock(&UserEvents_BlockChanged, NULL, MPConnection_BlockChanged);
|
||||
Platform_SocketClose(net_socket);
|
||||
ServerConnection_Disconnected = true;
|
||||
}
|
||||
}
|
||||
|
||||
IGameComponent ServerConnection_MakeComponent(void) {
|
||||
IGameComponent comp = IGameComponent_MakeEmpty();
|
||||
comp.OnNewMap = MPConnection_OnNewMap;
|
||||
comp.Reset = MPConnection_Reset;
|
||||
comp.Free = ServerConnection_Free;
|
||||
return comp;
|
||||
}
|
@ -7,52 +7,54 @@
|
||||
Copyright 2014-2017 ClassicalSharp | Licensed under BSD-3
|
||||
*/
|
||||
|
||||
#define OPCODE_HANDSHAKE 0
|
||||
#define OPCODE_PING 1
|
||||
#define OPCODE_LEVEL_INIT 2
|
||||
#define OPCODE_LEVEL_DATA_CHUNK 3
|
||||
#define OPCODE_LEVEL_FINALISE 4
|
||||
#define OPCODE_SET_BLOCK_CLIENT 5
|
||||
#define OPCODE_SET_BLOCK 6
|
||||
#define OPCODE_ADD_ENTITY 7
|
||||
#define OPCODE_ENTITY_TELEPORT 8
|
||||
#define OPCODE_RELPOS_AND_ORIENTATION_UPDATE 9
|
||||
#define OPCODE_RELPOS_UPDATE 10
|
||||
#define OPCODE_ORIENTATION_UPDATE 11
|
||||
#define OPCODE_REMOVE_ENTITY 12
|
||||
#define OPCODE_MESSAGE 13
|
||||
#define OPCODE_KICK 14
|
||||
#define OPCODE_SET_PERMISSION 15
|
||||
enum OPCODE_ {
|
||||
OPCODE_HANDSHAKE,
|
||||
OPCODE_PING,
|
||||
OPCODE_LEVEL_INIT,
|
||||
OPCODE_LEVEL_DATA_CHUNK,
|
||||
OPCODE_LEVEL_FINALISE,
|
||||
OPCODE_SET_BLOCK_CLIENT,
|
||||
OPCODE_SET_BLOCK,
|
||||
OPCODE_ADD_ENTITY,
|
||||
OPCODE_ENTITY_TELEPORT,
|
||||
OPCODE_RELPOS_AND_ORIENTATION_UPDATE,
|
||||
OPCODE_RELPOS_UPDATE,
|
||||
OPCODE_ORIENTATION_UPDATE,
|
||||
OPCODE_REMOVE_ENTITY,
|
||||
OPCODE_MESSAGE ,
|
||||
OPCODE_KICK,
|
||||
OPCODE_SET_PERMISSION,
|
||||
|
||||
#define OPCODE_CPE_EXT_INFO 16
|
||||
#define OPCODE_CPE_EXT_ENTRY 17
|
||||
#define OPCODE_CPE_SET_CLICK_DISTANCE 18
|
||||
#define OPCODE_CPE_CUSTOM_BLOCK_LEVEL 19
|
||||
#define OPCODE_CPE_HOLD_THIS 20
|
||||
#define OPCODE_CPE_SET_TEXT_HOTKEY 21
|
||||
#define OPCODE_CPE_EXT_ADD_PLAYER_NAME 22
|
||||
#define OPCODE_CPE_EXT_ADD_ENTITY 23
|
||||
#define OPCODE_CPE_EXT_REMOVE_PLAYER_NAME 24
|
||||
#define OPCODE_CPE_ENV_SET_COLOR 25
|
||||
#define OPCODE_CPE_MAKE_SELECTION 26
|
||||
#define OPCODE_CPE_REMOVE_SELECTION 27
|
||||
#define OPCODE_CPE_SET_BLOCK_PERMISSION 28
|
||||
#define OPCODE_CPE_SET_MODEL 29
|
||||
#define OPCODE_CPE_ENV_SET_MAP_APPEARANCE 30
|
||||
#define OPCODE_CPE_ENV_SET_WEATHER 31
|
||||
#define OPCODE_CPE_HACK_CONTROL 32
|
||||
#define OPCODE_CPE_EXT_ADD_ENTITY2 33
|
||||
#define OPCODE_CPE_PLAYER_CLICK 34
|
||||
#define OPCODE_CPE_DEFINE_BLOCK 35
|
||||
#define OPCODE_CPE_UNDEFINE_BLOCK 36
|
||||
#define OPCODE_CPE_DEFINE_BLOCK_EXT 37
|
||||
#define OPCODE_CPE_BULK_BLOCK_UPDATE 38
|
||||
#define OPCODE_CPE_SET_TEXT_COLOR 39
|
||||
#define OPCODE_CPE_ENV_SET_MAP_URL 40
|
||||
#define OPCODE_CPE_ENV_SET_MAP_PROPERTY 41
|
||||
#define OPCODE_CPE_SET_ENTITY_PROPERTY 42
|
||||
#define OPCODE_CPE_TWO_WAY_PING 43
|
||||
#define OPCODE_CPE_SET_INVENTORY_ORDER 44
|
||||
OPCODE_CPE_EXT_INFO,
|
||||
OPCODE_CPE_EXT_ENTRY,
|
||||
OPCODE_CPE_SET_CLICK_DISTANCE,
|
||||
OPCODE_CPE_CUSTOM_BLOCK_LEVEL,
|
||||
OPCODE_CPE_HOLD_THIS,
|
||||
OPCODE_CPE_SET_TEXT_HOTKEY,
|
||||
OPCODE_CPE_EXT_ADD_PLAYER_NAME,
|
||||
OPCODE_CPE_EXT_ADD_ENTITY,
|
||||
OPCODE_CPE_EXT_REMOVE_PLAYER_NAME,
|
||||
OPCODE_CPE_ENV_SET_COLOR,
|
||||
OPCODE_CPE_MAKE_SELECTION,
|
||||
OPCODE_CPE_REMOVE_SELECTION,
|
||||
OPCODE_CPE_SET_BLOCK_PERMISSION,
|
||||
OPCODE_CPE_SET_MODEL,
|
||||
OPCODE_CPE_ENV_SET_MAP_APPEARANCE,
|
||||
OPCODE_CPE_ENV_SET_WEATHER,
|
||||
OPCODE_CPE_HACK_CONTROL,
|
||||
OPCODE_CPE_EXT_ADD_ENTITY2,
|
||||
OPCODE_CPE_PLAYER_CLICK,
|
||||
OPCODE_CPE_DEFINE_BLOCK,
|
||||
OPCODE_CPE_UNDEFINE_BLOCK,
|
||||
OPCODE_CPE_DEFINE_BLOCK_EXT,
|
||||
OPCODE_CPE_BULK_BLOCK_UPDATE,
|
||||
OPCODE_CPE_SET_TEXT_COLOR,
|
||||
OPCODE_CPE_ENV_SET_MAP_URL,
|
||||
OPCODE_CPE_ENV_SET_MAP_PROPERTY,
|
||||
OPCODE_CPE_SET_ENTITY_PROPERTY,
|
||||
OPCODE_CPE_TWO_WAY_PING,
|
||||
OPCODE_CPE_SET_INVENTORY_ORDER,
|
||||
};
|
||||
|
||||
typedef struct PickedPos_ PickedPos;
|
||||
typedef struct Stream_ Stream;
|
||||
@ -73,7 +75,6 @@ void (*ServerConnection_SendPlayerClick)(MouseButton button, bool isDown, Entity
|
||||
void (*ServerConnection_Tick)(ScheduledTask* task);
|
||||
Stream* (*ServerConnection_ReadStream)(void);
|
||||
Stream* (*ServerConnection_WriteStream)(void);
|
||||
void (*ServerConnection_SendPacket)(void);
|
||||
|
||||
/* Whether the network processor is currently disconnected from the server. */
|
||||
bool ServerConnection_Disconnected;
|
||||
@ -99,4 +100,5 @@ typedef void (*Net_Handler)(Stream* stream);
|
||||
UInt16 Net_PacketSizes[OPCODE_COUNT];
|
||||
Net_Handler Net_Handlers[OPCODE_COUNT];
|
||||
void Net_Set(UInt8 opcode, Net_Handler handler, UInt16 size);
|
||||
void Net_SendPacket(void);
|
||||
#endif
|
@ -72,6 +72,10 @@ ReturnCode Stream_Skip(Stream* stream, UInt32 count) {
|
||||
return count > 0;
|
||||
}
|
||||
|
||||
ReturnCode Stream_UnsupportedIO(Stream* stream, UInt8* data, UInt32 count, UInt32* modified) {
|
||||
*modified = 0; return 1;
|
||||
}
|
||||
|
||||
|
||||
/*########################################################################################################################*
|
||||
*-------------------------------------------------------FileStream--------------------------------------------------------*
|
||||
@ -112,9 +116,6 @@ ReturnCode Stream_PortionRead(Stream* stream, UInt8* data, UInt32 count, UInt32*
|
||||
stream->Meta_Portion_Count -= *modified;
|
||||
return code;
|
||||
}
|
||||
ReturnCode Stream_PortionWrite(Stream* stream, UInt8* data, UInt32 count, UInt32* modified) {
|
||||
*modified = 0; return 1;
|
||||
}
|
||||
ReturnCode Stream_PortionClose(Stream* stream) { return 0; }
|
||||
ReturnCode Stream_PortionSeek(Stream* stream, Int32 offset, Int32 seekType) { return 1; }
|
||||
|
||||
@ -124,7 +125,7 @@ void Stream_ReadonlyPortion(Stream* stream, Stream* underlying, UInt32 len) {
|
||||
stream->Meta_Portion_Count = len;
|
||||
|
||||
stream->Read = Stream_PortionRead;
|
||||
stream->Write = Stream_PortionWrite;
|
||||
stream->Write = Stream_UnsupportedIO;
|
||||
stream->Close = Stream_PortionClose;
|
||||
stream->Seek = Stream_PortionSeek;
|
||||
}
|
||||
@ -143,17 +144,33 @@ ReturnCode Stream_MemoryRead(Stream* stream, UInt8* data, UInt32 count, UInt32*
|
||||
return 0;
|
||||
}
|
||||
|
||||
ReturnCode Stream_MemoryWrite(Stream* stream, UInt8* data, UInt32 count, UInt32* modified) {
|
||||
count = min(count, stream->Meta_Mem_Count);
|
||||
if (count > 0) { Platform_MemCpy(stream->Meta_Mem_Buffer, data, count); }
|
||||
|
||||
stream->Meta_Mem_Buffer += count;
|
||||
stream->Meta_Mem_Count -= count;
|
||||
*modified = count;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Stream_ReadonlyMemory(Stream* stream, void* data, UInt32 len, STRING_PURE String* name) {
|
||||
Stream_SetName(stream, name);
|
||||
stream->Meta_Mem_Buffer = data;
|
||||
stream->Meta_Mem_Count = len;
|
||||
|
||||
stream->Read = Stream_MemoryRead;
|
||||
stream->Write = Stream_PortionWrite;
|
||||
stream->Write = Stream_UnsupportedIO;
|
||||
stream->Close = Stream_PortionClose;
|
||||
stream->Seek = Stream_PortionSeek;
|
||||
}
|
||||
|
||||
void Stream_WriteonlyMemory(Stream* stream, void* data, UInt32 len, STRING_PURE String* name) {
|
||||
Stream_ReadonlyMemory(stream, data, len, name);
|
||||
stream->Read = Stream_UnsupportedIO;
|
||||
stream->Write = Stream_MemoryWrite;
|
||||
}
|
||||
|
||||
|
||||
/*########################################################################################################################*
|
||||
*-------------------------------------------------Read/Write primitives---------------------------------------------------*
|
||||
|
@ -39,6 +39,8 @@ void Stream_FromFile(Stream* stream, void* file, STRING_PURE String* name);
|
||||
/* Readonly Stream wrapping another Stream, only allows reading up to 'len' bytes from the wrapped stream. */
|
||||
void Stream_ReadonlyPortion(Stream* stream, Stream* underlying, UInt32 len);
|
||||
void Stream_ReadonlyMemory(Stream* stream, void* data, UInt32 len, STRING_PURE String* name);
|
||||
void Stream_WriteonlyMemory(Stream* stream, void* data, UInt32 len, STRING_PURE String* name);
|
||||
|
||||
|
||||
UInt8 Stream_ReadU8(Stream* stream);
|
||||
#define Stream_ReadI8(stream) ((Int8)Stream_ReadU8(stream))
|
||||
|
@ -10,6 +10,8 @@
|
||||
#define NOIME
|
||||
#include <Windows.h>
|
||||
#include <stdlib.h>
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
|
||||
HDC hdc;
|
||||
HANDLE heap;
|
||||
@ -28,6 +30,10 @@ void Platform_Init(void) {
|
||||
|
||||
stopwatch_highResolution = QueryPerformanceFrequency(&stopwatch_freq);
|
||||
|
||||
WSADATA wsaData;
|
||||
ReturnCode wsaResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
|
||||
ErrorHandler_CheckOrFail(wsaResult, "WSAStartup failed");
|
||||
|
||||
UInt32 deviceNum = 0;
|
||||
/* Get available video adapters and enumerate all monitors */
|
||||
DISPLAY_DEVICEA device = { 0 };
|
||||
@ -61,6 +67,7 @@ void Platform_Init(void) {
|
||||
void Platform_Free(void) {
|
||||
HeapDestroy(heap);
|
||||
DeleteDC(hdc);
|
||||
WSACleanup();
|
||||
}
|
||||
|
||||
void Platform_Exit(ReturnCode code) {
|
||||
@ -384,3 +391,53 @@ void Platform_DrawText(DrawTextArgs* args, Int32 x, Int32 y) {
|
||||
&r, DT_NOPREFIX | DT_SINGLELINE | DT_NOCLIP);
|
||||
SelectObject(hDC, oldFont);
|
||||
}
|
||||
|
||||
|
||||
void Platform_SocketCreate(void** socketResult) {
|
||||
*socketResult = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
if (*socketResult == INVALID_SOCKET) {
|
||||
ErrorHandler_FailWithCode(WSAGetLastError(), "Failed to create socket");
|
||||
}
|
||||
}
|
||||
|
||||
ReturnCode Platform_SocketConnect(void* socket, STRING_PURE String* ip, Int32 port) {
|
||||
struct sockaddr_in addr;
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr.s_addr = inet_addr(ip->buffer);
|
||||
addr.sin_port = htons((UInt16)port);
|
||||
|
||||
ReturnCode result = connect(socket, (SOCKADDR*)(&addr), sizeof(addr));
|
||||
return result == SOCKET_ERROR ? WSAGetLastError() : 0;
|
||||
}
|
||||
|
||||
ReturnCode Platform_SocketRead(void* socket, UInt8* buffer, UInt32 count, UInt32* modified) {
|
||||
Int32 recvCount = recv(socket, buffer, count, 0);
|
||||
if (recvCount == SOCKET_ERROR) {
|
||||
*modified = 0; return WSAGetLastError();
|
||||
} else {
|
||||
*modified = recvCount; return 0;
|
||||
}
|
||||
}
|
||||
|
||||
ReturnCode Platform_SocketWrite(void* socket, UInt8* buffer, UInt32 count, UInt32* modified) {
|
||||
Int32 sentCount = send(socket, buffer, count, 0);
|
||||
if (sentCount == SOCKET_ERROR) {
|
||||
*modified = 0; return WSAGetLastError();
|
||||
} else {
|
||||
*modified = sentCount; return 0;
|
||||
}
|
||||
}
|
||||
|
||||
ReturnCode Platform_SocketClose(void* socket) {
|
||||
ReturnCode result = 0;
|
||||
ReturnCode result1 = shutdown(socket, SD_BOTH);
|
||||
if (result1 == SOCKET_ERROR) result = WSAGetLastError();
|
||||
|
||||
ReturnCode result2 = closesocket(socket);
|
||||
if (result2 == SOCKET_ERROR) result = WSAGetLastError();
|
||||
return result;
|
||||
}
|
||||
|
||||
ReturnCode Platform_SocketAvailable(void* socket, UInt32* available) {
|
||||
return ioctlsocket(socket, FIONBIO, available);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user