Compare commits

...

3 Commits

24 changed files with 356 additions and 102 deletions

2
.gitignore vendored
View File

@ -36,3 +36,5 @@ loom-cache/
__pycache__/ __pycache__/
*.pyc *.pyc
run-*/

View File

@ -25,3 +25,21 @@ java {
languageVersion = JavaLanguageVersion.of(17) languageVersion = JavaLanguageVersion.of(17)
} }
} }
loom {
runs {
server {
server()
runDir "run-server"
}
client1 {
client()
runDir "run-1"
}
client2 {
client()
runDir "run-2"
}
}
}

261
chipper
View File

@ -1,119 +1,226 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Do NOT use `set -e` with Gradle
set -u set -u
RUN_DIR="run"
OPTIONS_SRC="./config/options.txt"
OPTIONS_DST="$RUN_DIR/options.txt"
GRADLE="./gradlew" GRADLE="./gradlew"
GRADLE_FLAGS=( GRADLE_BASE_FLAGS=(
"--console=plain" "--console=plain"
"--configuration-cache" "--no-daemon"
"-Dfabric.development=true"
"-Dminecraft.api.env=dev"
"-Dminecraft.session.disable=true"
) )
# ---------- helpers ---------- OPTIONS_SRC="./config/options.txt"
ROLE=""
ROLE_INDEX=""
PASSTHROUGH=()
# ---------- utils ----------
rand() { shuf -i 0-$(($1 - 1)) -n 1; }
parse_args() {
local cleaned=()
for ((i=0; i<${#ARGS[@]}; i++)); do
case "${ARGS[$i]}" in
-s|--server)
ROLE="server"
;;
-p1|--player1)
ROLE="client"
ROLE_INDEX=1
;;
-p2|--player2)
ROLE="client"
ROLE_INDEX=2
;;
--)
PASSTHROUGH=( "${ARGS[@]:$((i+1))}" )
break
;;
*)
cleaned+=( "${ARGS[$i]}" )
;;
esac
done
ARGS=( "${cleaned[@]}" )
}
# ---------- run-dir helpers ----------
prepare_run_dir() {
local dir="$1"
rm -rf "$dir"
mkdir -p "$dir"
}
inject_options() { inject_options() {
local dir="$1"
# options.txt
if [[ -f "$OPTIONS_SRC" ]]; then if [[ -f "$OPTIONS_SRC" ]]; then
echo "[*] Injecting options.txt..." cp "$OPTIONS_SRC" "$dir/options.txt"
cp "$OPTIONS_SRC" "$OPTIONS_DST" fi
else
echo "[!] Warning: $OPTIONS_SRC not found, skipping injection" # servers.dat (client server list)
if [[ -f "./config/servers.dat" ]]; then
cp "./config/servers.dat" "$dir/servers.dat"
fi fi
} }
ensure_run_dir() { ensure_server_files() {
if [[ ! -d "$RUN_DIR" ]]; then local dir="$1"
echo "[*] Creating run directory..."
mkdir -p "$RUN_DIR" [[ -f "$dir/eula.txt" ]] || echo "eula=true" > "$dir/eula.txt"
if [[ ! -f "$dir/server.properties" ]]; then
cat > "$dir/server.properties" <<EOF
online-mode=false
server-port=25565
level-name=world
enable-command-block=true
EOF
fi fi
} }
# ---------- actions ---------- # ---------- squirrel ----------
header() {
local action="$1"
local squirrels=(
' /\_/\
( o.o )
> ^ <'
' /\_/\
( -.- )
o_(")(")'
' /\_/\
( @.@ )
> ~ <'
)
clean() {
clear clear
echo "[*] Running gradle clean..." echo "${squirrels[$(rand ${#squirrels[@]})]}"
$GRADLE clean "${GRADLE_FLAGS[@]}" echo
case "$action" in
run) echo "🐿️ running chipper" ;;
clean) echo "🐿️ cleaning workspace" ;;
build) echo "🐿️ building project" ;;
tree) echo "🐿️ observing project tree" ;;
*) echo "🐿️ thinking..." ;;
esac
echo
} }
build() { # ---------- commands ----------
clear
ensure_run_dir cmd_run() {
inject_options header run
echo "[*] Running client..." case "$ROLE" in
$GRADLE runClient "${GRADLE_FLAGS[@]}" server)
run_dir="run-server"
prepare_run_dir "$run_dir"
inject_options "$run_dir"
ensure_server_files "$run_dir"
echo "🖥️🐿️ starting dedicated server"
exec "$GRADLE" runServer "${GRADLE_BASE_FLAGS[@]}" "${PASSTHROUGH[@]}"
;;
client)
run_dir="run-$ROLE_INDEX"
task="runClient$ROLE_INDEX"
prepare_run_dir "$run_dir"
inject_options "$run_dir"
echo "🎮🐿️ starting client $ROLE_INDEX"
exec "$GRADLE" "$task" "${GRADLE_BASE_FLAGS[@]}" "${PASSTHROUGH[@]}"
;;
*)
echo "❌🐿️ no role selected"
echo "👉🐿️ use one of:"
echo " --server"
echo " --player1"
echo " --player2"
exit 1
;;
esac
} }
show_tree() { cmd_clean() {
clear header clean
echo "[*] Project tree (respecting .gitignore):" "$GRADLE" clean "${GRADLE_BASE_FLAGS[@]}" "${PASSTHROUGH[@]}"
}
cmd_build() {
header build
"$GRADLE" build "${GRADLE_BASE_FLAGS[@]}" "${PASSTHROUGH[@]}"
}
cmd_tree() {
header tree
tree --gitignore tree --gitignore
} }
ship() { cmd_help() {
clear
echo "[*] Building project..."
$GRADLE build "${GRADLE_FLAGS[@]}"
echo "[+] Build ready to ship."
}
# ---------- help ----------
help_menu() {
cat <<'EOF' cat <<'EOF'
🐿️ S Q U I R R E L B U I L D T O O L 🐿️ /\_/\
( o.o )
> ^ <
🐿️ C H I P P E R
Usage: Usage:
./chipper [option] ./chipper run [role] [-- passthrough]
Options: Roles:
--clean Run gradle clean -s, --server Start ONLY server
--build Run client (fast path) -p1, --player1 Start ONLY client 1
--tree Show project tree only -p2, --player2 Start ONLY client 2
--ship Build project
--help Show this help menu Other commands:
clean Gradle clean
build Gradle build
tree Show project tree
help Show this menu
Examples:
./chipper run --server
./chipper run --player1
./chipper run --player2
./chipper run --player1 -- --debug
./chipper build
./chipper tree
Notes: Notes:
- Uses Gradle configuration cache - No legacy player spawning
- Plain console output (no fake progress) - Each role has its own run directory
- Safe to Ctrl+C after EXECUTION starts - options.txt injected per run dir
- Everything after `--` is forwarded
The squirrel believes in you. - The squirrel is mandatory
EOF EOF
} }
# ---------- argument parsing ---------- # ---------- entry ----------
if [[ $# -eq 0 ]]; then ARGS=( "$@" )
help_menu parse_args
exit 0
fi
case "$1" in case "${ARGS[0]:-help}" in
--clean) run) cmd_run ;;
clean clean) cmd_clean ;;
;; build) cmd_build ;;
--build) tree) cmd_tree ;;
build help|-h) cmd_help ;;
;;
--tree)
show_tree
;;
--ship)
ship
;;
--help|-h)
help_menu
;;
*) *)
echo "[!] Unknown option: $1" echo "[!] Unknown command: ${ARGS[0]}"
echo "" cmd_help
help_menu
exit 1 exit 1
;; ;;
esac esac

