mirror of
https://github.com/ClassiCube/ClassiCube.git
synced 2025-09-16 11:06:06 -04:00
Make connect non-blocking, partially addresses #521
This commit is contained in:
parent
1102ec3a10
commit
e6a044ce22
@ -347,7 +347,8 @@ namespace ClassicalSharp.Gui.Screens {
|
|||||||
if (HandlesAllInput) { // text input bar
|
if (HandlesAllInput) { // text input bar
|
||||||
if (key == game.Mapping(KeyBind.SendChat) || key == Key.KeypadEnter || key == game.Mapping(KeyBind.PauseOrExit)) {
|
if (key == game.Mapping(KeyBind.SendChat) || key == Key.KeypadEnter || key == game.Mapping(KeyBind.PauseOrExit)) {
|
||||||
SetHandlesAllInput(false);
|
SetHandlesAllInput(false);
|
||||||
game.CursorVisible = false;
|
// when underlying screen is HUD, user is interacting with the world normally
|
||||||
|
game.CursorVisible = game.Gui.UnderlyingScreen != game.Gui.hudScreen;
|
||||||
game.Camera.RegrabMouse();
|
game.Camera.RegrabMouse();
|
||||||
game.Keyboard.KeyRepeat = false;
|
game.Keyboard.KeyRepeat = false;
|
||||||
|
|
||||||
|
@ -74,8 +74,8 @@ namespace ClassicalSharp.Gui.Screens {
|
|||||||
|
|
||||||
if (!reconnect.Disabled && reconnect.Contains(mouseX, mouseY)) {
|
if (!reconnect.Disabled && reconnect.Contains(mouseX, mouseY)) {
|
||||||
string connect = "Connecting to " + game.IPAddress + ":" + game.Port + "..";
|
string connect = "Connecting to " + game.IPAddress + ":" + game.Port + "..";
|
||||||
game.Gui.SetNewScreen(new LoadingMapScreen(game, connect, ""));
|
game.Gui.SetNewScreen(new LoadingScreen(game, connect, ""));
|
||||||
game.Server.Connect(game.IPAddress, game.Port);
|
game.Server.BeginConnect();
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -10,10 +10,10 @@ using ClassicalSharp.Textures;
|
|||||||
using OpenTK.Input;
|
using OpenTK.Input;
|
||||||
|
|
||||||
namespace ClassicalSharp.Gui.Screens {
|
namespace ClassicalSharp.Gui.Screens {
|
||||||
public class LoadingMapScreen : Screen {
|
public class LoadingScreen : Screen {
|
||||||
|
|
||||||
readonly Font font;
|
readonly Font font;
|
||||||
public LoadingMapScreen(Game game, string title, string message) : base(game) {
|
public LoadingScreen(Game game, string title, string message) : base(game) {
|
||||||
this.title = title;
|
this.title = title;
|
||||||
this.message = message;
|
this.message = message;
|
||||||
font = new Font(game.FontName, 16);
|
font = new Font(game.FontName, 16);
|
||||||
@ -34,7 +34,7 @@ namespace ClassicalSharp.Gui.Screens {
|
|||||||
game.Graphics.Fog = false;
|
game.Graphics.Fog = false;
|
||||||
ContextRecreated();
|
ContextRecreated();
|
||||||
|
|
||||||
game.WorldEvents.MapLoading += MapLoading;
|
game.WorldEvents.Loading += Loading;
|
||||||
game.Graphics.ContextLost += ContextLost;
|
game.Graphics.ContextLost += ContextLost;
|
||||||
game.Graphics.ContextRecreated += ContextRecreated;
|
game.Graphics.ContextRecreated += ContextRecreated;
|
||||||
}
|
}
|
||||||
@ -63,7 +63,7 @@ namespace ClassicalSharp.Gui.Screens {
|
|||||||
font.Dispose();
|
font.Dispose();
|
||||||
ContextLost();
|
ContextLost();
|
||||||
|
|
||||||
game.WorldEvents.MapLoading -= MapLoading;
|
game.WorldEvents.Loading -= Loading;
|
||||||
game.Graphics.ContextLost -= ContextLost;
|
game.Graphics.ContextLost -= ContextLost;
|
||||||
game.Graphics.ContextRecreated -= ContextRecreated;
|
game.Graphics.ContextRecreated -= ContextRecreated;
|
||||||
}
|
}
|
||||||
@ -73,7 +73,7 @@ namespace ClassicalSharp.Gui.Screens {
|
|||||||
titleWidget.Reposition();
|
titleWidget.Reposition();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MapLoading(object sender, MapLoadingEventArgs e) {
|
void Loading(object sender, LoadingEventArgs e) {
|
||||||
progress = e.Progress;
|
progress = e.Progress;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -160,7 +160,7 @@ namespace ClassicalSharp.Gui.Screens {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class GeneratingMapScreen : LoadingMapScreen {
|
public class GeneratingMapScreen : LoadingScreen {
|
||||||
|
|
||||||
string lastState;
|
string lastState;
|
||||||
IMapGenerator gen;
|
IMapGenerator gen;
|
||||||
|
@ -10,8 +10,8 @@ namespace ClassicalSharp.Events {
|
|||||||
public void RaiseOnNewMap() { Raise(OnNewMap); }
|
public void RaiseOnNewMap() { Raise(OnNewMap); }
|
||||||
|
|
||||||
/// <summary> Raised when a portion of the world is read and decompressed, or generated. </summary>
|
/// <summary> Raised when a portion of the world is read and decompressed, or generated. </summary>
|
||||||
public event EventHandler<MapLoadingEventArgs> MapLoading;
|
public event EventHandler<LoadingEventArgs> Loading;
|
||||||
public void RaiseMapLoading(float progress) { loadingArgs.Progress = progress; Raise(MapLoading, loadingArgs); }
|
public void RaiseLoading(float progress) { loadingArgs.Progress = progress; Raise(Loading, loadingArgs); }
|
||||||
|
|
||||||
/// <summary> Raised when new world has finished loading and the player can now interact with it. </summary>
|
/// <summary> Raised when new world has finished loading and the player can now interact with it. </summary>
|
||||||
public event EventHandler OnNewMapLoaded;
|
public event EventHandler OnNewMapLoaded;
|
||||||
@ -21,11 +21,11 @@ namespace ClassicalSharp.Events {
|
|||||||
public event EventHandler<EnvVarEventArgs> EnvVariableChanged;
|
public event EventHandler<EnvVarEventArgs> EnvVariableChanged;
|
||||||
public void RaiseEnvVariableChanged(EnvVar envVar) { envArgs.Var = envVar; Raise(EnvVariableChanged, envArgs); }
|
public void RaiseEnvVariableChanged(EnvVar envVar) { envArgs.Var = envVar; Raise(EnvVariableChanged, envArgs); }
|
||||||
|
|
||||||
MapLoadingEventArgs loadingArgs = new MapLoadingEventArgs();
|
LoadingEventArgs loadingArgs = new LoadingEventArgs();
|
||||||
EnvVarEventArgs envArgs = new EnvVarEventArgs();
|
EnvVarEventArgs envArgs = new EnvVarEventArgs();
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class MapLoadingEventArgs : EventArgs {
|
public sealed class LoadingEventArgs : EventArgs {
|
||||||
|
|
||||||
/// <summary> Percentage of the map that has been fully decompressed, or generated. </summary>
|
/// <summary> Percentage of the map that has been fully decompressed, or generated. </summary>
|
||||||
public float Progress;
|
public float Progress;
|
||||||
|
@ -162,8 +162,8 @@ namespace ClassicalSharp {
|
|||||||
MapBordersRenderer.UseLegacyMode(true);
|
MapBordersRenderer.UseLegacyMode(true);
|
||||||
EnvRenderer.UseLegacyMode(true);
|
EnvRenderer.UseLegacyMode(true);
|
||||||
}
|
}
|
||||||
Gui.SetNewScreen(new LoadingMapScreen(this, connectString, ""));
|
Gui.SetNewScreen(new LoadingScreen(this, connectString, ""));
|
||||||
Server.Connect(IPAddress, Port);
|
Server.BeginConnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExtractInitialTexturePack() {
|
void ExtractInitialTexturePack() {
|
||||||
|
@ -21,8 +21,8 @@ namespace ClassicalSharp {
|
|||||||
|
|
||||||
public bool IsSinglePlayer;
|
public bool IsSinglePlayer;
|
||||||
|
|
||||||
/// <summary> Opens a connection to the given IP address and port, and prepares the initial state of the client. </summary>
|
/// <summary> Opens a connection to the server, and prepares the initial state of the client. </summary>
|
||||||
public abstract void Connect(IPAddress address, int port);
|
public abstract void BeginConnect();
|
||||||
|
|
||||||
public abstract void SendChat(string text);
|
public abstract void SendChat(string text);
|
||||||
|
|
||||||
|
@ -31,27 +31,52 @@ namespace ClassicalSharp.Network {
|
|||||||
public NetReader reader;
|
public NetReader reader;
|
||||||
public NetWriter writer;
|
public NetWriter writer;
|
||||||
|
|
||||||
|
bool connecting = false;
|
||||||
|
DateTime connectTimeout;
|
||||||
|
const int timeoutSecs = 15;
|
||||||
|
|
||||||
internal ClassicProtocol classic;
|
internal ClassicProtocol classic;
|
||||||
internal CPEProtocol cpe;
|
internal CPEProtocol cpe;
|
||||||
internal CPEProtocolBlockDefs cpeBlockDefs;
|
internal CPEProtocolBlockDefs cpeBlockDefs;
|
||||||
internal WoMProtocol wom;
|
internal WoMProtocol wom;
|
||||||
internal CPESupport cpeData;
|
internal CPESupport cpeData;
|
||||||
|
|
||||||
public override void Connect(IPAddress address, int port) {
|
public override void BeginConnect() {
|
||||||
socket = new Socket(address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
|
socket = new Socket(game.IPAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
|
||||||
game.UserEvents.BlockChanged += BlockChanged;
|
game.UserEvents.BlockChanged += BlockChanged;
|
||||||
Disconnected = false;
|
Disconnected = false;
|
||||||
|
|
||||||
try {
|
socket.Blocking = false;
|
||||||
socket.Connect(address, port);
|
connecting = true;
|
||||||
} catch (SocketException ex) {
|
connectTimeout = DateTime.UtcNow.AddSeconds(timeoutSecs);
|
||||||
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!");
|
|
||||||
Dispose();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
socket.Connect(game.IPAddress, game.Port);
|
||||||
|
} catch (SocketException ex) {
|
||||||
|
SocketError err = ex.SocketErrorCode;
|
||||||
|
if (err == SocketError.InProgress || err == SocketError.WouldBlock) return;
|
||||||
|
|
||||||
|
ErrorHandler.LogError("connecting to server", ex);
|
||||||
|
FailConnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TickConnect() {
|
||||||
|
DateTime now = DateTime.UtcNow;
|
||||||
|
if (socket.Connected) {
|
||||||
|
socket.Blocking = true;
|
||||||
|
FinishConnect();
|
||||||
|
} else if (now > connectTimeout) {
|
||||||
|
FailConnect();
|
||||||
|
} else {
|
||||||
|
double leftSecs = (connectTimeout - now).TotalSeconds;
|
||||||
|
game.WorldEvents.RaiseLoading((float)leftSecs / timeoutSecs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FinishConnect() {
|
||||||
|
connecting = false;
|
||||||
|
game.WorldEvents.RaiseLoading(0);
|
||||||
reader = new NetReader(socket);
|
reader = new NetReader(socket);
|
||||||
writer = new NetWriter(socket);
|
writer = new NetWriter(socket);
|
||||||
|
|
||||||
@ -66,8 +91,15 @@ namespace ClassicalSharp.Network {
|
|||||||
lastPacket = DateTime.UtcNow;
|
lastPacket = DateTime.UtcNow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FailConnect() {
|
||||||
|
connecting = false;
|
||||||
|
game.Disconnect("Failed to connect to " + game.IPAddress + ":" + game.Port,
|
||||||
|
"You failed to connect to the server. It's probably down!");
|
||||||
|
Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
public override void SendChat(string text) {
|
public override void SendChat(string text) {
|
||||||
if (String.IsNullOrEmpty(text)) return;
|
if (String.IsNullOrEmpty(text) || connecting) return;
|
||||||
|
|
||||||
while (text.Length > Utils.StringLength) {
|
while (text.Length > Utils.StringLength) {
|
||||||
classic.WriteChat(text.Substring(0, Utils.StringLength), true);
|
classic.WriteChat(text.Substring(0, Utils.StringLength), true);
|
||||||
@ -97,6 +129,8 @@ namespace ClassicalSharp.Network {
|
|||||||
|
|
||||||
public override void Tick(ScheduledTask task) {
|
public override void Tick(ScheduledTask task) {
|
||||||
if (Disconnected) return;
|
if (Disconnected) return;
|
||||||
|
if (connecting) { TickConnect(); return; }
|
||||||
|
|
||||||
if ((DateTime.UtcNow - lastPacket).TotalSeconds >= 30) {
|
if ((DateTime.UtcNow - lastPacket).TotalSeconds >= 30) {
|
||||||
CheckDisconnection(task.Interval);
|
CheckDisconnection(task.Interval);
|
||||||
}
|
}
|
||||||
@ -237,7 +271,7 @@ namespace ClassicalSharp.Network {
|
|||||||
if (testAcc < 1) return;
|
if (testAcc < 1) return;
|
||||||
testAcc = 0;
|
testAcc = 0;
|
||||||
|
|
||||||
if (!socket.Connected || (socket.Poll(1000, SelectMode.SelectRead) && socket.Available == 0)) {
|
if (!socket.Connected || (socket.Available == 0 && socket.Poll(0, SelectMode.SelectRead))) {
|
||||||
game.Disconnect("Disconnected!", "You've lost connection to the server");
|
game.Disconnect("Disconnected!", "You've lost connection to the server");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -95,11 +95,11 @@ namespace ClassicalSharp.Network.Protocols {
|
|||||||
game.WorldEvents.RaiseOnNewMap();
|
game.WorldEvents.RaiseOnNewMap();
|
||||||
|
|
||||||
prevScreen = game.Gui.activeScreen;
|
prevScreen = game.Gui.activeScreen;
|
||||||
if (prevScreen is LoadingMapScreen)
|
if (prevScreen is LoadingScreen)
|
||||||
prevScreen = null;
|
prevScreen = null;
|
||||||
prevCursorVisible = game.CursorVisible;
|
prevCursorVisible = game.CursorVisible;
|
||||||
|
|
||||||
game.Gui.SetNewScreen(new LoadingMapScreen(game, net.ServerName, net.ServerMotd), false);
|
game.Gui.SetNewScreen(new LoadingScreen(game, net.ServerName, net.ServerMotd), false);
|
||||||
net.wom.CheckMotd();
|
net.wom.CheckMotd();
|
||||||
receivedFirstPosition = false;
|
receivedFirstPosition = false;
|
||||||
gzipHeader = new GZipHeaderReader();
|
gzipHeader = new GZipHeaderReader();
|
||||||
@ -171,7 +171,7 @@ namespace ClassicalSharp.Network.Protocols {
|
|||||||
}
|
}
|
||||||
|
|
||||||
float progress = map == null ? 0 : (float)mapIndex / map.Length;
|
float progress = map == null ? 0 : (float)mapIndex / map.Length;
|
||||||
game.WorldEvents.RaiseMapLoading(progress);
|
game.WorldEvents.RaiseLoading(progress);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HandleLevelFinalise() {
|
void HandleLevelFinalise() {
|
||||||
|
@ -23,7 +23,7 @@ namespace ClassicalSharp.Singleplayer {
|
|||||||
IsSinglePlayer = true;
|
IsSinglePlayer = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Connect(IPAddress address, int port) {
|
public override void BeginConnect() {
|
||||||
game.Chat.SetLogName("Singleplayer");
|
game.Chat.SetLogName("Singleplayer");
|
||||||
game.SupportsCPEBlocks = game.UseCPE;
|
game.SupportsCPEBlocks = game.UseCPE;
|
||||||
int max = game.SupportsCPEBlocks ? Block.MaxCpeBlock : Block.MaxOriginalBlock;
|
int max = game.SupportsCPEBlocks ? Block.MaxCpeBlock : Block.MaxOriginalBlock;
|
||||||
|
@ -104,7 +104,7 @@ Event_Void BlockEvents_PermissionsChanged; /* Block permissions (can place/delet
|
|||||||
Event_Void BlockEvents_BlockDefChanged; /* Block definition is changed or removed. */
|
Event_Void BlockEvents_BlockDefChanged; /* Block definition is changed or removed. */
|
||||||
|
|
||||||
Event_Void WorldEvents_NewMap; /* Player begins loading a new world. */
|
Event_Void WorldEvents_NewMap; /* Player begins loading a new world. */
|
||||||
Event_Real WorldEvents_MapLoading; /* Portion of world is decompressed/generated. (Arg is progress from 0-1) */
|
Event_Real WorldEvents_Loading; /* Portion of world is decompressed/generated. (Arg is progress from 0-1) */
|
||||||
Event_Void WorldEvents_MapLoaded; /* New world has finished loading, player can now interact with it. */
|
Event_Void WorldEvents_MapLoaded; /* New world has finished loading, player can now interact with it. */
|
||||||
Event_Int WorldEvents_EnvVarChanged; /* World environment variable changed by player/CPE/WoM config. */
|
Event_Int WorldEvents_EnvVarChanged; /* World environment variable changed by player/CPE/WoM config. */
|
||||||
|
|
||||||
|
@ -536,7 +536,7 @@ void Game_Load(void) {
|
|||||||
|
|
||||||
Gui_FreeActive();
|
Gui_FreeActive();
|
||||||
Gui_SetActive(LoadingScreen_MakeInstance(&loadTitle, &loadMsg));
|
Gui_SetActive(LoadingScreen_MakeInstance(&loadTitle, &loadMsg));
|
||||||
ServerConnection_Connect(&Game_IPAddress, Game_Port);
|
ServerConnection_BeginConnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
Stopwatch game_frameTimer;
|
Stopwatch game_frameTimer;
|
||||||
|
@ -446,7 +446,7 @@ static void Classic_LevelDataChunk(Stream* stream) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Real32 progress = map == NULL ? 0.0f : (Real32)mapIndex / mapVolume;
|
Real32 progress = map == NULL ? 0.0f : (Real32)mapIndex / mapVolume;
|
||||||
Event_RaiseReal(&WorldEvents_MapLoading, progress);
|
Event_RaiseReal(&WorldEvents_Loading, progress);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void Classic_LevelFinalise(Stream* stream) {
|
static void Classic_LevelFinalise(Stream* stream) {
|
||||||
|
@ -15,6 +15,8 @@ extern UInt8 Platform_DirectorySeparator;
|
|||||||
extern ReturnCode ReturnCode_FileShareViolation;
|
extern ReturnCode ReturnCode_FileShareViolation;
|
||||||
extern ReturnCode ReturnCode_FileNotFound;
|
extern ReturnCode ReturnCode_FileNotFound;
|
||||||
extern ReturnCode ReturnCode_NotSupported;
|
extern ReturnCode ReturnCode_NotSupported;
|
||||||
|
extern ReturnCode ReturnCode_SocketInProgess;
|
||||||
|
extern ReturnCode ReturnCode_SocketWouldBlock;
|
||||||
|
|
||||||
void Platform_Init(void);
|
void Platform_Init(void);
|
||||||
void Platform_Free(void);
|
void Platform_Free(void);
|
||||||
@ -83,12 +85,13 @@ Size2D Platform_TextDraw(DrawTextArgs* args, Int32 x, Int32 y, PackedCol col);
|
|||||||
void Platform_ReleaseBitmap(void);
|
void Platform_ReleaseBitmap(void);
|
||||||
|
|
||||||
void Platform_SocketCreate(void** socket);
|
void Platform_SocketCreate(void** socket);
|
||||||
|
ReturnCode Platform_SocketSetBlocking(void* socket, bool blocking);
|
||||||
ReturnCode Platform_SocketConnect(void* socket, STRING_PURE String* ip, Int32 port);
|
ReturnCode Platform_SocketConnect(void* socket, STRING_PURE String* ip, Int32 port);
|
||||||
ReturnCode Platform_SocketRead(void* socket, UInt8* buffer, UInt32 count, UInt32* modified);
|
ReturnCode Platform_SocketRead(void* socket, UInt8* buffer, UInt32 count, UInt32* modified);
|
||||||
ReturnCode Platform_SocketWrite(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_SocketClose(void* socket);
|
||||||
ReturnCode Platform_SocketAvailable(void* socket, UInt32* available);
|
ReturnCode Platform_SocketAvailable(void* socket, UInt32* available);
|
||||||
ReturnCode Platform_SocketSelectRead(void* socket, Int32 microseconds, bool* success);
|
ReturnCode Platform_SocketSelect(void* socket, bool selectRead, bool* success);
|
||||||
|
|
||||||
void Platform_HttpInit(void);
|
void Platform_HttpInit(void);
|
||||||
ReturnCode Platform_HttpMakeRequest(AsyncRequest* request, void** handle);
|
ReturnCode Platform_HttpMakeRequest(AsyncRequest* request, void** handle);
|
||||||
|
@ -549,7 +549,7 @@ static void LoadingScreen_Init(GuiElement* elem) {
|
|||||||
Gfx_SetFog(false);
|
Gfx_SetFog(false);
|
||||||
LoadingScreen_ContextRecreated(screen);
|
LoadingScreen_ContextRecreated(screen);
|
||||||
|
|
||||||
Event_RegisterReal(&WorldEvents_MapLoading, screen, LoadingScreen_MapLoading);
|
Event_RegisterReal(&WorldEvents_Loading, screen, LoadingScreen_MapLoading);
|
||||||
Event_RegisterVoid(&GfxEvents_ContextLost, screen, LoadingScreen_ContextLost);
|
Event_RegisterVoid(&GfxEvents_ContextLost, screen, LoadingScreen_ContextLost);
|
||||||
Event_RegisterVoid(&GfxEvents_ContextRecreated, screen, LoadingScreen_ContextRecreated);
|
Event_RegisterVoid(&GfxEvents_ContextRecreated, screen, LoadingScreen_ContextRecreated);
|
||||||
}
|
}
|
||||||
@ -579,7 +579,7 @@ static void LoadingScreen_Free(GuiElement* elem) {
|
|||||||
Platform_FontFree(&screen->Font);
|
Platform_FontFree(&screen->Font);
|
||||||
LoadingScreen_ContextLost(screen);
|
LoadingScreen_ContextLost(screen);
|
||||||
|
|
||||||
Event_UnregisterReal(&WorldEvents_MapLoading, screen, LoadingScreen_MapLoading);
|
Event_UnregisterReal(&WorldEvents_Loading, screen, LoadingScreen_MapLoading);
|
||||||
Event_UnregisterVoid(&GfxEvents_ContextLost, screen, LoadingScreen_ContextLost);
|
Event_UnregisterVoid(&GfxEvents_ContextLost, screen, LoadingScreen_ContextLost);
|
||||||
Event_UnregisterVoid(&GfxEvents_ContextRecreated, screen, LoadingScreen_ContextRecreated);
|
Event_UnregisterVoid(&GfxEvents_ContextRecreated, screen, LoadingScreen_ContextRecreated);
|
||||||
}
|
}
|
||||||
@ -865,7 +865,8 @@ static bool ChatScreen_HandlesKeyDown(GuiElement* elem, Key key) {
|
|||||||
if (screen->HandlesAllInput) { /* text input bar */
|
if (screen->HandlesAllInput) { /* text input bar */
|
||||||
if (key == KeyBind_Get(KeyBind_SendChat) || key == Key_KeypadEnter || key == KeyBind_Get(KeyBind_PauseOrExit)) {
|
if (key == KeyBind_Get(KeyBind_SendChat) || key == Key_KeypadEnter || key == KeyBind_Get(KeyBind_PauseOrExit)) {
|
||||||
ChatScreen_SetHandlesAllInput(screen, false);
|
ChatScreen_SetHandlesAllInput(screen, false);
|
||||||
Game_SetCursorVisible(false);
|
/* when underlying screen is HUD, user is interacting with the world normally */
|
||||||
|
Game_SetCursorVisible(Gui_GetUnderlyingScreen() != Gui_HUD);
|
||||||
Camera_Active->RegrabMouse();
|
Camera_Active->RegrabMouse();
|
||||||
Key_KeyRepeat = false;
|
Key_KeyRepeat = false;
|
||||||
|
|
||||||
@ -1512,10 +1513,10 @@ static bool DisconnectScreen_HandlesMouseDown(GuiElement* elem, Int32 x, Int32 y
|
|||||||
UInt8 connectBuffer[String_BufferSize(STRING_SIZE)];
|
UInt8 connectBuffer[String_BufferSize(STRING_SIZE)];
|
||||||
String connect = String_InitAndClearArray(connectBuffer);
|
String connect = String_InitAndClearArray(connectBuffer);
|
||||||
String empty = String_MakeNull();
|
String empty = String_MakeNull();
|
||||||
String_Format2(&connect, "Connecting to %s: %i..", &Game_IPAddress, &Game_Port);
|
String_Format2(&connect, "Connecting to %s:%i..", &Game_IPAddress, &Game_Port);
|
||||||
|
|
||||||
Gui_ReplaceActive(LoadingScreen_MakeInstance(&connect, &empty));
|
Gui_ReplaceActive(LoadingScreen_MakeInstance(&connect, &empty));
|
||||||
ServerConnection_Connect(&Game_IPAddress, Game_Port);
|
ServerConnection_BeginConnect();
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -207,7 +207,7 @@ Int32 PingList_AveragePingMs(void) {
|
|||||||
/*########################################################################################################################*
|
/*########################################################################################################################*
|
||||||
*-------------------------------------------------Singleplayer connection-------------------------------------------------*
|
*-------------------------------------------------Singleplayer connection-------------------------------------------------*
|
||||||
*#########################################################################################################################*/
|
*#########################################################################################################################*/
|
||||||
static void SPConnection_Connect(STRING_PURE String* ip, Int32 port) {
|
static void SPConnection_BeginConnect(void) {
|
||||||
String logName = String_FromConst("Singleplayer");
|
String logName = String_FromConst("Singleplayer");
|
||||||
Chat_SetLogName(&logName);
|
Chat_SetLogName(&logName);
|
||||||
Game_UseCPEBlocks = Game_UseCPE;
|
Game_UseCPEBlocks = Game_UseCPE;
|
||||||
@ -280,7 +280,7 @@ void ServerConnection_InitSingleplayer(void) {
|
|||||||
ServerConnection_SupportsPartialMessages = true;
|
ServerConnection_SupportsPartialMessages = true;
|
||||||
ServerConnection_IsSinglePlayer = true;
|
ServerConnection_IsSinglePlayer = true;
|
||||||
|
|
||||||
ServerConnection_Connect = SPConnection_Connect;
|
ServerConnection_BeginConnect = SPConnection_BeginConnect;
|
||||||
ServerConnection_SendChat = SPConnection_SendChat;
|
ServerConnection_SendChat = SPConnection_SendChat;
|
||||||
ServerConnection_SendPosition = SPConnection_SendPosition;
|
ServerConnection_SendPosition = SPConnection_SendPosition;
|
||||||
ServerConnection_SendPlayerClick = SPConnection_SendPlayerClick;
|
ServerConnection_SendPlayerClick = SPConnection_SendPlayerClick;
|
||||||
@ -295,17 +295,22 @@ void ServerConnection_InitSingleplayer(void) {
|
|||||||
*--------------------------------------------------Multiplayer connection-------------------------------------------------*
|
*--------------------------------------------------Multiplayer connection-------------------------------------------------*
|
||||||
*#########################################################################################################################*/
|
*#########################################################################################################################*/
|
||||||
void* net_socket;
|
void* net_socket;
|
||||||
DateTime net_lastPacket;
|
|
||||||
UInt8 net_lastOpcode;
|
|
||||||
Stream net_readStream;
|
Stream net_readStream;
|
||||||
Stream net_writeStream;
|
Stream net_writeStream;
|
||||||
UInt8 net_readBuffer[4096 * 5];
|
UInt8 net_readBuffer[4096 * 5];
|
||||||
UInt8 net_writeBuffer[131];
|
UInt8 net_writeBuffer[131];
|
||||||
|
|
||||||
Int32 net_maxHandledPacket;
|
Int32 net_maxHandledPacket;
|
||||||
bool net_writeFailed;
|
bool net_writeFailed;
|
||||||
Int32 net_ticks;
|
Int32 net_ticks;
|
||||||
|
DateTime net_lastPacket;
|
||||||
|
UInt8 net_lastOpcode;
|
||||||
Real64 net_discAccumulator;
|
Real64 net_discAccumulator;
|
||||||
|
|
||||||
|
bool net_connecting;
|
||||||
|
Int64 net_connectTimeout;
|
||||||
|
#define NET_TIMEOUT_MS (15 * 1000)
|
||||||
|
|
||||||
static void MPConnection_BlockChanged(void* obj, Vector3I coords, BlockID oldBlock, BlockID block) {
|
static void MPConnection_BlockChanged(void* obj, Vector3I coords, BlockID oldBlock, BlockID block) {
|
||||||
Vector3I p = coords;
|
Vector3I p = coords;
|
||||||
if (block == BLOCK_AIR) {
|
if (block == BLOCK_AIR) {
|
||||||
@ -318,26 +323,9 @@ static void MPConnection_BlockChanged(void* obj, Vector3I coords, BlockID oldBlo
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void ServerConnection_Free(void);
|
static void ServerConnection_Free(void);
|
||||||
static void MPConnection_Connect(STRING_PURE String* ip, Int32 port) {
|
static void MPConnection_FinishConnect(void) {
|
||||||
Platform_SocketCreate(&net_socket);
|
net_connecting = false;
|
||||||
Event_RegisterBlock(&UserEvents_BlockChanged, NULL, MPConnection_BlockChanged);
|
Event_RaiseReal(&WorldEvents_Loading, 0.0f);
|
||||||
ServerConnection_Disconnected = false;
|
|
||||||
|
|
||||||
ReturnCode result = Platform_SocketConnect(net_socket, &Game_IPAddress, Game_Port);
|
|
||||||
if (result != 0) {
|
|
||||||
UInt8 msgBuffer[String_BufferSize(STRING_SIZE * 2)];
|
|
||||||
String msg = String_InitAndClearArray(msgBuffer);
|
|
||||||
String_Format3(&msg, "Error connecting to %s:%i: %i", ip, &port, &result);
|
|
||||||
ErrorHandler_Log(&msg);
|
|
||||||
|
|
||||||
String_Clear(&msg);
|
|
||||||
String_Format2(&msg, "Failed to connect to %s:%i", ip, &port);
|
|
||||||
String reason = String_FromConst("You failed to connect to the server. It's probably down!");
|
|
||||||
Game_Disconnect(&msg, &reason);
|
|
||||||
|
|
||||||
ServerConnection_Free();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String streamName = String_FromConst("network socket");
|
String streamName = String_FromConst("network socket");
|
||||||
Stream_ReadonlyMemory(&net_readStream, net_readBuffer, sizeof(net_readBuffer), &streamName);
|
Stream_ReadonlyMemory(&net_readStream, net_readBuffer, sizeof(net_readBuffer), &streamName);
|
||||||
@ -352,8 +340,63 @@ static void MPConnection_Connect(STRING_PURE String* ip, Int32 port) {
|
|||||||
Platform_CurrentUTCTime(&net_lastPacket);
|
Platform_CurrentUTCTime(&net_lastPacket);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void MPConnection_FailConnect(ReturnCode result) {
|
||||||
|
net_connecting = false;
|
||||||
|
UInt8 msgBuffer[String_BufferSize(STRING_SIZE * 2)];
|
||||||
|
String msg = String_InitAndClearArray(msgBuffer);
|
||||||
|
|
||||||
|
if (result != 0) {
|
||||||
|
String_Format3(&msg, "Error connecting to %s:%i: %i", &Game_IPAddress, &Game_Port, &result);
|
||||||
|
ErrorHandler_Log(&msg);
|
||||||
|
String_Clear(&msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
String_Format2(&msg, "Failed to connect to %s:%i", &Game_IPAddress, &Game_Port);
|
||||||
|
String reason = String_FromConst("You failed to connect to the server. It's probably down!");
|
||||||
|
Game_Disconnect(&msg, &reason);
|
||||||
|
|
||||||
|
ServerConnection_Free();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void MPConnection_TickConnect(void) {
|
||||||
|
DateTime now; Platform_CurrentUTCTime(&now);
|
||||||
|
Int64 nowMS = DateTime_TotalMs(&now);
|
||||||
|
|
||||||
|
bool poll_success = false;
|
||||||
|
ReturnCode result = Platform_SocketSelect(net_socket, false, &poll_success);
|
||||||
|
|
||||||
|
if (result != 0) {
|
||||||
|
MPConnection_FailConnect(result);
|
||||||
|
} else if (poll_success) {
|
||||||
|
Platform_SocketSetBlocking(net_socket, true);
|
||||||
|
MPConnection_FinishConnect();
|
||||||
|
} else if (nowMS > net_connectTimeout) {
|
||||||
|
MPConnection_FailConnect(0);
|
||||||
|
} else {
|
||||||
|
Int64 leftMS = net_connectTimeout - nowMS;
|
||||||
|
Event_RaiseReal(&WorldEvents_Loading, (Real32)leftMS / NET_TIMEOUT_MS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void MPConnection_BeginConnect(void) {
|
||||||
|
Platform_SocketCreate(&net_socket);
|
||||||
|
Event_RegisterBlock(&UserEvents_BlockChanged, NULL, MPConnection_BlockChanged);
|
||||||
|
ServerConnection_Disconnected = false;
|
||||||
|
|
||||||
|
Platform_SocketSetBlocking(net_socket, false);
|
||||||
|
net_connecting = true;
|
||||||
|
DateTime now; Platform_CurrentUTCTime(&now);
|
||||||
|
net_connectTimeout = DateTime_TotalMs(&now) + NET_TIMEOUT_MS;
|
||||||
|
|
||||||
|
ReturnCode result = Platform_SocketConnect(net_socket, &Game_IPAddress, Game_Port);
|
||||||
|
if (result == 0) return;
|
||||||
|
if (result != ReturnCode_SocketInProgess && result != ReturnCode_SocketWouldBlock) {
|
||||||
|
MPConnection_FailConnect(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void MPConnection_SendChat(STRING_PURE String* text) {
|
static void MPConnection_SendChat(STRING_PURE String* text) {
|
||||||
if (text->length == 0) return;
|
if (text->length == 0 || net_connecting) return;
|
||||||
String remaining = *text;
|
String remaining = *text;
|
||||||
|
|
||||||
while (remaining.length > STRING_SIZE) {
|
while (remaining.length > STRING_SIZE) {
|
||||||
@ -382,11 +425,11 @@ static void MPConnection_CheckDisconnection(Real64 delta) {
|
|||||||
if (net_discAccumulator < 1.0) return;
|
if (net_discAccumulator < 1.0) return;
|
||||||
net_discAccumulator = 0.0;
|
net_discAccumulator = 0.0;
|
||||||
|
|
||||||
UInt32 available = 0; bool selected = false;
|
UInt32 available = 0; bool poll_success = false;
|
||||||
ReturnCode availResult = Platform_SocketAvailable(net_socket, &available);
|
ReturnCode availResult = Platform_SocketAvailable(net_socket, &available);
|
||||||
ReturnCode selectResult = Platform_SocketSelectRead(net_socket, 1000, &selected);
|
ReturnCode selectResult = Platform_SocketSelect(net_socket, true, &poll_success);
|
||||||
|
|
||||||
if (net_writeFailed || availResult != 0 || selectResult != 0 || (selected && available == 0)) {
|
if (net_writeFailed || availResult != 0 || selectResult != 0 || (available == 0 && poll_success)) {
|
||||||
String title = String_FromConst("Disconnected!");
|
String title = String_FromConst("Disconnected!");
|
||||||
String reason = String_FromConst("You've lost connection to the server");
|
String reason = String_FromConst("You've lost connection to the server");
|
||||||
Game_Disconnect(&title, &reason);
|
Game_Disconnect(&title, &reason);
|
||||||
@ -395,6 +438,8 @@ static void MPConnection_CheckDisconnection(Real64 delta) {
|
|||||||
|
|
||||||
static void MPConnection_Tick(ScheduledTask* task) {
|
static void MPConnection_Tick(ScheduledTask* task) {
|
||||||
if (ServerConnection_Disconnected) return;
|
if (ServerConnection_Disconnected) return;
|
||||||
|
if (net_connecting) { MPConnection_TickConnect(); return; }
|
||||||
|
|
||||||
DateTime now; Platform_CurrentUTCTime(&now);
|
DateTime now; Platform_CurrentUTCTime(&now);
|
||||||
if (DateTime_MsBetween(&net_lastPacket, &now) >= 30 * 1000) {
|
if (DateTime_MsBetween(&net_lastPacket, &now) >= 30 * 1000) {
|
||||||
MPConnection_CheckDisconnection(task->Interval);
|
MPConnection_CheckDisconnection(task->Interval);
|
||||||
@ -496,7 +541,7 @@ void ServerConnection_InitMultiplayer(void) {
|
|||||||
ServerConnection_ResetState();
|
ServerConnection_ResetState();
|
||||||
ServerConnection_IsSinglePlayer = false;
|
ServerConnection_IsSinglePlayer = false;
|
||||||
|
|
||||||
ServerConnection_Connect = MPConnection_Connect;
|
ServerConnection_BeginConnect = MPConnection_BeginConnect;
|
||||||
ServerConnection_SendChat = MPConnection_SendChat;
|
ServerConnection_SendChat = MPConnection_SendChat;
|
||||||
ServerConnection_SendPosition = MPConnection_SendPosition;
|
ServerConnection_SendPosition = MPConnection_SendPosition;
|
||||||
ServerConnection_SendPlayerClick = MPConnection_SendPlayerClick;
|
ServerConnection_SendPlayerClick = MPConnection_SendPlayerClick;
|
||||||
|
@ -68,7 +68,7 @@ extern String ServerConnection_ServerName;
|
|||||||
extern String ServerConnection_ServerMOTD;
|
extern String ServerConnection_ServerMOTD;
|
||||||
extern String ServerConnection_AppName;
|
extern String ServerConnection_AppName;
|
||||||
|
|
||||||
void (*ServerConnection_Connect)(STRING_PURE String* ip, Int32 port);
|
void (*ServerConnection_BeginConnect)(void);
|
||||||
void (*ServerConnection_SendChat)(STRING_PURE String* text);
|
void (*ServerConnection_SendChat)(STRING_PURE String* text);
|
||||||
void (*ServerConnection_SendPosition)(Vector3 pos, Real32 rotY, Real32 headX);
|
void (*ServerConnection_SendPosition)(Vector3 pos, Real32 rotY, Real32 headX);
|
||||||
void (*ServerConnection_SendPlayerClick)(MouseButton button, bool isDown, EntityID targetId, PickedPos* pos);
|
void (*ServerConnection_SendPlayerClick)(MouseButton button, bool isDown, EntityID targetId, PickedPos* pos);
|
||||||
|
@ -25,6 +25,8 @@ UInt8 Platform_DirectorySeparator = '\\';
|
|||||||
ReturnCode ReturnCode_FileShareViolation = ERROR_SHARING_VIOLATION;
|
ReturnCode ReturnCode_FileShareViolation = ERROR_SHARING_VIOLATION;
|
||||||
ReturnCode ReturnCode_FileNotFound = ERROR_FILE_NOT_FOUND;
|
ReturnCode ReturnCode_FileNotFound = ERROR_FILE_NOT_FOUND;
|
||||||
ReturnCode ReturnCode_NotSupported = ERROR_NOT_SUPPORTED;
|
ReturnCode ReturnCode_NotSupported = ERROR_NOT_SUPPORTED;
|
||||||
|
ReturnCode ReturnCode_SocketInProgess = WSAEINPROGRESS;
|
||||||
|
ReturnCode ReturnCode_SocketWouldBlock = WSAEWOULDBLOCK;
|
||||||
|
|
||||||
void Platform_Init(void) {
|
void Platform_Init(void) {
|
||||||
heap = GetProcessHeap(); /* TODO: HeapCreate instead? probably not */
|
heap = GetProcessHeap(); /* TODO: HeapCreate instead? probably not */
|
||||||
@ -533,6 +535,11 @@ void Platform_SocketCreate(void** socketResult) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ReturnCode Platform_SocketSetBlocking(void* socket, bool blocking) {
|
||||||
|
Int32 blocking_raw = blocking ? 0 : -1;
|
||||||
|
return ioctlsocket(socket, FIONBIO, &blocking_raw);
|
||||||
|
}
|
||||||
|
|
||||||
ReturnCode Platform_SocketConnect(void* socket, STRING_PURE String* ip, Int32 port) {
|
ReturnCode Platform_SocketConnect(void* socket, STRING_PURE String* ip, Int32 port) {
|
||||||
struct sockaddr_in addr;
|
struct sockaddr_in addr;
|
||||||
addr.sin_family = AF_INET;
|
addr.sin_family = AF_INET;
|
||||||
@ -575,16 +582,17 @@ ReturnCode Platform_SocketAvailable(void* socket, UInt32* available) {
|
|||||||
return ioctlsocket(socket, FIONREAD, available);
|
return ioctlsocket(socket, FIONREAD, available);
|
||||||
}
|
}
|
||||||
|
|
||||||
ReturnCode Platform_SocketSelectRead(void* socket, Int32 microseconds, bool* success) {
|
ReturnCode Platform_SocketSelect(void* socket, bool selectRead, bool* success) {
|
||||||
void* args[2];
|
void* args[2]; args[0] = (void*)1; args[1] = socket;
|
||||||
args[0] = (void*)1;
|
TIMEVAL time = { 0 };
|
||||||
args[1] = socket;
|
Int32 selectCount;
|
||||||
|
|
||||||
TIMEVAL time;
|
if (selectRead) {
|
||||||
time.tv_usec = microseconds % (1000 * 1000);
|
selectCount = select(1, &args, NULL, NULL, &time);
|
||||||
time.tv_sec = microseconds / (1000 * 1000);
|
} else {
|
||||||
|
selectCount = select(1, NULL, &args, NULL, &time);
|
||||||
|
}
|
||||||
|
|
||||||
Int32 selectCount = select(1, &args, NULL, NULL, &time);
|
|
||||||
if (selectCount == SOCKET_ERROR) {
|
if (selectCount == SOCKET_ERROR) {
|
||||||
*success = false; return WSAGetLastError();
|
*success = false; return WSAGetLastError();
|
||||||
} else {
|
} else {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user