From f15ee650181a401e77e8f048946aa6062c259016 Mon Sep 17 00:00:00 2001 From: UnknownShadow200 Date: Thu, 9 Aug 2018 19:31:58 +1000 Subject: [PATCH] fix issues with sometimes being invisible when it should be visible, and vice versa --- ClassicalSharp/2D/GuiElement.cs | 2 +- ClassicalSharp/2D/Screens/ChatScreen.cs | 7 +- ClassicalSharp/2D/Screens/DisconnectScreen.cs | 1 - ClassicalSharp/2D/Screens/HudScreen.cs | 4 +- ClassicalSharp/2D/Screens/InventoryScreen.cs | 32 +- ClassicalSharp/2D/Screens/LoadingMapScreen.cs | 1 - ClassicalSharp/2D/Screens/Menu/ListScreen.cs | 4 +- ClassicalSharp/2D/Screens/Menu/MenuScreen.cs | 130 ++-- ClassicalSharp/2D/Screens/StatusScreen.cs | 1 + ClassicalSharp/2D/Widgets/TableWidget.cs | 4 +- ClassicalSharp/Entities/Entity.cs | 3 - ClassicalSharp/Game/Game.Properties.cs | 26 +- ClassicalSharp/Game/Game.cs | 2 +- ClassicalSharp/Game/GuiInterface.cs | 34 +- ClassicalSharp/GraphicsAPI/GL.cs | 157 ++-- ClassicalSharp/Network/Protocols/Classic.cs | 705 +++++++++--------- ClassicalSharp/Utils/Camera.cs | 8 +- src/Client/Game.c | 15 - src/Client/Game.h | 3 - src/Client/Gui.c | 31 +- src/Client/Gui.h | 2 +- src/Client/Menus.c | 2 - src/Client/PacketHandlers.c | 7 +- src/Client/Screens.c | 36 +- src/Client/Widgets.c | 2 - 25 files changed, 588 insertions(+), 631 deletions(-) diff --git a/ClassicalSharp/2D/GuiElement.cs b/ClassicalSharp/2D/GuiElement.cs index 28b3fd27c..b32c93745 100644 --- a/ClassicalSharp/2D/GuiElement.cs +++ b/ClassicalSharp/2D/GuiElement.cs @@ -43,7 +43,7 @@ namespace ClassicalSharp.Gui { public abstract class Screen : GuiElement { public Screen(Game game) : base(game) { } - public bool HandlesAllInput, BlocksWorld, HidesHud, RenderHudOver; + public bool HandlesAllInput = true, BlocksWorld, HidesHud, RenderHudOver; public abstract void OnResize(); protected abstract void ContextLost(); diff --git a/ClassicalSharp/2D/Screens/ChatScreen.cs b/ClassicalSharp/2D/Screens/ChatScreen.cs index 955fe9811..a55ede99d 100644 --- a/ClassicalSharp/2D/Screens/ChatScreen.cs +++ b/ClassicalSharp/2D/Screens/ChatScreen.cs @@ -14,6 +14,7 @@ namespace ClassicalSharp.Gui.Screens { public ChatScreen(Game game, HudScreen hud) : base(game) { chatLines = game.ChatLines; this.hud = hud; + HandlesAllInput = false; } HudScreen hud; @@ -275,7 +276,6 @@ namespace ClassicalSharp.Gui.Screens { protected override void ContextLost() { if (HandlesAllInput) { chatInInputBuffer = input.Text.ToString(); - game.CursorVisible = false; } else { chatInInputBuffer = null; } @@ -330,7 +330,6 @@ namespace ClassicalSharp.Gui.Screens { } public void OpenInput(string initialText) { - game.CursorVisible = true; suppressNextPress = true; SetHandlesAllInput(true); Keyboard.KeyRepeat = true; @@ -350,9 +349,6 @@ namespace ClassicalSharp.Gui.Screens { if (HandlesAllInput) { // text input bar if (key == game.Mapping(KeyBind.SendChat) || key == Key.KeypadEnter || key == game.Mapping(KeyBind.PauseOrExit)) { SetHandlesAllInput(false); - // when underlying screen is HUD, user is interacting with the world normally - game.CursorVisible = game.Gui.UnderlyingScreen != game.Gui.hudScreen; - game.Camera.RegrabMouse(); Keyboard.KeyRepeat = false; if (key == game.Mapping(KeyBind.PauseOrExit)) @@ -466,6 +462,7 @@ namespace ClassicalSharp.Gui.Screens { void SetHandlesAllInput(bool handles) { HandlesAllInput = handles; game.Gui.hudScreen.HandlesAllInput = handles; + game.Gui.CalcCursorVisible(); } } } \ No newline at end of file diff --git a/ClassicalSharp/2D/Screens/DisconnectScreen.cs b/ClassicalSharp/2D/Screens/DisconnectScreen.cs index e34755782..62b3bb5d0 100644 --- a/ClassicalSharp/2D/Screens/DisconnectScreen.cs +++ b/ClassicalSharp/2D/Screens/DisconnectScreen.cs @@ -25,7 +25,6 @@ namespace ClassicalSharp.Gui.Screens { messageFont = new Font(game.FontName, 16); BlocksWorld = true; HidesHud = true; - HandlesAllInput = true; } public override void Init() { diff --git a/ClassicalSharp/2D/Screens/HudScreen.cs b/ClassicalSharp/2D/Screens/HudScreen.cs index 348f5ed75..cb59b2f3c 100644 --- a/ClassicalSharp/2D/Screens/HudScreen.cs +++ b/ClassicalSharp/2D/Screens/HudScreen.cs @@ -8,7 +8,9 @@ using OpenTK.Input; namespace ClassicalSharp.Gui.Screens { public class HudScreen : Screen, IGameComponent { - public HudScreen(Game game) : base(game) { } + public HudScreen(Game game) : base(game) { + HandlesAllInput = false; + } ChatScreen chat; internal Widget hotbar; diff --git a/ClassicalSharp/2D/Screens/InventoryScreen.cs b/ClassicalSharp/2D/Screens/InventoryScreen.cs index f54379a3c..da9f5cc1c 100644 --- a/ClassicalSharp/2D/Screens/InventoryScreen.cs +++ b/ClassicalSharp/2D/Screens/InventoryScreen.cs @@ -5,13 +5,24 @@ using ClassicalSharp.Gui.Widgets; using OpenTK.Input; namespace ClassicalSharp.Gui.Screens { - public partial class InventoryScreen : Screen { + public class InventoryScreen : Screen { TableWidget table; Font font; + bool deferredSelect; public InventoryScreen(Game game) : base(game) { font = new Font(game.FontName, 16); - HandlesAllInput = true; + } + + void MoveToSelected() { + table.SetBlockTo(game.Inventory.Selected); + table.Recreate(); + deferredSelect = false; + + // User is holding invalid block + if (table.SelectedIndex == -1) { + table.MakeDescTex(game.Inventory.Selected); + } } public override void Init() { @@ -20,19 +31,22 @@ namespace ClassicalSharp.Gui.Screens { table.ElementsPerRow = game.PureClassic ? 9 : 10; table.Init(); - // User is holding invalid block - if (table.SelectedIndex == -1) { - table.MakeDescTex(game.Inventory.Selected); - } - + // Can't immediately move to selected here, because cursor visibility + // might be toggled after Init() is called. This causes the cursor to + // be moved back to the middle of the window. + deferredSelect = true; + game.Events.BlockPermissionsChanged += OnBlockChanged; - game.Events.BlockDefinitionChanged += OnBlockChanged; + game.Events.BlockDefinitionChanged += OnBlockChanged; Keyboard.KeyRepeat = true; game.Graphics.ContextLost += ContextLost; game.Graphics.ContextRecreated += ContextRecreated; } - public override void Render(double delta) { table.Render(delta); } + public override void Render(double delta) { + if (deferredSelect) MoveToSelected(); + table.Render(delta); + } public override void Dispose() { font.Dispose(); diff --git a/ClassicalSharp/2D/Screens/LoadingMapScreen.cs b/ClassicalSharp/2D/Screens/LoadingMapScreen.cs index 36b1bd377..c7bf67b68 100644 --- a/ClassicalSharp/2D/Screens/LoadingMapScreen.cs +++ b/ClassicalSharp/2D/Screens/LoadingMapScreen.cs @@ -21,7 +21,6 @@ namespace ClassicalSharp.Gui.Screens { font = new Font(game.FontName, 16); BlocksWorld = true; RenderHudOver = true; - HandlesAllInput = true; } string title, message; diff --git a/ClassicalSharp/2D/Screens/Menu/ListScreen.cs b/ClassicalSharp/2D/Screens/Menu/ListScreen.cs index 818a19099..c1fe0f35a 100644 --- a/ClassicalSharp/2D/Screens/Menu/ListScreen.cs +++ b/ClassicalSharp/2D/Screens/Menu/ListScreen.cs @@ -7,9 +7,7 @@ using OpenTK.Input; namespace ClassicalSharp.Gui.Screens { public abstract class ListScreen : ClickableScreen { - public ListScreen(Game game) : base(game) { - HandlesAllInput = true; - } + public ListScreen(Game game) : base(game) { } protected Font font; protected string[] entries; diff --git a/ClassicalSharp/2D/Screens/Menu/MenuScreen.cs b/ClassicalSharp/2D/Screens/Menu/MenuScreen.cs index 796bb4540..831a362f6 100644 --- a/ClassicalSharp/2D/Screens/Menu/MenuScreen.cs +++ b/ClassicalSharp/2D/Screens/Menu/MenuScreen.cs @@ -1,67 +1,65 @@ -// Copyright 2014-2017 ClassicalSharp | Licensed under BSD-3 -using System; -using System.Drawing; -using OpenTK.Input; - -namespace ClassicalSharp.Gui.Screens { - public abstract class MenuScreen : ClickableScreen { - - public MenuScreen(Game game) : base(game) { - HandlesAllInput = true; - } - - protected Widget[] widgets; - protected Font titleFont, textFont; - protected int IndexWidget(Widget w) { return IndexWidget(widgets, w); } - - public override void Render(double delta) { - RenderMenuBounds(); - game.Graphics.Texturing = true; - RenderWidgets(widgets, delta); - game.Graphics.Texturing = false; - } - - public override void Init() { - if (titleFont == null) titleFont = new Font(game.FontName, 16, FontStyle.Bold); - if (textFont == null) textFont = new Font(game.FontName, 16); - - game.Graphics.ContextLost += ContextLost; - game.Graphics.ContextRecreated += ContextRecreated; - } - - public override void Dispose() { - ContextLost(); - if (titleFont != null) { titleFont.Dispose(); titleFont = null; } - if (textFont != null) { textFont.Dispose(); textFont = null; } - - game.Graphics.ContextLost -= ContextLost; - game.Graphics.ContextRecreated -= ContextRecreated; - } - - protected override void ContextLost() { DisposeWidgets(widgets); } - - public override void OnResize() { - RepositionWidgets(widgets); - } - - - public override bool HandlesMouseDown(int mouseX, int mouseY, MouseButton button) { - return HandleMouseDown(widgets, mouseX, mouseY, button) >= 0; - } - - public override bool HandlesMouseMove(int mouseX, int mouseY) { - return HandleMouseMove(widgets, mouseX, mouseY) >= 0; - } - public override bool HandlesMouseScroll(float delta) { return true; } - - public override bool HandlesKeyDown(Key key) { - if (key == Key.Escape) { - game.Gui.SetNewScreen(null); - return true; - } - return key < Key.F1 || key > Key.F35; - } - public override bool HandlesKeyPress(char key) { return true; } - public override bool HandlesKeyUp(Key key) { return true; } - } +// Copyright 2014-2017 ClassicalSharp | Licensed under BSD-3 +using System; +using System.Drawing; +using OpenTK.Input; + +namespace ClassicalSharp.Gui.Screens { + public abstract class MenuScreen : ClickableScreen { + + public MenuScreen(Game game) : base(game) { } + + protected Widget[] widgets; + protected Font titleFont, textFont; + protected int IndexWidget(Widget w) { return IndexWidget(widgets, w); } + + public override void Render(double delta) { + RenderMenuBounds(); + game.Graphics.Texturing = true; + RenderWidgets(widgets, delta); + game.Graphics.Texturing = false; + } + + public override void Init() { + if (titleFont == null) titleFont = new Font(game.FontName, 16, FontStyle.Bold); + if (textFont == null) textFont = new Font(game.FontName, 16); + + game.Graphics.ContextLost += ContextLost; + game.Graphics.ContextRecreated += ContextRecreated; + } + + public override void Dispose() { + ContextLost(); + if (titleFont != null) { titleFont.Dispose(); titleFont = null; } + if (textFont != null) { textFont.Dispose(); textFont = null; } + + game.Graphics.ContextLost -= ContextLost; + game.Graphics.ContextRecreated -= ContextRecreated; + } + + protected override void ContextLost() { DisposeWidgets(widgets); } + + public override void OnResize() { + RepositionWidgets(widgets); + } + + + public override bool HandlesMouseDown(int mouseX, int mouseY, MouseButton button) { + return HandleMouseDown(widgets, mouseX, mouseY, button) >= 0; + } + + public override bool HandlesMouseMove(int mouseX, int mouseY) { + return HandleMouseMove(widgets, mouseX, mouseY) >= 0; + } + public override bool HandlesMouseScroll(float delta) { return true; } + + public override bool HandlesKeyDown(Key key) { + if (key == Key.Escape) { + game.Gui.SetNewScreen(null); + return true; + } + return key < Key.F1 || key > Key.F35; + } + public override bool HandlesKeyPress(char key) { return true; } + public override bool HandlesKeyUp(Key key) { return true; } + } } \ No newline at end of file diff --git a/ClassicalSharp/2D/Screens/StatusScreen.cs b/ClassicalSharp/2D/Screens/StatusScreen.cs index 51ea3cc87..64fc07636 100644 --- a/ClassicalSharp/2D/Screens/StatusScreen.cs +++ b/ClassicalSharp/2D/Screens/StatusScreen.cs @@ -19,6 +19,7 @@ namespace ClassicalSharp.Gui.Screens { public StatusScreen(Game game) : base(game) { statusBuffer = new StringBuffer(128); + HandlesAllInput = false; } void IGameComponent.Init(Game game) { } diff --git a/ClassicalSharp/2D/Widgets/TableWidget.cs b/ClassicalSharp/2D/Widgets/TableWidget.cs index 6186f8194..6fc09bf2e 100644 --- a/ClassicalSharp/2D/Widgets/TableWidget.cs +++ b/ClassicalSharp/2D/Widgets/TableWidget.cs @@ -106,8 +106,6 @@ namespace ClassicalSharp.Gui.Widgets { scroll = new ScrollbarWidget(game); RecreateElements(); Reposition(); - SetBlockTo(game.Inventory.Selected); - Recreate(); } public override void Dispose() { @@ -177,7 +175,7 @@ namespace ClassicalSharp.Gui.Widgets { void MoveCursorToSelected() { if (SelectedIndex == -1) return; - game.DesktopCursorPos = GetMouseCoords(SelectedIndex); + game.window.DesktopCursorPos = GetMouseCoords(SelectedIndex); } void UpdateBlockInfoString(BlockID block) { diff --git a/ClassicalSharp/Entities/Entity.cs b/ClassicalSharp/Entities/Entity.cs index 82639d16c..1a15a300d 100644 --- a/ClassicalSharp/Entities/Entity.cs +++ b/ClassicalSharp/Entities/Entity.cs @@ -97,15 +97,12 @@ namespace ClassicalSharp.Entities { return m; } - /// Gets the brightness colour of this entity. public virtual PackedCol Colour() { Vector3I P = Vector3I.Floor(EyePosition); return game.World.IsValidPos(P) ? game.Lighting.LightCol(P.X, P.Y, P.Z) : game.Lighting.Outside; } - /// Sets the model associated with this entity. - /// Can be either 'name' or 'name'|'scale'. public void SetModel(string model) { ModelScale = new Vector3(1.0f); int sep = model.IndexOf('|'); diff --git a/ClassicalSharp/Game/Game.Properties.cs b/ClassicalSharp/Game/Game.Properties.cs index 97418f9a0..4b9bfcd09 100644 --- a/ClassicalSharp/Game/Game.Properties.cs +++ b/ClassicalSharp/Game/Game.Properties.cs @@ -204,31 +204,7 @@ namespace ClassicalSharp { } } - public INativeWindow window; - + public INativeWindow window; public int Width, Height; - - public bool Focused { get { return window.Focused; } } - - bool visible = true; - internal bool realVisible = true; - public bool CursorVisible { - get { return visible; } - set { - // Defer mouse visibility changes. - realVisible = value; - if (Gui.overlays.Count > 0) return; - - // Only set the value when it has changes. - if (visible == value) return; - window.CursorVisible = value; - visible = value; - } - } - - public Point DesktopCursorPos { - get { return window.DesktopCursorPos; } - set { window.DesktopCursorPos = value; } - } } } \ No newline at end of file diff --git a/ClassicalSharp/Game/Game.cs b/ClassicalSharp/Game/Game.cs index e5fe82fe2..db99e72e8 100644 --- a/ClassicalSharp/Game/Game.cs +++ b/ClassicalSharp/Game/Game.cs @@ -306,7 +306,7 @@ namespace ClassicalSharp { Vertices = 0; Camera.UpdateMouse(); - if (!Focused && !Gui.ActiveScreen.HandlesAllInput) { + if (!window.Focused && !Gui.ActiveScreen.HandlesAllInput) { Gui.SetNewScreen(new PauseScreen(this)); } diff --git a/ClassicalSharp/Game/GuiInterface.cs b/ClassicalSharp/Game/GuiInterface.cs index f2afba0b2..5fbe70597 100644 --- a/ClassicalSharp/Game/GuiInterface.cs +++ b/ClassicalSharp/Game/GuiInterface.cs @@ -84,41 +84,28 @@ namespace ClassicalSharp { if (activeScreen != null && disposeOld) activeScreen.Dispose(); - if (screen == null) { - game.CursorVisible = false; - if (game.Focused) game.Camera.RegrabMouse(); - } else if (activeScreen == null) { - game.CursorVisible = true; - } - - if (screen != null) - screen.Init(); + if (screen != null) screen.Init(); activeScreen = screen; + CalcCursorVisible(); } public void RefreshHud() { hudScreen.Recreate(); } public void ShowOverlay(Overlay overlay, bool inFront) { - bool cursorVis = game.CursorVisible; - if (overlays.Count == 0) game.CursorVisible = true; - if (inFront) { overlays.Insert(0, overlay); } else { overlays.Add(overlay); } - if (overlays.Count == 1) game.CursorVisible = cursorVis; - // Save cursor visibility state + overlay.Init(); + CalcCursorVisible(); } public void DisposeOverlay(Overlay overlay) { overlay.Dispose(); overlays.Remove(overlay); - - if (overlays.Count == 0) - game.CursorVisible = game.realVisible; - game.Camera.RegrabMouse(); + CalcCursorVisible(); } @@ -144,5 +131,16 @@ namespace ClassicalSharp { overlays[i].OnResize(); } } + + bool cursorVisible = true; + public void CalcCursorVisible() { + bool vis = ActiveScreen.HandlesAllInput; + if (vis == cursorVisible) return; + cursorVisible = vis; + + game.window.CursorVisible = vis; + if (game.window.Focused) + game.Camera.RegrabMouse(); + } } } \ No newline at end of file diff --git a/ClassicalSharp/GraphicsAPI/GL.cs b/ClassicalSharp/GraphicsAPI/GL.cs index c78395fa0..a14ec7aa0 100644 --- a/ClassicalSharp/GraphicsAPI/GL.cs +++ b/ClassicalSharp/GraphicsAPI/GL.cs @@ -32,7 +32,7 @@ using OpenTK; namespace OpenTK.Graphics.OpenGL { [InteropPatch] - public unsafe static partial class GL { + public unsafe static class GL { public static void AlphaFunc(Compare func, float value) { Interop.Calli((int)func, value, AlphaFuncAddress); @@ -243,6 +243,83 @@ namespace OpenTK.Graphics.OpenGL { public static void Viewport(int x, int y, int width, int height) { Interop.Calli(x, y, width, height, ViewportAddress); } static IntPtr ViewportAddress; + + + internal static void LoadEntryPoints(IGraphicsContext context) { + Debug.Print("Loading OpenGL function pointers... "); + AlphaFuncAddress = context.GetAddress("glAlphaFunc"); + + BeginAddress = context.GetAddress("glBegin"); + BindBufferAddress = context.GetAddress("glBindBuffer"); + BindBufferARBAddress = context.GetAddress("glBindBufferARB"); + BindTextureAddress = context.GetAddress("glBindTexture"); + BlendFuncAddress = context.GetAddress("glBlendFunc"); + BufferDataAddress = context.GetAddress("glBufferData"); + BufferDataARBAddress = context.GetAddress("glBufferDataARB"); + BufferSubDataAddress = context.GetAddress("glBufferSubData"); + BufferSubDataARBAddress = context.GetAddress("glBufferSubDataARB"); + + CallListAddress = context.GetAddress("glCallList"); + ClearAddress = context.GetAddress("glClear"); + ClearColorAddress = context.GetAddress("glClearColor"); + Color4ubAddress = context.GetAddress("glColor4ub"); + ColorMaskAddress = context.GetAddress("glColorMask"); + ColorPointerAddress = context.GetAddress("glColorPointer"); + CullFaceAddress = context.GetAddress("glCullFace"); + + DeleteBuffersAddress = context.GetAddress("glDeleteBuffers"); + DeleteBuffersARBAddress = context.GetAddress("glDeleteBuffersARB"); + DeleteListsAddress = context.GetAddress("glDeleteLists"); + DeleteTexturesAddress = context.GetAddress("glDeleteTextures"); + DepthFuncAddress = context.GetAddress("glDepthFunc"); + DepthMaskAddress = context.GetAddress("glDepthMask"); + DisableAddress = context.GetAddress("glDisable"); + DisableClientStateAddress = context.GetAddress("glDisableClientState"); + DrawArraysAddress = context.GetAddress("glDrawArrays"); + DrawElementsAddress = context.GetAddress("glDrawElements"); + + EnableAddress = context.GetAddress("glEnable"); + EnableClientStateAddress = context.GetAddress("glEnableClientState"); + EndAddress = context.GetAddress("glEnd"); + EndListAddress = context.GetAddress("glEndList"); + FogfAddress = context.GetAddress("glFogf"); + FogfvAddress = context.GetAddress("glFogfv"); + FogiAddress = context.GetAddress("glFogi"); + + GenBuffersAddress = context.GetAddress("glGenBuffers"); + GenBuffersARBAddress = context.GetAddress("glGenBuffersARB"); + GenListsAddress = context.GetAddress("glGenLists"); + GenTexturesAddress = context.GetAddress("glGenTextures"); + GetErrorAddress = context.GetAddress("glGetError"); + GetIntegervAddress = context.GetAddress("glGetIntegerv"); + GetStringAddress = context.GetAddress("glGetString"); + GetTexImageAddress = context.GetAddress("glGetTexImage"); + + HintAddress = context.GetAddress("glHint"); + LoadIdentityAddress = context.GetAddress("glLoadIdentity"); + LoadMatrixfAddress = context.GetAddress("glLoadMatrixf"); + MatrixModeAddress = context.GetAddress("glMatrixMode"); + MultMatrixfAddress = context.GetAddress("glMultMatrixf"); + NewListAddress = context.GetAddress("glNewList"); + ReadPixelsAddress = context.GetAddress("glReadPixels"); + + TexCoord2fAddress = context.GetAddress("glTexCoord2f"); + TexCoordPointerAddress = context.GetAddress("glTexCoordPointer"); + TexImage2DAddress = context.GetAddress("glTexImage2D"); + TexParameteriAddress = context.GetAddress("glTexParameteri"); + TexSubImage2DAddress = context.GetAddress("glTexSubImage2D"); + Vertex3fAddress = context.GetAddress("glVertex3f"); + VertexPointerAddress = context.GetAddress("glVertexPointer"); + ViewportAddress = context.GetAddress("glViewport"); + } + + public static void UseArbVboAddresses() { + BindBufferAddress = BindBufferARBAddress; + BufferDataAddress = BufferDataARBAddress; + BufferSubDataAddress = BufferSubDataARBAddress; + DeleteBuffersAddress = DeleteBuffersARBAddress; + GenBuffersAddress = GenBuffersARBAddress; + } } public enum ArrayCap : int { @@ -418,83 +495,5 @@ namespace OpenTK.Graphics.OpenGL { public enum TextureTarget : int { Texture2D = 0x0DE1, } - - public unsafe static partial class GL { - internal static void LoadEntryPoints(IGraphicsContext context) { - Debug.Print("Loading OpenGL function pointers... "); - AlphaFuncAddress = context.GetAddress("glAlphaFunc"); - - BeginAddress = context.GetAddress("glBegin"); - BindBufferAddress = context.GetAddress("glBindBuffer"); - BindBufferARBAddress = context.GetAddress("glBindBufferARB"); - BindTextureAddress = context.GetAddress("glBindTexture"); - BlendFuncAddress = context.GetAddress("glBlendFunc"); - BufferDataAddress = context.GetAddress("glBufferData"); - BufferDataARBAddress = context.GetAddress("glBufferDataARB"); - BufferSubDataAddress = context.GetAddress("glBufferSubData"); - BufferSubDataARBAddress = context.GetAddress("glBufferSubDataARB"); - - CallListAddress = context.GetAddress("glCallList"); - ClearAddress = context.GetAddress("glClear"); - ClearColorAddress = context.GetAddress("glClearColor"); - Color4ubAddress = context.GetAddress("glColor4ub"); - ColorMaskAddress = context.GetAddress("glColorMask"); - ColorPointerAddress = context.GetAddress("glColorPointer"); - CullFaceAddress = context.GetAddress("glCullFace"); - - DeleteBuffersAddress = context.GetAddress("glDeleteBuffers"); - DeleteBuffersARBAddress = context.GetAddress("glDeleteBuffersARB"); - DeleteListsAddress = context.GetAddress("glDeleteLists"); - DeleteTexturesAddress = context.GetAddress("glDeleteTextures"); - DepthFuncAddress = context.GetAddress("glDepthFunc"); - DepthMaskAddress = context.GetAddress("glDepthMask"); - DisableAddress = context.GetAddress("glDisable"); - DisableClientStateAddress = context.GetAddress("glDisableClientState"); - DrawArraysAddress = context.GetAddress("glDrawArrays"); - DrawElementsAddress = context.GetAddress("glDrawElements"); - - EnableAddress = context.GetAddress("glEnable"); - EnableClientStateAddress = context.GetAddress("glEnableClientState"); - EndAddress = context.GetAddress("glEnd"); - EndListAddress = context.GetAddress("glEndList"); - FogfAddress = context.GetAddress("glFogf"); - FogfvAddress = context.GetAddress("glFogfv"); - FogiAddress = context.GetAddress("glFogi"); - - GenBuffersAddress = context.GetAddress("glGenBuffers"); - GenBuffersARBAddress = context.GetAddress("glGenBuffersARB"); - GenListsAddress = context.GetAddress("glGenLists"); - GenTexturesAddress = context.GetAddress("glGenTextures"); - GetErrorAddress = context.GetAddress("glGetError"); - GetIntegervAddress = context.GetAddress("glGetIntegerv"); - GetStringAddress = context.GetAddress("glGetString"); - GetTexImageAddress = context.GetAddress("glGetTexImage"); - - HintAddress = context.GetAddress("glHint"); - LoadIdentityAddress = context.GetAddress("glLoadIdentity"); - LoadMatrixfAddress = context.GetAddress("glLoadMatrixf"); - MatrixModeAddress = context.GetAddress("glMatrixMode"); - MultMatrixfAddress = context.GetAddress("glMultMatrixf"); - NewListAddress = context.GetAddress("glNewList"); - ReadPixelsAddress = context.GetAddress("glReadPixels"); - - TexCoord2fAddress = context.GetAddress("glTexCoord2f"); - TexCoordPointerAddress = context.GetAddress("glTexCoordPointer"); - TexImage2DAddress = context.GetAddress("glTexImage2D"); - TexParameteriAddress = context.GetAddress("glTexParameteri"); - TexSubImage2DAddress = context.GetAddress("glTexSubImage2D"); - Vertex3fAddress = context.GetAddress("glVertex3f"); - VertexPointerAddress = context.GetAddress("glVertexPointer"); - ViewportAddress = context.GetAddress("glViewport"); - } - - public static void UseArbVboAddresses() { - BindBufferAddress = BindBufferARBAddress; - BufferDataAddress = BufferDataARBAddress; - BufferSubDataAddress = BufferSubDataARBAddress; - DeleteBuffersAddress = DeleteBuffersARBAddress; - GenBuffersAddress = GenBuffersARBAddress; - } - } } #endif diff --git a/ClassicalSharp/Network/Protocols/Classic.cs b/ClassicalSharp/Network/Protocols/Classic.cs index e60f33f12..834ab1bfa 100644 --- a/ClassicalSharp/Network/Protocols/Classic.cs +++ b/ClassicalSharp/Network/Protocols/Classic.cs @@ -1,355 +1,350 @@ -// Copyright 2014-2017 ClassicalSharp | Licensed under BSD-3 -using System; -using ClassicalSharp.Gui; -using ClassicalSharp.Gui.Screens; -using ClassicalSharp.Entities; -using OpenTK; -#if __MonoCS__ -using Ionic.Zlib; -#else -using System.IO.Compression; -#endif -using BlockID = System.UInt16; - -namespace ClassicalSharp.Network.Protocols { - - /// Implements the packets for the original classic. - public sealed class ClassicProtocol : IProtocol { - - public ClassicProtocol(Game game) : base(game) { } - bool receivedFirstPosition; - DateTime mapReceiveStart; - DeflateStream gzipStream; - GZipHeaderReader gzipHeader; - int mapSizeIndex, mapIndex, mapVolume; - byte[] mapSize = new byte[4], map; - FixedBufferStream mapPartStream; - Screen prevScreen; - bool prevCursorVisible; - - public override void Reset() { - if (mapPartStream == null) mapPartStream = new FixedBufferStream(net.reader.buffer); - receivedFirstPosition = false; - - net.Set(Opcode.Handshake, HandleHandshake, 131); - net.Set(Opcode.Ping, HandlePing, 1); - net.Set(Opcode.LevelInit, HandleLevelInit, 1); - net.Set(Opcode.LevelDataChunk, HandleLevelDataChunk, 1028); - net.Set(Opcode.LevelFinalise, HandleLevelFinalise, 7); - net.Set(Opcode.SetBlock, HandleSetBlock, 8); - - net.Set(Opcode.AddEntity, HandleAddEntity, 74); - net.Set(Opcode.EntityTeleport, HandleEntityTeleport, 10); - net.Set(Opcode.RelPosAndOrientationUpdate, HandleRelPosAndOrientationUpdate, 7); - net.Set(Opcode.RelPosUpdate, HandleRelPositionUpdate, 5); - net.Set(Opcode.OrientationUpdate, HandleOrientationUpdate, 4); - net.Set(Opcode.RemoveEntity, HandleRemoveEntity, 2); - - net.Set(Opcode.Message, HandleMessage, 66); - net.Set(Opcode.Kick, HandleKick, 65); - net.Set(Opcode.SetPermission, HandleSetPermission, 2); - } - - public override void Tick() { - if (receivedFirstPosition) { - LocalPlayer player = game.LocalPlayer; - WritePosition(player.Position, player.HeadY, player.HeadX); - } - } - - #if !ONLY_8BIT - DeflateStream gzipStream2; - byte[] map2; - int mapIndex2; - #endif - - #region Read - - void HandleHandshake() { - byte protocolVer = reader.ReadUInt8(); - net.ServerName = reader.ReadString(); - net.ServerMotd = reader.ReadString(); - game.Chat.SetLogName(net.ServerName); - - game.LocalPlayer.Hacks.SetUserType(reader.ReadUInt8(), !net.cpeData.blockPerms); - game.LocalPlayer.Hacks.HacksFlags = net.ServerName + net.ServerMotd; - game.LocalPlayer.Hacks.UpdateHacksState(); - } - - void HandlePing() { } - - void HandleLevelInit() { - if (gzipStream == null) StartLoadingState(); - - // Fast map puts volume in header, doesn't bother with gzip - if (net.cpeData.fastMap) { - mapVolume = reader.ReadInt32(); - gzipHeader.done = true; - mapSizeIndex = 4; - map = new byte[mapVolume]; - } - } - - void StartLoadingState() { - game.World.Reset(); - game.WorldEvents.RaiseOnNewMap(); - - prevScreen = game.Gui.activeScreen; - if (prevScreen is LoadingScreen) - prevScreen = null; - prevCursorVisible = game.CursorVisible; - - game.Gui.SetNewScreen(new LoadingScreen(game, net.ServerName, net.ServerMotd), false); - net.wom.CheckMotd(); - receivedFirstPosition = false; - gzipHeader = new GZipHeaderReader(); - - // Workaround because built in mono stream assumes that the end of stream - // has been reached the first time a read call returns 0. (MS.NET doesn't) - #if __MonoCS__ - gzipStream = new DeflateStream(mapPartStream, true); - #else - gzipStream = new DeflateStream(mapPartStream, CompressionMode.Decompress); - if (OpenTK.Configuration.RunningOnMono) { - throw new InvalidOperationException("You must compile ClassicalSharp with __MonoCS__ defined " + - "to run on Mono, due to a limitation in Mono."); - } - #endif - - #if !ONLY_8BIT - #if __MonoCS__ - gzipStream2 = new DeflateStream(mapPartStream, true); - #else - gzipStream2 = new DeflateStream(mapPartStream, CompressionMode.Decompress); - #endif - #endif - - mapSizeIndex = 0; - mapIndex = 0; - #if !ONLY_8BIT - mapIndex2 = 0; - #endif - mapReceiveStart = DateTime.UtcNow; - } - - void HandleLevelDataChunk() { - // Workaround for some servers that send LevelDataChunk before LevelInit - // due to their async packet sending behaviour. - if (gzipStream == null) StartLoadingState(); - - int usedLength = reader.ReadUInt16(); - mapPartStream.pos = 0; - mapPartStream.bufferPos = reader.index; - mapPartStream.len = usedLength; - - reader.Skip(1024); - byte value = reader.ReadUInt8(); // progress in original classic, but we ignore it - - if (gzipHeader.done || gzipHeader.ReadHeader(mapPartStream)) { - if (mapSizeIndex < 4) { - mapSizeIndex += gzipStream.Read(mapSize, mapSizeIndex, 4 - mapSizeIndex); - } - - if (mapSizeIndex == 4) { - if (map == null) { - mapVolume = mapSize[0] << 24 | mapSize[1] << 16 | mapSize[2] << 8 | mapSize[3]; - map = new byte[mapVolume]; - } - - #if !ONLY_8BIT - if (reader.ExtendedBlocks && value != 0) { - // Only allocate map2 when needed - if (map2 == null) map2 = new byte[mapVolume]; - mapIndex2 += gzipStream2.Read(map2, mapIndex2, map2.Length - mapIndex2); - } else { - mapIndex += gzipStream.Read(map, mapIndex, map.Length - mapIndex); - } - #else - mapIndex += gzipStream.Read(map, mapIndex, map.Length - mapIndex); - #endif - } - } - - float progress = map == null ? 0 : (float)mapIndex / map.Length; - game.WorldEvents.RaiseLoading(progress); - } - - void HandleLevelFinalise() { - game.Gui.SetNewScreen(null); - game.Gui.activeScreen = prevScreen; - if (prevScreen != null && prevCursorVisible != game.CursorVisible) { - game.CursorVisible = prevCursorVisible; - } - prevScreen = null; - - int mapWidth = reader.ReadUInt16(); - int mapHeight = reader.ReadUInt16(); - int mapLength = reader.ReadUInt16(); - - double loadingMs = (DateTime.UtcNow - mapReceiveStart).TotalMilliseconds; - Utils.LogDebug("map loading took: " + loadingMs); - - game.World.SetNewMap(map, mapWidth, mapHeight, mapLength); - #if !ONLY_8BIT - if (reader.ExtendedBlocks) { - // defer allocation of scond map array if possible - game.World.blocks2 = map2 == null ? map : map2; - BlockInfo.SetMaxUsed(map2 == null ? 255 : 767); - } - #endif - game.WorldEvents.RaiseOnNewMapLoaded(); - net.wom.CheckSendWomID(); - - map = null; - gzipStream.Dispose(); - gzipStream = null; - #if !ONLY_8BIT - map2 = null; - gzipStream2.Dispose(); - gzipStream2 = null; - #endif - GC.Collect(); - } - - void HandleSetBlock() { - int x = reader.ReadUInt16(); - int y = reader.ReadUInt16(); - int z = reader.ReadUInt16(); - BlockID block = reader.ReadBlock(); - if (game.World.IsValidPos(x, y, z)) { - game.UpdateBlock(x, y, z, block); - } - } - - void HandleAddEntity() { - byte id = reader.ReadUInt8(); - string name = reader.ReadString(); - string skin = name; - CheckName(id, ref name, ref skin); - AddEntity(id, name, skin, true); - - // Workaround for some servers that declare they support ExtPlayerList, - // but doesn't send ExtAddPlayerName packets. - AddTablistEntry(id, name, name, "Players", 0); - classicTabList[id >> 3] |= (byte)(1 << (id & 0x7)); - } - - void HandleEntityTeleport() { - byte id = reader.ReadUInt8(); - ReadAbsoluteLocation(id, true); - } - - void HandleRelPosAndOrientationUpdate() { - byte id = reader.ReadUInt8(); - Vector3 v; - v.X = reader.ReadInt8() / 32f; - v.Y = reader.ReadInt8() / 32f; - v.Z = reader.ReadInt8() / 32f; - - float rotY = (float)Utils.PackedToDegrees(reader.ReadUInt8()); - float headX = (float)Utils.PackedToDegrees(reader.ReadUInt8()); - LocationUpdate update = LocationUpdate.MakePosAndOri(v, rotY, headX, true); - UpdateLocation(id, update, true); - } - - void HandleRelPositionUpdate() { - byte id = reader.ReadUInt8(); - Vector3 v; - v.X = reader.ReadInt8() / 32f; - v.Y = reader.ReadInt8() / 32f; - v.Z = reader.ReadInt8() / 32f; - - LocationUpdate update = LocationUpdate.MakePos(v, true); - UpdateLocation(id, update, true); - } - - void HandleOrientationUpdate() { - byte id = reader.ReadUInt8(); - float rotY = (float)Utils.PackedToDegrees(reader.ReadUInt8()); - float headX = (float)Utils.PackedToDegrees(reader.ReadUInt8()); - - LocationUpdate update = LocationUpdate.MakeOri(rotY, headX); - UpdateLocation(id, update, true); - } - - void HandleRemoveEntity() { - byte id = reader.ReadUInt8(); - RemoveEntity(id); - } - - void HandleMessage() { - byte type = reader.ReadUInt8(); - // Original vanilla server uses player ids in message types, 255 for server messages. - bool prepend = !net.cpeData.useMessageTypes && type == 0xFF; - - if (!net.cpeData.useMessageTypes) type = (byte)MessageType.Normal; - string text = reader.ReadChatString(ref type); - if (prepend) text = "&e" + text; - - if (!Utils.CaselessStarts(text, "^detail.user")) { - game.Chat.Add(text, (MessageType)type); - } - } - - void HandleKick() { - string reason = reader.ReadString(); - game.Disconnect("&eLost connection to the server", reason); - } - - void HandleSetPermission() { - game.LocalPlayer.Hacks.SetUserType(reader.ReadUInt8(), !net.cpeData.blockPerms); - game.LocalPlayer.Hacks.UpdateHacksState(); - } - - internal void ReadAbsoluteLocation(byte id, bool interpolate) { - Vector3 P = reader.ReadPosition(id); - float rotY = (float)Utils.PackedToDegrees(reader.ReadUInt8()); - float headX = (float)Utils.PackedToDegrees(reader.ReadUInt8()); - - if (id == EntityList.SelfID) receivedFirstPosition = true; - LocationUpdate update = LocationUpdate.MakePosAndOri(P, rotY, headX, false); - UpdateLocation(id, update, interpolate); - } - #endregion - - #region Write - - internal void WriteChat(string text, bool partial) { - int payload = !net.SupportsPartialMessages ? EntityList.SelfID : (partial ? 1 : 0); - writer.WriteUInt8((byte)Opcode.Message); - writer.WriteUInt8((byte)payload); - writer.WriteString(text); - } - - internal void WritePosition(Vector3 pos, float rotY, float headX) { - int payload = net.cpeData.sendHeldBlock ? game.Inventory.Selected : EntityList.SelfID; - writer.WriteUInt8((byte)Opcode.EntityTeleport); - - writer.WriteBlock((BlockID)payload); // held block when using HeldBlock, otherwise just 255 - writer.WritePosition(pos); - writer.WriteUInt8(Utils.DegreesToPacked(rotY)); - writer.WriteUInt8(Utils.DegreesToPacked(headX)); - } - - internal void WriteSetBlock(int x, int y, int z, bool place, BlockID block) { - writer.WriteUInt8((byte)Opcode.SetBlockClient); - writer.WriteInt16((short)x); - writer.WriteInt16((short)y); - writer.WriteInt16((short)z); - writer.WriteUInt8(place ? (byte)1 : (byte)0); - writer.WriteBlock(block); - } - - internal void WriteLogin(string username, string verKey) { - byte payload = game.UseCPE ? (byte)0x42 : (byte)0x00; - writer.WriteUInt8((byte)Opcode.Handshake); - - writer.WriteUInt8(7); // protocol version - writer.WriteString(username); - writer.WriteString(verKey); - writer.WriteUInt8(payload); - } - - #endregion - } -} +// Copyright 2014-2017 ClassicalSharp | Licensed under BSD-3 +using System; +using ClassicalSharp.Gui; +using ClassicalSharp.Gui.Screens; +using ClassicalSharp.Entities; +using OpenTK; +#if __MonoCS__ +using Ionic.Zlib; +#else +using System.IO.Compression; +#endif +using BlockID = System.UInt16; + +namespace ClassicalSharp.Network.Protocols { + + /// Implements the packets for the original classic. + public sealed class ClassicProtocol : IProtocol { + + public ClassicProtocol(Game game) : base(game) { } + bool receivedFirstPosition; + DateTime mapReceiveStart; + DeflateStream gzipStream; + GZipHeaderReader gzipHeader; + int mapSizeIndex, mapIndex, mapVolume; + byte[] mapSize = new byte[4], map; + FixedBufferStream mapPartStream; + Screen prevScreen; + + public override void Reset() { + if (mapPartStream == null) mapPartStream = new FixedBufferStream(net.reader.buffer); + receivedFirstPosition = false; + + net.Set(Opcode.Handshake, HandleHandshake, 131); + net.Set(Opcode.Ping, HandlePing, 1); + net.Set(Opcode.LevelInit, HandleLevelInit, 1); + net.Set(Opcode.LevelDataChunk, HandleLevelDataChunk, 1028); + net.Set(Opcode.LevelFinalise, HandleLevelFinalise, 7); + net.Set(Opcode.SetBlock, HandleSetBlock, 8); + + net.Set(Opcode.AddEntity, HandleAddEntity, 74); + net.Set(Opcode.EntityTeleport, HandleEntityTeleport, 10); + net.Set(Opcode.RelPosAndOrientationUpdate, HandleRelPosAndOrientationUpdate, 7); + net.Set(Opcode.RelPosUpdate, HandleRelPositionUpdate, 5); + net.Set(Opcode.OrientationUpdate, HandleOrientationUpdate, 4); + net.Set(Opcode.RemoveEntity, HandleRemoveEntity, 2); + + net.Set(Opcode.Message, HandleMessage, 66); + net.Set(Opcode.Kick, HandleKick, 65); + net.Set(Opcode.SetPermission, HandleSetPermission, 2); + } + + public override void Tick() { + if (receivedFirstPosition) { + LocalPlayer player = game.LocalPlayer; + WritePosition(player.Position, player.HeadY, player.HeadX); + } + } + + #if !ONLY_8BIT + DeflateStream gzipStream2; + byte[] map2; + int mapIndex2; + #endif + + #region Read + + void HandleHandshake() { + byte protocolVer = reader.ReadUInt8(); + net.ServerName = reader.ReadString(); + net.ServerMotd = reader.ReadString(); + game.Chat.SetLogName(net.ServerName); + + game.LocalPlayer.Hacks.SetUserType(reader.ReadUInt8(), !net.cpeData.blockPerms); + game.LocalPlayer.Hacks.HacksFlags = net.ServerName + net.ServerMotd; + game.LocalPlayer.Hacks.UpdateHacksState(); + } + + void HandlePing() { } + + void HandleLevelInit() { + if (gzipStream == null) StartLoadingState(); + + // Fast map puts volume in header, doesn't bother with gzip + if (net.cpeData.fastMap) { + mapVolume = reader.ReadInt32(); + gzipHeader.done = true; + mapSizeIndex = 4; + map = new byte[mapVolume]; + } + } + + void StartLoadingState() { + game.World.Reset(); + game.WorldEvents.RaiseOnNewMap(); + + prevScreen = game.Gui.activeScreen; + if (prevScreen is LoadingScreen) prevScreen = null; + + game.Gui.SetNewScreen(new LoadingScreen(game, net.ServerName, net.ServerMotd), false); + net.wom.CheckMotd(); + receivedFirstPosition = false; + gzipHeader = new GZipHeaderReader(); + + // Workaround because built in mono stream assumes that the end of stream + // has been reached the first time a read call returns 0. (MS.NET doesn't) + #if __MonoCS__ + gzipStream = new DeflateStream(mapPartStream, true); + #else + gzipStream = new DeflateStream(mapPartStream, CompressionMode.Decompress); + if (OpenTK.Configuration.RunningOnMono) { + throw new InvalidOperationException("You must compile ClassicalSharp with __MonoCS__ defined " + + "to run on Mono, due to a limitation in Mono."); + } + #endif + + #if !ONLY_8BIT + #if __MonoCS__ + gzipStream2 = new DeflateStream(mapPartStream, true); + #else + gzipStream2 = new DeflateStream(mapPartStream, CompressionMode.Decompress); + #endif + #endif + + mapSizeIndex = 0; + mapIndex = 0; + #if !ONLY_8BIT + mapIndex2 = 0; + #endif + mapReceiveStart = DateTime.UtcNow; + } + + void HandleLevelDataChunk() { + // Workaround for some servers that send LevelDataChunk before LevelInit + // due to their async packet sending behaviour. + if (gzipStream == null) StartLoadingState(); + + int usedLength = reader.ReadUInt16(); + mapPartStream.pos = 0; + mapPartStream.bufferPos = reader.index; + mapPartStream.len = usedLength; + + reader.Skip(1024); + byte value = reader.ReadUInt8(); // progress in original classic, but we ignore it + + if (gzipHeader.done || gzipHeader.ReadHeader(mapPartStream)) { + if (mapSizeIndex < 4) { + mapSizeIndex += gzipStream.Read(mapSize, mapSizeIndex, 4 - mapSizeIndex); + } + + if (mapSizeIndex == 4) { + if (map == null) { + mapVolume = mapSize[0] << 24 | mapSize[1] << 16 | mapSize[2] << 8 | mapSize[3]; + map = new byte[mapVolume]; + } + + #if !ONLY_8BIT + if (reader.ExtendedBlocks && value != 0) { + // Only allocate map2 when needed + if (map2 == null) map2 = new byte[mapVolume]; + mapIndex2 += gzipStream2.Read(map2, mapIndex2, map2.Length - mapIndex2); + } else { + mapIndex += gzipStream.Read(map, mapIndex, map.Length - mapIndex); + } + #else + mapIndex += gzipStream.Read(map, mapIndex, map.Length - mapIndex); + #endif + } + } + + float progress = map == null ? 0 : (float)mapIndex / map.Length; + game.WorldEvents.RaiseLoading(progress); + } + + void HandleLevelFinalise() { + game.Gui.SetNewScreen(null); + game.Gui.activeScreen = prevScreen; + game.Gui.CalcCursorVisible(); + prevScreen = null; + + int mapWidth = reader.ReadUInt16(); + int mapHeight = reader.ReadUInt16(); + int mapLength = reader.ReadUInt16(); + + double loadingMs = (DateTime.UtcNow - mapReceiveStart).TotalMilliseconds; + Utils.LogDebug("map loading took: " + loadingMs); + + game.World.SetNewMap(map, mapWidth, mapHeight, mapLength); + #if !ONLY_8BIT + if (reader.ExtendedBlocks) { + // defer allocation of scond map array if possible + game.World.blocks2 = map2 == null ? map : map2; + BlockInfo.SetMaxUsed(map2 == null ? 255 : 767); + } + #endif + game.WorldEvents.RaiseOnNewMapLoaded(); + net.wom.CheckSendWomID(); + + map = null; + gzipStream.Dispose(); + gzipStream = null; + #if !ONLY_8BIT + map2 = null; + gzipStream2.Dispose(); + gzipStream2 = null; + #endif + GC.Collect(); + } + + void HandleSetBlock() { + int x = reader.ReadUInt16(); + int y = reader.ReadUInt16(); + int z = reader.ReadUInt16(); + BlockID block = reader.ReadBlock(); + if (game.World.IsValidPos(x, y, z)) { + game.UpdateBlock(x, y, z, block); + } + } + + void HandleAddEntity() { + byte id = reader.ReadUInt8(); + string name = reader.ReadString(); + string skin = name; + CheckName(id, ref name, ref skin); + AddEntity(id, name, skin, true); + + // Workaround for some servers that declare they support ExtPlayerList, + // but doesn't send ExtAddPlayerName packets. + AddTablistEntry(id, name, name, "Players", 0); + classicTabList[id >> 3] |= (byte)(1 << (id & 0x7)); + } + + void HandleEntityTeleport() { + byte id = reader.ReadUInt8(); + ReadAbsoluteLocation(id, true); + } + + void HandleRelPosAndOrientationUpdate() { + byte id = reader.ReadUInt8(); + Vector3 v; + v.X = reader.ReadInt8() / 32f; + v.Y = reader.ReadInt8() / 32f; + v.Z = reader.ReadInt8() / 32f; + + float rotY = (float)Utils.PackedToDegrees(reader.ReadUInt8()); + float headX = (float)Utils.PackedToDegrees(reader.ReadUInt8()); + LocationUpdate update = LocationUpdate.MakePosAndOri(v, rotY, headX, true); + UpdateLocation(id, update, true); + } + + void HandleRelPositionUpdate() { + byte id = reader.ReadUInt8(); + Vector3 v; + v.X = reader.ReadInt8() / 32f; + v.Y = reader.ReadInt8() / 32f; + v.Z = reader.ReadInt8() / 32f; + + LocationUpdate update = LocationUpdate.MakePos(v, true); + UpdateLocation(id, update, true); + } + + void HandleOrientationUpdate() { + byte id = reader.ReadUInt8(); + float rotY = (float)Utils.PackedToDegrees(reader.ReadUInt8()); + float headX = (float)Utils.PackedToDegrees(reader.ReadUInt8()); + + LocationUpdate update = LocationUpdate.MakeOri(rotY, headX); + UpdateLocation(id, update, true); + } + + void HandleRemoveEntity() { + byte id = reader.ReadUInt8(); + RemoveEntity(id); + } + + void HandleMessage() { + byte type = reader.ReadUInt8(); + // Original vanilla server uses player ids in message types, 255 for server messages. + bool prepend = !net.cpeData.useMessageTypes && type == 0xFF; + + if (!net.cpeData.useMessageTypes) type = (byte)MessageType.Normal; + string text = reader.ReadChatString(ref type); + if (prepend) text = "&e" + text; + + if (!Utils.CaselessStarts(text, "^detail.user")) { + game.Chat.Add(text, (MessageType)type); + } + } + + void HandleKick() { + string reason = reader.ReadString(); + game.Disconnect("&eLost connection to the server", reason); + } + + void HandleSetPermission() { + game.LocalPlayer.Hacks.SetUserType(reader.ReadUInt8(), !net.cpeData.blockPerms); + game.LocalPlayer.Hacks.UpdateHacksState(); + } + + internal void ReadAbsoluteLocation(byte id, bool interpolate) { + Vector3 P = reader.ReadPosition(id); + float rotY = (float)Utils.PackedToDegrees(reader.ReadUInt8()); + float headX = (float)Utils.PackedToDegrees(reader.ReadUInt8()); + + if (id == EntityList.SelfID) receivedFirstPosition = true; + LocationUpdate update = LocationUpdate.MakePosAndOri(P, rotY, headX, false); + UpdateLocation(id, update, interpolate); + } + #endregion + + #region Write + + internal void WriteChat(string text, bool partial) { + int payload = !net.SupportsPartialMessages ? EntityList.SelfID : (partial ? 1 : 0); + writer.WriteUInt8((byte)Opcode.Message); + writer.WriteUInt8((byte)payload); + writer.WriteString(text); + } + + internal void WritePosition(Vector3 pos, float rotY, float headX) { + int payload = net.cpeData.sendHeldBlock ? game.Inventory.Selected : EntityList.SelfID; + writer.WriteUInt8((byte)Opcode.EntityTeleport); + + writer.WriteBlock((BlockID)payload); // held block when using HeldBlock, otherwise just 255 + writer.WritePosition(pos); + writer.WriteUInt8(Utils.DegreesToPacked(rotY)); + writer.WriteUInt8(Utils.DegreesToPacked(headX)); + } + + internal void WriteSetBlock(int x, int y, int z, bool place, BlockID block) { + writer.WriteUInt8((byte)Opcode.SetBlockClient); + writer.WriteInt16((short)x); + writer.WriteInt16((short)y); + writer.WriteInt16((short)z); + writer.WriteUInt8(place ? (byte)1 : (byte)0); + writer.WriteBlock(block); + } + + internal void WriteLogin(string username, string verKey) { + byte payload = game.UseCPE ? (byte)0x42 : (byte)0x00; + writer.WriteUInt8((byte)Opcode.Handshake); + + writer.WriteUInt8(7); // protocol version + writer.WriteString(username); + writer.WriteString(verKey); + writer.WriteUInt8(payload); + } + + #endregion + } +} diff --git a/ClassicalSharp/Utils/Camera.cs b/ClassicalSharp/Utils/Camera.cs index dea1a3a54..22396c58b 100644 --- a/ClassicalSharp/Utils/Camera.cs +++ b/ClassicalSharp/Utils/Camera.cs @@ -67,9 +67,9 @@ namespace ClassicalSharp { int cenX = topLeft.X + game.Width / 2; int cenY = topLeft.Y + game.Height / 2; - game.DesktopCursorPos = new Point(cenX, cenY); + game.window.DesktopCursorPos = new Point(cenX, cenY); // Fixes issues with large DPI displays on Windows >= 8.0. - previous = game.DesktopCursorPos; + previous = game.window.DesktopCursorPos; } public override void RegrabMouse() { @@ -127,8 +127,8 @@ namespace ClassicalSharp { public override void UpdateMouse() { if (game.Gui.ActiveScreen.HandlesAllInput) { delta = Point.Empty; - } else if (game.Focused) { - Point pos = game.DesktopCursorPos; + } else if (game.window.Focused) { + Point pos = game.window.DesktopCursorPos; delta = new Point(pos.X - previous.X, pos.Y - previous.Y); CentreMousePosition(); } diff --git a/src/Client/Game.c b/src/Client/Game.c index c980dc677..76aa0be45 100644 --- a/src/Client/Game.c +++ b/src/Client/Game.c @@ -120,21 +120,6 @@ void Game_SetDefaultTexturePack(STRING_PURE String* texPack) { Options_Set(OPT_DEFAULT_TEX_PACK, texPack); } - -bool game_CursorVisible = true, game_realCursorVisible = true; -bool Game_GetCursorVisible(void) { return game_CursorVisible; } -bool Game_GetRealCursorVisible(void) { return game_realCursorVisible; } -void Game_SetCursorVisible(bool visible) { - /* Defer mouse visibility changes */ - game_realCursorVisible = visible; - if (Gui_OverlaysCount > 0) return; - - /* Only set the value when it has changed */ - if (game_CursorVisible == visible) return; - Window_SetCursorVisible(visible); - game_CursorVisible = visible; -} - bool Game_ChangeTerrainAtlas(struct Bitmap* atlas) { String terrain = String_FromConst("terrain.png"); if (!Game_ValidateBitmap(&terrain, atlas)) return false; diff --git a/src/Client/Game.h b/src/Client/Game.h index 05fd74337..ba4cbd671 100644 --- a/src/Client/Game.h +++ b/src/Client/Game.h @@ -76,9 +76,6 @@ Real32 Game_GetChatScale(void); void Game_GetDefaultTexturePack(STRING_TRANSIENT String* texPack); void Game_SetDefaultTexturePack(STRING_PURE String* texPack); -bool Game_GetCursorVisible(void); -bool Game_GetRealCursorVisible(void); -void Game_SetCursorVisible(bool visible); bool Game_ChangeTerrainAtlas(struct Bitmap* atlas); void Game_SetViewDistance(Int32 distance, bool userDist); diff --git a/src/Client/Gui.c b/src/Client/Gui.c index fc1c59eb1..3cd70c054 100644 --- a/src/Client/Gui.c +++ b/src/Client/Gui.c @@ -42,7 +42,7 @@ void GuiElement_Reset(struct GuiElem* elem) { void Screen_Reset(struct Screen* screen) { GuiElement_Reset((struct GuiElem*)screen); - screen->HandlesAllInput = false; + screen->HandlesAllInput = true; screen->BlocksWorld = false; screen->HidesHUD = false; screen->RenderHUDOver = false; @@ -147,16 +147,10 @@ void Gui_FreeActive(void) { void Gui_SetActive(struct Screen* screen) { InputHandler_ScreenChanged(Gui_Active, screen); - - if (!screen) { - Game_SetCursorVisible(false); - if (Window_Focused) { Camera_Active->RegrabMouse(); } - } else if (!Gui_Active) { - Game_SetCursorVisible(true); - } - if (screen) { Elem_Init(screen); } + Gui_Active = screen; + Gui_CalcCursorVisible(); } void Gui_RefreshHud(void) { Elem_Recreate(Gui_HUD); } @@ -164,8 +158,6 @@ void Gui_ShowOverlay(struct Screen* overlay, bool atFront) { if (Gui_OverlaysCount == GUI_MAX_OVERLAYS) { ErrorHandler_Fail("Cannot have more than 6 overlays"); } - bool visible = Game_GetCursorVisible(); - if (!Gui_OverlaysCount) Game_SetCursorVisible(true); if (atFront) { Int32 i; @@ -179,8 +171,8 @@ void Gui_ShowOverlay(struct Screen* overlay, bool atFront) { } Gui_OverlaysCount++; - if (Gui_OverlaysCount == 1) Game_SetCursorVisible(visible); /* Save cursor visibility state */ Elem_Init(overlay); + Gui_CalcCursorVisible(); } Int32 Gui_IndexOverlay(struct Screen* overlay) { @@ -202,9 +194,7 @@ void Gui_FreeOverlay(struct Screen* overlay) { Gui_OverlaysCount--; Gui_Overlays[Gui_OverlaysCount] = NULL; - - if (!Gui_OverlaysCount) { Game_SetCursorVisible(Game_GetRealCursorVisible()); } - Camera_Active->RegrabMouse(); + Gui_CalcCursorVisible(); } void Gui_RenderGui(Real64 delta) { @@ -233,6 +223,17 @@ void Gui_OnResize(void) { } } +bool gui_cursorVisible = true; +void Gui_CalcCursorVisible(void) { + bool vis = Gui_GetActiveScreen()->HandlesAllInput; + if (vis == gui_cursorVisible) return; + gui_cursorVisible = vis; + + Window_SetCursorVisible(vis); + if (Window_Focused) + Camera_Active->RegrabMouse(); +} + void TextAtlas_Make(struct TextAtlas* atlas, STRING_PURE String* chars, struct FontDesc* font, STRING_PURE String* prefix) { struct DrawTextArgs args; DrawTextArgs_Make(&args, prefix, font, true); diff --git a/src/Client/Gui.h b/src/Client/Gui.h index 704481b3e..d3a497a4a 100644 --- a/src/Client/Gui.h +++ b/src/Client/Gui.h @@ -87,7 +87,7 @@ Int32 Gui_IndexOverlay(struct Screen* overlay); void Gui_FreeOverlay(struct Screen* overlay); void Gui_RenderGui(Real64 delta); void Gui_OnResize(void); - +void Gui_CalcCursorVisible(void); #define TEXTATLAS_MAX_WIDTHS 16 struct TextAtlas { diff --git a/src/Client/Menus.c b/src/Client/Menus.c index 176338745..7d28c07e6 100644 --- a/src/Client/Menus.c +++ b/src/Client/Menus.c @@ -523,7 +523,6 @@ struct ListScreen* ListScreen_MakeInstance(void) { screen->VTABLE->Init = ListScreen_Init; screen->VTABLE->Render = ListScreen_Render; screen->VTABLE->Free = ListScreen_Free; - screen->HandlesAllInput = true; return screen; } @@ -622,7 +621,6 @@ static void MenuScreen_MakeInstance(struct MenuScreen* screen, struct Widget** w screen->VTABLE->Render = MenuScreen_Render; screen->VTABLE->Free = MenuScreen_Free; - screen->HandlesAllInput = true; screen->Widgets = widgets; screen->WidgetsCount = count; screen->ContextLost = MenuScreen_ContextLost; diff --git a/src/Client/PacketHandlers.c b/src/Client/PacketHandlers.c index 7d0fbaa3d..e410a672c 100644 --- a/src/Client/PacketHandlers.c +++ b/src/Client/PacketHandlers.c @@ -312,7 +312,7 @@ UInt8 mapSize[4]; UInt8* map; struct Stream mapPartStream; struct Screen* prevScreen; -bool prevCursorVisible, receivedFirstPosition; +bool receivedFirstPosition; void Classic_WriteChat(struct Stream* stream, STRING_PURE String* text, bool partial) { Int32 payload = !ServerConnection_SupportsPartialMessages ? ENTITIES_SELF_ID : (partial ? 1 : 0); @@ -385,7 +385,6 @@ static void Classic_StartLoading(struct Stream* stream) { Gui_FreeActive(); prevScreen = NULL; } - prevCursorVisible = Game_GetCursorVisible(); Gui_SetActive(LoadingScreen_MakeInstance(&ServerConnection_ServerName, &ServerConnection_ServerMOTD)); WoM_CheckMotd(); @@ -458,10 +457,8 @@ static void Classic_LevelDataChunk(struct Stream* stream) { static void Classic_LevelFinalise(struct Stream* stream) { Gui_ReplaceActive(NULL); Gui_Active = prevScreen; - if (prevScreen && prevCursorVisible != Game_GetCursorVisible()) { - Game_SetCursorVisible(prevCursorVisible); - } prevScreen = NULL; + Gui_CalcCursorVisible(); Int32 mapWidth = Stream_ReadU16_BE(stream); Int32 mapHeight = Stream_ReadU16_BE(stream); diff --git a/src/Client/Screens.c b/src/Client/Screens.c index ed04d5200..d15440da7 100644 --- a/src/Client/Screens.c +++ b/src/Client/Screens.c @@ -25,7 +25,7 @@ struct InventoryScreen { Screen_Layout struct FontDesc Font; struct TableWidget Table; - bool ReleasedInv; + bool ReleasedInv, DeferredSelect; }; struct StatusScreen { @@ -126,6 +126,18 @@ static void InventoryScreen_ContextRecreated(void* obj) { Elem_Recreate(&screen->Table); } +static void InventoryScreen_MoveToSelected(struct InventoryScreen* screen) { + struct TableWidget* table = &screen->Table; + TableWidget_SetBlockTo(table, Inventory_SelectedBlock); + Elem_Recreate(table); + screen->DeferredSelect = false; + + /* User is holding invalid block */ + if (table->SelectedIndex == -1) { + TableWidget_MakeDescTex(table, Inventory_SelectedBlock); + } +} + static void InventoryScreen_Init(struct GuiElem* elem) { struct InventoryScreen* screen = (struct InventoryScreen*)elem; Font_Make(&screen->Font, &Game_FontName, 16, FONT_STYLE_NORMAL); @@ -135,10 +147,10 @@ static void InventoryScreen_Init(struct GuiElem* elem) { screen->Table.ElementsPerRow = Game_PureClassic ? 9 : 10; Elem_Init(&screen->Table); - /* User is holding invalid block */ - if (screen->Table.SelectedIndex == -1) { - TableWidget_MakeDescTex(&screen->Table, Inventory_SelectedBlock); - } + /* Can't immediately move to selected here, because cursor visibility + might be toggled after Init() is called. This causes the cursor to + be moved back to the middle of the window. */ + screen->DeferredSelect = true; Key_KeyRepeat = true; Event_RegisterVoid(&BlockEvents_PermissionsChanged, screen, InventoryScreen_OnBlockChanged); @@ -149,6 +161,7 @@ static void InventoryScreen_Init(struct GuiElem* elem) { static void InventoryScreen_Render(struct GuiElem* elem, Real64 delta) { struct InventoryScreen* screen = (struct InventoryScreen*)elem; + if (screen->DeferredSelect) InventoryScreen_MoveToSelected(screen); Elem_Render(&screen->Table, delta); } @@ -238,7 +251,6 @@ struct Screen* InventoryScreen_MakeInstance(void) { Mem_Set(screen, 0, sizeof(struct InventoryScreen)); screen->VTABLE = &InventoryScreen_VTABLE; Screen_Reset((struct Screen*)screen); - screen->HandlesAllInput = true; screen->VTABLE->HandlesKeyDown = InventoryScreen_HandlesKeyDown; screen->VTABLE->HandlesKeyUp = InventoryScreen_HandlesKeyUp; @@ -424,6 +436,7 @@ struct Screen* StatusScreen_MakeInstance(void) { Mem_Set(screen, 0, sizeof(struct StatusScreen)); screen->VTABLE = &StatusScreen_VTABLE; Screen_Reset((struct Screen*)screen); + screen->HandlesAllInput = false; screen->OnResize = StatusScreen_OnResize; screen->VTABLE->Init = StatusScreen_Init; @@ -592,7 +605,6 @@ static void LoadingScreen_Make(struct LoadingScreen* screen, struct GuiElementVT Mem_Set(screen, 0, sizeof(struct LoadingScreen)); screen->VTABLE = vtable; Screen_Reset((struct Screen*)screen); - screen->HandlesAllInput = true; screen->VTABLE->HandlesKeyDown = LoadingScreen_HandlesKeyDown; screen->VTABLE->HandlesKeyPress = LoadingScreen_HandlesKeyPress; @@ -721,10 +733,10 @@ static void ChatScreen_UpdateAltTextY(struct ChatScreen* screen) { static void ChatScreen_SetHandlesAllInput(struct ChatScreen* screen, bool handles) { screen->HandlesAllInput = handles; Gui_HUD->HandlesAllInput = handles; + Gui_CalcCursorVisible(); } static void ChatScreen_OpenInput(struct ChatScreen* screen, STRING_PURE String* initialText) { - Game_SetCursorVisible(true); screen->SuppressNextPress = true; ChatScreen_SetHandlesAllInput(screen, true); Key_KeyRepeat = true; @@ -908,9 +920,6 @@ static bool ChatScreen_HandlesKeyDown(struct GuiElem* elem, Key key) { if (screen->HandlesAllInput) { /* text input bar */ if (key == KeyBind_Get(KeyBind_SendChat) || key == Key_KeypadEnter || key == KeyBind_Get(KeyBind_PauseOrExit)) { ChatScreen_SetHandlesAllInput(screen, false); - /* when underlying screen is HUD, user is interacting with the world normally */ - Game_SetCursorVisible(Gui_GetUnderlyingScreen() != Gui_HUD); - Camera_Active->RegrabMouse(); Key_KeyRepeat = false; if (key == KeyBind_Get(KeyBind_PauseOrExit)) { @@ -1064,7 +1073,7 @@ static void ChatScreen_ContextLost(void* obj) { if (screen->HandlesAllInput) { String_AppendString(&chatInInput, &screen->Input.Base.Text); - Game_SetCursorVisible(false); + Gui_CalcCursorVisible(); } Elem_TryFree(&screen->Chat); @@ -1181,6 +1190,7 @@ struct Screen* ChatScreen_MakeInstance(void) { Mem_Set(screen, 0, sizeof(struct ChatScreen)); screen->VTABLE = &ChatScreen_VTABLE; Screen_Reset((struct Screen*)screen); + screen->HandlesAllInput = false; screen->InputOldHeight = -1; screen->LastDownloadStatus = Int32_MinValue; @@ -1378,6 +1388,7 @@ struct Screen* HUDScreen_MakeInstance(void) { Mem_Set(screen, 0, sizeof(struct HUDScreen)); screen->VTABLE = &HUDScreenVTABLE; Screen_Reset((struct Screen*)screen); + screen->HandlesAllInput = false; screen->OnResize = HUDScreen_OnResize; screen->VTABLE->Init = HUDScreen_Init; @@ -1577,7 +1588,6 @@ struct Screen* DisconnectScreen_MakeInstance(STRING_PURE String* title, STRING_P Mem_Set(screen, 0, sizeof(struct DisconnectScreen)); screen->VTABLE = &DisconnectScreen_VTABLE; Screen_Reset((struct Screen*)screen); - screen->HandlesAllInput = true; String titleScreen = String_InitAndClearArray(screen->TitleBuffer); String_AppendString(&titleScreen, title); diff --git a/src/Client/Widgets.c b/src/Client/Widgets.c index 8fa52aff4..a407c8091 100644 --- a/src/Client/Widgets.c +++ b/src/Client/Widgets.c @@ -658,8 +658,6 @@ static void TableWidget_Init(struct GuiElem* elem) { ScrollbarWidget_Create(&widget->Scroll); TableWidget_RecreateElements(widget); Widget_Reposition(widget); - TableWidget_SetBlockTo(widget, Inventory_SelectedBlock); - Elem_Recreate(widget); } static void TableWidget_Render(struct GuiElem* elem, Real64 delta) {