9
0
mirror of https://github.com/Xiao-MoMi/craft-engine.git synced 2025-12-24 01:19:24 +00:00

Initial commit

This commit is contained in:
XiaoMoMi
2025-03-13 15:52:31 +08:00
commit 29276e92cc
3956 changed files with 108711 additions and 0 deletions

View File

@@ -0,0 +1,50 @@
plugins {
id("java-library")
id("com.gradleup.shadow") version "9.0.0-beta6"
id("io.papermc.paperweight.userdev") version "2.0.0-beta.14"
}
repositories {
mavenCentral()
maven("https://maven.fabricmc.net/")
maven("https://oss.sonatype.org/content/groups/public/")
maven("https://repo.papermc.io/repository/maven-public/")
maven("https://repo.spongepowered.org/maven/")
}
dependencies {
implementation(project(":shared"))
remapper("net.fabricmc:tiny-remapper:0.10.4:fat")
paperweightDevelopmentBundle("io.papermc.paper:dev-bundle:1.21.4-R0.1-SNAPSHOT")
compileOnly("space.vectrix.ignite:ignite-api:1.1.0")
compileOnly("net.fabricmc:sponge-mixin:0.15.2+mixin.0.8.7")
compileOnly("io.github.llamalad7:mixinextras-common:0.4.1")
}
java {
sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
}
tasks.withType<JavaCompile> {
options.encoding = "UTF-8"
options.release.set(21)
dependsOn(tasks.clean)
}
paperweight.reobfArtifactConfiguration = io.papermc.paperweight.userdev.ReobfArtifactConfiguration.MOJANG_PRODUCTION
artifacts {
archives(tasks.shadowJar)
}
tasks {
shadowJar {
archiveClassifier = ""
archiveFileName = "${rootProject.name}-bukkit-mod-${rootProject.properties["project_version"]}.jar"
destinationDirectory.set(file("$rootDir/target"))
}
}

View File

