mirror of
https://github.com/Sollace/Unicopia.git
synced 2024-11-23 21:38:00 +01:00
Added multi-block destruction
This commit is contained in:
parent
f7a2053c78
commit
d1f6679882
10 changed files with 286 additions and 7 deletions
|
@ -0,0 +1,87 @@
|
||||||
|
package com.minelittlepony.unicopia;
|
||||||
|
|
||||||
|
import com.minelittlepony.unicopia.network.Channel;
|
||||||
|
import com.minelittlepony.unicopia.network.MsgBlockDestruction;
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
||||||
|
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||||
|
import net.minecraft.server.world.ServerWorld;
|
||||||
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
|
public class BlockDestructionManager {
|
||||||
|
|
||||||
|
public static final int UNSET_DAMAGE = -1;
|
||||||
|
public static final int MAX_DAMAGE = 10;
|
||||||
|
|
||||||
|
private final Destruction emptyDestruction = new Destruction(BlockPos.ORIGIN);
|
||||||
|
|
||||||
|
private final World world;
|
||||||
|
|
||||||
|
private final Long2ObjectMap<Destruction> destructions = new Long2ObjectOpenHashMap<>();
|
||||||
|
|
||||||
|
private final Object locker = new Object();
|
||||||
|
|
||||||
|
public BlockDestructionManager(World world) {
|
||||||
|
this.world = world;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getBlockDestruction(BlockPos pos) {
|
||||||
|
return destructions.getOrDefault(pos.asLong(), emptyDestruction).amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearBlockDestruction(BlockPos pos) {
|
||||||
|
setBlockDestruction(pos, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBlockDestruction(BlockPos pos, int amount) {
|
||||||
|
synchronized (locker) {
|
||||||
|
destructions.computeIfAbsent(pos.asLong(), p -> new Destruction(pos)).set(amount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int damageBlock(BlockPos pos, int amount) {
|
||||||
|
amount = Math.max(getBlockDestruction(pos), 0) + amount;
|
||||||
|
setBlockDestruction(pos, amount);
|
||||||
|
return amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void tick() {
|
||||||
|
synchronized (locker) {
|
||||||
|
destructions.long2ObjectEntrySet().removeIf(entry -> entry.getValue().tick());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Destruction {
|
||||||
|
BlockPos pos;
|
||||||
|
int amount = -1;
|
||||||
|
int age = 50;
|
||||||
|
|
||||||
|
Destruction(BlockPos pos) {
|
||||||
|
this.pos = pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean tick() {
|
||||||
|
if (age-- > 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (amount >= 0) {
|
||||||
|
set(amount - 1);
|
||||||
|
}
|
||||||
|
return amount < 0 || age-- <= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set(int amount) {
|
||||||
|
this.age = 50;
|
||||||
|
this.amount = amount >= 0 && amount < MAX_DAMAGE ? amount : UNSET_DAMAGE;
|
||||||
|
if (world instanceof ServerWorld) {
|
||||||
|
Channel.SERVER_BLOCK_DESTRUCTION.send(world, new MsgBlockDestruction(pos, this.amount));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface Source {
|
||||||
|
BlockDestructionManager getDestructionManager();
|
||||||
|
}
|
||||||
|
}
|
|
@ -33,7 +33,10 @@ public class Unicopia implements ModInitializer {
|
||||||
UTags.bootstrap();
|
UTags.bootstrap();
|
||||||
Commands.bootstrap();
|
Commands.bootstrap();
|
||||||
|
|
||||||
ServerTickEvents.END_WORLD_TICK.register(AwaitTickQueue::tick);
|
ServerTickEvents.END_WORLD_TICK.register(w -> {
|
||||||
|
AwaitTickQueue.tick(w);
|
||||||
|
((BlockDestructionManager.Source)w).getDestructionManager().tick();
|
||||||
|
});
|
||||||
|
|
||||||
UItems.bootstrap();
|
UItems.bootstrap();
|
||||||
UPotions.bootstrap();
|
UPotions.bootstrap();
|
||||||
|
|
|
@ -10,7 +10,7 @@ public class WorldTribeManager extends PersistentState {
|
||||||
private Race defaultRace = Unicopia.getConfig().getPrefferedRace();
|
private Race defaultRace = Unicopia.getConfig().getPrefferedRace();
|
||||||
|
|
||||||
public WorldTribeManager(ServerWorld world) {
|
public WorldTribeManager(ServerWorld world) {
|
||||||
super("unicopia:tribes" + world.getDimension().getSuffix());
|
super(nameFor(world.getDimension()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Race getDefaultRace() {
|
public Race getDefaultRace() {
|
||||||
|
|
|
@ -0,0 +1,83 @@
|
||||||
|
package com.minelittlepony.unicopia.client;
|
||||||
|
|
||||||
|
import java.util.SortedSet;
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
|
import com.minelittlepony.unicopia.BlockDestructionManager;
|
||||||
|
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
||||||
|
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||||
|
import net.minecraft.client.render.BlockBreakingInfo;
|
||||||
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
|
||||||
|
public class ClientBlockDestructionManager {
|
||||||
|
|
||||||
|
private final Long2ObjectMap<Destruction> destructions = new Long2ObjectOpenHashMap<>();
|
||||||
|
|
||||||
|
private final Long2ObjectMap<SortedSet<BlockBreakingInfo>> combined = new Long2ObjectOpenHashMap<>();
|
||||||
|
|
||||||
|
private final Object locker = new Object();
|
||||||
|
|
||||||
|
public void setBlockDestruction(BlockPos pos, int amount) {
|
||||||
|
synchronized (locker) {
|
||||||
|
if (amount <= 0 || amount > BlockDestructionManager.MAX_DAMAGE) {
|
||||||
|
destructions.remove(pos.asLong());
|
||||||
|
} else {
|
||||||
|
destructions.computeIfAbsent(pos.asLong(), p -> new Destruction(pos)).set(amount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void tick(Long2ObjectMap<SortedSet<BlockBreakingInfo>> vanilla) {
|
||||||
|
synchronized (locker) {
|
||||||
|
destructions.long2ObjectEntrySet().removeIf(entry -> entry.getValue().tick());
|
||||||
|
|
||||||
|
combined.clear();
|
||||||
|
|
||||||
|
if (!destructions.isEmpty()) {
|
||||||
|
destructions.forEach((pos, value) -> {
|
||||||
|
combined.computeIfAbsent(pos.longValue(), p -> Sets.newTreeSet()).add(value.info);
|
||||||
|
});
|
||||||
|
vanilla.forEach((pos, value) -> {
|
||||||
|
combined.computeIfAbsent(pos.longValue(), p -> Sets.newTreeSet()).addAll(value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long2ObjectMap<SortedSet<BlockBreakingInfo>> getCombinedDestructions(Long2ObjectMap<SortedSet<BlockBreakingInfo>> vanilla) {
|
||||||
|
return destructions.isEmpty() ? vanilla : combined;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Destruction {
|
||||||
|
int age = 50;
|
||||||
|
|
||||||
|
BlockBreakingInfo info;
|
||||||
|
|
||||||
|
Destruction(BlockPos pos) {
|
||||||
|
this.info = new BlockBreakingInfo(0, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean tick() {
|
||||||
|
if (age-- > 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int amount = info.getStage();
|
||||||
|
|
||||||
|
if (amount >= 0) {
|
||||||
|
amount--;
|
||||||
|
set(amount);
|
||||||
|
}
|
||||||
|
return amount < 0 || age-- <= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set(int amount) {
|
||||||
|
this.age = 50;
|
||||||
|
info.setStage(amount >= 0 && amount < BlockDestructionManager.MAX_DAMAGE ? amount : BlockDestructionManager.UNSET_DAMAGE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface Source {
|
||||||
|
ClientBlockDestructionManager getDestructionManager();
|
||||||
|
|
||||||
|
int getTicks();
|
||||||
|
}
|
||||||
|
}
|
|
@ -179,7 +179,7 @@ public class Pony implements Caster<PlayerEntity>, Equine<PlayerEntity>, Transmi
|
||||||
if (entity instanceof ServerPlayerEntity) {
|
if (entity instanceof ServerPlayerEntity) {
|
||||||
MsgOtherPlayerCapabilities packet = new MsgOtherPlayerCapabilities(full, this);
|
MsgOtherPlayerCapabilities packet = new MsgOtherPlayerCapabilities(full, this);
|
||||||
Channel.SERVER_PLAYER_CAPABILITIES.send(entity, packet);
|
Channel.SERVER_PLAYER_CAPABILITIES.send(entity, packet);
|
||||||
Channel.SERVER_OTHER_PLAYER_CAPABILITIES.send(entity, packet);
|
Channel.SERVER_OTHER_PLAYER_CAPABILITIES.send(entity.world, packet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,8 @@ import java.util.stream.Stream;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
|
||||||
|
import com.minelittlepony.unicopia.BlockDestructionManager;
|
||||||
import com.minelittlepony.unicopia.entity.behaviour.Disguise;
|
import com.minelittlepony.unicopia.entity.behaviour.Disguise;
|
||||||
|
|
||||||
import net.minecraft.entity.Entity;
|
import net.minecraft.entity.Entity;
|
||||||
|
@ -16,7 +18,15 @@ import net.minecraft.world.World;
|
||||||
import net.minecraft.world.WorldAccess;
|
import net.minecraft.world.WorldAccess;
|
||||||
|
|
||||||
@Mixin(World.class)
|
@Mixin(World.class)
|
||||||
abstract class MixinWorld implements WorldAccess {
|
abstract class MixinWorld implements WorldAccess, BlockDestructionManager.Source {
|
||||||
|
|
||||||
|
private final BlockDestructionManager destructions = new BlockDestructionManager((World)(Object)this);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BlockDestructionManager getDestructionManager() {
|
||||||
|
return destructions;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Stream<VoxelShape> getEntityCollisions(@Nullable Entity entity, Box box, Predicate<Entity> predicate) {
|
public Stream<VoxelShape> getEntityCollisions(@Nullable Entity entity, Box box, Predicate<Entity> predicate) {
|
||||||
if (box.getAverageSideLength() >= 1.0E-7D) {
|
if (box.getAverageSideLength() >= 1.0E-7D) {
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
package com.minelittlepony.unicopia.mixin.client;
|
||||||
|
|
||||||
|
import java.util.SortedSet;
|
||||||
|
|
||||||
|
import org.spongepowered.asm.mixin.Final;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
|
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
|
||||||
|
import com.minelittlepony.unicopia.client.ClientBlockDestructionManager;
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
||||||
|
import net.minecraft.client.render.BlockBreakingInfo;
|
||||||
|
import net.minecraft.client.render.WorldRenderer;
|
||||||
|
import net.minecraft.resource.SynchronousResourceReloadListener;
|
||||||
|
|
||||||
|
@Mixin(WorldRenderer.class)
|
||||||
|
abstract class MixinWorldRenderer implements SynchronousResourceReloadListener, AutoCloseable, ClientBlockDestructionManager.Source {
|
||||||
|
|
||||||
|
private final ClientBlockDestructionManager destructions = new ClientBlockDestructionManager();
|
||||||
|
|
||||||
|
@Shadow
|
||||||
|
private @Final Long2ObjectMap<SortedSet<BlockBreakingInfo>> blockBreakingProgressions;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ClientBlockDestructionManager getDestructionManager() {
|
||||||
|
return destructions;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Accessor("ticks")
|
||||||
|
public abstract int getTicks();
|
||||||
|
|
||||||
|
@Redirect(method = "render("
|
||||||
|
+ "Lnet/minecraft/client/util/math/MatrixStack;"
|
||||||
|
+ "FLZ"
|
||||||
|
+ "Lnet/minecraft/client/render/Camera;"
|
||||||
|
+ "Lnet/minecraft/client/render/GameRenderer;"
|
||||||
|
+ "Lnet/minecraft/client/render/LightmapTextureManager;"
|
||||||
|
+ "Lnet/minecraft/util/math/Matrix4f;"
|
||||||
|
+ ")V",
|
||||||
|
at = @At(value = "FIELD", target = "Lnet/minecraft/client/render/WorldRenderer;blockBreakingProgressions:Lit/unimi/dsi/fastutil/longs/Long2ObjectMap;")
|
||||||
|
)
|
||||||
|
private Long2ObjectMap<SortedSet<BlockBreakingInfo>> redirectGetDamagesMap(WorldRenderer sender) {
|
||||||
|
return destructions.getCombinedDestructions(blockBreakingProgressions);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject(method = "tick()V", at = @At("RETURN"))
|
||||||
|
private void onTick(CallbackInfo info) {
|
||||||
|
destructions.tick(blockBreakingProgressions);
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,6 +10,7 @@ import net.fabricmc.fabric.api.network.ServerSidePacketRegistry;
|
||||||
import net.fabricmc.loader.api.FabricLoader;
|
import net.fabricmc.loader.api.FabricLoader;
|
||||||
import net.minecraft.entity.player.PlayerEntity;
|
import net.minecraft.entity.player.PlayerEntity;
|
||||||
import net.minecraft.util.Identifier;
|
import net.minecraft.util.Identifier;
|
||||||
|
import net.minecraft.world.World;
|
||||||
import net.minecraft.network.PacketByteBuf;
|
import net.minecraft.network.PacketByteBuf;
|
||||||
|
|
||||||
public interface Channel {
|
public interface Channel {
|
||||||
|
@ -21,6 +22,8 @@ public interface Channel {
|
||||||
MPacketType<MsgOtherPlayerCapabilities> SERVER_OTHER_PLAYER_CAPABILITIES = serverToClients(new Identifier("unicopia", "other_player_capabilities"), MsgOtherPlayerCapabilities::new);
|
MPacketType<MsgOtherPlayerCapabilities> SERVER_OTHER_PLAYER_CAPABILITIES = serverToClients(new Identifier("unicopia", "other_player_capabilities"), MsgOtherPlayerCapabilities::new);
|
||||||
CPacketType<MsgSpawnProjectile> SERVER_SPAWN_PROJECTILE = serverToClient(new Identifier("unicopia", "projectile_entity"), MsgSpawnProjectile::new);
|
CPacketType<MsgSpawnProjectile> SERVER_SPAWN_PROJECTILE = serverToClient(new Identifier("unicopia", "projectile_entity"), MsgSpawnProjectile::new);
|
||||||
|
|
||||||
|
MPacketType<MsgBlockDestruction> SERVER_BLOCK_DESTRUCTION = serverToClients(new Identifier("unicopia", "block_destruction"), MsgBlockDestruction::new);
|
||||||
|
|
||||||
static void bootstrap() { }
|
static void bootstrap() { }
|
||||||
|
|
||||||
static <T extends Packet> SPacketType<T> clientToServer(Identifier id, Function<PacketByteBuf, T> factory) {
|
static <T extends Packet> SPacketType<T> clientToServer(Identifier id, Function<PacketByteBuf, T> factory) {
|
||||||
|
@ -45,8 +48,8 @@ public interface Channel {
|
||||||
interface MPacketType<T extends Packet> {
|
interface MPacketType<T extends Packet> {
|
||||||
Identifier getId();
|
Identifier getId();
|
||||||
|
|
||||||
default void send(PlayerEntity sender, T packet) {
|
default void send(World world, T packet) {
|
||||||
sender.world.getPlayers().forEach(player -> {
|
world.getPlayers().forEach(player -> {
|
||||||
if (player != null) {
|
if (player != null) {
|
||||||
ServerSidePacketRegistry.INSTANCE.sendToPlayer(player, getId(), packet.toBuffer());
|
ServerSidePacketRegistry.INSTANCE.sendToPlayer(player, getId(), packet.toBuffer());
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
package com.minelittlepony.unicopia.network;
|
||||||
|
|
||||||
|
import com.minelittlepony.unicopia.client.ClientBlockDestructionManager;
|
||||||
|
|
||||||
|
import net.fabricmc.fabric.api.network.PacketContext;
|
||||||
|
import net.minecraft.network.PacketByteBuf;
|
||||||
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
import net.minecraft.client.MinecraftClient;
|
||||||
|
|
||||||
|
public class MsgBlockDestruction implements Channel.Packet {
|
||||||
|
|
||||||
|
private final BlockPos pos;
|
||||||
|
|
||||||
|
private final int amount;
|
||||||
|
|
||||||
|
MsgBlockDestruction(PacketByteBuf buffer) {
|
||||||
|
pos = buffer.readBlockPos();
|
||||||
|
amount = buffer.readByte();
|
||||||
|
}
|
||||||
|
|
||||||
|
public MsgBlockDestruction(BlockPos pos, int amount) {
|
||||||
|
this.pos = pos;
|
||||||
|
this.amount = amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void toBuffer(PacketByteBuf buffer) {
|
||||||
|
buffer.writeBlockPos(pos);
|
||||||
|
buffer.writeByte(amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle(PacketContext context) {
|
||||||
|
((ClientBlockDestructionManager.Source)MinecraftClient.getInstance().worldRenderer).getDestructionManager().setBlockDestruction(pos, amount);
|
||||||
|
}
|
||||||
|
}
|
|
@ -31,7 +31,8 @@
|
||||||
"client.MixinItemModels",
|
"client.MixinItemModels",
|
||||||
"client.MixinKeyboardInput",
|
"client.MixinKeyboardInput",
|
||||||
"client.MixinLightmapTextureManager",
|
"client.MixinLightmapTextureManager",
|
||||||
"client.MixinMouse"
|
"client.MixinMouse",
|
||||||
|
"client.MixinWorldRenderer"
|
||||||
],
|
],
|
||||||
"injectors": {
|
"injectors": {
|
||||||
"defaultRequire": 1
|
"defaultRequire": 1
|
||||||
|
|
Loading…
Reference in a new issue