Skip to content

Commit

Permalink
fix: server crash when loading Expansion
Browse files Browse the repository at this point in the history
  • Loading branch information
MrTJP committed Jun 26, 2023
1 parent ec7c6e3 commit 71ce517
Show file tree
Hide file tree
Showing 8 changed files with 2,763 additions and 81 deletions.
57 changes: 35 additions & 22 deletions core/src/main/java/mrtjp/projectred/lib/ModelVoxelShape.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.phys.shapes.VoxelShape;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import org.jetbrains.annotations.Nullable;

import java.util.LinkedList;
Expand All @@ -37,14 +39,11 @@ public ModelVoxelShape(VoxelShape parent, List<Tri> tris) {
this.tris = tris;
}

@OnlyIn(Dist.CLIENT)
public ModelVoxelShape(VoxelShape parent, CCModel model) {
this(parent, trisFromCCModel(model));
}

public ModelVoxelShape(VoxelShape parent, Vertex5[] verts, VertexFormat.Mode vertexMode) {
this(parent, trisFromVerts(verts, vertexMode));
}

@Override
public DoubleList getCoords(Direction.Axis axis) {
return parent.getCoords(axis);
Expand Down Expand Up @@ -82,31 +81,45 @@ public BlockHitResult clip(Vec3 start, Vec3 end, BlockPos pos) {
return new BlockHitResult(intersection.add(pos).vec3(), side, pos, true);
}

public static ModelVoxelShape fromTriangles(VoxelShape parent, Vertex5[] verts) {
return new ModelVoxelShape(parent, trisFromTriangles(verts));
}

public static ModelVoxelShape fromQuads(VoxelShape parent, Vertex5[] verts) {
return new ModelVoxelShape(parent, trisFromQuads(verts));
}

@OnlyIn(Dist.CLIENT)
public static List<Tri> trisFromCCModel(CCModel model) {
return trisFromVerts(model.verts, model.vertexMode);
return model.vertexMode == VertexFormat.Mode.QUADS ? trisFromQuads(model.verts) : trisFromTriangles(model.verts);
}

public static List<Tri> trisFromVerts(Vertex5[] verts, VertexFormat.Mode vertexMode) {
public static List<Tri> trisFromQuads(Vertex5[] verts) {
int vp = 4;
List<Tri> triList = new LinkedList<>();
for (int i = 0; i < verts.length; i += vp) {
// Convert 1 quad into 2 triangles
triList.add(new Tri(
verts[i ].copy(),
verts[i + 1].copy(),
verts[i + 2].copy()));
triList.add(new Tri(
verts[i ].copy(),
verts[i + 2].copy(),
verts[i + 3].copy()));
}

int vp = vertexMode == VertexFormat.Mode.QUADS ? 4 : 3;
return triList;
}

public static List<Tri> trisFromTriangles(Vertex5[] verts) {
int vp = 3;
List<Tri> triList = new LinkedList<>();
for (int i = 0; i < verts.length; i += vp) {
if (vertexMode == VertexFormat.Mode.QUADS) {
triList.add(new Tri(
verts[i ].copy(),
verts[i + 1].copy(),
verts[i + 2].copy()));
triList.add(new Tri(
verts[i ].copy(),
verts[i + 2].copy(),
verts[i + 3].copy()));
} else {
triList.add(new Tri(
verts[i ].copy(),
verts[i + 1].copy(),
verts[i + 2].copy()));
}
triList.add(new Tri(
verts[i ].copy(),
verts[i + 1].copy(),
verts[i + 2].copy()));
}

return triList;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ private static class ClientHandler implements ICustomPacketHandler.IClientPacket
public void handlePacket(PacketCustom packet, Minecraft mc, ClientPacketListener handler) {
switch (packet.getType()) {
case MM_FROM_SERVER:
MovementManager.getInstance(mc.level).read(packet);
MovementManager.getInstance(mc.level).read(packet, mc.level);
break;

default:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.client.ForgeHooksClient;
import net.minecraftforge.client.MinecraftForgeClient;
import net.minecraftforge.client.event.RenderLevelStageEvent;
Expand Down Expand Up @@ -106,7 +108,9 @@ public static void onLevelTick(TickEvent.WorldTickEvent event) {
}
}

@OnlyIn(Dist.CLIENT)
public static void onRenderLevelStage(RenderLevelStageEvent event) {
//TODO move to separate class

Level level = Minecraft.getInstance().level;
if (level == null) return;
Expand Down Expand Up @@ -288,18 +292,18 @@ private PacketCustom createPacket(int key) {
.writeByte(key);
}

public void read(MCDataInput input) {
public void read(MCDataInput input, Level level) {
int key = input.readUByte();
switch (key) {
case KEY_BULK_DESC -> readStructureDescriptions(input);
case KEY_NEW_STRUCT -> readNewStructure(input);
case KEY_EXECUTE_MOVE -> readStructureExecution(input);
case KEY_CANCEL_MOVE -> readStructureCancellation(input);
case KEY_BULK_DESC -> readStructureDescriptions(input, level);
case KEY_NEW_STRUCT -> readNewStructure(input, level);
case KEY_EXECUTE_MOVE -> readStructureExecution(input, level);
case KEY_CANCEL_MOVE -> readStructureCancellation(input, level);
default -> LOGGER.warn("Movement manager received unknown key " + key);
}
}

private void readStructureDescriptions(MCDataInput input) {
private void readStructureDescriptions(MCDataInput input, Level level) {

int count = input.readUShort();
for (int i = 0; i < count; i++) {
Expand All @@ -311,37 +315,37 @@ private void readStructureDescriptions(MCDataInput input) {
}
}

private void readNewStructure(MCDataInput input) {
private void readNewStructure(MCDataInput input, Level level) {
MovingStructure structure = MovingStructure.fromDesc(input);

if (structures.containsKey(structure.id)) {
LOGGER.debug("Client overwriting existing structure with id {}", structure.id);
}

structures.put(structure.id, structure);
structure.beginMove(Minecraft.getInstance().level);
structure.beginMove(level);
}

private void readStructureExecution(MCDataInput input) {
private void readStructureExecution(MCDataInput input, Level level) {
// Generate a new structure in case it was stale on client
MovingStructure structure = MovingStructure.fromDesc(input);

// Execute move
structure.executeMove(Minecraft.getInstance().level);
structure.executeMove(level);

// Delete struct that *should* have been on the client
if (structures.remove(structure.id) == null) {
LOGGER.warn("Move executed for unknown structure id {}", structure.id);
}
}

private void readStructureCancellation(MCDataInput input) {
private void readStructureCancellation(MCDataInput input, Level level) {
int id = input.readUShort();
MovingStructure structure = structures.get(id);
if (structure == null) {
LOGGER.debug("Received cancellation for unknown structure id {}", id);
} else {
structure.cancelMove(Minecraft.getInstance().level);
structure.cancelMove(level);
structures.remove(id);
}
}
Expand Down Expand Up @@ -580,6 +584,7 @@ public void cancelMove(Level level) {
cancelled = true;
}

@OnlyIn(Dist.CLIENT)
private void markChunksForRender() {
FastStream.of(renderChunks.get()).forEach(p -> Minecraft.getInstance().levelRenderer.setSectionDirty(p.x(), p.y(), p.z(), true));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package mrtjp.projectred.expansion.block;

import codechicken.lib.raytracer.VoxelShapeCache;
import codechicken.lib.render.particle.CustomParticleHandler;
import codechicken.lib.vec.Cuboid6;
import codechicken.lib.vec.*;
import com.google.common.collect.ImmutableSet;
import mrtjp.projectred.api.Frame;
import mrtjp.projectred.expansion.client.FrameModelRenderer;
import mrtjp.projectred.expansion.client.FrameModelVerts;
import mrtjp.projectred.lib.ModelVoxelShape;
import net.minecraft.client.particle.ParticleEngine;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
Expand All @@ -20,16 +24,22 @@
import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.world.phys.shapes.VoxelShape;
import net.minecraftforge.client.IBlockRenderProperties;
import net.minecraftforge.fml.DistExecutor;

import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.function.Consumer;

public class FrameBlock extends Block implements Frame {

private static final VoxelShape[] shapes = new VoxelShape[64];

public FrameBlock() {
super(BlockBehaviour.Properties.of(Material.WOOD)
.strength(2.0F)
.sound(SoundType.WOOD));
.sound(SoundType.WOOD)
.dynamicShape()); // To prevent early caching before modelVerts can be loaded
}

@Override
Expand Down Expand Up @@ -77,7 +87,7 @@ public boolean canBeGrabbed(Level w, BlockPos pos, Direction side) {
//region Shapes
@Override
public VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
return FrameModelRenderer.getShape(0);
return FrameBlock.getOrGenerateShape(0);
}

@Override
Expand All @@ -96,4 +106,59 @@ public VoxelShape getCollisionShape(BlockState state, BlockGetter level, BlockPo
return Shapes.block();
}
//endregion

//region Shape generation
public static VoxelShape getOrGenerateShape(int mask) {
VoxelShape s = shapes[mask & 0x3F];
if (s == null) {
s = generateShape(mask);
shapes[mask & 0x3F] = s;
}
return s;
}

private static VoxelShape generateShape(int mask) {

// bottom face cuboids
double th = 2/16D; // Frame thickness
Cuboid6 e0 = new Cuboid6(0, 0, 0, 1, th, th);
Cuboid6 e1 = e0.copy().apply(Rotation.quarterRotations[1].at(Vector3.CENTER));
Cuboid6 e2 = e0.copy().apply(Rotation.quarterRotations[2].at(Vector3.CENTER));
Cuboid6 e3 = e0.copy().apply(Rotation.quarterRotations[3].at(Vector3.CENTER));

List<VoxelShape> faceShapes = new LinkedList<>();
for (int s = 0; s < 6; s++) {
Transformation t = Rotation.sideOrientation(s, 0).at(Vector3.CENTER);
ImmutableSet.Builder<VoxelShape> fb = ImmutableSet.builder();
fb.add(VoxelShapeCache.getShape(e0.copy().apply(t)));
fb.add(VoxelShapeCache.getShape(e2.copy().apply(t)));
fb.add(VoxelShapeCache.getShape(e1.copy().apply(t)));
fb.add(VoxelShapeCache.getShape(e3.copy().apply(t)));
faceShapes.add(VoxelShapeCache.merge(fb.build()));
}

// Clients will use verts directly from parsed OBJ.
// Server will use data-pack JSON file with verts from OBJ
Vertex5[] verts = DistExecutor.unsafeRunForDist(
() -> () -> FrameModelRenderer.getQuadsForMask(mask),
() -> () -> getQuadsForMask(mask));

VoxelShape parent = VoxelShapeCache.merge(ImmutableSet.copyOf(faceShapes));
return ModelVoxelShape.fromQuads(parent, verts);
}

private static Vertex5[] getQuadsForMask(int mask) {
List<Vertex5> verts = new LinkedList<>();
verts.addAll(List.of(FrameModelVerts.verts.get("frame")));

for (int i = 0; i < 6; i++) {
if ((mask & (1 << i)) == 0) {
Vertex5[] crossModel = FrameModelVerts.verts.get("cross_" + i);
verts.addAll(List.of(crossModel));
}
}

return verts.toArray(new Vertex5[0]);
}
//endregion
}
Loading

0 comments on commit 71ce517

Please sign in to comment.