@@ -0,0 +1,212 @@
package net.momirealms.craftengine.mod;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.server.level.ServerChunkCache;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.RandomSource;
import net.minecraft.world.entity.item.FallingBlockEntity;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.ScheduledTickAccess;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.BonemealableBlock;
import net.minecraft.world.level.block.Fallable;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.VoxelShape;
import net.momirealms.craftengine.mod.util.NoteBlockUtils;
import net.momirealms.craftengine.shared.ObjectHolder;
import net.momirealms.craftengine.shared.block.*;
import org.jetbrains.annotations.NotNull;
public class CraftEngineBlock extends Block implements BehaviorHolder, ShapeHolder, NoteBlockIndicator, Fallable, BonemealableBlock {
private static final PaperWeightStoneBlockShape STONE = new PaperWeightStoneBlockShape(Blocks.STONE.defaultBlockState());
private boolean isNoteBlock;
public ObjectHolder<BlockBehavior> behaviorHolder;
public ObjectHolder<BlockShape> shapeHolder;
public boolean isClientSideNoteBlock;
public CraftEngineBlock(Properties properties) {
super(properties);
this.behaviorHolder = new ObjectHolder<>(EmptyBlockBehavior.INSTANCE);
this.shapeHolder = new ObjectHolder<>(STONE);
}
public void setNoteBlock(boolean noteBlock) {
isNoteBlock = noteBlock;
}
@Override
public ObjectHolder<BlockBehavior> getBehaviorHolder() {
return behaviorHolder;
}
@Override
public ObjectHolder<BlockShape> getShapeHolder() {
return shapeHolder;
}
@Override
public boolean isNoteBlock() {
return isClientSideNoteBlock;
}
@Override
protected @NotNull VoxelShape getShape(@NotNull BlockState state, @NotNull BlockGetter level, @NotNull BlockPos pos, @NotNull CollisionContext context) {
try {
return (VoxelShape) shapeHolder.value().getShape(this, new Object[]{state, level, pos, context});
} catch (Exception e) {
e.printStackTrace();
return super.getShape(state, level, pos, context);
}
}
@Override
protected void tick(@NotNull BlockState state, @NotNull ServerLevel level, @NotNull BlockPos pos, @NotNull RandomSource random) {
try {
behaviorHolder.value().tick(this, new Object[]{state, level, pos, random}, () -> {
super.tick(state, level, pos, random);
return null;
});
} catch (Exception e) {
e.printStackTrace();
super.tick(state, level, pos, random);
}
}
@Override
protected void randomTick(@NotNull BlockState state, @NotNull ServerLevel level, @NotNull BlockPos pos, @NotNull RandomSource random) {
try {
behaviorHolder.value().randomTick(this, new Object[]{state, level, pos, random}, () -> {
super.randomTick(state, level, pos, random);
return null;
});
} catch (Exception e) {
e.printStackTrace();
super.randomTick(state, level, pos, random);
}
}
@Override
protected void onPlace(@NotNull BlockState state, @NotNull Level level, @NotNull BlockPos pos, @NotNull BlockState oldState, boolean movedByPiston) {
try {
behaviorHolder.value().onPlace(this, new Object[]{state, level, pos, oldState, movedByPiston}, () -> {
super.onPlace(state, level, pos, oldState, movedByPiston);
return null;
});
} catch (Exception e) {
e.printStackTrace();
super.onPlace(state, level, pos, oldState, movedByPiston);
}
}
@Override
public void onBrokenAfterFall(@NotNull Level level, @NotNull BlockPos pos, @NotNull FallingBlockEntity fallingBlock) {
try {
behaviorHolder.value().onBrokenAfterFall(this, new Object[]{level, pos, fallingBlock}, () -> {
Fallable.super.onBrokenAfterFall(level, pos, fallingBlock);
return null;
});
} catch (Exception e) {
e.printStackTrace();
Fallable.super.onBrokenAfterFall(level, pos, fallingBlock);
}
}
@Override
protected boolean canSurvive(@NotNull BlockState state, @NotNull LevelReader level, @NotNull BlockPos pos) {
try {
return behaviorHolder.value().canSurvive(this, new Object[]{state, level, pos}, () -> super.canSurvive(state, level, pos));
} catch (Exception e) {
e.printStackTrace();
return super.canSurvive(state, level, pos);
}
}
@Override
protected BlockState updateShape(@NotNull BlockState state,
@NotNull LevelReader level,
@NotNull ScheduledTickAccess scheduledTickAccess,
@NotNull BlockPos pos,
@NotNull Direction direction,
@NotNull BlockPos neighborPos,
@NotNull BlockState neighborState,
@NotNull RandomSource random) {
try {
if (isNoteBlock && level instanceof ServerLevel serverLevel) {
startNoteBlockChain(direction, serverLevel, pos);
}
return (BlockState) behaviorHolder.value().updateShape(this, new Object[]{state, level, scheduledTickAccess, pos, direction, neighborPos, neighborState, random}, () -> super.updateShape(state, level, scheduledTickAccess, pos, direction, neighborPos, neighborState, random));
} catch (Exception e) {
e.printStackTrace();
return super.updateShape(state, level, scheduledTickAccess, pos, direction, neighborPos, neighborState, random);
}
}
private static void startNoteBlockChain(Direction direction, ServerLevel serverLevel, BlockPos blockPos) {
int id = direction.get3DDataValue();
// Y axis
if (id == 0 || id == 1) {
ServerChunkCache chunkSource = serverLevel.chunkSource;
chunkSource.blockChanged(blockPos);
if (id == 1) {
noteBlockChainUpdate(serverLevel, chunkSource, Direction.DOWN, blockPos, 0);
} else {
noteBlockChainUpdate(serverLevel, chunkSource, Direction.UP, blockPos, 0);
}
}
}
public static void noteBlockChainUpdate(ServerLevel level, ServerChunkCache chunkSource, Direction direction, BlockPos blockPos, int times) {
if (times >= CraftEnginePlugin.maxChainUpdate()) return;
BlockPos relativePos = blockPos.relative(direction);
BlockState state = level.getBlockState(relativePos);
if (NoteBlockUtils.CLIENT_SIDE_NOTE_BLOCKS.contains(state)) {
chunkSource.blockChanged(relativePos);
noteBlockChainUpdate(level, chunkSource, direction, relativePos, times+1);
}
}
@Override
protected @NotNull FluidState getFluidState(@NotNull BlockState state) {
try {
return (FluidState) behaviorHolder.value().getFluidState(this, new Object[]{state}, () -> super.getFluidState(state));
} catch (Exception e) {
e.printStackTrace();
return super.getFluidState(state);
}
}
@Override
public boolean isValidBonemealTarget(@NotNull LevelReader levelReader, @NotNull BlockPos blockPos, @NotNull BlockState blockState) {
try {
return behaviorHolder.value().isValidBoneMealTarget(this, new Object[]{levelReader, blockPos, blockState});
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
@Override
public boolean isBonemealSuccess(@NotNull Level level, @NotNull RandomSource randomSource, @NotNull BlockPos blockPos, @NotNull BlockState blockState) {
try {
return behaviorHolder.value().isBoneMealSuccess(this, new Object[]{level, randomSource, blockPos, blockState});
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
@Override
public void performBonemeal(@NotNull ServerLevel serverLevel, @NotNull RandomSource randomSource, @NotNull BlockPos blockPos, @NotNull BlockState blockState) {
try {
behaviorHolder.value().performBoneMeal(this, new Object[]{serverLevel, randomSource, blockPos, blockState});
} catch (Exception e) {
e.printStackTrace();
}
}
}

View File

@@ -0,0 +1,103 @@
package net.momirealms.craftengine.mod;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.objectweb.asm.tree.ClassNode;
import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin;
import org.spongepowered.asm.mixin.extensibility.IMixinInfo;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.CodeSource;
import java.security.ProtectionDomain;
import java.util.List;
import java.util.Set;
import java.util.logging.Logger;
public final class CraftEnginePlugin implements IMixinConfigPlugin {
public static final Logger LOGGER = Logger.getLogger(CraftEnginePlugin.class.getName());
private static int vanillaRegistrySize;
private static boolean isSuccessfullyRegistered = false;
private static int maxChainUpdate = 32;
public static void setVanillaRegistrySize(int vanillaRegistrySize) {
CraftEnginePlugin.vanillaRegistrySize = vanillaRegistrySize;
}
public static void setIsSuccessfullyRegistered(boolean isSuccessfullyRegistered) {
CraftEnginePlugin.isSuccessfullyRegistered = isSuccessfullyRegistered;
}
public static int maxChainUpdate() {
return maxChainUpdate;
}
public static void setMaxChainUpdate(int maxChainUpdate) {
CraftEnginePlugin.maxChainUpdate = maxChainUpdate;
}
@Override
public void onLoad(final @NotNull String mixinPackage) {
}
@Override
public @Nullable String getRefMapperConfig() {
return null;
}
@Override
public boolean shouldApplyMixin(@NotNull String targetClassName,
@NotNull String mixinClassName) {
return true;
}
@Override
public void acceptTargets(@NotNull Set<String> myTargets,
@NotNull Set<String> otherTargets) {
}
@Override
public @Nullable List<String> getMixins() {
return null;
}
@Override
public void preApply(@NotNull String targetClassName,
@NotNull ClassNode targetClass,
@NotNull String mixinClassName,
@NotNull IMixinInfo mixinInfo) {
}
@Override
public void postApply(@NotNull String targetClassName,
@NotNull ClassNode targetClass,
@NotNull String mixinClassName,
@NotNull IMixinInfo mixinInfo) {
}
public static Path getPluginFolderPath() {
ProtectionDomain protectionDomain = CraftEnginePlugin.class.getProtectionDomain();
CodeSource codeSource = protectionDomain.getCodeSource();
URL jarUrl = codeSource.getLocation();
try {
return Paths.get(jarUrl.toURI()).getParent().getParent().resolve("plugins");
} catch (URISyntaxException e) {
e.printStackTrace();
}
return null;
}
public static Path getCraftEngineMappingsPath() {
return getPluginFolderPath()
.resolve("CraftEngine")
.resolve("mappings.yml");
}
public static Path getCraftEngineAdditionalBlocksPath() {
return getPluginFolderPath()
.resolve("CraftEngine")
.resolve("additional-real-blocks.yml");
}
}

View File

@@ -0,0 +1,20 @@
package net.momirealms.craftengine.mod;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.momirealms.craftengine.shared.block.BlockShape;
public class PaperWeightStoneBlockShape implements BlockShape {
private final BlockState rawBlockState;
public PaperWeightStoneBlockShape(BlockState rawBlockState) {
this.rawBlockState = rawBlockState;
}
@Override
public Object getShape(Object thisObj, Object[] args) {
return rawBlockState.getShape((BlockGetter) args[1], (BlockPos) args[2], (CollisionContext) args[3]);
}
}

View File

@@ -0,0 +1,131 @@
package net.momirealms.craftengine.mod.mixin;
import com.mojang.brigadier.StringReader;
import net.minecraft.commands.arguments.blocks.BlockStateParser;
import net.minecraft.core.Registry;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.block.state.BlockState;
import net.momirealms.craftengine.mod.CraftEngineBlock;
import net.momirealms.craftengine.mod.CraftEnginePlugin;
import net.momirealms.craftengine.mod.util.NoteBlockUtils;
import org.bukkit.configuration.file.YamlConfiguration;
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.CallbackInfo;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
@Mixin(value = Blocks.class)
public abstract class MixinBlocks {
@Inject(method = "<clinit>", at = @At("RETURN"))
private static void onBlocksInit(CallbackInfo ci) {
CraftEnginePlugin.setVanillaRegistrySize(Block.BLOCK_STATE_REGISTRY.size());
ResourceLocation noteBlock = ResourceLocation.fromNamespaceAndPath("minecraft", "note_block");
Map<ResourceLocation, Integer> map = loadMappingsAndAdditionalBlocks();
for (Map.Entry<ResourceLocation, Integer> entry : map.entrySet()) {
ResourceLocation replacedBlockId = entry.getKey();
boolean isNoteBlock = replacedBlockId.equals(noteBlock);
Block replacedBlock = BuiltInRegistries.BLOCK.getValue(replacedBlockId);
for (int i = 0; i < entry.getValue(); i++) {
ResourceLocation location = ResourceLocation.fromNamespaceAndPath("craftengine", replacedBlockId.getPath() + "_" + i);
ResourceKey<Block> resourceKey = ResourceKey.create(Registries.BLOCK, location);
BlockBehaviour.Properties properties = BlockBehaviour.Properties.of()
.setId(resourceKey);
if (!replacedBlock.hasCollision) {
properties.noCollission();
}
CraftEngineBlock block = new CraftEngineBlock(properties);
if (isNoteBlock) {
block.setNoteBlock(true);
NoteBlockUtils.CLIENT_SIDE_NOTE_BLOCKS.add(block.defaultBlockState());
}
Registry.register(BuiltInRegistries.BLOCK, location, block);
Block.BLOCK_STATE_REGISTRY.add(block.defaultBlockState());
}
}
NoteBlockUtils.CLIENT_SIDE_NOTE_BLOCKS.addAll(Blocks.NOTE_BLOCK.getStateDefinition().getPossibleStates());
if (!map.isEmpty()) {
CraftEnginePlugin.setIsSuccessfullyRegistered(true);
}
}
private static Map<ResourceLocation, Integer> loadMappingsAndAdditionalBlocks() {
Path mappingPath = CraftEnginePlugin.getCraftEngineMappingsPath();
if (!Files.exists(mappingPath)) return Map.of();
YamlConfiguration mappings = YamlConfiguration.loadConfiguration(mappingPath.toFile());
Map<String, String> blockStateMappings = loadBlockStateMappings(mappings);
validateBlockStateMappings(blockStateMappings);
Map<ResourceLocation, Integer> blockTypeCounter = new LinkedHashMap<>();
Map<Integer, Integer> appearanceMapper = new HashMap<>();
for (Map.Entry<String, String> entry : blockStateMappings.entrySet()) {
processBlockStateMapping(entry, appearanceMapper, blockTypeCounter);
}
YamlConfiguration additionalYaml = YamlConfiguration.loadConfiguration(CraftEnginePlugin.getCraftEngineAdditionalBlocksPath().toFile());
return buildRegisteredRealBlockSlots(blockTypeCounter, additionalYaml);
}
private static Map<String, String> loadBlockStateMappings(YamlConfiguration mappings) {
Map<String, String> blockStateMappings = new LinkedHashMap<>();
for (Map.Entry<String, Object> entry : mappings.getValues(false).entrySet()) {
if (entry.getValue() instanceof String afterValue) {
blockStateMappings.put(entry.getKey(), afterValue);
}
}
return blockStateMappings;
}
private static void validateBlockStateMappings(Map<String, String> blockStateMappings) {
Map<String, String> temp = new HashMap<>(blockStateMappings);
for (Map.Entry<String, String> entry : temp.entrySet()) {
String state = entry.getValue();
blockStateMappings.remove(state);
}
}
private static LinkedHashMap<ResourceLocation, Integer> buildRegisteredRealBlockSlots(Map<ResourceLocation, Integer> counter, YamlConfiguration additionalYaml) {
LinkedHashMap<ResourceLocation, Integer> map = new LinkedHashMap<>();
for (Map.Entry<ResourceLocation, Integer> entry : counter.entrySet()) {
String id = entry.getKey().toString();
int additionalStates = additionalYaml.getInt(id, 0);
int internalIds = entry.getValue() + additionalStates;
map.put(entry.getKey(), internalIds);
}
return map;
}
private static void processBlockStateMapping(Map.Entry<String, String> entry, Map<Integer, Integer> mapper, Map<ResourceLocation, Integer> counter) {
BlockState before = createBlockData(entry.getKey());
BlockState after = createBlockData(entry.getValue());
if (before == null || after == null) return;
int beforeId = Block.BLOCK_STATE_REGISTRY.getId(before);
int afterId = Block.BLOCK_STATE_REGISTRY.getId(after);
Integer previous = mapper.put(beforeId, afterId);
if (previous == null) {
counter.compute(BuiltInRegistries.BLOCK.getKey(before.getBlock()), (k, count) -> count == null ? 1 : count + 1);
}
}
private static BlockState createBlockData(String blockState) {
try {
StringReader reader = new StringReader(blockState);
BlockStateParser.BlockResult arg = BlockStateParser.parseForBlock(BuiltInRegistries.BLOCK, reader, false);
return arg.blockState();
} catch (Exception e) {
return null;
}
}
}

View File

@@ -0,0 +1,11 @@
package net.momirealms.craftengine.mod.util;
import net.minecraft.world.level.block.state.BlockState;
import java.util.HashSet;
import java.util.Set;
public class NoteBlockUtils {
public static final Set<BlockState> CLIENT_SIDE_NOTE_BLOCKS = new HashSet<>();
}

View File

@@ -0,0 +1,7 @@
{
"id": "craftengine",
"version": "${project_version}",
"mixins": [
"mixins.craftengine.json"
]
}

View File

@@ -0,0 +1,11 @@
{
"required": true,
"minVersion": "0.8.7",
"package": "net.momirealms.craftengine.mod.mixin",
"plugin": "net.momirealms.craftengine.mod.CraftEnginePlugin",
"target": "@env(DEFAULT)",
"compatibilityLevel": "JAVA_21",
"server": [
"MixinBlocks"
]
}