diff --git a/ClassicalSharp/Blocks/BlockInfo.cs b/ClassicalSharp/Blocks/BlockInfo.cs index c41b66564..af4a5002b 100644 --- a/ClassicalSharp/Blocks/BlockInfo.cs +++ b/ClassicalSharp/Blocks/BlockInfo.cs @@ -70,7 +70,7 @@ namespace ClassicalSharp { public static FastColour[] FogColour; public static Vector3[] MinBB, MaxBB, RenderMinBB, RenderMaxBB; static uint[] DefinedCustomBlocks; - public static int MaxDefined, Count, IDMask; + public static int MaxUsed, Count, IDMask; public static void Allocate(int count) { IsLiquid = new bool[count]; @@ -100,9 +100,13 @@ namespace ClassicalSharp { RenderMaxBB = new Vector3[count]; DefinedCustomBlocks = new uint[count >> 5]; - MaxDefined = count - 1; Count = count; - IDMask = Utils.NextPowerOf2(Count) - 1; + SetMaxUsed(count - 1); + } + + public static void SetMaxUsed(int max) { + MaxUsed = max; + IDMask = Utils.NextPowerOf2(max + 1) - 1; } public static void Reset() { diff --git a/ClassicalSharp/Entities/Entity.cs b/ClassicalSharp/Entities/Entity.cs index c6926864d..6e83c7840 100644 --- a/ClassicalSharp/Entities/Entity.cs +++ b/ClassicalSharp/Entities/Entity.cs @@ -123,7 +123,7 @@ namespace ClassicalSharp.Entities { ModelBlock = Block.Air; BlockID block; - if (BlockID.TryParse(ModelName, out block) && block <= BlockInfo.MaxDefined) { + if (BlockID.TryParse(ModelName, out block) && block < BlockInfo.Count) { ModelName = "block"; ModelBlock = block; } diff --git a/ClassicalSharp/Map/Lighting/BasicLighting.Heightmap.cs b/ClassicalSharp/Map/Lighting/BasicLighting.Heightmap.cs index baea8e9d5..762efbaa7 100644 --- a/ClassicalSharp/Map/Lighting/BasicLighting.Heightmap.cs +++ b/ClassicalSharp/Map/Lighting/BasicLighting.Heightmap.cs @@ -12,7 +12,7 @@ namespace ClassicalSharp.Map { int i = (maxY * length + z) * width + x; BlockRaw[] blocks = game.World.blocks; - if (BlockInfo.MaxDefined < 256) { + if (BlockInfo.MaxUsed < 256) { for (int y = maxY; y >= 0; y--, i -= oneY) { int block = blocks[i]; if (BlockInfo.BlocksLight[block]) { diff --git a/ClassicalSharp/Map/Lighting/BasicLighting.Updater.cs b/ClassicalSharp/Map/Lighting/BasicLighting.Updater.cs index bd6df1bb1..c8378e66f 100644 --- a/ClassicalSharp/Map/Lighting/BasicLighting.Updater.cs +++ b/ClassicalSharp/Map/Lighting/BasicLighting.Updater.cs @@ -117,7 +117,7 @@ namespace ClassicalSharp.Map { BlockRaw[] blocks = world.blocks; // Update if any blocks in the chunk are affected by light change - if (BlockInfo.MaxDefined < 256) { + if (BlockInfo.MaxUsed < 256) { for (; y >= minY; y--, i -= world.OneY) { BlockID other = blocks[i]; bool affected = y == nY ? Needs(block, other) : BlockInfo.Draw[other] != DrawType.Gas; diff --git a/ClassicalSharp/Map/Lighting/BasicLighting.cs b/ClassicalSharp/Map/Lighting/BasicLighting.cs index d3bb3d994..9a14180c5 100644 --- a/ClassicalSharp/Map/Lighting/BasicLighting.cs +++ b/ClassicalSharp/Map/Lighting/BasicLighting.cs @@ -71,7 +71,7 @@ namespace ClassicalSharp.Map { int elemsLeft = InitialHeightmapCoverage(x1, z1, xCount, zCount, skip); #if !ONLY_8BIT - if (BlockInfo.MaxDefined >= 256) { + if (BlockInfo.MaxUsed >= 256) { fixed (BlockRaw* mapPtr2 = game.World.blocks2) { if (!CalculateHeightmapCoverage_16Bit(x1, z1, xCount, zCount, elemsLeft, skip, mapPtr, mapPtr2)) { FinishHeightmapCoverage(x1, z1, xCount, zCount, skip); diff --git a/ClassicalSharp/Map/World.cs b/ClassicalSharp/Map/World.cs index 00d3a8b72..56f7855ef 100644 --- a/ClassicalSharp/Map/World.cs +++ b/ClassicalSharp/Map/World.cs @@ -66,7 +66,13 @@ namespace ClassicalSharp.Map { public void SetBlock(int x, int y, int z, BlockID blockId) { int i = (y * Length + z) * Width + x; blocks[i] = (BlockRaw)blockId; - if (blocks == blocks2) return; + + // defer allocation of second map array if possible + if (blocks == blocks2) { + if (blockId < 256) return; + blocks2 = new BlockRaw[blocks.Length]; + BlockInfo.SetMaxUsed(767); + } blocks2[i] = (BlockRaw)(blockId >> 8); } diff --git a/ClassicalSharp/MeshBuilder/Builder.cs b/ClassicalSharp/MeshBuilder/Builder.cs index 6c297034e..a112fa626 100644 --- a/ClassicalSharp/MeshBuilder/Builder.cs +++ b/ClassicalSharp/MeshBuilder/Builder.cs @@ -48,7 +48,7 @@ namespace ClassicalSharp { bool allSolid = false; fixed (BlockRaw* mapPtr = map.blocks) { #if !ONLY_8BIT - if (BlockInfo.MaxDefined >= 256) { + if (BlockInfo.MaxUsed >= 256) { ReadChunkData_16Bit(x1, y1, z1, mapPtr, ref allAir, ref allSolid); } else { ReadChunkData_8Bit(x1, y1, z1, mapPtr, ref allAir, ref allSolid); diff --git a/ClassicalSharp/Network/NetworkProcessor.cs b/ClassicalSharp/Network/NetworkProcessor.cs index dc2a10a21..923eab7cc 100644 --- a/ClassicalSharp/Network/NetworkProcessor.cs +++ b/ClassicalSharp/Network/NetworkProcessor.cs @@ -198,6 +198,7 @@ namespace ClassicalSharp.Network { reader.ExtendedPositions = false; reader.ExtendedBlocks = false; writer.ExtendedPositions = false; writer.ExtendedBlocks = false; + BlockInfo.SetMaxUsed(255); } internal Action[] handlers = new Action[256]; diff --git a/ClassicalSharp/Network/Protocols/CPE.cs b/ClassicalSharp/Network/Protocols/CPE.cs index 0a8c07ff9..ce3eb1a6a 100644 --- a/ClassicalSharp/Network/Protocols/CPE.cs +++ b/ClassicalSharp/Network/Protocols/CPE.cs @@ -324,13 +324,14 @@ namespace ClassicalSharp.Network.Protocols { int value = reader.ReadInt32(); WorldEnv env = game.World.Env; Utils.Clamp(ref value, -0xFFFFFF, 0xFFFFFF); + int maxBlock = BlockInfo.Count - 1; switch (type) { case 0: - Utils.Clamp(ref value, 0, BlockInfo.MaxDefined); + Utils.Clamp(ref value, 0, maxBlock); env.SetSidesBlock((BlockID)value); break; case 1: - Utils.Clamp(ref value, 0, BlockInfo.MaxDefined); + Utils.Clamp(ref value, 0, maxBlock); env.SetEdgeBlock((BlockID)value); break; case 2: env.SetEdgeLevel(value); break; diff --git a/ClassicalSharp/Network/Protocols/Classic.cs b/ClassicalSharp/Network/Protocols/Classic.cs index 6f6a4937f..50c3fa900 100644 --- a/ClassicalSharp/Network/Protocols/Classic.cs +++ b/ClassicalSharp/Network/Protocols/Classic.cs @@ -46,7 +46,7 @@ namespace ClassicalSharp.Network.Protocols { DateTime mapReceiveStart; DeflateStream gzipStream; GZipHeaderReader gzipHeader; - int mapSizeIndex, mapIndex; + int mapSizeIndex, mapIndex, mapVolume; byte[] mapSize = new byte[4], map; FixedBufferStream mapPartStream; Screen prevScreen; @@ -78,13 +78,10 @@ namespace ClassicalSharp.Network.Protocols { // Fast map puts volume in header, doesn't bother with gzip if (net.cpeData.fastMap) { - int size = reader.ReadInt32(); + mapVolume = reader.ReadInt32(); gzipHeader.done = true; mapSizeIndex = 4; - map = new byte[size]; - #if !ONLY_8BIT - if (reader.ExtendedBlocks) map2 = new byte[size]; - #endif + map = new byte[mapVolume]; } } @@ -150,15 +147,14 @@ namespace ClassicalSharp.Network.Protocols { if (mapSizeIndex == 4) { if (map == null) { - int size = mapSize[0] << 24 | mapSize[1] << 16 | mapSize[2] << 8 | mapSize[3]; - map = new byte[size]; - #if !ONLY_8BIT - if (reader.ExtendedBlocks) map2 = new byte[size]; - #endif + 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); @@ -181,7 +177,7 @@ namespace ClassicalSharp.Network.Protocols { } prevScreen = null; - int mapWidth = reader.ReadUInt16(); + int mapWidth = reader.ReadUInt16(); int mapHeight = reader.ReadUInt16(); int mapLength = reader.ReadUInt16(); @@ -190,7 +186,11 @@ namespace ClassicalSharp.Network.Protocols { game.World.SetNewMap(map, mapWidth, mapHeight, mapLength); #if !ONLY_8BIT - if (reader.ExtendedBlocks) game.World.blocks2 = map2; + 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(); diff --git a/ClassicalSharp/Rendering/Env/WeatherRenderer.cs b/ClassicalSharp/Rendering/Env/WeatherRenderer.cs index 0578c7f92..19f8e62f3 100644 --- a/ClassicalSharp/Rendering/Env/WeatherRenderer.cs +++ b/ClassicalSharp/Rendering/Env/WeatherRenderer.cs @@ -161,7 +161,7 @@ namespace ClassicalSharp.Renderers { int i = (maxY * length + z) * width + x; BlockRaw[] blocks = map.blocks; - if (BlockInfo.MaxDefined < 256) { + if (BlockInfo.MaxUsed < 256) { for (int y = maxY; y >= 0; y--, i -= oneY) { byte draw = BlockInfo.Draw[blocks[i]]; if (!(draw == DrawType.Gas || draw == DrawType.Sprite)) { diff --git a/src/Client/Gui.h b/src/Client/Gui.h index ad0cafc44..4ec231ec7 100644 --- a/src/Client/Gui.h +++ b/src/Client/Gui.h @@ -31,28 +31,28 @@ typedef struct GuiElementVTABLE_ { typedef struct GuiElement_ { GuiElementVTABLE* VTABLE; } GuiElement; void GuiElement_Reset(GuiElement* elem); -/* - HandlesAllInput; / Whether this screen handles all input. Prevents user interacting with the world - BlocksWorld; / Whether this screen completely and opaquely covers the game world behind it - HidesHUD; / Whether this screen hides the normal in-game HUD - RenderHUDOver; / Whether the normal in-game HUD should be drawn over the top of this screen */ -#define Screen_Layout GuiElementVTABLE* VTABLE; bool HandlesAllInput, BlocksWorld; \ -bool HidesHUD, RenderHUDOver; void (*OnResize)(GuiElement* elem); + +#define Screen_Layout GuiElementVTABLE* VTABLE; \ +bool HandlesAllInput; /* Whether this screen handles all input. Prevents user interacting with the world */ \ +bool BlocksWorld; /* Whether this screen completely and opaquely covers the game world behind it */ \ +bool HidesHUD; /* Whether this screen hides the normal in-game HUD */ \ +bool RenderHUDOver; /* Whether the normal in-game HUD should be drawn over the top of this screen */ \ +void (*OnResize)(GuiElement* elem); /* Represents a container of widgets and other 2D elements. May cover entire window. */ typedef struct Screen_ { Screen_Layout } Screen; void Screen_Reset(Screen* screen); -/* - X, Y, Width, Height; / Top left corner, and dimensions, of this widget - Active; / Whether this widget is currently being moused over - Disabled; / Whether widget is prevented from being interacted with - HorAnchor, VerAnchor; / Specifies the reference point for when this widget is resized - XOffset, YOffset; / Offset from the reference point */ typedef void (*Widget_LeftClick)(GuiElement* screenElem, GuiElement* widget); -#define Widget_Layout GuiElementVTABLE* VTABLE; Int32 X, Y, Width, Height; bool Active, Disabled; \ -UInt8 HorAnchor, VerAnchor; Int32 XOffset, YOffset; void (*Reposition)(GuiElement* elem); Widget_LeftClick MenuClick; +#define Widget_Layout GuiElementVTABLE* VTABLE; \ +Int32 X, Y, Width, Height; /* Top left corner, and dimensions, of this widget */ \ +bool Active; /* Whether this widget is currently being moused over*/ \ +bool Disabled; /* Whether widget is prevented from being interacted with */ \ +UInt8 HorAnchor, VerAnchor; /* Specifies the reference point for when this widget is resized */ \ +Int32 XOffset, YOffset; /* Offset from the reference point */ \ +void (*Reposition)(GuiElement* elem); \ +Widget_LeftClick MenuClick; /* Represents an individual 2D gui component. */ typedef struct Widget_ { Widget_Layout } Widget; @@ -60,6 +60,7 @@ void Widget_DoReposition(GuiElement* elem); void Widget_Init(Widget* widget); bool Widget_Contains(Widget* widget, Int32 x, Int32 y); + GfxResourceID Gui_GuiTex, Gui_GuiClassicTex, Gui_IconsTex; Screen* Gui_HUD; Screen* Gui_Active; diff --git a/src/Client/Widgets.c b/src/Client/Widgets.c index ab39b72b0..c3e74748e 100644 --- a/src/Client/Widgets.c +++ b/src/Client/Widgets.c @@ -588,9 +588,8 @@ void TableWidget_MakeDescTex(TableWidget* widget, BlockID block) { TableWidget_UpdateDescTexPos(widget); } -bool TableWidget_RowEmpty(Int32 i) { - BlockID[] map = game.Inventory.Map; - int max = Math.Min(i + ElementsPerRow, map.Length); +bool TableWidget_RowEmpty(TableWidget* widget, Int32 i) { + Int32 max = min(i + widget->ElementsPerRow, (Int32)Array_Elems(Inventory_Map)); Int32 j; for (j = i; j < max; j++) {