View File

@ -72,8 +72,8 @@ tutorialStep:movement
mouseWheelSensitivity:1.0 mouseWheelSensitivity:1.0
rawMouseInput:true rawMouseInput:true
glDebugVerbosity:1 glDebugVerbosity:1
skipMultiplayerWarning:false skipMultiplayerWarning:true
skipRealms32bitWarning:false skipRealms32bitWarning:true
hideMatchedNames:true hideMatchedNames:true
joinedFirstServer:false joinedFirstServer:false
hideBundleTutorial:false hideBundleTutorial:false

BIN
config/servers.dat Normal file

Binary file not shown.

View File

@ -50,6 +50,9 @@ public final class ModTooltips {
if (stack.isOf(ModItems.MEP_MILK)) if (stack.isOf(ModItems.MEP_MILK))
lines.add(Text.translatable("tooltip.chipi.mep_milk")); lines.add(Text.translatable("tooltip.chipi.mep_milk"));
if (stack.isOf(ModItems.PLAYER_MILK))
lines.add(Text.translatable("tooltip.chipi.player_milk"));
// ===== ARMOR ===== // ===== ARMOR =====
if (stack.isOf(ModItems.CHIPPER_HELMET)) if (stack.isOf(ModItems.CHIPPER_HELMET))
lines.add(Text.translatable("tooltip.chipi.chipper_helmet")); lines.add(Text.translatable("tooltip.chipi.chipper_helmet"));

View File

@ -15,4 +15,10 @@ public class ModFoodComponents {
new FoodComponent.Builder() new FoodComponent.Builder()
.alwaysEdible() .alwaysEdible()
.build(); .build();
public static final FoodComponent PLAYER_MILK =
new FoodComponent.Builder()
.alwaysEdible()
.build();
} }

