diff --git a/Common/src/main/java/tschipp/carryon/CarryOnCommon.java b/Common/src/main/java/tschipp/carryon/CarryOnCommon.java index d453ef0..2d5c0b1 100644 --- a/Common/src/main/java/tschipp/carryon/CarryOnCommon.java +++ b/Common/src/main/java/tschipp/carryon/CarryOnCommon.java @@ -2,6 +2,17 @@ package tschipp.carryon; import com.mojang.brigadier.CommandDispatcher; import net.minecraft.commands.CommandSourceStack; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.effect.MobEffectInstance; +import net.minecraft.world.effect.MobEffects; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.Level; +import tschipp.carryon.common.carry.CarryOnData; +import tschipp.carryon.common.carry.CarryOnData.CarryType; +import tschipp.carryon.common.carry.CarryOnDataManager; +import tschipp.carryon.common.carry.PlacementHandler; import tschipp.carryon.common.command.CommandCarryOn; import tschipp.carryon.config.ConfigLoader; import tschipp.carryon.networking.clientbound.ClientboundStartRidingPacket; @@ -55,4 +66,98 @@ public class CarryOnCommon CommandCarryOn.register(dispatcher); } + + public static void onCarryTick(ServerPlayer player) + { + CarryOnData carry = CarryOnDataManager.getCarryData(player); + if(carry.isCarrying()) + { + if(carry.getActiveScript().isPresent()) + { + String cmd = carry.getActiveScript().get().scriptEffects().commandLoop(); + if(!cmd.isEmpty()) + player.getServer().getCommands().performPrefixedCommand(player.getServer().createCommandSourceStack(), "/execute as " + player.getGameProfile().getName() + " run " + cmd); + } + + if (!Constants.COMMON_CONFIG.settings.slownessInCreative && player.isCreative()) + return; + + player.addEffect(new MobEffectInstance(MobEffects.MOVEMENT_SLOWDOWN, 1, potionLevel(carry, player.level), false, false)); + + Inventory inv = player.getInventory(); + inv.selected = carry.getSelected(); + } + } + + /** + * Returns true if the block can be broken. + */ + public static boolean onTryBreakBlock(Player player) + { + if (player != null && !Constants.COMMON_CONFIG.settings.hitWhileCarrying) + { + CarryOnData carry = CarryOnDataManager.getCarryData(player); + if(carry.isCarrying()) + return false; + } + return true; + } + + /** + * Returns true of the entity can be attacked + */ + public static boolean onAttackedByPlayer(Player player) + { + if (player != null && !Constants.COMMON_CONFIG.settings.hitWhileCarrying) + { + CarryOnData carry = CarryOnDataManager.getCarryData(player); + if(carry.isCarrying()) + return false; + } + return true; + } + + public static void onPlayerAttacked(Player player) + { + if (Constants.COMMON_CONFIG.settings.dropCarriedWhenHit && !player.level.isClientSide) + { + CarryOnData carry = CarryOnDataManager.getCarryData(player); + if (carry.isCarrying()) + { + PlacementHandler.placeCarried((ServerPlayer) player); + } + + } + } + + + private static int potionLevel(CarryOnData carry, Level level) + { + if(carry.isCarrying(CarryType.PLAYER)) + return 1; + if(carry.isCarrying(CarryType.ENTITY)) + { + Entity entity = carry.getEntity(level); + int i = (int) (entity.getBbHeight() * entity.getBbWidth()); + if (i > 4) + i = 4; + if (!Constants.COMMON_CONFIG.settings.heavyEntities) + i = 1; + return (int) (i * Constants.COMMON_CONFIG.settings.entitySlownessMultiplier); + } + if(carry.isCarrying(CarryType.BLOCK)) + { + String nbt = carry.getNbt().toString(); + int i = nbt.length() / 500; + + if (i > 4) + i = 4; + + if (!Constants.COMMON_CONFIG.settings.heavyTiles) + i = 1; + + return (int) (i * Constants.COMMON_CONFIG.settings.blockSlownessMultiplier); + } + return 0; + } } diff --git a/Common/src/main/java/tschipp/carryon/CarryOnCommonClient.java b/Common/src/main/java/tschipp/carryon/CarryOnCommonClient.java index db6202d..4b26ff7 100644 --- a/Common/src/main/java/tschipp/carryon/CarryOnCommonClient.java +++ b/Common/src/main/java/tschipp/carryon/CarryOnCommonClient.java @@ -10,14 +10,15 @@ public class CarryOnCommonClient { public static void checkForKeybinds() { - Player player = Minecraft.getInstance().player; + Minecraft mc = Minecraft.getInstance(); + Player player = mc.player; if(player != null) { CarryOnData carry = CarryOnDataManager.getCarryData(player); - if (CarryOnKeybinds.carryKey.isDown() && !carry.isKeyPressed()) { + if ((CarryOnKeybinds.carryKey.isUnbound() ? player.isShiftKeyDown() : CarryOnKeybinds.carryKey.isDown()) && !carry.isKeyPressed()) { CarryOnKeybinds.onCarryKey(true); carry.setKeyPressed(true); CarryOnDataManager.setCarryData(player, carry); - } else if (!CarryOnKeybinds.carryKey.isDown() && carry.isKeyPressed()) { + } else if (!(CarryOnKeybinds.carryKey.isUnbound() ? player.isShiftKeyDown() : CarryOnKeybinds.carryKey.isDown()) && carry.isKeyPressed()) { CarryOnKeybinds.onCarryKey(false); carry.setKeyPressed(false); CarryOnDataManager.setCarryData(player, carry); @@ -25,4 +26,20 @@ public class CarryOnCommonClient } } + public static void onCarryClientTick() + { + Player player = Minecraft.getInstance().player; + if(player != null) { + CarryOnData carry = CarryOnDataManager.getCarryData(player); + if(carry.isCarrying()) + { + player.getInventory().selected = carry.getSelected(); + } + } + } + + public static Player getPlayer() + { + return Minecraft.getInstance().player; + } } diff --git a/Common/src/main/java/tschipp/carryon/client/CarryOnClient.java b/Common/src/main/java/tschipp/carryon/client/CarryOnClient.java deleted file mode 100644 index 63289d9..0000000 --- a/Common/src/main/java/tschipp/carryon/client/CarryOnClient.java +++ /dev/null @@ -1,12 +0,0 @@ -package tschipp.carryon.client; - -import net.minecraft.client.Minecraft; -import net.minecraft.world.entity.player.Player; - -public class CarryOnClient -{ - public static Player getPlayer() - { - return Minecraft.getInstance().player; - } -} diff --git a/Common/src/main/java/tschipp/carryon/client/keybinds/CarryOnKeybinds.java b/Common/src/main/java/tschipp/carryon/client/keybinds/CarryOnKeybinds.java index d118e19..93c638b 100644 --- a/Common/src/main/java/tschipp/carryon/client/keybinds/CarryOnKeybinds.java +++ b/Common/src/main/java/tschipp/carryon/client/keybinds/CarryOnKeybinds.java @@ -1,5 +1,6 @@ package tschipp.carryon.client.keybinds; +import com.mojang.blaze3d.platform.InputConstants; import net.minecraft.client.KeyMapping; import tschipp.carryon.Constants; import tschipp.carryon.networking.serverbound.ServerboundCarryKeyPressedPacket; @@ -13,7 +14,7 @@ public class CarryOnKeybinds public static void registerKeybinds(Consumer registrar) { - carryKey = new KeyMapping("key.carry.desc", 340, "key.carry.category"); + carryKey = new ConflictFreeKeyMapping("key.carry.desc", Services.PLATFORM.getPlatformName().equals("Forge") ? InputConstants.KEY_LSHIFT : InputConstants.UNKNOWN.getValue(), "key.carry.category"); registrar.accept(carryKey); } diff --git a/Common/src/main/java/tschipp/carryon/client/keybinds/ConflictFreeKeyMapping.java b/Common/src/main/java/tschipp/carryon/client/keybinds/ConflictFreeKeyMapping.java new file mode 100644 index 0000000..4fc8081 --- /dev/null +++ b/Common/src/main/java/tschipp/carryon/client/keybinds/ConflictFreeKeyMapping.java @@ -0,0 +1,23 @@ +package tschipp.carryon.client.keybinds; + +import com.mojang.blaze3d.platform.InputConstants.Type; +import net.minecraft.client.KeyMapping; + +public class ConflictFreeKeyMapping extends KeyMapping +{ + public ConflictFreeKeyMapping(String $$0, int $$1, String $$2) + { + super($$0, $$1, $$2); + } + + public ConflictFreeKeyMapping(String $$0, Type $$1, int $$2, String $$3) + { + super($$0, $$1, $$2, $$3); + } + + @Override + public boolean same(KeyMapping $$0) + { + return false; + } +} diff --git a/Common/src/main/java/tschipp/carryon/client/modeloverride/ModelOverride.java b/Common/src/main/java/tschipp/carryon/client/modeloverride/ModelOverride.java new file mode 100644 index 0000000..2f10a02 --- /dev/null +++ b/Common/src/main/java/tschipp/carryon/client/modeloverride/ModelOverride.java @@ -0,0 +1,125 @@ +package tschipp.carryon.client.modeloverride; + +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.datafixers.util.Either; +import com.mojang.serialization.Codec; +import com.mojang.serialization.DataResult; +import net.minecraft.commands.arguments.blocks.BlockStateParser; +import net.minecraft.commands.arguments.blocks.BlockStateParser.BlockResult; +import net.minecraft.commands.arguments.item.ItemParser; +import net.minecraft.commands.arguments.item.ItemParser.ItemResult; +import net.minecraft.core.HolderLookup; +import net.minecraft.core.Registry; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.properties.Property; +import tschipp.carryon.common.scripting.Matchables.NBTCondition; + +import javax.annotation.Nullable; +import java.util.Map; + +public class ModelOverride +{ + public static Codec CODEC = Codec.STRING.comapFlatMap(ModelOverride::of, override -> override.raw); + + private String raw; + private BlockResult parsedBlock; + private Type type; + private Either parsedRHS; + private Either renderObject; + + private ModelOverride(String raw, BlockResult parsedBlock, Type type, Either parsedRHS) + { + this.raw = raw; + this.parsedBlock = parsedBlock; + this.type = type; + this.parsedRHS = parsedRHS; + + parsedRHS.ifLeft(res -> { + ItemStack stack = new ItemStack(res.item()); + if(res.nbt() != null) + stack.setTag(res.nbt()); + this.renderObject = Either.left(stack); + }); + + parsedRHS.ifRight(res -> { + BlockState state = res.blockState(); + this.renderObject = Either.right(state); + }); + } + + public static DataResult of(String str) + { + if(!str.contains("->")) + return DataResult.error(str + " must contain -> Arrow!"); + String[] split = str.split("->"); + String from = split[0]; + String to = split[1]; + + BlockResult res; + + try { + res = BlockStateParser.parseForBlock(Registry.BLOCK, from, true); + } catch (CommandSyntaxException e) { + return DataResult.error("Error while parsing " + from + ":" + e.getMessage()); + } + + Type type = Type.ITEM; + + if(to.contains("(") && to.contains(")")) + { + String t = to.substring(to.indexOf("(") + 1, to.indexOf(")")); + if(t.equals("block")) + type = Type.BLOCK; + to = to.substring(to.indexOf(")") + 1); + } + + Either either; + try { + if(type == Type.ITEM) + either = Either.left(ItemParser.parseForItem(HolderLookup.forRegistry(Registry.ITEM), new StringReader(to))); + else + either = Either.right(BlockStateParser.parseForBlock(Registry.BLOCK, to, true)); + }catch (CommandSyntaxException e) { + return DataResult.error("Error while parsing " + to + ":" + e.getMessage()); + } + + return DataResult.success(new ModelOverride(str, res, type, either)); + } + + public boolean matches(BlockState state, @Nullable CompoundTag tag) + { + if(state.getBlock() == parsedBlock.blockState().getBlock() && matchesProperties(state, parsedBlock.properties())) + { + if(tag == null || parsedBlock.nbt() == null) + return true; + NBTCondition nbt = new NBTCondition(parsedBlock.nbt()); + return nbt.matches(tag); + } + return false; + } + + public Either getRenderObject() + { + return this.renderObject; + } + + private boolean matchesProperties(BlockState state, Map, Comparable> props) + { + for(var entry : props.entrySet()) + { + var val = state.getValue(entry.getKey()); + if(val != entry.getValue()) + return false; + } + return true; + } + + public enum Type { + ITEM, + BLOCK + } + +} diff --git a/Common/src/main/java/tschipp/carryon/client/modeloverride/ModelOverrideHandler.java b/Common/src/main/java/tschipp/carryon/client/modeloverride/ModelOverrideHandler.java new file mode 100644 index 0000000..2ee8463 --- /dev/null +++ b/Common/src/main/java/tschipp/carryon/client/modeloverride/ModelOverrideHandler.java @@ -0,0 +1,51 @@ +package tschipp.carryon.client.modeloverride; + +import com.mojang.serialization.DataResult; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.level.block.state.BlockState; +import tschipp.carryon.Constants; + +import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +public class ModelOverrideHandler +{ + private static List OVERRIDES = new ArrayList<>(); + + public static void initModelOverrides() + { + OVERRIDES.clear(); + + for(String ov : Constants.CLIENT_CONFIG.modelOverrides) + { + addFromString(ov); + } + } + + public static Optional getModelOverride(BlockState state, @Nullable CompoundTag tag) + { + for(ModelOverride ov : OVERRIDES) + { + if(ov.matches(state, tag)) + return Optional.of(ov); + } + return Optional.empty(); + } + + public static void addFromString(String str) + { + DataResult res = ModelOverride.of(str); + if(res.result().isPresent()) + { + ModelOverride override = res.result().get(); + OVERRIDES.add(override); + } + else + { + Constants.LOG.debug("Error while parsing ModelOverride: " + res.error().get().message()); + } + } + +} diff --git a/Common/src/main/java/tschipp/carryon/client/render/CarriedObjectRender.java b/Common/src/main/java/tschipp/carryon/client/render/CarriedObjectRender.java index d4709bc..e945f30 100644 --- a/Common/src/main/java/tschipp/carryon/client/render/CarriedObjectRender.java +++ b/Common/src/main/java/tschipp/carryon/client/render/CarriedObjectRender.java @@ -61,7 +61,7 @@ public class CarriedObjectRender CarryOnData carry = CarryOnDataManager.getCarryData(player); - if (!Constants.CLIENT_CONFIG.facePlayer && CarryRenderHelper.isChest(state.getBlock())) { + if (Constants.CLIENT_CONFIG.facePlayer != CarryRenderHelper.isChest(state.getBlock())) { matrix.mulPose(Vector3f.YP.rotationDegrees(180)); matrix.mulPose(Vector3f.XN.rotationDegrees(8)); } else { diff --git a/Common/src/main/java/tschipp/carryon/client/render/CarryRenderHelper.java b/Common/src/main/java/tschipp/carryon/client/render/CarryRenderHelper.java index 6be830e..1b83782 100644 --- a/Common/src/main/java/tschipp/carryon/client/render/CarryRenderHelper.java +++ b/Common/src/main/java/tschipp/carryon/client/render/CarryRenderHelper.java @@ -24,12 +24,16 @@ import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.shapes.VoxelShape; import tschipp.carryon.Constants; +import tschipp.carryon.client.modeloverride.ModelOverride; +import tschipp.carryon.client.modeloverride.ModelOverrideHandler; import tschipp.carryon.common.carry.CarryOnData; import tschipp.carryon.common.carry.CarryOnData.CarryType; import tschipp.carryon.common.carry.CarryOnDataManager; import tschipp.carryon.common.scripting.CarryOnScript; import tschipp.carryon.common.scripting.CarryOnScript.ScriptRender; +import java.util.Optional; + public class CarryRenderHelper { public static Vec3 getExactPos(Entity entity, float partialticks) @@ -139,8 +143,9 @@ public class CarryRenderHelper applyGeneralTransformations(player, partialticks, matrix); - if (!Constants.CLIENT_CONFIG.facePlayer && isChest(block)) + if (Constants.CLIENT_CONFIG.facePlayer != CarryRenderHelper.isChest(block)) { + //TODO: RealFirstPersonRender //if ((ModList.get().isLoaded("realrender") || ModList.get().isLoaded("rfpr")) && perspective == 0) // matrix.translate(0, 0, -0.4); matrix.mulPose(Vector3f.YP.rotationDegrees(180)); @@ -149,6 +154,8 @@ public class CarryRenderHelper // matrix.translate(0, 0, 0.4); //matrix.mulPose(Vector3f.YP.rotationDegrees(180)); + + float height = getRenderHeight(player); float offset = (height - 1f) / 1.2f; matrix.translate(0, -offset, 0); @@ -202,7 +209,7 @@ public class CarryRenderHelper Vec3 translation = render.renderTranslation().getVec(); Vec3 rotation = render.renderRotation().getVec(); - Vec3 scale = render.renderscale().getVec(1); + Vec3 scale = render.renderscale().getVec(1, 1, 1); Quaternion rot = Vector3f.XP.rotationDegrees((float) rotation.x); rot.mul(Vector3f.YP.rotationDegrees((float) rotation.y)); @@ -232,18 +239,36 @@ public class CarryRenderHelper if(render.renderNameBlock().isPresent()) state = Registry.BLOCK.get(render.renderNameBlock().get()).defaultBlockState(); } + + Optional ov = ModelOverrideHandler.getModelOverride(state, carry.getContentNbt()); + if(ov.isPresent()) + { + var renderObj = ov.get().getRenderObject(); + if(renderObj.right().isPresent()) + state = renderObj.right().get(); + } + return state; } public static BakedModel getRenderBlock(Player player) { + CarryOnData carry = CarryOnDataManager.getCarryData(player); + ItemRenderer renderer = Minecraft.getInstance().getItemRenderer(); BlockState state = getRenderState(player); - BakedModel model = Minecraft.getInstance().getBlockRenderer().getBlockModel(state); if(state.getRenderShape() != RenderShape.MODEL || model.isCustomRenderer() || model.getQuads(state, null, RandomSource.create()).size() <= 0) { ItemStack stack = new ItemStack(state.getBlock()); - model = Minecraft.getInstance().getItemRenderer().getModel(stack, player.level, player, 0); + model = renderer.getModel(stack, player.level, player, 0); + } + + Optional ov = ModelOverrideHandler.getModelOverride(state, carry.getContentNbt()); + if(ov.isPresent()) + { + var renderObj = ov.get().getRenderObject(); + if(renderObj.left().isPresent()) + model = renderer.getModel(renderObj.left().get(), player.level, player, 0); } return model; @@ -273,9 +298,17 @@ public class CarryRenderHelper CarryOnData carry = CarryOnDataManager.getCarryData(player); if(carry.isCarrying(CarryType.BLOCK)) { - VoxelShape shape = getRenderState(player).getShape(player.level, player.blockPosition()); + BlockState state = getRenderState(player); + VoxelShape shape = state.getShape(player.level, player.blockPosition()); if(shape == null) return 1f; + Optional ov = ModelOverrideHandler.getModelOverride(state, carry.getContentNbt()); + if(ov.isPresent()) + { + var renderObj = ov.get().getRenderObject(); + if(renderObj.left().isPresent()) + return 0.8f; + } float width = (float)Math.abs(shape.bounds().maxX - shape.bounds().minX); return width; } @@ -293,9 +326,17 @@ public class CarryRenderHelper CarryOnData carry = CarryOnDataManager.getCarryData(player); if(carry.isCarrying(CarryType.BLOCK)) { - VoxelShape shape = getRenderState(player).getShape(player.level, player.blockPosition()); + BlockState state = getRenderState(player); + VoxelShape shape = state.getShape(player.level, player.blockPosition()); if(shape == null) return 1f; + Optional ov = ModelOverrideHandler.getModelOverride(state, carry.getContentNbt()); + if(ov.isPresent()) + { + var renderObj = ov.get().getRenderObject(); + if(renderObj.left().isPresent()) + return 0.5f; + } float height = (float)Math.abs(shape.bounds().maxY - shape.bounds().minY); return height; } diff --git a/Common/src/main/java/tschipp/carryon/common/carry/CarryOnData.java b/Common/src/main/java/tschipp/carryon/common/carry/CarryOnData.java index 4f9781a..62e7c52 100644 --- a/Common/src/main/java/tschipp/carryon/common/carry/CarryOnData.java +++ b/Common/src/main/java/tschipp/carryon/common/carry/CarryOnData.java @@ -23,6 +23,7 @@ public class CarryOnData { private CompoundTag nbt; private boolean keyPressed = false; private CarryOnScript activeScript; + private int selectedSlot = 0; public CarryOnData(CompoundTag data) { @@ -41,6 +42,10 @@ public class CarryOnData { DataResult res = CarryOnScript.CODEC.parse(NbtOps.INSTANCE, data.get("activeScript")); this.activeScript = res.getOrThrow(false, (s) -> {throw new RuntimeException("Failed to decode activeScript during CarryOnData serialization: " + s);}); } + + if(data.contains("selected")) + this.selectedSlot = data.getInt("selected"); + } public CompoundTag getNbt() @@ -53,9 +58,19 @@ public class CarryOnData { Tag tag = res.getOrThrow(false, (s) -> {throw new RuntimeException("Failed to encode activeScript during CarryOnData serialization: " + s);}); nbt.put("activeScript", tag); } + nbt.putInt("selected", this.selectedSlot); return nbt; } + public CompoundTag getContentNbt() + { + if(type == CarryType.BLOCK && nbt.contains("block")) + return nbt.getCompound("block"); + else if(type == CarryType.ENTITY && nbt.contains("entity")) + return nbt.getCompound("entity"); + return null; + } + public void setBlock(BlockState state, @Nullable BlockEntity tile) { this.type = CarryType.BLOCK; @@ -142,6 +157,14 @@ public class CarryOnData { this.nbt.putBoolean("keyPressed", val); } + public void setSelected(int selectedSlot) { + this.selectedSlot = selectedSlot; + } + + public int getSelected() { + return this.selectedSlot; + } + public void clear() { this.type = CarryType.INVALID; diff --git a/Common/src/main/java/tschipp/carryon/common/carry/CarryOnDataManager.java b/Common/src/main/java/tschipp/carryon/common/carry/CarryOnDataManager.java index cb3b127..b27e621 100644 --- a/Common/src/main/java/tschipp/carryon/common/carry/CarryOnDataManager.java +++ b/Common/src/main/java/tschipp/carryon/common/carry/CarryOnDataManager.java @@ -19,6 +19,7 @@ public class CarryOnDataManager { public static void setCarryData(Player player, CarryOnData data) { + data.setSelected(player.getInventory().selected); CompoundTag nbt = data.getNbt(); nbt.putInt("tick", player.tickCount); player.getEntityData().set(CARRY_DATA_KEY, nbt); diff --git a/Common/src/main/java/tschipp/carryon/common/carry/PickupHandler.java b/Common/src/main/java/tschipp/carryon/common/carry/PickupHandler.java index a9fec15..7a6077b 100644 --- a/Common/src/main/java/tschipp/carryon/common/carry/PickupHandler.java +++ b/Common/src/main/java/tschipp/carryon/common/carry/PickupHandler.java @@ -19,13 +19,19 @@ import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.Vec3; import tschipp.carryon.Constants; +import tschipp.carryon.common.config.ListHandler; +import tschipp.carryon.common.pickupcondition.PickupCondition; +import tschipp.carryon.common.pickupcondition.PickupConditionHandler; import tschipp.carryon.common.scripting.CarryOnScript; import tschipp.carryon.common.scripting.ScriptManager; import tschipp.carryon.networking.clientbound.ClientboundStartRidingPacket; import tschipp.carryon.platform.Services; +import javax.annotation.Nullable; import java.util.Optional; import java.util.UUID; +import java.util.function.BiFunction; +import java.util.function.Function; public class PickupHandler { @@ -57,7 +63,7 @@ public class PickupHandler { } - public static boolean tryPickUpBlock(ServerPlayer player, BlockPos pos, Level level) + public static boolean tryPickUpBlock(ServerPlayer player, BlockPos pos, Level level, @Nullable BiFunction pickupCallback) { if(!canCarryGeneral(player, Vec3.atCenterOf(pos))) return false; @@ -85,9 +91,16 @@ public class PickupHandler { return false; } - //TODO: Gamestages conditions check + Optional cond = PickupConditionHandler.getPickupCondition(state); + if(cond.isPresent()) + { + if(!cond.get().isFulfilled(player)) + return false; + } - //TODO: Protections + boolean doPickup = pickupCallback == null ? true : pickupCallback.apply(state, pos); + if(!doPickup) + return false; Optional result = ScriptManager.inspectBlock(state, level, pos, nbt); if(result.isPresent()) @@ -116,7 +129,7 @@ public class PickupHandler { - public static boolean tryPickupEntity(ServerPlayer player, Entity entity) + public static boolean tryPickupEntity(ServerPlayer player, Entity entity, @Nullable Function pickupCallback) { if(!canCarryGeneral(player, entity.position())) return false; @@ -149,10 +162,16 @@ public class PickupHandler { return false; } - //TODO: Gamestages conditions check - - //TODO: Protections + Optional cond = PickupConditionHandler.getPickupCondition(entity); + if(cond.isPresent()) + { + if(!cond.get().isFulfilled(player)) + return false; + } + boolean doPickup = pickupCallback == null ? true : pickupCallback.apply(entity); + if(!doPickup) + return false; CarryOnData carry = CarryOnDataManager.getCarryData(player); @@ -214,18 +233,4 @@ public class PickupHandler { return true; } - public static void onCarryTick(ServerPlayer player) - { - CarryOnData carry = CarryOnDataManager.getCarryData(player); - if(carry.isCarrying()) - { - if(carry.getActiveScript().isPresent()) - { - String cmd = carry.getActiveScript().get().scriptEffects().commandLoop(); - if(!cmd.isEmpty()) - player.getServer().getCommands().performPrefixedCommand(player.getServer().createCommandSourceStack(), "/execute as " + player.getGameProfile().getName() + " run " + cmd); - } - } - } - } diff --git a/Common/src/main/java/tschipp/carryon/common/carry/PlacementHandler.java b/Common/src/main/java/tschipp/carryon/common/carry/PlacementHandler.java index 5a51b78..e8ff37b 100644 --- a/Common/src/main/java/tschipp/carryon/common/carry/PlacementHandler.java +++ b/Common/src/main/java/tschipp/carryon/common/carry/PlacementHandler.java @@ -1,6 +1,7 @@ package tschipp.carryon.common.carry; import net.minecraft.core.BlockPos; +import net.minecraft.core.BlockPos.MutableBlockPos; import net.minecraft.core.Direction; import net.minecraft.server.level.ServerPlayer; import net.minecraft.sounds.SoundEvents; @@ -11,6 +12,7 @@ import net.minecraft.world.entity.Mob; import net.minecraft.world.entity.animal.horse.Horse; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.level.GameRules; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntity; @@ -23,6 +25,7 @@ import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.shapes.CollisionContext; import tschipp.carryon.Constants; import tschipp.carryon.common.carry.CarryOnData.CarryType; +import tschipp.carryon.common.config.ListHandler; import tschipp.carryon.common.scripting.CarryOnScript.ScriptEffects; import javax.annotation.Nullable; @@ -53,30 +56,30 @@ public class PlacementHandler BlockEntity blockEntity = carry.getBlockEntity(pos); + state = getPlacementState(state, player, context, pos); + boolean canPlace = state.canSurvive(level, pos) && level.mayInteract(player, pos) && level.getBlockState(pos).canBeReplaced(context) && level.isUnobstructed(state, pos, CollisionContext.of(player)); - if (!canPlace) - { + if (!canPlace) { level.playSound(null, player.getX(), player.getY(), player.getZ(), SoundEvents.LAVA_POP, SoundSource.PLAYERS, 0.5F, 0.5F); return false; } - state = getPlacementState(state, player, context, pos); boolean doPlace = placementCallback == null || placementCallback.apply(pos, state); if (!doPlace) return false; - if(carry.getActiveScript().isPresent()) - { + if (carry.getActiveScript().isPresent()) { ScriptEffects effects = carry.getActiveScript().get().scriptEffects(); String cmd = effects.commandPlace(); - if(!cmd.isEmpty()) + if (!cmd.isEmpty()) player.getServer().getCommands().performPrefixedCommand(player.getServer().createCommandSourceStack(), "/execute as " + player.getGameProfile().getName() + " run " + cmd); } - level.setBlock(pos, state, 3); + level.setBlockAndUpdate(pos, state); if (blockEntity != null) level.setBlockEntity(blockEntity); + level.updateNeighborsAt(pos.relative(Direction.DOWN), level.getBlockState(pos.relative(Direction.DOWN)).getBlock()); carry.clear(); CarryOnDataManager.setCarryData(player, carry); player.playSound(state.getSoundType().getPlaceSound(), 1.0f, 0.5f); @@ -88,24 +91,25 @@ public class PlacementHandler private static BlockState getPlacementState(BlockState state, ServerPlayer player, BlockPlaceContext context, BlockPos pos) { BlockState placementState = state.getBlock().getStateForPlacement(context); + if (placementState == null || placementState.getBlock() != state.getBlock()) + placementState = state; for (var prop : placementState.getProperties()) { - if (prop instanceof DirectionProperty) { + if (prop instanceof DirectionProperty) state = updateProperty(state, placementState, prop); - } - if (prop.getValueClass() == Direction.Axis.class) { + if (prop.getValueClass() == Direction.Axis.class) state = updateProperty(state, placementState, prop); - } - //This is needed for certain blocks, otherwise we get problems like chests not connecting if (ListHandler.isPropertyException(prop)) { state = updateProperty(state, placementState, prop); } } - state = Block.updateFromNeighbourShapes(state, player.level, pos); + BlockState updatedState = Block.updateFromNeighbourShapes(state, player.level, pos); + if (updatedState.getBlock() == state.getBlock()) + state = updatedState; - if (placementState.hasProperty(BlockStateProperties.WATERLOGGED)) + if (placementState.hasProperty(BlockStateProperties.WATERLOGGED) && state.hasProperty(BlockStateProperties.WATERLOGGED)) state = state.setValue(BlockStateProperties.WATERLOGGED, placementState.getValue(BlockStateProperties.WATERLOGGED)); return state; @@ -135,13 +139,12 @@ public class PlacementHandler Vec3 placementPos = Vec3.atBottomCenterOf(pos); - if(carry.isCarrying(CarryType.PLAYER)) - { + if (carry.isCarrying(CarryType.PLAYER)) { Entity otherPlayer = player.getFirstPassenger(); player.ejectPassengers(); carry.clear(); CarryOnDataManager.setCarryData(player, carry); - if(otherPlayer == null) + if (otherPlayer == null) return true; otherPlayer.teleportTo(placementPos.x, placementPos.y, placementPos.z); player.swing(InteractionHand.MAIN_HAND, true); @@ -155,33 +158,35 @@ public class PlacementHandler if (!doPlace) return false; - if(carry.getActiveScript().isPresent()) - { + if (carry.getActiveScript().isPresent()) { ScriptEffects effects = carry.getActiveScript().get().scriptEffects(); String cmd = effects.commandPlace(); - if(!cmd.isEmpty()) + if (!cmd.isEmpty()) player.getServer().getCommands().performPrefixedCommand(player.getServer().createCommandSourceStack(), "/execute as " + player.getGameProfile().getName() + " run " + cmd); } - level.addFreshEntity(entity); - if(entity instanceof Mob mob) - mob.playAmbientSound(); + level.addFreshEntity(entity); + if (entity instanceof Mob mob) + mob.playAmbientSound(); - player.swing(InteractionHand.MAIN_HAND, true); - carry.clear(); - CarryOnDataManager.setCarryData(player, carry); - return true; + player.swing(InteractionHand.MAIN_HAND, true); + carry.clear(); + CarryOnDataManager.setCarryData(player, carry); + return true; } public static void tryStackEntity(ServerPlayer player, Entity entityClicked) { + if(!Constants.COMMON_CONFIG.settings.stackableEntities) + return; + CarryOnData carry = CarryOnDataManager.getCarryData(player); - if(!carry.isCarrying(CarryType.ENTITY) && !carry.isCarrying(CarryType.PLAYER)) + if (!carry.isCarrying(CarryType.ENTITY) && !carry.isCarrying(CarryType.PLAYER)) return; Level level = player.level; Entity entityHeld; - if(carry.isCarrying(CarryType.ENTITY)) + if (carry.isCarrying(CarryType.ENTITY)) entityHeld = carry.getEntity(level); else entityHeld = player.getFirstPassenger(); @@ -191,47 +196,40 @@ public class PlacementHandler double distance = entityClicked.blockPosition().distSqr(player.blockPosition()); Entity lowestEntity = entityClicked.getRootVehicle(); int numPassengers = getPassengerCount(lowestEntity); - if (numPassengers < Constants.COMMON_CONFIG.settings.maxEntityStackLimit - 1) - { + if (numPassengers < Constants.COMMON_CONFIG.settings.maxEntityStackLimit - 1) { Entity topEntity = getTopPassenger(lowestEntity); - if(topEntity == entityHeld) + if (topEntity == entityHeld) return; - if (ListHandler.isStackingPermitted(topEntity)) - { + if (ListHandler.isStackingPermitted(topEntity)) { double sizeEntity = topEntity.getBbHeight() * topEntity.getBbWidth(); - if (Constants.COMMON_CONFIG.settings.entitySizeMattersStacking && sizeHeldEntity <= sizeEntity || !Constants.COMMON_CONFIG.settings.entitySizeMattersStacking) - { + if (Constants.COMMON_CONFIG.settings.entitySizeMattersStacking && sizeHeldEntity <= sizeEntity || !Constants.COMMON_CONFIG.settings.entitySizeMattersStacking) { if (topEntity instanceof Horse horse) horse.setTamed(true); - if (distance < 6) - { + if (distance < 6) { double tempX = entityClicked.getX(); double tempY = entityClicked.getY(); double tempZ = entityClicked.getZ(); - if(carry.isCarrying(CarryType.ENTITY)) { + if (carry.isCarrying(CarryType.ENTITY)) { entityHeld.setPos(tempX, tempY + 2.6, tempZ); level.addFreshEntity(entityHeld); entityHeld.teleportTo(tempX, tempY, tempZ); } entityHeld.startRiding(topEntity, false); - } - else - { - if(carry.isCarrying(CarryType.ENTITY)) { + } else { + if (carry.isCarrying(CarryType.ENTITY)) { entityHeld.setPos(entityClicked.getX(), entityClicked.getY(), entityClicked.getZ()); level.addFreshEntity(entityHeld); } entityHeld.startRiding(topEntity, false); } - if(carry.getActiveScript().isPresent()) - { + if (carry.getActiveScript().isPresent()) { ScriptEffects effects = carry.getActiveScript().get().scriptEffects(); String cmd = effects.commandPlace(); - if(!cmd.isEmpty()) + if (!cmd.isEmpty()) player.getServer().getCommands().performPrefixedCommand(player.getServer().createCommandSourceStack(), "/execute as " + player.getGameProfile().getName() + " run " + cmd); } @@ -239,27 +237,86 @@ public class PlacementHandler carry.clear(); CarryOnDataManager.setCarryData(player, carry); level.playSound(null, player.getX(), player.getY(), player.getZ(), SoundEvents.HORSE_SADDLE, SoundSource.PLAYERS, 0.5F, 1.5F); - } - else - { + } else { level.playSound(null, player.getX(), player.getY(), player.getZ(), SoundEvents.LAVA_POP, SoundSource.PLAYERS, 0.5F, 0.5F); } } - } - else - { + } else { level.playSound(null, player.getX(), player.getY(), player.getZ(), SoundEvents.LAVA_POP, SoundSource.PLAYERS, 0.5F, 0.5F); } } + public static void placeCarriedOnDeath(ServerPlayer oldPlayer, ServerPlayer newPlayer, boolean died) + { + CarryOnData carry = CarryOnDataManager.getCarryData(oldPlayer); + if (oldPlayer.level.getGameRules().getBoolean(GameRules.RULE_KEEPINVENTORY) || !died) { + if (!carry.isCarrying(CarryType.PLAYER)) { + CarryOnDataManager.setCarryData(newPlayer, carry); + newPlayer.getInventory().selected = oldPlayer.getInventory().selected; + return; + } + } + + placeCarried(oldPlayer); + } + + public static void placeCarried(ServerPlayer player) + { + CarryOnData carry = CarryOnDataManager.getCarryData(player); + if (carry.isCarrying(CarryType.ENTITY)) { + Entity entity = carry.getEntity(player.level); + entity.setPos(player.position()); + player.level.addFreshEntity(entity); + } else if (carry.isCarrying(CarryType.BLOCK)) { + BlockPlaceContext context = new BlockPlaceContext(player, InteractionHand.MAIN_HAND, ItemStack.EMPTY, BlockHitResult.miss(Vec3.atCenterOf(player.blockPosition()), Direction.DOWN, player.blockPosition())); + BlockState state = getPlacementState(carry.getBlock(), player, context, player.blockPosition()); + BlockPos pos = getDeathPlacementPos(state, player); + BlockEntity blockEntity = carry.getBlockEntity(pos); + player.level.setBlock(pos, state, 3); + if (blockEntity != null) + player.level.setBlockEntity(blockEntity); + } else if (carry.isCarrying(CarryType.PLAYER)) { + player.ejectPassengers(); + } + carry.clear(); + CarryOnDataManager.setCarryData(player, carry); + } + + private static BlockPos getDeathPlacementPos(BlockState state, ServerPlayer player) + { + MutableBlockPos pos = new MutableBlockPos(); + BlockPos p = player.blockPosition(); + + int DISTANCE = 15; + + for (int j = 0; j < DISTANCE * 2; j++) { + for (int i = 0; i < DISTANCE * 2; i++) { + for (int k = 0; k < DISTANCE * 2; k++) { + int x = i % 2 == 0 ? i / 2 : -(i / 2); + int y = j % 2 == 0 ? j / 2 : -(j / 2); + int z = k % 2 == 0 ? k / 2 : -(k / 2); + + pos.set(p.getX() + x, p.getY() + y, p.getZ() + z); + + BlockPlaceContext context = new BlockPlaceContext(player, InteractionHand.MAIN_HAND, ItemStack.EMPTY, BlockHitResult.miss(Vec3.atCenterOf(pos), Direction.DOWN, pos)); + + boolean canPlace = state.canSurvive(player.level, pos) && player.level.getBlockState(pos).canBeReplaced(context) && player.level.isUnobstructed(state, pos, CollisionContext.of(player)); + + if (canPlace) + return pos; + } + } + } + + return p; + } + private static int getPassengerCount(Entity entity) { int passengers = 0; - while (entity.isVehicle()) - { + while (entity.isVehicle()) { List pass = entity.getPassengers(); - if (!pass.isEmpty()) - { + if (!pass.isEmpty()) { entity = pass.get(0); passengers++; } @@ -271,11 +328,9 @@ public class PlacementHandler private static Entity getTopPassenger(Entity entity) { Entity top = entity; - while (entity.isVehicle()) - { + while (entity.isVehicle()) { List pass = entity.getPassengers(); - if (!pass.isEmpty()) - { + if (!pass.isEmpty()) { entity = pass.get(0); top = entity; } diff --git a/Common/src/main/java/tschipp/carryon/common/command/CommandCarryOn.java b/Common/src/main/java/tschipp/carryon/common/command/CommandCarryOn.java index 3f55cc7..dc59aa4 100644 --- a/Common/src/main/java/tschipp/carryon/common/command/CommandCarryOn.java +++ b/Common/src/main/java/tschipp/carryon/common/command/CommandCarryOn.java @@ -8,13 +8,20 @@ import net.minecraft.commands.Commands; import net.minecraft.commands.arguments.EntityArgument; import net.minecraft.network.chat.Component; import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.block.state.BlockState; import tschipp.carryon.Constants; +import tschipp.carryon.client.modeloverride.ModelOverride; +import tschipp.carryon.client.modeloverride.ModelOverrideHandler; import tschipp.carryon.common.carry.CarryOnData; import tschipp.carryon.common.carry.CarryOnData.CarryType; import tschipp.carryon.common.carry.CarryOnDataManager; +import tschipp.carryon.common.pickupcondition.PickupCondition; +import tschipp.carryon.common.pickupcondition.PickupConditionHandler; import java.util.Collection; import java.util.Collections; +import java.util.Optional; public class CommandCarryOn { @@ -45,27 +52,32 @@ public class CommandCarryOn CarryOnData carry = CarryOnDataManager.getCarryData(player); if (carry.isCarrying(CarryType.BLOCK)) { - log(source,"Block: " + carry.getBlock().getBlock()); - log(source,"BlockState: " + carry.getBlock()); + BlockState block = carry.getBlock(); + log(source,"Block: " + block.getBlock()); + log(source,"BlockState: " + block); log(source,"NBT: " + carry.getNbt()); - //TODO -// if (ModelOverridesHandler.hasCustomOverrideModel(ItemCarryonBlock.getBlockState(main), ItemCarryonBlock.getTileData(main))) -// source.sendSuccess(Component.literal("Override Model: " + ModelOverridesHandler.getOverrideObject(ItemCarryonBlock.getBlockState(main), ItemCarryonBlock.getTileData(main))), true); + Optional ov = ModelOverrideHandler.getModelOverride(block, carry.getContentNbt()); + if(ov.isPresent()) + log(source, "Override Model: " + ov.get().getRenderObject()); + + Optional cond = PickupConditionHandler.getPickupCondition(block); + if(cond.isPresent()) + log(source, "Custom Pickup Condition: " + cond.get().getCondition()); -// if (CustomPickupOverrideHandler.hasSpecialPickupConditions(ItemCarryonBlock.getBlockState(main))) -// source.sendSuccess(Component.literal("Custom Pickup Condition: " + CustomPickupOverrideHandler.getPickupCondition(ItemCarryonBlock.getBlockState(main))), true); return 1; } else if (carry.isCarrying(CarryType.ENTITY)) { - log(source,"Entity: " + carry.getEntity(player.level)); - log(source,"Entity Name: " + carry.getEntity(player.level).getType()); + Entity entity = carry.getEntity(player.level); + log(source,"Entity: " + entity); + log(source,"Entity Name: " + entity.getType()); log(source,"NBT: " + carry.getNbt()); -// if (CustomPickupOverrideHandler.hasSpecialPickupConditions(ItemCarryonEntity.getEntity(main, player.level))) -// source.sendSuccess(Component.literal("Custom Pickup Condition: " + CustomPickupOverrideHandler.getPickupCondition(ItemCarryonEntity.getEntity(main, player.level))), true); + Optional cond = PickupConditionHandler.getPickupCondition(entity); + if(cond.isPresent()) + log(source, "Custom Pickup Condition: " + cond.get().getCondition()); return 1; } diff --git a/Common/src/main/java/tschipp/carryon/common/config/CarryConfig.java b/Common/src/main/java/tschipp/carryon/common/config/CarryConfig.java index 37daaea..934e67c 100644 --- a/Common/src/main/java/tschipp/carryon/common/config/CarryConfig.java +++ b/Common/src/main/java/tschipp/carryon/common/config/CarryConfig.java @@ -151,7 +151,9 @@ public class CarryConfig { description = "Usually all the block state information is retained when placing a block that was picked up. But some information is changed to a modified property, like rotation or orientation. In this list, add additional properties that should NOT be saved and instead be updated when placed. Format: modid:block[propertyname]. Note: You don't need to add an entry for every subtype of a same block. For example, we only add an entry for one type of slab, but the change is applied to all slabs." ) public String[] placementStateExceptions = { - "minecraft:chest[type]" + "minecraft:chest[type]", + "minecraft:stone_button[face]", + "minecraft:vine[north,east,south,west,up]" }; @Property( @@ -291,26 +293,8 @@ public class CarryConfig { description = "Model Overrides based on NBT or Meta. Advanced users only! Read about the format here: https://github.com/Tschipp/CarryOn/wiki/Model-Override-Config" ) public String[] modelOverrides = { - "minecraft:hopper->(block)minecraft:hopper", - "minecraft:comparator->(block)minecraft:comparator", - "minecraft:repeater->(block)minecraft:repeater", - "minecraft:cauldron->(block)minecraft:cauldron", - "minecraft:brewing_stand->(item)minecraft:brewing_stand", - "minecraft:flower_pot->(block)minecraft:flower_pot", - "minecraft:sugar_cane->(block)minecraft:sugar_cane", "minecraft:redstone_wire->(item)minecraft:redstone", - "animania:block_nest->(block)animania:block_nest", - "animania:cheese_mold;0->(block)animania:cheese_mold;0", - "animania:cheese_mold;1->(block)animania:cheese_mold;1", - "animania:cheese_mold;2->(block)animania:cheese_mold;2", - "animania:cheese_mold;3->(block)animania:cheese_mold;3", - "animania:cheese_mold;4->(block)animania:cheese_mold;4", - "animania:cheese_mold;5->(block)animania:cheese_mold;5", - "animania:cheese_mold;6->(block)animania:cheese_mold;6", - "animania:cheese_mold;7->(block)animania:cheese_mold;7", - "animania:cheese_mold;8->(block)animania:cheese_mold;8", - "animania:cheese_mold;9->(block)animania:cheese_mold;9", - "animania:cheese_mold;10->(block)animania:cheese_mold;10" + "minecraft:bamboo_sapling->(block)minecraft:bamboo" }; } diff --git a/Common/src/main/java/tschipp/carryon/common/carry/ListHandler.java b/Common/src/main/java/tschipp/carryon/common/config/ListHandler.java similarity index 93% rename from Common/src/main/java/tschipp/carryon/common/carry/ListHandler.java rename to Common/src/main/java/tschipp/carryon/common/config/ListHandler.java index f12ff4a..711bb7d 100644 --- a/Common/src/main/java/tschipp/carryon/common/carry/ListHandler.java +++ b/Common/src/main/java/tschipp/carryon/common/config/ListHandler.java @@ -1,4 +1,4 @@ -package tschipp.carryon.common.carry; +package tschipp.carryon.common.config; import net.minecraft.core.Registry; import net.minecraft.resources.ResourceLocation; @@ -8,6 +8,7 @@ import net.minecraft.world.entity.EntityType; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.properties.Property; import tschipp.carryon.Constants; +import tschipp.carryon.utils.StringHelper; import java.util.*; import java.util.stream.Collectors; @@ -131,12 +132,13 @@ public class ListHandler { if(!propString.contains("[") || !propString.contains("]")) continue; String name = propString.substring(0, propString.indexOf("[")); - String propName = propString.substring(propString.indexOf("[") + 1, propString.indexOf("]")); + String props = propString.substring(propString.indexOf("[") + 1, propString.indexOf("]")); Block blk = Registry.BLOCK.get(new ResourceLocation(name)); - for(Property prop : blk.defaultBlockState().getProperties()) - { - if(prop.getName().equals(propName)) - PROPERTY_EXCEPTION_CLASSES.add(prop.getValueClass()); + for(String propName : props.split(",")) { + for (Property prop : blk.defaultBlockState().getProperties()) { + if (prop.getName().equals(propName)) + PROPERTY_EXCEPTION_CLASSES.add(prop.getValueClass()); + } } } @@ -164,6 +166,7 @@ public class ListHandler { for (ResourceLocation key : keys) { + if (containsAll(key.toString(), filter)) { toAddTo.add(key.toString()); @@ -180,15 +183,7 @@ public class ListHandler { public static boolean containsAll(String str, String... strings) { - boolean containsAll = true; - - for (String s : strings) - { - if (!str.contains(s)) - containsAll = false; - } - - return containsAll; + return StringHelper.matchesWildcards(str, strings); } public static void addForbiddenTiles(String toAdd) diff --git a/Common/src/main/java/tschipp/carryon/common/pickupcondition/PickupCondition.java b/Common/src/main/java/tschipp/carryon/common/pickupcondition/PickupCondition.java new file mode 100644 index 0000000..a91536a --- /dev/null +++ b/Common/src/main/java/tschipp/carryon/common/pickupcondition/PickupCondition.java @@ -0,0 +1,123 @@ +package tschipp.carryon.common.pickupcondition; + +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.serialization.Codec; +import com.mojang.serialization.DataResult; +import net.minecraft.commands.arguments.blocks.BlockStateParser; +import net.minecraft.commands.arguments.blocks.BlockStateParser.BlockResult; +import net.minecraft.core.Registry; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.properties.Property; +import tschipp.carryon.platform.Services; +import tschipp.carryon.utils.StringHelper; + +import java.util.Map; + +public class PickupCondition +{ + public static Codec CODEC = Codec.STRING.comapFlatMap(PickupCondition::of, pickupCondition -> pickupCondition.str); + + private String str, cond, match; + private boolean wildcards; + + private PickupCondition(String str, String cond, String match) + { + this.str = str; + this.cond = cond; + this.match = match; + } + + public static DataResult of(String str) + { + if(!(str.contains("(") && str.endsWith(")"))) + return DataResult.error("Error while parsing: "+ str +". Pickup Condition must contain proper brackets."); + + String cond = str.substring(str.indexOf("("), str.length()-1); + + String match = str.substring(0, str.indexOf("(")); + + PickupCondition condition = new PickupCondition(str, cond, match); + if(match.contains("*")) + condition.wildcards = true; + + return DataResult.success(condition); + } + + public boolean matches(BlockState state) + { + if(wildcards) + { + String name = match.contains("[") ? match.substring(0, match.indexOf("[")) : match; + String[] split = name.replace("*", ",").split(","); + String stateName = Registry.BLOCK.getKey(state.getBlock()).toString(); + + if(StringHelper.matchesWildcards(stateName, split)) + { + if(match.contains("[")) + { + stateName = stateName + match.substring(match.indexOf("[")); + BlockResult result = parseState(stateName); + return matchesProperties(state, result.properties()); + } + else + return true; + } + else + return false; + } + else + { + BlockResult res = parseState(match); + return res.blockState().getBlock() == state.getBlock() && matchesProperties(state, res.properties()); + } + } + + public boolean matches(Entity entity) + { + String entityName = Registry.ENTITY_TYPE.getKey(entity.getType()).toString(); + if(wildcards) + { + String[] split = match.replace("*", ",").split(","); + return StringHelper.matchesWildcards(entityName, split); + } + else + return entityName.equals(match); + } + + public String getCondition() + { + return cond; + } + + public boolean isFulfilled(ServerPlayer player) + { + return Services.GAMESTAGES.hasStage(player, cond); + } + + private BlockResult parseState(String state) + { + try { + BlockResult result = BlockStateParser.parseForBlock(Registry.BLOCK, state, false); + return result; + } catch (CommandSyntaxException e) { + e.printStackTrace(); + } + return null; + } + + private boolean matchesProperties(BlockState state, Map, Comparable> props) + { + for(var entry : props.entrySet()) + { + var val = state.getValue(entry.getKey()); + if(val != entry.getValue()) + return false; + } + return true; + } + + +} + diff --git a/Common/src/main/java/tschipp/carryon/common/pickupcondition/PickupConditionHandler.java b/Common/src/main/java/tschipp/carryon/common/pickupcondition/PickupConditionHandler.java new file mode 100644 index 0000000..e1ec08d --- /dev/null +++ b/Common/src/main/java/tschipp/carryon/common/pickupcondition/PickupConditionHandler.java @@ -0,0 +1,71 @@ +package tschipp.carryon.common.pickupcondition; + +import com.mojang.serialization.DataResult; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.block.state.BlockState; +import tschipp.carryon.Constants; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +public class PickupConditionHandler +{ + private static final List BLOCK_CONDITIONS = new ArrayList<>(); + private static final List ENTITY_CONDITIONS = new ArrayList<>(); + + public static void initPickupConditions() + { + BLOCK_CONDITIONS.clear(); + ENTITY_CONDITIONS.clear(); + + for(String cond : Constants.COMMON_CONFIG.customPickupConditions.customPickupConditionsBlocks) + { + DataResult res = PickupCondition.of(cond); + if(res.result().isPresent()) + { + PickupCondition pickupCondition = res.result().get(); + BLOCK_CONDITIONS.add(pickupCondition); + } + else + { + Constants.LOG.debug("Error while parsing Pickup Conditions: " + res.error().get().message()); + } + + } + + for(String cond : Constants.COMMON_CONFIG.customPickupConditions.customPickupConditionsEntities) + { + DataResult res = PickupCondition.of(cond); + if(res.result().isPresent()) + { + PickupCondition pickupCondition = res.result().get(); + ENTITY_CONDITIONS.add(pickupCondition); + } + else + { + Constants.LOG.debug("Error while parsing Pickup Conditions: " + res.error().get().message()); + } + } + } + + public static Optional getPickupCondition(BlockState state) + { + for(PickupCondition cond : BLOCK_CONDITIONS) + { + if(cond.matches(state)) + return Optional.of(cond); + } + return Optional.empty(); + } + + public static Optional getPickupCondition(Entity entity) + { + for(PickupCondition cond : ENTITY_CONDITIONS) + { + if(cond.matches(entity)) + return Optional.of(cond); + } + return Optional.empty(); + } +} diff --git a/Common/src/main/java/tschipp/carryon/common/scripting/Matchables.java b/Common/src/main/java/tschipp/carryon/common/scripting/Matchables.java index 2632f0d..90242b0 100644 --- a/Common/src/main/java/tschipp/carryon/common/scripting/Matchables.java +++ b/Common/src/main/java/tschipp/carryon/common/scripting/Matchables.java @@ -17,10 +17,7 @@ import net.minecraft.world.scores.Score; import net.minecraft.world.scores.Scoreboard; import tschipp.carryon.platform.Services; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Map; +import java.util.*; public final class Matchables { @@ -29,10 +26,18 @@ public final class Matchables boolean matches(T elem); } - private static float getValueFromString(String toGetFrom, String key, float defaultValue) + private static float getValueFromStringOrDefault(String toGetFrom, String key, float defaultVal) + { + Optional val = getValueFromString(toGetFrom, key); + if(val.isPresent()) + return val.get(); + return defaultVal; + } + + private static Optional getValueFromString(String toGetFrom, String key) { if (toGetFrom == null || toGetFrom.isEmpty()) - return defaultValue; + return Optional.empty(); String[] s = toGetFrom.split(","); for (String string : s) @@ -50,11 +55,11 @@ public final class Matchables { } - return numb; + return Optional.of(numb); } } - return defaultValue; + return Optional.empty(); } public record NumberBoundCondition(String bounds) implements Matchable @@ -203,14 +208,15 @@ public final class Matchables public static final GamestageCondition NONE = new GamestageCondition(""); @Override - public boolean matches(ServerPlayer elem) + public boolean matches(ServerPlayer player) { if(!Services.PLATFORM.isModLoaded("gamestages")) return true; - //TODO: Gamestages + if(gamestage == null || gamestage.isEmpty()) + return true; - return true; + return Services.GAMESTAGES.hasStage(player, gamestage); } } @@ -271,8 +277,8 @@ public final class Matchables if (cond == null || cond.isEmpty()) return true; - BlockPos blockpos = new BlockPos(getValueFromString(cond, "x", 0), getValueFromString(cond, "y", 0), getValueFromString(cond, "z", 0)); - BlockPos expand = new BlockPos(getValueFromString(cond, "dx", 0), getValueFromString(cond, "dy", 0), getValueFromString(cond, "dz", 0)); + BlockPos blockpos = new BlockPos(getValueFromStringOrDefault(cond, "x", 0), getValueFromStringOrDefault(cond, "y", 0), getValueFromStringOrDefault(cond, "z", 0)); + BlockPos expand = new BlockPos(getValueFromStringOrDefault(cond, "dx", 0), getValueFromStringOrDefault(cond, "dy", 0), getValueFromStringOrDefault(cond, "dz", 0)); BlockPos expanded = blockpos.offset(expand); BlockPos pos = elem.blockPosition(); @@ -339,7 +345,7 @@ public final class Matchables int idx = names.indexOf(name); int lev = levels.get(idx); - if (lev == amp) + if (lev <= amp) matches++; } } @@ -372,12 +378,30 @@ public final class Matchables String source; Vec3 vec; + boolean x, y, z; + public OptionalVec3(String source) { this.source = source; - double x = getValueFromString(source, "x", 0); - double y = getValueFromString(source, "y", 0); - double z = getValueFromString(source, "z", 0); + Optional xOpt = getValueFromString(source, "x"); + Optional yOpt = getValueFromString(source, "y"); + Optional zOpt = getValueFromString(source, "z"); + + float x = 0, y = 0, z = 0; + + if(xOpt.isPresent()) { + x = xOpt.get(); + this.x = true; + } + if(yOpt.isPresent()) { + y = yOpt.get(); + this.y = true; + } + if(zOpt.isPresent()) { + z = zOpt.get(); + this.z = true; + } + vec = new Vec3(x, y, z); } @@ -386,16 +410,19 @@ public final class Matchables return source; } + /** + * Gets the contained optional vector. Nonexisting numbers are set to 0. + */ public Vec3 getVec() { return vec; } - public Vec3 getVec(double defaultValue) + public Vec3 getVec(double dX, double dY, double dZ) { - double x = vec.x == 0f ? defaultValue : vec.x; - double y = vec.y == 0f ? defaultValue : vec.y; - double z = vec.z == 0f ? defaultValue : vec.z; + double x = !this.x ? dX : vec.x; + double y = !this.y ? dY : vec.y; + double z = !this.z ? dZ : vec.z; return new Vec3(x, y, z); } } diff --git a/Common/src/main/java/tschipp/carryon/config/ConfigLoader.java b/Common/src/main/java/tschipp/carryon/config/ConfigLoader.java index ffe129a..8c63804 100644 --- a/Common/src/main/java/tschipp/carryon/config/ConfigLoader.java +++ b/Common/src/main/java/tschipp/carryon/config/ConfigLoader.java @@ -2,7 +2,9 @@ package tschipp.carryon.config; //Many Thanks to ThatGravyBoat for this template! -import tschipp.carryon.common.carry.ListHandler; +import tschipp.carryon.client.modeloverride.ModelOverrideHandler; +import tschipp.carryon.common.config.ListHandler; +import tschipp.carryon.common.pickupcondition.PickupConditionHandler; import tschipp.carryon.config.annotations.Category; import tschipp.carryon.config.annotations.Config; import tschipp.carryon.config.annotations.Property; @@ -33,6 +35,8 @@ public class ConfigLoader { public static void onConfigLoaded() { ListHandler.initConfigLists(); + PickupConditionHandler.initPickupConditions(); + ModelOverrideHandler.initModelOverrides(); } public static BuiltCategory buildCategory(String categoryDesc, Object object) throws IllegalAccessException { diff --git a/Common/src/main/java/tschipp/carryon/mixin/HumanoidModelMixin.java b/Common/src/main/java/tschipp/carryon/mixin/HumanoidModelMixin.java index 587bd38..9496ee6 100644 --- a/Common/src/main/java/tschipp/carryon/mixin/HumanoidModelMixin.java +++ b/Common/src/main/java/tschipp/carryon/mixin/HumanoidModelMixin.java @@ -11,6 +11,7 @@ import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import tschipp.carryon.Constants; import tschipp.carryon.client.render.CarryRenderHelper; import tschipp.carryon.common.carry.CarryOnData; import tschipp.carryon.common.carry.CarryOnDataManager; @@ -28,7 +29,7 @@ public class HumanoidModelMixin { @Inject(at = @At("RETURN"), method = "setupAnim(Lnet/minecraft/world/entity/LivingEntity;FFFFF)V") private void onSetupAnimations(LivingEntity living, float f1, float f2, float f3, float f4, float f5, CallbackInfo ci) { - if(living instanceof Player player) + if(living instanceof Player player && Constants.CLIENT_CONFIG.renderArms) { CarryOnData carry = CarryOnDataManager.getCarryData(player); if(carry.isCarrying() && !player.isVisuallySwimming() && !player.isFallFlying()) @@ -47,8 +48,8 @@ public class HumanoidModelMixin { boolean renderLeft = render.renderLeftArm(); boolean renderRight = render.renderRightArm(); - Vec3 rotLeft = render.renderRotationLeftArm().getVec(); - Vec3 rotRight = render.renderRotationRightArm().getVec(); + Vec3 rotLeft = render.renderRotationLeftArm().getVec(-x, -offset, z); + Vec3 rotRight = render.renderRotationRightArm().getVec(-x, offset, -z); if(renderLeft) changeRotation(leftArm, (float) rotLeft.x, (float) rotLeft.y, (float) rotLeft.z); diff --git a/Common/src/main/java/tschipp/carryon/platform/Services.java b/Common/src/main/java/tschipp/carryon/platform/Services.java index 2343834..8ce675d 100644 --- a/Common/src/main/java/tschipp/carryon/platform/Services.java +++ b/Common/src/main/java/tschipp/carryon/platform/Services.java @@ -1,6 +1,7 @@ package tschipp.carryon.platform; import tschipp.carryon.Constants; +import tschipp.carryon.platform.services.IGamestagePlatformHelper; import tschipp.carryon.platform.services.IPlatformHelper; import java.util.ServiceLoader; @@ -9,6 +10,8 @@ public class Services { public static final IPlatformHelper PLATFORM = load(IPlatformHelper.class); + public static final IGamestagePlatformHelper GAMESTAGES = load(IGamestagePlatformHelper.class); + public static T load(Class clazz) { final T loadedService = ServiceLoader.load(clazz) diff --git a/Common/src/main/java/tschipp/carryon/platform/services/IGamestagePlatformHelper.java b/Common/src/main/java/tschipp/carryon/platform/services/IGamestagePlatformHelper.java new file mode 100644 index 0000000..ce885f2 --- /dev/null +++ b/Common/src/main/java/tschipp/carryon/platform/services/IGamestagePlatformHelper.java @@ -0,0 +1,8 @@ +package tschipp.carryon.platform.services; + +import net.minecraft.world.entity.player.Player; + +public interface IGamestagePlatformHelper +{ + boolean hasStage(Player player, String stage); +} diff --git a/Common/src/main/java/tschipp/carryon/utils/StringHelper.java b/Common/src/main/java/tschipp/carryon/utils/StringHelper.java new file mode 100644 index 0000000..2fb66ab --- /dev/null +++ b/Common/src/main/java/tschipp/carryon/utils/StringHelper.java @@ -0,0 +1,18 @@ +package tschipp.carryon.utils; + +public class StringHelper +{ + public static boolean matchesWildcards(String str, String[] wildcards) + { + for(String w : wildcards) + { + if(!str.contains(w)) + return false; + int i = str.indexOf(w); + str = str.substring(i + w.length()); + } + + return true; + } + +} diff --git a/Common/src/main/resources/assets/carryon/logo.png b/Common/src/main/resources/logo.png similarity index 100% rename from Common/src/main/resources/assets/carryon/logo.png rename to Common/src/main/resources/logo.png diff --git a/Fabric/build.gradle b/Fabric/build.gradle index 3da1d98..8496fd1 100644 --- a/Fabric/build.gradle +++ b/Fabric/build.gradle @@ -6,6 +6,15 @@ plugins { archivesBaseName = "${mod_id}-fabric-${minecraft_version}" +apply from: 'https://raw.githubusercontent.com/MinecraftModDevelopment/Gradle-Collection/22e7d543a18cd30675277fbfa3669e3d9e206010/generic/secrets.gradle' + +if (project.hasProperty('secretFile')) { + loadSecrets(new File((String) findProperty('secretFile'))) +} +if (System.getenv('BUILD_NUMBER') != null) { + version += "." + System.getenv('BUILD_NUMBER') +} + dependencies { minecraft "com.mojang:minecraft:${minecraft_version}" mappings loom.officialMojangMappings() diff --git a/Fabric/src/main/java/tschipp/carryon/events/ClientEvents.java b/Fabric/src/main/java/tschipp/carryon/events/ClientEvents.java index 7c3a7d2..5c9caa2 100644 --- a/Fabric/src/main/java/tschipp/carryon/events/ClientEvents.java +++ b/Fabric/src/main/java/tschipp/carryon/events/ClientEvents.java @@ -9,6 +9,7 @@ public class ClientEvents { { ClientTickEvents.END_CLIENT_TICK.register(mc -> { CarryOnCommonClient.checkForKeybinds(); + CarryOnCommonClient.onCarryClientTick(); }); } diff --git a/Fabric/src/main/java/tschipp/carryon/events/CommonEvents.java b/Fabric/src/main/java/tschipp/carryon/events/CommonEvents.java index 874f05b..41120f5 100644 --- a/Fabric/src/main/java/tschipp/carryon/events/CommonEvents.java +++ b/Fabric/src/main/java/tschipp/carryon/events/CommonEvents.java @@ -1,10 +1,10 @@ package tschipp.carryon.events; import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; +import net.fabricmc.fabric.api.entity.event.v1.ServerPlayerEvents; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents; -import net.fabricmc.fabric.api.event.player.UseBlockCallback; -import net.fabricmc.fabric.api.event.player.UseEntityCallback; +import net.fabricmc.fabric.api.event.player.*; import net.fabricmc.fabric.api.resource.ResourceManagerHelper; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; @@ -35,7 +35,7 @@ public class CommonEvents { CarryOnData carry = CarryOnDataManager.getCarryData(player); if(!carry.isCarrying()) { - if (PickupHandler.tryPickUpBlock((ServerPlayer) player, pos, world)) + if (PickupHandler.tryPickUpBlock((ServerPlayer) player, pos, world, null)) return InteractionResult.SUCCESS; return InteractionResult.PASS; } @@ -67,7 +67,7 @@ public class CommonEvents { CarryOnData carry = CarryOnDataManager.getCarryData(player); if (!carry.isCarrying()) { - if (PickupHandler.tryPickupEntity((ServerPlayer) player, entity)) { + if (PickupHandler.tryPickupEntity((ServerPlayer) player, entity, null)) { return InteractionResult.SUCCESS; } } @@ -92,11 +92,37 @@ public class CommonEvents { ScriptReloadListener.syncScriptsWithClient(player); }); + ServerTickEvents.END_SERVER_TICK.register(server -> { for(ServerPlayer player : server.getPlayerList().getPlayers()) - PickupHandler.onCarryTick(player); + CarryOnCommon.onCarryTick(player); }); + + ServerPlayerEvents.COPY_FROM.register(((oldPlayer, newPlayer, alive) -> { + PlacementHandler.placeCarriedOnDeath(oldPlayer, newPlayer, !alive); + })); + + + PlayerBlockBreakEvents.BEFORE.register(((world, player, pos, state, blockEntity) -> { + if(!CarryOnCommon.onTryBreakBlock(player)) + return false; + return true; + })); + + AttackBlockCallback.EVENT.register(((player, world, hand, pos, direction) -> { + if(!CarryOnCommon.onTryBreakBlock(player)) + return InteractionResult.SUCCESS; + return InteractionResult.PASS; + })); + + AttackEntityCallback.EVENT.register(((player, world, hand, entity, hitResult) -> { + if(!CarryOnCommon.onAttackedByPlayer(player)) + return InteractionResult.SUCCESS; + return InteractionResult.PASS; + })); + + //TODO: drop carried when attacked } } diff --git a/Fabric/src/main/java/tschipp/carryon/mixin/PlayerMixinFabric.java b/Fabric/src/main/java/tschipp/carryon/mixin/PlayerMixinFabric.java new file mode 100644 index 0000000..f17be85 --- /dev/null +++ b/Fabric/src/main/java/tschipp/carryon/mixin/PlayerMixinFabric.java @@ -0,0 +1,29 @@ +package tschipp.carryon.mixin; + +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.entity.player.Player; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import tschipp.carryon.Constants; +import tschipp.carryon.common.carry.CarryOnData; +import tschipp.carryon.common.carry.CarryOnDataManager; +import tschipp.carryon.common.carry.PlacementHandler; + +@Mixin(Player.class) +public class PlayerMixinFabric +{ + @Inject(at = @At("HEAD"), method = "hurt(Lnet/minecraft/world/damagesource/DamageSource;F)Z") + private void onHurt(DamageSource damageSource, float f, CallbackInfoReturnable cir) { + if(Constants.COMMON_CONFIG.settings.dropCarriedWhenHit) + { + Player player = ((Player)(Object)this); + CarryOnData carry = CarryOnDataManager.getCarryData(player); + if(carry.isCarrying() && !player.level.isClientSide) + PlacementHandler.placeCarried((ServerPlayer)player); + } + + } +} diff --git a/Fabric/src/main/java/tschipp/carryon/platform/FabricGamestagesHelper.java b/Fabric/src/main/java/tschipp/carryon/platform/FabricGamestagesHelper.java new file mode 100644 index 0000000..71aa67f --- /dev/null +++ b/Fabric/src/main/java/tschipp/carryon/platform/FabricGamestagesHelper.java @@ -0,0 +1,13 @@ +package tschipp.carryon.platform; + +import net.minecraft.world.entity.player.Player; +import tschipp.carryon.platform.services.IGamestagePlatformHelper; + +public class FabricGamestagesHelper implements IGamestagePlatformHelper +{ + @Override + public boolean hasStage(Player player, String stage) + { + return true; + } +} diff --git a/Fabric/src/main/resources/META-INF/services/tschipp.carryon.platform.services.IGamestagePlatformHelper b/Fabric/src/main/resources/META-INF/services/tschipp.carryon.platform.services.IGamestagePlatformHelper new file mode 100644 index 0000000..aafced4 --- /dev/null +++ b/Fabric/src/main/resources/META-INF/services/tschipp.carryon.platform.services.IGamestagePlatformHelper @@ -0,0 +1 @@ +tschipp.carryon.platform.FabricGamestagesHelper \ No newline at end of file diff --git a/Fabric/src/main/resources/carryon.fabric.mixins.json b/Fabric/src/main/resources/carryon.fabric.mixins.json index 562850c..53b0cff 100644 --- a/Fabric/src/main/resources/carryon.fabric.mixins.json +++ b/Fabric/src/main/resources/carryon.fabric.mixins.json @@ -4,6 +4,7 @@ "package": "tschipp.carryon.mixin", "compatibilityLevel": "JAVA_17", "mixins": [ + "PlayerMixinFabric" ], "client": [ "ItemInHandRendererMixin", diff --git a/Fabric/src/main/resources/fabric.mod.json b/Fabric/src/main/resources/fabric.mod.json index 3e1988d..7810ae7 100644 --- a/Fabric/src/main/resources/fabric.mod.json +++ b/Fabric/src/main/resources/fabric.mod.json @@ -2,19 +2,19 @@ "schemaVersion": 1, "id": "carryon", "version": "${version}", - "name": "Carry On", - "description": "This is an example description! Tell everyone what your mod is about!", + "description": "Carry On is a simple mod that improves game interaction by allowing players to pick up, carry, and place single block Tile Entities using only their empty hands.", "authors": [ - "Tschipp" + "Tschipp", "Purplicious_Cow", "cy4n" ], "contact": { - "homepage": "https://fabricmc.net/", - "sources": "https://github.com/FabricMC/fabric-example-mod" + "homepage": "https://tschipp.ch/", + "sources": "https://github.com/Tschipp/CarryOn", + "issues": "https://github.com/Tschipp/CarryOn/issues" }, - "license": "CC0-1.0", - "icon": "assets/carryon/logo.png", + "license": "GNU LGPLv3", + "icon": "logo.png", "environment": "*", "entrypoints": { diff --git a/Forge/build.gradle b/Forge/build.gradle index d099e22..3c11a11 100644 --- a/Forge/build.gradle +++ b/Forge/build.gradle @@ -18,6 +18,15 @@ apply plugin: 'org.spongepowered.mixin' apply plugin: 'maven-publish' apply plugin: 'org.parchmentmc.librarian.forgegradle' +apply from: 'https://raw.githubusercontent.com/MinecraftModDevelopment/Gradle-Collection/22e7d543a18cd30675277fbfa3669e3d9e206010/generic/secrets.gradle' + +if (project.hasProperty('secretFile')) { + loadSecrets(new File((String) findProperty('secretFile'))) +} +if (System.getenv('BUILD_NUMBER') != null) { + version += "." + System.getenv('BUILD_NUMBER') +} + archivesBaseName = "${mod_id}-forge-${minecraft_version}" mixin { @@ -88,9 +97,16 @@ minecraft { sourceSets.main.resources.srcDir 'src/generated/resources' +repositories { + maven { + url 'https://maven.blamejared.com' + } +} + dependencies { minecraft "net.minecraftforge:forge:${minecraft_version}-${forge_version}" compileOnly project(":Common") + compileOnly fg.deobf("net.darkhax.gamestages:GameStages-Forge-1.19.2:11.0.2") annotationProcessor 'org.spongepowered:mixin:0.8.4-SNAPSHOT:processor' } diff --git a/Forge/src/main/java/tschipp/carryon/compat/GamestageCompat.java b/Forge/src/main/java/tschipp/carryon/compat/GamestageCompat.java new file mode 100644 index 0000000..cdf8cbb --- /dev/null +++ b/Forge/src/main/java/tschipp/carryon/compat/GamestageCompat.java @@ -0,0 +1,12 @@ +package tschipp.carryon.compat; + +import net.darkhax.gamestages.GameStageHelper; +import net.minecraft.world.entity.player.Player; + +public class GamestageCompat +{ + public static boolean hasStage(Player player, String stage) + { + return GameStageHelper.hasStage(player, stage); + } +} diff --git a/Forge/src/main/java/tschipp/carryon/events/ClientEvents.java b/Forge/src/main/java/tschipp/carryon/events/ClientEvents.java index 207cc39..9b213b8 100644 --- a/Forge/src/main/java/tschipp/carryon/events/ClientEvents.java +++ b/Forge/src/main/java/tschipp/carryon/events/ClientEvents.java @@ -78,6 +78,7 @@ public class ClientEvents { if(event.phase == Phase.END) { CarryOnCommonClient.checkForKeybinds(); + CarryOnCommonClient.onCarryClientTick(); } } diff --git a/Forge/src/main/java/tschipp/carryon/events/CommonEvents.java b/Forge/src/main/java/tschipp/carryon/events/CommonEvents.java index f31824f..e0e515d 100644 --- a/Forge/src/main/java/tschipp/carryon/events/CommonEvents.java +++ b/Forge/src/main/java/tschipp/carryon/events/CommonEvents.java @@ -4,6 +4,8 @@ import net.minecraft.core.BlockPos; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.InteractionResult; import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.Mob; +import net.minecraft.world.entity.MobSpawnType; import net.minecraft.world.entity.player.Player; import net.minecraft.world.level.Level; import net.minecraftforge.common.MinecraftForge; @@ -13,24 +15,29 @@ import net.minecraftforge.event.OnDatapackSyncEvent; import net.minecraftforge.event.RegisterCommandsEvent; import net.minecraftforge.event.TickEvent.Phase; import net.minecraftforge.event.TickEvent.ServerTickEvent; +import net.minecraftforge.event.entity.living.LivingAttackEvent; +import net.minecraftforge.event.entity.living.LivingSpawnEvent.CheckSpawn; +import net.minecraftforge.event.entity.player.AttackEntityEvent; +import net.minecraftforge.event.entity.player.PlayerEvent.BreakSpeed; +import net.minecraftforge.event.entity.player.PlayerEvent.Clone; import net.minecraftforge.event.entity.player.PlayerInteractEvent; +import net.minecraftforge.event.level.BlockEvent; +import net.minecraftforge.event.level.BlockEvent.BreakEvent; import net.minecraftforge.event.level.BlockEvent.EntityPlaceEvent; import net.minecraftforge.eventbus.api.Event; import net.minecraftforge.eventbus.api.Event.Result; import net.minecraftforge.eventbus.api.EventPriority; import net.minecraftforge.eventbus.api.SubscribeEvent; -import net.minecraftforge.fml.InterModComms; -import net.minecraftforge.fml.InterModComms.IMCMessage; import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.event.lifecycle.FMLDedicatedServerSetupEvent; import tschipp.carryon.CarryOnCommon; import tschipp.carryon.Constants; -import tschipp.carryon.common.carry.*; +import tschipp.carryon.common.carry.CarryOnData; import tschipp.carryon.common.carry.CarryOnData.CarryType; +import tschipp.carryon.common.carry.CarryOnDataManager; +import tschipp.carryon.common.carry.PickupHandler; +import tschipp.carryon.common.carry.PlacementHandler; import tschipp.carryon.common.scripting.ScriptReloadListener; -import java.util.stream.Stream; - @Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.FORGE, modid = Constants.MOD_ID) public class CommonEvents { @@ -51,30 +58,35 @@ public class CommonEvents CarryOnData carry = CarryOnDataManager.getCarryData(player); if (!carry.isCarrying()) { - if (PickupHandler.tryPickUpBlock((ServerPlayer) player, pos, level)) { + if (PickupHandler.tryPickUpBlock((ServerPlayer) player, pos, level, (pState, pPos) -> { + BlockEvent.BreakEvent breakEvent = new BreakEvent(level, pPos, pState, player); + MinecraftForge.EVENT_BUS.post(breakEvent); + return !breakEvent.isCanceled(); + })) { success = true; } } else { if (carry.isCarrying(CarryType.BLOCK)) { - if (PlacementHandler.tryPlaceBlock((ServerPlayer) player, pos, event.getFace(), (pos2, state) -> { + PlacementHandler.tryPlaceBlock((ServerPlayer) player, pos, event.getFace(), (pos2, state) -> { BlockSnapshot snapshot = BlockSnapshot.create(level.dimension(), level, pos2); EntityPlaceEvent event1 = new EntityPlaceEvent(snapshot, level.getBlockState(pos), player); MinecraftForge.EVENT_BUS.post(event1); return !event1.isCanceled(); - })) { - success = true; - } + }); } else { - //TODO: Entity place perms - if (PlacementHandler.tryPlaceEntity((ServerPlayer) player,pos, event.getFace(), null)) - { - success = true; - } + PlacementHandler.tryPlaceEntity((ServerPlayer) player, pos, event.getFace(), (pPos, toPlace) -> { + if (toPlace instanceof Mob mob) { + CheckSpawn checkSpawn = new CheckSpawn(mob, level, pPos.x, pPos.y, pPos.z, null, MobSpawnType.EVENT); + MinecraftForge.EVENT_BUS.post(checkSpawn); + return event.getResult() != Result.DENY; + } + return true; + }); } + success = true; } - if(success) - { + if (success) { event.setUseBlock(Event.Result.DENY); event.setUseItem(Event.Result.DENY); event.setCancellationResult(InteractionResult.SUCCESS); @@ -97,16 +109,18 @@ public class CommonEvents CarryOnData carry = CarryOnDataManager.getCarryData(player); if (!carry.isCarrying()) { - if (PickupHandler.tryPickupEntity((ServerPlayer) player, target)) { + if (PickupHandler.tryPickupEntity((ServerPlayer) player, target, (toPickup) -> { + AttackEntityEvent attackEvent = new AttackEntityEvent(player, toPickup); + MinecraftForge.EVENT_BUS.post(attackEvent); + return !attackEvent.isCanceled(); + })) { event.setResult(Result.DENY); event.setCancellationResult(InteractionResult.SUCCESS); event.setCanceled(true); return; } - } - else if(carry.isCarrying(CarryType.ENTITY) || carry.isCarrying(CarryType.PLAYER)) - { - PlacementHandler.tryStackEntity((ServerPlayer)player, target); + } else if (carry.isCarrying(CarryType.ENTITY) || carry.isCarrying(CarryType.PLAYER)) { + PlacementHandler.tryStackEntity((ServerPlayer) player, target); } } @@ -116,49 +130,6 @@ public class CommonEvents CarryOnCommon.registerCommands(event.getDispatcher()); } - @SubscribeEvent(priority = EventPriority.LOW) - public void serverLoad(FMLDedicatedServerSetupEvent event) - { - Stream messages = InterModComms.getMessages(Constants.MOD_ID); - - messages.forEach(msg -> { - - String method = msg.method(); - Object obj = msg.messageSupplier().get(); - - if (!(obj instanceof String str)) - return; - - switch (method) - { - case "blacklistBlock": - ListHandler.addForbiddenTiles(str); - break; - case "blacklistEntity": - ListHandler.addForbiddenEntities(str); - break; - case "whitelistBlock": - ListHandler.addAllowedTiles(str); - break; - case "whitelistEntity": - ListHandler.addAllowedEntities(str); - break; - case "blacklistStacking": - ListHandler.addForbiddenStacking(str); - break; - case "whitelistStacking": - ListHandler.addAllowedStacking(str); - break; - //TODO -// case "addModelOverride": -// ModelOverridesHandler.parseOverride(str, 0); -// break; - } - - }); - - } - @SubscribeEvent public static void onDatapackRegister(AddReloadListenerEvent event) { @@ -169,21 +140,55 @@ public class CommonEvents public static void onDatapackSync(OnDatapackSyncEvent event) { ServerPlayer player = event.getPlayer(); - if(player == null) - { - for(ServerPlayer p : event.getPlayerList().getPlayers()) + if (player == null) { + for (ServerPlayer p : event.getPlayerList().getPlayers()) ScriptReloadListener.syncScriptsWithClient(p); - } - else + } else ScriptReloadListener.syncScriptsWithClient(player); } @SubscribeEvent public static void onServerTick(ServerTickEvent event) { - if(event.phase == Phase.END) - for(ServerPlayer player : event.getServer().getPlayerList().getPlayers()) - PickupHandler.onCarryTick(player); + if (event.phase == Phase.END) + for (ServerPlayer player : event.getServer().getPlayerList().getPlayers()) + CarryOnCommon.onCarryTick(player); + } + + @SubscribeEvent + public static void onClone(Clone event) + { + if (!event.getOriginal().level.isClientSide) + PlacementHandler.placeCarriedOnDeath((ServerPlayer) event.getOriginal(), (ServerPlayer) event.getEntity(), event.isWasDeath()); + } + + @SubscribeEvent + public static void harvestSpeed(BreakSpeed event) + { + if (!CarryOnCommon.onTryBreakBlock(event.getEntity())) + event.setNewSpeed(0); + } + + @SubscribeEvent + public static void attackEntity(AttackEntityEvent event) + { + if(!CarryOnCommon.onAttackedByPlayer(event.getEntity())) + event.setCanceled(true); + } + + @SubscribeEvent + public static void onBreakBlock(BreakEvent event) + { + if (!CarryOnCommon.onTryBreakBlock(event.getPlayer())) { + event.setCanceled(true); + } + } + + @SubscribeEvent + public static void playerAttack(LivingAttackEvent event) + { + if(event.getEntity() instanceof Player player) + CarryOnCommon.onPlayerAttacked(player); } } diff --git a/Forge/src/main/java/tschipp/carryon/events/ModBusEvents.java b/Forge/src/main/java/tschipp/carryon/events/ModBusEvents.java index 764d8a4..be71979 100644 --- a/Forge/src/main/java/tschipp/carryon/events/ModBusEvents.java +++ b/Forge/src/main/java/tschipp/carryon/events/ModBusEvents.java @@ -1,8 +1,58 @@ package tschipp.carryon.events; +import net.minecraftforge.eventbus.api.EventPriority; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.InterModComms; +import net.minecraftforge.fml.InterModComms.IMCMessage; import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.event.lifecycle.FMLDedicatedServerSetupEvent; import tschipp.carryon.Constants; +import tschipp.carryon.client.modeloverride.ModelOverrideHandler; +import tschipp.carryon.common.config.ListHandler; + +import java.util.stream.Stream; @Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.MOD, modid = Constants.MOD_ID) public class ModBusEvents { + + @SubscribeEvent(priority = EventPriority.LOW) + public void serverLoad(FMLDedicatedServerSetupEvent event) + { + Stream messages = InterModComms.getMessages(Constants.MOD_ID); + + messages.forEach(msg -> { + + String method = msg.method(); + Object obj = msg.messageSupplier().get(); + + if (!(obj instanceof String str)) + return; + + switch (method) { + case "blacklistBlock": + ListHandler.addForbiddenTiles(str); + break; + case "blacklistEntity": + ListHandler.addForbiddenEntities(str); + break; + case "whitelistBlock": + ListHandler.addAllowedTiles(str); + break; + case "whitelistEntity": + ListHandler.addAllowedEntities(str); + break; + case "blacklistStacking": + ListHandler.addForbiddenStacking(str); + break; + case "whitelistStacking": + ListHandler.addAllowedStacking(str); + break; + case "addModelOverride": + ModelOverrideHandler.addFromString(str); + break; + } + + }); + + } } diff --git a/Forge/src/main/java/tschipp/carryon/platform/ForgeGamestagesHelper.java b/Forge/src/main/java/tschipp/carryon/platform/ForgeGamestagesHelper.java new file mode 100644 index 0000000..8b9d567 --- /dev/null +++ b/Forge/src/main/java/tschipp/carryon/platform/ForgeGamestagesHelper.java @@ -0,0 +1,17 @@ +package tschipp.carryon.platform; + +import net.minecraft.world.entity.player.Player; +import tschipp.carryon.compat.GamestageCompat; +import tschipp.carryon.platform.services.IGamestagePlatformHelper; + +public class ForgeGamestagesHelper implements IGamestagePlatformHelper +{ + @Override + public boolean hasStage(Player player, String stage) + { + if(!Services.PLATFORM.isModLoaded("gamestages")) + return true; + + return GamestageCompat.hasStage(player, stage); + } +} diff --git a/Forge/src/main/java/tschipp/carryon/platform/ForgePlatformHelper.java b/Forge/src/main/java/tschipp/carryon/platform/ForgePlatformHelper.java index ea0f35c..2c9b495 100644 --- a/Forge/src/main/java/tschipp/carryon/platform/ForgePlatformHelper.java +++ b/Forge/src/main/java/tschipp/carryon/platform/ForgePlatformHelper.java @@ -9,8 +9,8 @@ import net.minecraftforge.fml.loading.FMLLoader; import net.minecraftforge.network.NetworkDirection; import net.minecraftforge.network.NetworkEvent.Context; import net.minecraftforge.network.PacketDistributor; +import tschipp.carryon.CarryOnCommonClient; import tschipp.carryon.CarryOnForge; -import tschipp.carryon.client.CarryOnClient; import tschipp.carryon.config.BuiltConfig; import tschipp.carryon.config.forge.ConfigLoaderImpl; import tschipp.carryon.networking.PacketBase; @@ -70,7 +70,7 @@ public class ForgePlatformHelper implements IPlatformHelper { { ctx.get().setPacketHandled(true); ctx.get().enqueueWork(() -> { - handler.accept(packet, CarryOnClient.getPlayer()); + handler.accept(packet, CarryOnCommonClient.getPlayer()); }); } }; diff --git a/Forge/src/main/resources/META-INF/mods.toml b/Forge/src/main/resources/META-INF/mods.toml index b13cc16..592bdfa 100644 --- a/Forge/src/main/resources/META-INF/mods.toml +++ b/Forge/src/main/resources/META-INF/mods.toml @@ -1,42 +1,18 @@ -# This is an example mods.toml file. It contains the data relating to the loading mods. -# There are several mandatory fields (#mandatory), and many more that are optional (#optional). -# The overall format is standard TOML format, v0.5.0. -# Note that there are a couple of TOML lists in this file. -# Find more information on toml format here: https://github.com/toml-lang/toml -# The name of the mod loader type to load - for regular FML @Mod mods it should be javafml modLoader="javafml" #mandatory -# A version range to match for said mod loader - for regular FML @Mod it will be the forge version loaderVersion="[43,)" #mandatory This is typically bumped every Minecraft version by Forge. See our download page for lists of versions. -# The license for you mod. This is mandatory metadata and allows for easier comprehension of your redistributive properties. -# Review your options at https://choosealicense.com/. All rights reserved is the default copyright stance, and is thus the default here. -license="All rights reserved" -# A URL to refer people to when problems occur with this mod -#issueTrackerURL="https://change.me.to.your.issue.tracker.example.invalid/" #optional -# A list of mods - how many allowed here is determined by the individual mod loader +license="GNU LGPLv3" +issueTrackerURL="https://github.com/Tschipp/CarryOn/issues" + + [[mods]] #mandatory -# The modid of the mod -modId="carryon" #mandatory -# The version number of the mod - there's a few well known ${} variables useable here or just hardcode it -# ${file.jarVersion} will substitute the value of the Implementation-Version as read from the mod's JAR file metadata -# see the associated build.gradle script for how to populate this completely automatically during a build -version="${file.jarVersion}" #mandatory - # A display name for the mod -displayName="Carry On" #mandatory -# A URL to query for updates for this mod. See the JSON update specification https://mcforge.readthedocs.io/en/latest/gettingstarted/autoupdate/ -#updateJSONURL="https://change.me.example.invalid/updates.json" #optional -# A URL for the "homepage" for this mod, displayed in the mod UI -#displayURL="https://change.me.to.your.mods.homepage.example.invalid/" #optional -# A file name (in the root of the mod JAR) containing a logo for display -logoFile="carryon.png" #optional -# A text field displayed in the mod UI -credits="Tschipp, PurpliciousCow, cy4n" #optional -# A text field displayed in the mod UI -authors="Tschipp, PurpliciousCow, cy4n" #optional -# The description text for the mod (multi line!) (#mandatory) -description=''' -Carry On Mod. -''' -# A dependency - use the . to indicate dependency for a specific modid. Dependencies are optional. + modId="carryon" #mandatory + version="${file.jarVersion}" #mandatory + displayName="Carry On" #mandatory + displayURL="https://tschipp.ch" #optional + logoFile="logo.png" #optional + authors="Tschipp, Purplicious_Cow, cy4n" #optional + description='''Carry On is a simple mod that improves game interaction by allowing players to pick up, carry, and place single block Tile Entities using only their empty hands.''' + [[dependencies.carryon]] #optional # the modid of the dependency modId="forge" #mandatory @@ -52,7 +28,6 @@ Carry On Mod. [[dependencies.carryon]] modId="minecraft" mandatory=true -# This version range declares a minimum of the current minecraft version up to but not including the next major version versionRange="[1.19.2,1.20)" ordering="NONE" side="BOTH" diff --git a/Forge/src/main/resources/META-INF/services/tschipp.carryon.platform.services.IGamestagePlatformHelper b/Forge/src/main/resources/META-INF/services/tschipp.carryon.platform.services.IGamestagePlatformHelper new file mode 100644 index 0000000..87eb56d --- /dev/null +++ b/Forge/src/main/resources/META-INF/services/tschipp.carryon.platform.services.IGamestagePlatformHelper @@ -0,0 +1 @@ +tschipp.carryon.platform.ForgeGamestagesHelper \ No newline at end of file diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 0000000..9c5872f --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,32 @@ +#!/usr/bin/env groovy + +pipeline { + agent any + tools { + jdk "jdk-17.0.1" + } + stages { + stage('Clean') { + steps { + withCredentials([file(credentialsId: 'mod_build_secrets', variable: 'ORG_GRADLE_PROJECT_secretFile')]) { + echo 'Cleaning Project' + sh 'chmod +x gradlew' + sh './gradlew clean' + } + } + } + stage('Build') { + steps { + withCredentials([file(credentialsId: 'mod_build_secrets', variable: 'ORG_GRADLE_PROJECT_secretFile')]) { + echo 'Building' + sh './gradlew build' + } + } + } + } + post { + always { + archive 'build/libs/**.jar' + } + } +} \ No newline at end of file diff --git a/LICENSE b/LICENSE index 0e259d4..02bbb60 100644 --- a/LICENSE +++ b/LICENSE @@ -1,121 +1,165 @@ -Creative Commons Legal Code + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 -CC0 1.0 Universal + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. - CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE - LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN - ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS - INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES - REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS - PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM - THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED - HEREUNDER. -Statement of Purpose + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. -The laws of most jurisdictions throughout the world automatically confer -exclusive Copyright and Related Rights (defined below) upon the creator -and subsequent owner(s) (each and all, an "owner") of an original work of -authorship and/or a database (each, a "Work"). + 0. Additional Definitions. -Certain owners wish to permanently relinquish those rights to a Work for -the purpose of contributing to a commons of creative, cultural and -scientific works ("Commons") that the public can reliably and without fear -of later claims of infringement build upon, modify, incorporate in other -works, reuse and redistribute as freely as possible in any form whatsoever -and for any purposes, including without limitation commercial purposes. -These owners may contribute to the Commons to promote the ideal of a free -culture and the further production of creative, cultural and scientific -works, or to gain reputation or greater distribution for their Work in -part through the use and efforts of others. + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. -For these and/or other purposes and motivations, and without any -expectation of additional consideration or compensation, the person -associating CC0 with a Work (the "Affirmer"), to the extent that he or she -is an owner of Copyright and Related Rights in the Work, voluntarily -elects to apply CC0 to the Work and publicly distribute the Work under its -terms, with knowledge of his or her Copyright and Related Rights in the -Work and the meaning and intended legal effect of CC0 on those rights. + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. -1. Copyright and Related Rights. A Work made available under CC0 may be -protected by copyright and related or neighboring rights ("Copyright and -Related Rights"). Copyright and Related Rights include, but are not -limited to, the following: + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. - i. the right to reproduce, adapt, distribute, perform, display, - communicate, and translate a Work; - ii. moral rights retained by the original author(s) and/or performer(s); -iii. publicity and privacy rights pertaining to a person's image or - likeness depicted in a Work; - iv. rights protecting against unfair competition in regards to a Work, - subject to the limitations in paragraph 4(a), below; - v. rights protecting the extraction, dissemination, use and reuse of data - in a Work; - vi. database rights (such as those arising under Directive 96/9/EC of the - European Parliament and of the Council of 11 March 1996 on the legal - protection of databases, and under any national implementation - thereof, including any amended or successor version of such - directive); and -vii. other similar, equivalent or corresponding rights throughout the - world based on applicable law or treaty, and any national - implementations thereof. + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". -2. Waiver. To the greatest extent permitted by, but not in contravention -of, applicable law, Affirmer hereby overtly, fully, permanently, -irrevocably and unconditionally waives, abandons, and surrenders all of -Affirmer's Copyright and Related Rights and associated claims and causes -of action, whether now known or unknown (including existing as well as -future claims and causes of action), in the Work (i) in all territories -worldwide, (ii) for the maximum duration provided by applicable law or -treaty (including future time extensions), (iii) in any current or future -medium and for any number of copies, and (iv) for any purpose whatsoever, -including without limitation commercial, advertising or promotional -purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each -member of the public at large and to the detriment of Affirmer's heirs and -successors, fully intending that such Waiver shall not be subject to -revocation, rescission, cancellation, termination, or any other legal or -equitable action to disrupt the quiet enjoyment of the Work by the public -as contemplated by Affirmer's express Statement of Purpose. + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. -3. Public License Fallback. Should any part of the Waiver for any reason -be judged legally invalid or ineffective under applicable law, then the -Waiver shall be preserved to the maximum extent permitted taking into -account Affirmer's express Statement of Purpose. In addition, to the -extent the Waiver is so judged Affirmer hereby grants to each affected -person a royalty-free, non transferable, non sublicensable, non exclusive, -irrevocable and unconditional license to exercise Affirmer's Copyright and -Related Rights in the Work (i) in all territories worldwide, (ii) for the -maximum duration provided by applicable law or treaty (including future -time extensions), (iii) in any current or future medium and for any number -of copies, and (iv) for any purpose whatsoever, including without -limitation commercial, advertising or promotional purposes (the -"License"). The License shall be deemed effective as of the date CC0 was -applied by Affirmer to the Work. Should any part of the License for any -reason be judged legally invalid or ineffective under applicable law, such -partial invalidity or ineffectiveness shall not invalidate the remainder -of the License, and in such case Affirmer hereby affirms that he or she -will not (i) exercise any of his or her remaining Copyright and Related -Rights in the Work or (ii) assert any associated claims and causes of -action with respect to the Work, in either case contrary to Affirmer's -express Statement of Purpose. + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. -4. Limitations and Disclaimers. + 1. Exception to Section 3 of the GNU GPL. - a. No trademark or patent rights held by Affirmer are waived, abandoned, - surrendered, licensed or otherwise affected by this document. - b. Affirmer offers the Work as-is and makes no representations or - warranties of any kind concerning the Work, express, implied, - statutory or otherwise, including without limitation warranties of - title, merchantability, fitness for a particular purpose, non - infringement, or the absence of latent or other defects, accuracy, or - the present or absence of errors, whether or not discoverable, all to - the greatest extent permissible under applicable law. - c. Affirmer disclaims responsibility for clearing rights of other persons - that may apply to the Work or any use thereof, including without - limitation any person's Copyright and Related Rights in the Work. - Further, Affirmer disclaims responsibility for obtaining any necessary - consents, permissions or other rights required for any use of the - Work. - d. Affirmer understands and acknowledges that Creative Commons is not a - party to this document and has no duty or obligation with respect to - this CC0 or use of the Work. + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. \ No newline at end of file diff --git a/README.md b/README.md index 1329c2c..07176f2 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,20 @@ -# MultiLoader Template +# Carry On [![](http://cf.way2muchnoise.eu/carry-on.svg)](https://minecraft.curseforge.com/projects/carry-on) [![](http://cf.way2muchnoise.eu/versions/carry-on.svg)](https://minecraft.curseforge.com/projects/carry-on) + +To use CarryOn in your projects, include this in your build.gradle: +``` +repositories { + maven { + url "https://maven.blamejared.com/" + } +} + +dependencies { + deobfCompile "tschipp.carryon:carryon-MCVERSION:MODVERSION" +} +``` +Make sure to replace `MCVERSION` and `MODVERSION` with the appropriate versions. + +# Development This project provides a Gradle project template that can compile mods for both Forge and Fabric using a common sourceset. This project does not require any third party libraries or dependencies. @@ -8,18 +24,12 @@ This project provides a Gradle project template that can compile mods for both F This guide will show how to import the MultiLoader Template into IntelliJ IDEA. The setup process is roughly equivalent to setting up Forge and Fabric independently and should be very familiar to anyone who has worked with their MDKs. 1. Clone or download this repository to your computer. -2. Configure the project by editing the `group`, `mod_name`, `mod_author`, and `mod_id` properties in the `gradle.properties` file. You will also need to change the `rootProject.name` property in `settings.gradle`. -3. Open the template's root folder as a new project in IDEA. This is the folder that contains this README file and the gradlew executable. -4. If your default JVM/JDK is not Java 17 you will encounter an error when opening the project. This error is fixed by going to `File > Settings > Build, Execution, Deployment > Build Tools > Gradle > Gradle JVM`and changing the value to a valid Java 17 JVM. You will also need to set the Project SDK to Java 17. This can be done by going to `File > Project Structure > Project SDK`. Once both have been set open the Gradle tab in IDEA and click the refresh button to reload the project. -5. Open the Gradle tab in IDEA if it has not already been opened. Navigate to `Your Project > Common > Tasks > vanilla gradle > decompile`. Run this task to decompile Minecraft. -6. Open the Gradle tab in IDEA if it has not already been opened. Navigate to `Your Project > Forge > Tasks > forgegradle runs > genIntellijRuns`. Run this task to set up run configurations for Forge. -7. Open your Run/Debug Configurations. Under the Application category there should now be options to run Forge and Fabric projects. Select one of the client options and try to run it. -8. Assuming you were able to run the game in step 7 your workspace should now be set up. +2. Open the template's root folder as a new project in IDEA. This is the folder that contains this README file and the gradlew executable. +3. If your default JVM/JDK is not Java 17 you will encounter an error when opening the project. This error is fixed by going to `File > Settings > Build, Execution, Deployment > Build Tools > Gradle > Gradle JVM`and changing the value to a valid Java 17 JVM. You will also need to set the Project SDK to Java 17. This can be done by going to `File > Project Structure > Project SDK`. Once both have been set open the Gradle tab in IDEA and click the refresh button to reload the project. +4. Open the Gradle tab in IDEA if it has not already been opened. Navigate to `Your Project > Common > Tasks > vanilla gradle > decompile`. Run this task to decompile Minecraft. +5. Open the Gradle tab in IDEA if it has not already been opened. Navigate to `Your Project > Forge > Tasks > forgegradle runs > genIntellijRuns`. Run this task to set up run configurations for Forge. +6. Open your Run/Debug Configurations. Under the Application category there should now be options to run Forge and Fabric projects. Select one of the client options and try to run it. +7. Assuming you were able to run the game in step 7 your workspace should now be set up. ### Eclipse While it is possible to use this template in Eclipse it is not recommended. During the development of this template multiple critical bugs and quirks related to Eclipse were found at nearly every level of the required build tools. While we continue to work with these tools to report and resolve issues support for projects like these are not there yet. For now Eclipse is considered unsupported by this project. The development cycle for build tools is notoriously slow so there are no ETAs available. - -## Development Guide -When using this template the majority of your mod is developed in the Common project. The Common project is compiled against the vanilla game and is used to hold code that is shared between the different loader-specific versions of your mod. The Common project has no knowledge or access to ModLoader specific code, apis, or concepts. Code that requires something from a specific loader must be done through the project that is specific to that loader, such as the Forge or Fabric project. - -Loader specific projects such as the Forge and Fabric project are used to load the Common project into the game. These projects also define code that is specific to that loader. Loader specific projects can access all of the code in the Common project. It is important to remember that the Common project can not access code from loader specific projects. diff --git a/build.gradle b/build.gradle index 98616ee..be08972 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ subprojects { jar { from(rootProject.file("LICENSE")) { - rename { "${it}_${mod_name}" } + rename { "${it}_${mod_id}" } } manifest { attributes([