Implement more collide types, fixes #417

This commit is contained in:
UnknownShadow200 2017-04-28 21:44:33 +10:00
parent b7d07f03a6
commit e5656bcf8e
12 changed files with 157 additions and 115 deletions

View file

@ -19,20 +19,20 @@ namespace ClassicalSharp {
public Vector3[] RenderMinBB = new Vector3[Block.Count];
public Vector3[] RenderMaxBB = new Vector3[Block.Count];
internal void CalcRenderBounds(BlockID id) {
Vector3 min = MinBB[id], max = MaxBB[id];
internal void CalcRenderBounds(BlockID block) {
Vector3 min = MinBB[block], max = MaxBB[block];
if (id >= Block.Water && id <= Block.StillLava) {
if (block >= Block.Water && block <= Block.StillLava) {
min.X -= 0.1f/16f; max.X -= 0.1f/16f;
min.Z -= 0.1f/16f; max.Z -= 0.1f/16f;
min.Y -= 1.5f/16f; max.Y -= 1.5f/16f;
} else if (Draw[id] == DrawType.Translucent && Collide[id] != CollideType.Solid) {
} else if (Draw[block] == DrawType.Translucent && Collide[block] != CollideType.Solid) {
min.X += 0.1f/16f; max.X += 0.1f/16f;
min.Z += 0.1f/16f; max.Z += 0.1f/16f;
min.Y -= 0.1f/16f; max.Y -= 0.1f/16f;
}
RenderMinBB[id] = min; RenderMaxBB[id] = max;
RenderMinBB[block] = min; RenderMaxBB[block] = max;
}
internal byte CalcLightOffset(BlockID block) {

View file

@ -83,7 +83,7 @@ namespace ClassicalSharp {
if (Draw[block] != DrawType.Translucent || Draw[other] != DrawType.Translucent) return false;
// e.g. for water / ice, don't need to draw water.
CollideType bType = Collide[block], oType = Collide[other];
byte bType = Collide[block], oType = Collide[other];
bool canSkip = (bType == CollideType.Solid && oType == CollideType.Solid)
|| bType != CollideType.Solid;
return canSkip;

View file

@ -40,15 +40,27 @@ namespace ClassicalSharp {
}
/// <summary> Describes the interaction a block has with a player when they collide with it. </summary>
public enum CollideType : byte {
/// <summary> No interaction when player collides. (typically gas or sprite blocks) </summary>
WalkThrough,
public static class CollideType {
/// <summary> No interaction when player collides. </summary>
public const byte Gas = 0;
/// <summary> 'swimming'/'bobbing' interaction when player collides. (typically liquid blocks) </summary>
SwimThrough,
/// <summary> 'swimming'/'bobbing' interaction when player collides. </summary>
public const byte Liquid = 1;
/// <summary> Block completely stops the player when they are moving. (typically most blocks) </summary>
Solid,
/// <summary> Block completely stops the player when they are moving. </summary>
public const byte Solid = 2;
/// <summary> Block is solid and partially slidable on. </summary>
public const byte Ice = 3;
/// <summary> Block is solid and fully slidable on. </summary>
public const byte SlipperyIce = 4;
/// <summary> Water style 'swimming'/'bobbing' interaction when player collides. </summary>
public const byte LiquidWater = 5;
/// <summary> Lava style 'swimming'/'bobbing' interaction when player collides. </summary>
public const byte LiquidLava = 6;
}
/// <summary> Stores various properties about the blocks in Minecraft Classic. </summary>
@ -74,7 +86,9 @@ namespace ClassicalSharp {
/// <remarks> A value of 0 means this block does not apply fog. </remarks>
public float[] FogDensity = new float[Block.Count];
public CollideType[] Collide = new CollideType[Block.Count];
public byte[] Collide = new byte[Block.Count];
public byte[] ExtendedCollide = new byte[Block.Count];
public float[] SpeedMultiplier = new float[Block.Count];
@ -128,57 +142,71 @@ namespace ClassicalSharp {
place[Block.Bedrock] = false; delete[Block.Bedrock] = false;
}
public void SetBlockDraw(BlockID id, byte draw) {
if (draw == DrawType.Opaque && Collide[id] != CollideType.Solid)
draw = DrawType.Transparent;
Draw[id] = draw;
public void SetCollide(BlockID block, byte collide) {
// necessary for cases where servers redefined core blocks before extended types were introduced
collide = DefaultSet.MapOldCollide(block, collide);
ExtendedCollide[block] = collide;
FullOpaque[id] = draw == DrawType.Opaque
&& MinBB[id] == Vector3.Zero && MaxBB[id] == Vector3.One;
// Reduce extended collision types to their simpler forms
if (collide == CollideType.Ice) collide = CollideType.Solid;
if (collide == CollideType.SlipperyIce) collide = CollideType.Solid;
if (collide == CollideType.LiquidWater) collide = CollideType.Liquid;
if (collide == CollideType.LiquidLava) collide = CollideType.Liquid;
Collide[block] = collide;
}
public void SetBlockDraw(BlockID block, byte draw) {
if (draw == DrawType.Opaque && Collide[block] != CollideType.Solid)
draw = DrawType.Transparent;
Draw[block] = draw;
FullOpaque[block] = draw == DrawType.Opaque
&& MinBB[block] == Vector3.Zero && MaxBB[block] == Vector3.One;
}
/// <summary> Resets the properties for the given block to their defaults. </summary>
public void ResetBlockProps(BlockID id) {
BlocksLight[id] = DefaultSet.BlocksLight(id);
FullBright[id] = DefaultSet.FullBright(id);
FogColour[id] = DefaultSet.FogColour(id);
FogDensity[id] = DefaultSet.FogDensity(id);
Collide[id] = DefaultSet.Collide(id);
DigSounds[id] = DefaultSet.DigSound(id);
StepSounds[id] = DefaultSet.StepSound(id);
SpeedMultiplier[id] = 1;
Name[id] = DefaultName(id);
Tinted[id] = false;
public void ResetBlockProps(BlockID block) {
BlocksLight[block] = DefaultSet.BlocksLight(block);
FullBright[block] = DefaultSet.FullBright(block);
FogColour[block] = DefaultSet.FogColour(block);
FogDensity[block] = DefaultSet.FogDensity(block);
SetCollide(block, DefaultSet.Collide(block));
DigSounds[block] = DefaultSet.DigSound(block);
StepSounds[block] = DefaultSet.StepSound(block);
SpeedMultiplier[block] = 1;
Name[block] = DefaultName(block);
Tinted[block] = false;
Draw[id] = DefaultSet.Draw(id);
if (Draw[id] == DrawType.Sprite) {
MinBB[id] = new Vector3(2.50f/16f, 0, 2.50f/16f);
MaxBB[id] = new Vector3(13.5f/16f, 1, 13.5f/16f);
Draw[block] = DefaultSet.Draw(block);
if (Draw[block] == DrawType.Sprite) {
MinBB[block] = new Vector3(2.50f/16f, 0, 2.50f/16f);
MaxBB[block] = new Vector3(13.5f/16f, 1, 13.5f/16f);
} else {
MinBB[id] = Vector3.Zero;
MaxBB[id] = Vector3.One;
MaxBB[id].Y = DefaultSet.Height(id);
MinBB[block] = Vector3.Zero;
MaxBB[block] = Vector3.One;
MaxBB[block].Y = DefaultSet.Height(block);
}
SetBlockDraw(id, Draw[id]);
CalcRenderBounds(id);
LightOffset[id] = CalcLightOffset(id);
SetBlockDraw(block, Draw[block]);
CalcRenderBounds(block);
LightOffset[block] = CalcLightOffset(block);
if (id >= Block.CpeCount) {
if (block >= Block.CpeCount) {
#if USE16_BIT
// give some random texture ids
SetTex((id * 10 + (id % 7) + 20) % 80, Side.Top, id);
SetTex((id * 8 + (id & 5) + 5 ) % 80, Side.Bottom, id);
SetSide((id * 4 + (id / 4) + 4 ) % 80, id);
SetTex((block * 10 + (block % 7) + 20) % 80, Side.Top, block);
SetTex((block * 8 + (block & 5) + 5 ) % 80, Side.Bottom, block);
SetSide((block * 4 + (block / 4) + 4 ) % 80, block);
#else
SetTex(0, Side.Top, id);
SetTex(0, Side.Bottom, id);
SetSide(0, id);
SetTex(0, Side.Top, block);
SetTex(0, Side.Bottom, block);
SetSide(0, block);
#endif
} else {
SetTex(topTex[id], Side.Top, id);
SetTex(bottomTex[id], Side.Bottom, id);
SetSide(sideTex[id], id);
SetTex(topTex[block], Side.Top, block);
SetTex(bottomTex[block], Side.Bottom, block);
SetSide(sideTex[block], block);
}
}

View file

@ -41,14 +41,28 @@ namespace ClassicalSharp.Blocks {
return default(FastColour);
}
public static CollideType Collide(BlockID b) {
if (b >= Block.Water && b <= Block.StillLava)
return CollideType.SwimThrough;
public static byte Collide(BlockID b) {
if (b == Block.Ice) return CollideType.Ice;
if (b == Block.Water || b == Block.StillWater)
return CollideType.LiquidWater;
if (b == Block.Lava || b == Block.StillLava)
return CollideType.LiquidLava;
if (b == Block.Snow || b == Block.Air || Draw(b) == DrawType.Sprite)
return CollideType.WalkThrough;
return CollideType.Gas;
return CollideType.Solid;
}
public static byte MapOldCollide(BlockID b, byte collide) {
if (b == Block.Ice && collide == CollideType.Solid)
return CollideType.Ice;
if ((b == Block.Water || b == Block.StillWater) && collide == CollideType.Liquid)
return CollideType.LiquidWater;
if ((b == Block.Lava || b == Block.StillLava) && collide == CollideType.Liquid)
return CollideType.LiquidLava;
return collide;
}
public static bool BlocksLight(BlockID b) {
return !(b == Block.Glass || b == Block.Leaves
|| b == Block.Air || Draw(b) == DrawType.Sprite);

View file

@ -99,7 +99,7 @@ namespace ClassicalSharp.Entities {
}
bool StandardLiquid(BlockID block) {
return info.Collide[block] == CollideType.SwimThrough;
return info.Collide[block] == CollideType.Liquid;
}
static Vector3 waterDrag = new Vector3(0.8f, 0.8f, 0.8f),
@ -136,7 +136,7 @@ namespace ClassicalSharp.Entities {
MoveNormal(vel, factor * horSpeed, entity.Model.Drag, gravity, verSpeed);
}
if (entity.BlockUnderFeet == Block.Ice && !hacks.Floating) {
if (OnIce(entity, info) && !hacks.Floating) {
// limit components to +-0.25f by rescaling vector to [-0.25, 0.25]
if (Math.Abs(entity.Velocity.X) > 0.25f || Math.Abs(entity.Velocity.Z) > 0.25f) {
float scale = Math.Min(
@ -152,6 +152,14 @@ namespace ClassicalSharp.Entities {
if (entity.onGround) { firstJump = false; secondJump = false; }
}
static bool OnIce(Entity entity, BlockInfo info) {
if (info.ExtendedCollide[entity.BlockUnderFeet] == CollideType.Ice) return true;
AABB bounds = entity.Bounds;
bounds.Min.Y -= 0.01f; bounds.Max.Y = bounds.Min.Y;
return entity.TouchesAny(bounds, b => info.ExtendedCollide[b] == CollideType.SlipperyIce);
}
void MoveHor(Vector3 vel, float factor) {
float dist = (float)Math.Sqrt(vel.X * vel.X + vel.Z * vel.Z);
if (dist < 0.00001f) return;
@ -219,8 +227,8 @@ namespace ClassicalSharp.Entities {
{
BlockID block = game.World.SafeGetBlock(x, y, z);
if (block == 0) continue;
CollideType type = info.Collide[block];
if (type == CollideType.Solid && !checkSolid)
byte collide = info.Collide[block];
if (collide == CollideType.Solid && !checkSolid)
continue;
Vector3 min = new Vector3(x, y, z) + info.MinBB[block];
@ -229,7 +237,7 @@ namespace ClassicalSharp.Entities {
if (!blockBB.Intersects(bounds)) continue;
modifier = Math.Min(modifier, info.SpeedMultiplier[block]);
if (!info.IsLiquid(block) && type == CollideType.SwimThrough)
if (!info.IsLiquid(block) && collide == CollideType.Liquid)
useLiquidGravity = true;
}
return modifier;

View file

@ -77,8 +77,8 @@ namespace ClassicalSharp.Entities {
float maxY = feetPos.Y + game.BlockInfo.MaxBB[blockUnder].Y;
SoundType typeUnder = game.BlockInfo.StepSounds[blockUnder];
CollideType collideType = game.BlockInfo.Collide[blockUnder];
if (maxY >= pos.Y && collideType == CollideType.Solid && typeUnder != SoundType.None) {
byte collideUnder = game.BlockInfo.Collide[blockUnder];
if (maxY >= pos.Y && collideUnder == CollideType.Solid && typeUnder != SoundType.None) {
anyNonAir = true; sndType = typeUnder; return;
}
@ -89,7 +89,7 @@ namespace ClassicalSharp.Entities {
bool CheckSoundNonSolid(BlockID b) {
SoundType newType = game.BlockInfo.StepSounds[b];
CollideType collide = game.BlockInfo.Collide[b];
byte collide = game.BlockInfo.Collide[b];
if (newType != SoundType.None && collide != CollideType.Solid)
sndType = newType;

View file

@ -71,7 +71,7 @@ namespace ClassicalSharp.Entities {
// If liquid block above, leave height same
// otherwise reduce water BB height by 0.5 blocks
bool TouchesAnyLiquid(AABB bounds, BlockID block1, BlockID block2) {
bool TouchesAnyLiquid(AABB bounds, byte collide) {
Vector3I bbMin = Vector3I.Floor(bounds.Min);
Vector3I bbMax = Vector3I.Floor(bounds.Max);
@ -98,7 +98,7 @@ namespace ClassicalSharp.Entities {
// max.Y -= 4/16f;
if (!blockBB.Intersects(bounds)) continue;
if (block == block1 || block == block2) return true;
if (info.ExtendedCollide[block] == collide) return true;
}
return false;
}
@ -109,14 +109,14 @@ namespace ClassicalSharp.Entities {
// NOTE: Original classic client uses offset (so you can only climb up
// alternating liquid-solid elevators on two sides)
AABB bounds = Bounds.Offset(liqExpand);
return TouchesAnyLiquid(bounds, Block.Lava, Block.StillLava);
return TouchesAnyLiquid(bounds, CollideType.LiquidLava);
}
/// <summary> Determines whether any of the blocks that intersect the
/// bounding box of this entity are water or still water. </summary>
public bool TouchesAnyWater() {
AABB bounds = Bounds.Offset(liqExpand);
return TouchesAnyLiquid(bounds, Block.Water, Block.StillWater);
return TouchesAnyLiquid(bounds, CollideType.LiquidWater);
}
}
}

View file

@ -355,7 +355,7 @@ namespace ClassicalSharp {
public bool CanPick(BlockID block) {
if (BlockInfo.Draw[block] == DrawType.Gas) return false;
if (BlockInfo.Draw[block] == DrawType.Sprite) return true;
if (BlockInfo.Collide[block] != CollideType.SwimThrough) return true;
if (BlockInfo.Collide[block] != CollideType.Liquid) return true;
return !ModifiableLiquids ? false :
Inventory.CanPlace[block] && Inventory.CanDelete[block];

View file

@ -135,7 +135,7 @@ namespace ClassicalSharp.Map {
byte id = (byte)compound["ID"].Value;
BlockInfo info = game.BlockInfo;
info.Name[id] = (string)compound["Name"].Value;
info.Collide[id] = (CollideType)compound["CollideType"].Value;
info.SetCollide(id, (byte)compound["CollideType"].Value);
info.SpeedMultiplier[id] = (float)compound["Speed"].Value;
byte[] data = (byte[])compound["Textures"].Value;

View file

@ -35,12 +35,12 @@ namespace ClassicalSharp.Network.Protocols {
}
void HandleRemoveBlockDefinition() {
byte id = reader.ReadUInt8();
byte block = reader.ReadUInt8();
BlockInfo info = game.BlockInfo;
info.DefinedCustomBlocks[id >> 5] &= ~(1u << (id & 0x1F));
info.ResetBlockProps(id);
info.UpdateCulling(id);
info.DefinedCustomBlocks[block >> 5] &= ~(1u << (block & 0x1F));
info.ResetBlockProps(block);
info.UpdateCulling(block);
game.Events.RaiseBlockDefinitionChanged();
}
@ -66,59 +66,59 @@ namespace ClassicalSharp.Network.Protocols {
}
byte HandleDefineBlockCommonStart(NetReader reader, bool uniqueSideTexs) {
byte id = reader.ReadUInt8();
byte block = reader.ReadUInt8();
BlockInfo info = game.BlockInfo;
bool didBlockLight = info.BlocksLight[id];
info.ResetBlockProps(id);
bool didBlockLight = info.BlocksLight[block];
info.ResetBlockProps(block);
info.Name[id] = reader.ReadString();
info.Collide[id] = (CollideType)reader.ReadUInt8();
info.Name[block] = reader.ReadString();
info.SetCollide(block, reader.ReadUInt8());
info.SpeedMultiplier[id] = (float)Math.Pow(2, (reader.ReadUInt8() - 128) / 64f);
info.SetTex(reader.ReadUInt8(), Side.Top, id);
info.SpeedMultiplier[block] = (float)Math.Pow(2, (reader.ReadUInt8() - 128) / 64f);
info.SetTex(reader.ReadUInt8(), Side.Top, block);
if (uniqueSideTexs) {
info.SetTex(reader.ReadUInt8(), Side.Left, id);
info.SetTex(reader.ReadUInt8(), Side.Right, id);
info.SetTex(reader.ReadUInt8(), Side.Front, id);
info.SetTex(reader.ReadUInt8(), Side.Back, id);
info.SetTex(reader.ReadUInt8(), Side.Left, block);
info.SetTex(reader.ReadUInt8(), Side.Right, block);
info.SetTex(reader.ReadUInt8(), Side.Front, block);
info.SetTex(reader.ReadUInt8(), Side.Back, block);
} else {
info.SetSide(reader.ReadUInt8(), id);
info.SetSide(reader.ReadUInt8(), block);
}
info.SetTex(reader.ReadUInt8(), Side.Bottom, id);
info.SetTex(reader.ReadUInt8(), Side.Bottom, block);
// Need to refresh lighting when a block's light blocking state changes
info.BlocksLight[id] = reader.ReadUInt8() == 0;
if (!game.World.IsNotLoaded && (didBlockLight != info.BlocksLight[id])) {
info.BlocksLight[block] = reader.ReadUInt8() == 0;
if (!game.World.IsNotLoaded && (didBlockLight != info.BlocksLight[block])) {
game.Lighting.Refresh();
}
byte sound = reader.ReadUInt8();
if (sound < breakSnds.Length) {
info.StepSounds[id] = stepSnds[sound];
info.DigSounds[id] = breakSnds[sound];
info.StepSounds[block] = stepSnds[sound];
info.DigSounds[block] = breakSnds[sound];
}
info.FullBright[id] = reader.ReadUInt8() != 0;
return id;
info.FullBright[block] = reader.ReadUInt8() != 0;
return block;
}
void HandleDefineBlockCommonEnd(NetReader reader, byte shape, byte id) {
void HandleDefineBlockCommonEnd(NetReader reader, byte shape, byte block) {
BlockInfo info = game.BlockInfo;
byte blockDraw = reader.ReadUInt8();
if (shape == 0) blockDraw = DrawType.Sprite;
info.LightOffset[id] = info.CalcLightOffset(id);
info.LightOffset[block] = info.CalcLightOffset(block);
byte fogDensity = reader.ReadUInt8();
info.FogDensity[id] = fogDensity == 0 ? 0 : (fogDensity + 1) / 128f;
info.FogColour[id] = new FastColour(
info.FogDensity[block] = fogDensity == 0 ? 0 : (fogDensity + 1) / 128f;
info.FogColour[block] = new FastColour(
reader.ReadUInt8(), reader.ReadUInt8(), reader.ReadUInt8());
info.Tinted[id] = info.FogColour[id] != FastColour.Black && info.Name[id].IndexOf('#') >= 0;
info.Tinted[block] = info.FogColour[block] != FastColour.Black && info.Name[block].IndexOf('#') >= 0;
info.SetBlockDraw(id, blockDraw);
info.CalcRenderBounds(id);
info.UpdateCulling(id);
info.SetBlockDraw(block, blockDraw);
info.CalcRenderBounds(block);
info.UpdateCulling(block);
game.Events.RaiseBlockDefinitionChanged();
info.DefinedCustomBlocks[id >> 5] |= (1u << (id & 0x1F));
info.DefinedCustomBlocks[block >> 5] |= (1u << (block & 0x1F));
}
#if FALSE

View file

@ -124,7 +124,7 @@ namespace ClassicalSharp.Network.Protocols {
}
void HandleExtAddPlayerName() {
short id = reader.ReadInt16();
int id = reader.ReadInt16() & 0xFF;
string playerName = Utils.StripColours(reader.ReadString());
playerName = Utils.RemoveEndPlus(playerName);
string listName = reader.ReadString();
@ -135,11 +135,7 @@ namespace ClassicalSharp.Network.Protocols {
// Some server software will declare they support ExtPlayerList, but send AddEntity then AddPlayerName
// we need to workaround this case by removing all the tab names we added for the AddEntity packets
net.DisableAddEntityHack();
// Workaround for some servers that don't cast signed bytes to unsigned, before converting them to shorts.
if (id < 0) id += 256;
if (id >= 0 && id <= 255)
net.AddTablistEntry((byte)id, playerName, listName, groupName, groupRank);
net.AddTablistEntry((byte)id, playerName, listName, groupName, groupRank);
}
void HandleExtAddEntity() {
@ -151,12 +147,8 @@ namespace ClassicalSharp.Network.Protocols {
}
void HandleExtRemovePlayerName() {
short id = reader.ReadInt16();
// Workaround for some servers that don't cast signed bytes to unsigned, before converting them to shorts.
if (id < 0) id += 256;
if (id >= 0 && id <= 255)
net.RemoveTablistEntry((byte)id);
int id = reader.ReadInt16() & 0xFF;
net.RemoveTablistEntry((byte)id);
}
void HandleMakeSelection() {

View file

@ -85,7 +85,7 @@ namespace ClassicalSharp.Singleplayer {
BlockID block = map.blocks[posIndex];
if (block == Block.Water || block == Block.StillWater) {
game.UpdateBlock(x, y, z, Block.Stone);
} else if (info.Collide[block] == CollideType.WalkThrough) {
} else if (info.Collide[block] == CollideType.Gas) {
Lava.Enqueue(defLavaTick | (uint)posIndex);
game.UpdateBlock(x, y, z, Block.Lava);
}
@ -122,7 +122,7 @@ namespace ClassicalSharp.Singleplayer {
BlockID block = map.blocks[posIndex];
if (block == Block.Lava || block == Block.StillLava) {
game.UpdateBlock(x, y, z, Block.Stone);
} else if (info.Collide[block] == CollideType.WalkThrough && block != Block.Rope) {
} else if (info.Collide[block] == CollideType.Gas && block != Block.Rope) {
// Sponge check
for (int yy = (y < 2 ? 0 : y - 2); yy <= (y > maxWaterY ? maxY : y + 2); yy++)
for (int zz = (z < 2 ? 0 : z - 2); zz <= (z > maxWaterZ ? maxZ : z + 2); zz++)