View File

@ -35,6 +35,7 @@ public class ModItemGroups {
// Food // Food
entries.add(ModItems.MEP_MILK); entries.add(ModItems.MEP_MILK);
entries.add(ModItems.NUT); entries.add(ModItems.NUT);
entries.add(ModItems.PLAYER_MILK);
// Armor // Armor
entries.add(ModItems.CHIPPER_HELMET); entries.add(ModItems.CHIPPER_HELMET);

View File

@ -89,6 +89,17 @@ public class ModItems {
new MepMilkItem(new Item.Settings().maxCount(1).recipeRemainder(Items.BUCKET).food(ModFoodComponents.MEP_MILK)) new MepMilkItem(new Item.Settings().maxCount(1).recipeRemainder(Items.BUCKET).food(ModFoodComponents.MEP_MILK))
); );
public static final Item PLAYER_MILK = Registry.register(
Registries.ITEM,
new Identifier(ChipiMod.MOD_ID, "player_milk"),
new PlayerMilkItem(
new Item.Settings()
.maxCount(1)
.recipeRemainder(Items.BUCKET)
.food(ModFoodComponents.PLAYER_MILK)
)
);
// ===== ARMOR ===== // ===== ARMOR =====
public static final Item CHIPPER_HELMET = Registry.register( public static final Item CHIPPER_HELMET = Registry.register(

View File

@ -0,0 +1,51 @@
package net.Chipperfluff.chipi.item;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.util.UseAction;
import net.minecraft.world.World;
import net.minecraft.entity.effect.StatusEffectInstance;
import net.Chipperfluff.chipi.effect.ModEffects;
import net.Chipperfluff.chipi.sound.ModSounds;
import net.minecraft.sound.SoundCategory;
public class PlayerMilkItem extends Item {
public PlayerMilkItem(Settings settings) {
super(settings);
}
@Override
public UseAction getUseAction(ItemStack stack) {
return UseAction.DRINK;
}
@Override
public int getMaxUseTime(ItemStack stack) {
return 32;
}
@Override
public ItemStack finishUsing(ItemStack stack, World world, LivingEntity user) {
if (!world.isClient && user instanceof PlayerEntity player) {
world.playSound(
null,
player.getBlockPos(),
ModSounds.PLAYER_MILK_SONG,
SoundCategory.PLAYERS,
1.0f,
1.0f
);
}
if (user instanceof PlayerEntity player && !player.getAbilities().creativeMode) {
return new ItemStack(Items.BUCKET);
}
return stack;
}
}

View File

@ -19,6 +19,17 @@ import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.world.GameRules; import net.minecraft.world.GameRules;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.fabricmc.fabric.api.biome.v1.BiomeModifications;
import net.fabricmc.fabric.api.biome.v1.BiomeSelectors;
import net.minecraft.world.gen.GenerationStep;
import net.fabricmc.fabric.api.event.player.UseEntityCallback;
import net.minecraft.util.ActionResult;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.sound.SoundCategory;
import net.minecraft.sound.SoundEvents;
import net.Chipperfluff.chipi.item.ModItems;
public final class ChipiServerEvents { public final class ChipiServerEvents {
@ -33,6 +44,44 @@ public final class ChipiServerEvents {
private ChipiServerEvents() {} private ChipiServerEvents() {}
public static void register() { public static void register() {
BiomeModifications.addFeature(
BiomeSelectors.foundInOverworld(),
GenerationStep.Feature.UNDERGROUND_ORES,
RegistryKey.of(
RegistryKeys.PLACED_FEATURE,
new Identifier("chipi", "chipper_ore")
)
);
// ===== PLAYER MILK INTERACTION =====
UseEntityCallback.EVENT.register((player, world, hand, entity, hitResult) -> {
if (world.isClient()) return ActionResult.PASS;
if (!(entity instanceof PlayerEntity target)) return ActionResult.PASS;
if (target == player) return ActionResult.FAIL;
ItemStack stack = player.getStackInHand(hand);
if (!stack.isOf(Items.BUCKET)) return ActionResult.PASS;
stack.decrement(1);
ItemStack milk = new ItemStack(ModItems.PLAYER_MILK);
if (!player.getInventory().insertStack(milk)) {
player.dropItem(milk, false);
}
world.playSound(
null,
player.getBlockPos(),
SoundEvents.ENTITY_COW_MILK,
SoundCategory.PLAYERS,
1.0f,
1.1f
);
return ActionResult.SUCCESS;
});
ServerLifecycleEvents.SERVER_STARTED.register(server -> SERVER = server); ServerLifecycleEvents.SERVER_STARTED.register(server -> SERVER = server);
ServerTickEvents.END_SERVER_TICK.register(ChipiServerEvents::tickPlayers); ServerTickEvents.END_SERVER_TICK.register(ChipiServerEvents::tickPlayers);
ServerTickEvents.END_WORLD_TICK.register(ChipiServerEvents::handleVoidFailsafe); ServerTickEvents.END_WORLD_TICK.register(ChipiServerEvents::handleVoidFailsafe);

View File

@ -9,6 +9,7 @@ import net.minecraft.util.Identifier;
public class ModSounds { public class ModSounds {
public static final SoundEvent MEP_MILK = register("entity.mep.milk"); public static final SoundEvent MEP_MILK = register("entity.mep.milk");
public static final SoundEvent PLAYER_MILK_SONG = register("player_milk_song");
private static SoundEvent register(String id) { private static SoundEvent register(String id) {
Identifier identifier = new Identifier(ChipiMod.MOD_ID, id); Identifier identifier = new Identifier(ChipiMod.MOD_ID, id);

View File

@ -2,18 +2,14 @@
"death.attack.void_block": "§8%1$s stepped beyond safety — the Outside answered.", "death.attack.void_block": "§8%1$s stepped beyond safety — the Outside answered.",
"death.attack.chipi.void_block_fire": "§c%1$s tried to save themselves.§r §8It got worse.", "death.attack.chipi.void_block_fire": "§c%1$s tried to save themselves.§r §8It got worse.",
"itemGroup.chipi.chipi": "Chipi",
"effect.chipi.chipi_blessing": "Chipi's Blessing",
"entity.chipi.mep": "Merp :3",
"block.chipi.void_block": "Void Block", "block.chipi.void_block": "Void Block",
"block.chipi.chipper_frame": "Chipper Frame", "block.chipi.chipper_frame": "Chipper Frame",
"block.chipi.chipper_portal": "Chipper Portal", "block.chipi.chipper_portal": "Chipper Portal",
"block.chipi.chipper_ore": "Chipper Ore", "block.chipi.chipper_ore": "Chipper Ore",
"block.chipi.chipper_alloy_block": "Chipper Alloy Block", "block.chipi.chipper_alloy_block": "Chipper Alloy Block",
"itemGroup.chipi.chipi": "Chipi",
"item.chipi.void_block": "Void Block", "item.chipi.void_block": "Void Block",
"item.chipi.chipper_frame": "Chipper Frame", "item.chipi.chipper_frame": "Chipper Frame",
"item.chipi.chipper_portal": "Chipper Portal", "item.chipi.chipper_portal": "Chipper Portal",
@ -24,9 +20,6 @@
"item.chipi.chipper_alloy": "Chipper Alloy", "item.chipi.chipper_alloy": "Chipper Alloy",
"item.chipi.mep_spawn_egg": "Mep Spawn Egg", "item.chipi.mep_spawn_egg": "Mep Spawn Egg",
"item.chipi.nut": "Nut",
"item.chipi.mep_milk": "Mep Milk",
"item.chipi.chipper_helmet": "Chipper Helmet", "item.chipi.chipper_helmet": "Chipper Helmet",
"item.chipi.chipper_chestplate": "Chipper Chestplate", "item.chipi.chipper_chestplate": "Chipper Chestplate",
"item.chipi.chipper_leggings": "Chipper Leggings", "item.chipi.chipper_leggings": "Chipper Leggings",
@ -38,19 +31,20 @@
"item.chipi.chipper_shovel": "Chipper Shovel", "item.chipi.chipper_shovel": "Chipper Shovel",
"item.chipi.chipper_hoe": "Chipper Hoe", "item.chipi.chipper_hoe": "Chipper Hoe",
"item.chipi.nut": "Nut",
"item.chipi.mep_milk": "Mep Milk",
"item.chipi.player_milk": "Player Milk",
"tooltip.chipi.void_block": "§8It hums quietly.§r §7You are not supposed to notice that.", "tooltip.chipi.void_block": "§8It hums quietly.§r §7You are not supposed to notice that.",
"tooltip.chipi.chipper_frame": "§7Built to hold a mistake.§r §8It is doing its best.", "tooltip.chipi.chipper_frame": "§7Built to hold a mistake.§r §8It is doing its best.",
"tooltip.chipi.chipper_portal": "§5Something on the other side noticed you.§r §8It did not look away.", "tooltip.chipi.chipper_portal": "§5Something on the other side noticed you.§r §8It did not look away.",
"tooltip.chipi.chipper_ore": "§7Common.§r §8Suspiciously so.", "tooltip.chipi.chipper_ore": "§7Common.§r §8Suspiciously so.",
"tooltip.chipi.chipper_alloy_block": "§7Pressed together until it stopped complaining.§r §8Mostly.", "tooltip.chipi.chipper_alloy_block": "§7Pressed together until it stopped complaining.§r §8Mostly.",
"tooltip.chipi.nut": "§7Probably edible.§r §8Confidence not included.",
"tooltip.chipi.raw_chipper_ore": "§7Still warm to the touch.§r §8That seems unnecessary.", "tooltip.chipi.raw_chipper_ore": "§7Still warm to the touch.§r §8That seems unnecessary.",
"tooltip.chipi.chipper_ingot": "§7Dense and stubborn.§r §8Refuses to fail politely.", "tooltip.chipi.chipper_ingot": "§7Dense and stubborn.§r §8Refuses to fail politely.",
"tooltip.chipi.chipper_alloy": "§7Stronger than it looks.§r §8Judges you silently.", "tooltip.chipi.chipper_alloy": "§7Stronger than it looks.§r §8Judges you silently.",
"tooltip.chipi.mep_spawn_egg": "§8It already knows where you are.§r §7Spawning is a courtesy.", "tooltip.chipi.mep_spawn_egg": "§8It already knows where you are.§r §7Spawning is a courtesy.",
"tooltip.chipi.mep_milk": "§7Temporary comfort in liquid form.§r §8The bucket survives.",
"tooltip.chipi.chipper_helmet": "§7Heavy and awkward.§r §8Feels incomplete alone.", "tooltip.chipi.chipper_helmet": "§7Heavy and awkward.§r §8Feels incomplete alone.",
"tooltip.chipi.chipper_chestplate": "§7Looks like armor.§r §8Does nothing by itself.", "tooltip.chipi.chipper_chestplate": "§7Looks like armor.§r §8Does nothing by itself.",
@ -61,5 +55,9 @@
"tooltip.chipi.chipper_pickaxe": "§7Fast bite on stone.§r §8Gives up immediately after.", "tooltip.chipi.chipper_pickaxe": "§7Fast bite on stone.§r §8Gives up immediately after.",
"tooltip.chipi.chipper_axe": "§7Clean chop.§r §8Handle resents you.", "tooltip.chipi.chipper_axe": "§7Clean chop.§r §8Handle resents you.",
"tooltip.chipi.chipper_shovel": "§7Quick dig.§r §8Gravel smells weakness.", "tooltip.chipi.chipper_shovel": "§7Quick dig.§r §8Gravel smells weakness.",
"tooltip.chipi.chipper_hoe": "§7Turns soil fast.§r §8Blunts before the sprouts." "tooltip.chipi.chipper_hoe": "§7Turns soil fast.§r §8Blunts before the sprouts.",
"tooltip.chipi.nut": "§7Probably edible.§r §8Confidence not included.",
"tooltip.chipi.mep_milk": "§7Temporary comfort in liquid form.§r §8The bucket survives.",
"tooltip.chipi.player_milk": "what the fuck did you just just put in me"
} }

View File

@ -0,0 +1,6 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "chipi:item/player_milk"
}
}

View File

@ -3,5 +3,10 @@
"sounds": [ "sounds": [
"chipi:mep_milk" "chipi:mep_milk"
] ]
},
"player_milk_song": {
"sounds": [
"chipi:player_milk"
]
} }
} }

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 336 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 759 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 263 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 339 B

View File

@ -1,6 +1,6 @@
{ {
"type": "fabric:add_features", "type": "fabric:add_features",
"biomes": "minecraft:overworld", "biomes": "#minecraft:is_overworld",
"features": [ "features": [
"chipi:chipper_ore" "chipi:chipper_ore"
], ],

View File

@ -1,8 +1,8 @@
{ {
"type": "minecraft:ore", "type": "minecraft:ore",
"config": { "config": {
"size": 5, "size": 10,
"discard_chance_on_air_exposure": 0.0, "discard_chance_on_air_exposure": 0.4,
"targets": [ "targets": [
{ {
"target": { "target": {

View File

@ -12,12 +12,8 @@
"type": "minecraft:height_range", "type": "minecraft:height_range",
"height": { "height": {
"type": "minecraft:uniform", "type": "minecraft:uniform",
"min_inclusive": { "min_inclusive": { "absolute": -24 },
"absolute": 40 "max_inclusive": { "absolute": 56 }
},
"max_inclusive": {
"absolute": 64
}
} }
}, },
{ {