Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ After publishing, the action prepares the next development version and archives

## Unreleased

- A Market Automaton trades on its own, at the same speed as a hopper. Choose an offer, put the payment in the two slots or let a hopper fill them, and take the result from the bottom. The choice stays when the trade locks, and you can take the payment back out of the window.
- The furnace arrow sits between the payment and the result. A button on the right side of the window chooses whether the block ignores redstone, runs only while powered, or runs only while unpowered.
- Each automaton offer shows `[max: uses left]`, or `[locked]` once none remain. That label updates while the window stays open.
- Similar is the icon beside the redstone button. It also buys other villagers' copies of the selected trade and picks the cheapest one the slots can pay.
- Simplified Chinese for the block, the creative tab, and the market window.

## 2.0.0-alpha
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import net.modcrafters.minecraft.villagermarket.Constants;
import net.modcrafters.minecraft.villagermarket.ModContent;
import net.modcrafters.minecraft.villagermarket.VillagerMarket;
import net.modcrafters.minecraft.villagermarket.network.AutomatonPayloads;
import net.modcrafters.minecraft.villagermarket.network.MarketPayloads;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.creativetab.v1.FabricCreativeModeTab;
Expand All @@ -18,14 +19,33 @@ public void onInitialize() {
Registry.register(BuiltInRegistries.BLOCK, ModContent.MARKET_ID.identifier(), ModContent.marketBlock());
Registry.register(BuiltInRegistries.ITEM, ModContent.MARKET_ITEM_ID.identifier(), ModContent.marketItem());
Registry.register(BuiltInRegistries.MENU, ModContent.MENU_ID.identifier(), ModContent.menu());
Registry.register(BuiltInRegistries.BLOCK, ModContent.AUTOMATON_ID.identifier(), ModContent.automatonBlock());
Registry.register(BuiltInRegistries.ITEM, ModContent.AUTOMATON_ITEM_ID.identifier(), ModContent.automatonItem());
Registry.register(BuiltInRegistries.BLOCK_ENTITY_TYPE, ModContent.AUTOMATON_ID.identifier(), ModContent.automatonType());
Registry.register(BuiltInRegistries.MENU, ModContent.AUTOMATON_MENU_ID.identifier(), ModContent.automatonMenu());
Registry.register(BuiltInRegistries.CREATIVE_MODE_TAB, ModContent.TAB_ID.identifier(),
ModContent.titled(FabricCreativeModeTab.builder())
.displayItems((parameters, output) -> output.accept(ModContent.MARKET_ITEM))
.displayItems((parameters, output) -> {
output.accept(ModContent.MARKET_ITEM);
output.accept(ModContent.AUTOMATON_ITEM);
})
.build());
PayloadTypeRegistry.clientboundPlay().register(MarketPayloads.Offers.TYPE, MarketPayloads.Offers.STREAM_CODEC);
PayloadTypeRegistry.serverboundPlay().register(MarketPayloads.Trade.TYPE, MarketPayloads.Trade.STREAM_CODEC);
PayloadTypeRegistry.clientboundPlay().register(AutomatonPayloads.Recipe.TYPE, AutomatonPayloads.Recipe.STREAM_CODEC);
PayloadTypeRegistry.serverboundPlay().register(AutomatonPayloads.Select.TYPE, AutomatonPayloads.Select.STREAM_CODEC);
PayloadTypeRegistry.clientboundPlay().register(AutomatonPayloads.Mode.TYPE, AutomatonPayloads.Mode.STREAM_CODEC);
PayloadTypeRegistry.serverboundPlay().register(AutomatonPayloads.Redstone.TYPE, AutomatonPayloads.Redstone.STREAM_CODEC);
PayloadTypeRegistry.clientboundPlay().register(AutomatonPayloads.SimilarState.TYPE, AutomatonPayloads.SimilarState.STREAM_CODEC);
PayloadTypeRegistry.serverboundPlay().register(AutomatonPayloads.Similar.TYPE, AutomatonPayloads.Similar.STREAM_CODEC);
ServerPlayNetworking.registerGlobalReceiver(MarketPayloads.Trade.TYPE,
(payload, context) -> context.server().execute(() -> MarketPayloads.handleTrade(context.player(), payload)));
ServerPlayNetworking.registerGlobalReceiver(AutomatonPayloads.Select.TYPE,
(payload, context) -> context.server().execute(() -> AutomatonPayloads.handleSelect(context.player(), payload)));
ServerPlayNetworking.registerGlobalReceiver(AutomatonPayloads.Redstone.TYPE,
(payload, context) -> context.server().execute(() -> AutomatonPayloads.handleRedstone(context.player(), payload)));
ServerPlayNetworking.registerGlobalReceiver(AutomatonPayloads.Similar.TYPE,
(payload, context) -> context.server().execute(() -> AutomatonPayloads.handleSimilar(context.player(), payload)));
Constants.LOG.info("Villager Market Fabric init");
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package net.modcrafters.minecraft.villagermarket.fabric.client;

import net.modcrafters.minecraft.villagermarket.ModContent;
import net.modcrafters.minecraft.villagermarket.client.MarketAutomatonScreen;
import net.modcrafters.minecraft.villagermarket.client.VillagerMarketScreen;
import net.modcrafters.minecraft.villagermarket.network.AutomatonPayloads;
import net.modcrafters.minecraft.villagermarket.network.MarketPayloads;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
Expand All @@ -11,7 +13,17 @@ public class VillagerMarketFabricClient implements ClientModInitializer {
@Override
public void onInitializeClient() {
MenuScreens.register(ModContent.menu(), VillagerMarketScreen::new);
MenuScreens.register(ModContent.automatonMenu(), MarketAutomatonScreen::new);
ClientPlayNetworking.registerGlobalReceiver(MarketPayloads.Offers.TYPE,
(payload, context) -> context.client().execute(() -> VillagerMarketScreen.apply(payload)));
(payload, context) -> context.client().execute(() -> {
VillagerMarketScreen.apply(payload);
MarketAutomatonScreen.applyOffers(payload);
}));
ClientPlayNetworking.registerGlobalReceiver(AutomatonPayloads.Recipe.TYPE,
(payload, context) -> context.client().execute(() -> MarketAutomatonScreen.apply(payload)));
ClientPlayNetworking.registerGlobalReceiver(AutomatonPayloads.Mode.TYPE,
(payload, context) -> context.client().execute(() -> MarketAutomatonScreen.applyMode(payload)));
ClientPlayNetworking.registerGlobalReceiver(AutomatonPayloads.SimilarState.TYPE,
(payload, context) -> context.client().execute(() -> MarketAutomatonScreen.applySimilar(payload)));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,24 @@ public void adjustedPriceIsWhatIsCharged(GameTestHelper helper) {
public void experienceLandsOnThePlayer(GameTestHelper helper) {
MarketGameTests.experienceLandsOnThePlayer(helper);
}

@GameTest(structure = "minecraft:empty")
public void automatonTradesFromItsSlots(GameTestHelper helper) {
MarketGameTests.automatonTradesFromItsSlots(helper);
}

@GameTest(structure = "minecraft:empty")
public void automatonRejectsADifferentBook(GameTestHelper helper) {
MarketGameTests.automatonRejectsADifferentBook(helper);
}

@GameTest(structure = "minecraft:empty")
public void automatonStopsWithoutRedstone(GameTestHelper helper) {
MarketGameTests.automatonStopsWithoutRedstone(helper);
}

@GameTest(structure = "minecraft:empty")
public void automatonBuysTheCheapestSimilar(GameTestHelper helper) {
MarketGameTests.automatonBuysTheCheapestSimilar(helper);
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
package net.modcrafters.minecraft.villagermarket.fabric.platform;

import net.modcrafters.minecraft.villagermarket.ModContent;
import net.modcrafters.minecraft.villagermarket.block.entity.MarketAutomatonBlockEntity;
import net.modcrafters.minecraft.villagermarket.menu.AutomatonOpening;
import net.modcrafters.minecraft.villagermarket.menu.MarketAutomatonMenu;
import net.modcrafters.minecraft.villagermarket.menu.OpeningData;
import net.modcrafters.minecraft.villagermarket.menu.VillagerMarketMenu;
import net.modcrafters.minecraft.villagermarket.platform.services.PlatformHelper;
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
import net.fabricmc.fabric.api.menu.v1.ExtendedMenuProvider;
import net.fabricmc.fabric.api.menu.v1.ExtendedMenuType;
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
//? if <26.1
/*import net.fabricmc.fabric.api.object.builder.v1.block.entity.FabricBlockEntityTypeBuilder;*/
import net.minecraft.core.BlockPos;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
Expand All @@ -16,6 +22,8 @@
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.inventory.MenuType;
import net.minecraft.world.level.block.entity.BlockEntityType;
import java.util.Set;

public class FabricPlatformHelper implements PlatformHelper {
@Override
Expand All @@ -28,6 +36,19 @@ public MenuType<VillagerMarketMenu> marketMenu() {
return new ExtendedMenuType<>(VillagerMarketMenu::new, OpeningData.STREAM_CODEC);
}

@Override
public MenuType<MarketAutomatonMenu> automatonMenu() {
return new ExtendedMenuType<>(MarketAutomatonMenu::new, AutomatonOpening.STREAM_CODEC);
}

@Override
public BlockEntityType<MarketAutomatonBlockEntity> automatonType() {
//? if >=26.1 {
return new BlockEntityType<>(MarketAutomatonBlockEntity::new, Set.of(ModContent.automatonBlock()));
//?} else
//return FabricBlockEntityTypeBuilder.create(MarketAutomatonBlockEntity::new, ModContent.automatonBlock()).build();
}

@Override
public void openMarket(ServerPlayer player, BlockPos pos) {
OpeningData data = VillagerMarketMenu.opening((ServerLevel) player.level(), pos);
Expand All @@ -49,6 +70,27 @@ public AbstractContainerMenu createMenu(int containerId, Inventory inventory, Pl
});
}

@Override
public void openAutomaton(ServerPlayer player, BlockPos pos) {
AutomatonOpening data = MarketAutomatonMenu.opening((ServerLevel) player.level(), pos);
player.openMenu(new ExtendedMenuProvider<AutomatonOpening>() {
@Override
public AutomatonOpening getScreenOpeningData(ServerPlayer player) {
return data;
}

@Override
public Component getDisplayName() {
return Component.translatable("block.villagermarket.market_automaton");
}

@Override
public AbstractContainerMenu createMenu(int containerId, Inventory inventory, Player player) {
return new MarketAutomatonMenu(containerId, inventory, data);
}
});
}

@Override
public void sendToPlayer(ServerPlayer player, CustomPacketPayload payload) {
ServerPlayNetworking.send(player, payload);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import net.modcrafters.minecraft.villagermarket.VillagerMarket;
import net.modcrafters.minecraft.villagermarket.neoforge.client.VillagerMarketNeoForgeClient;
import net.modcrafters.minecraft.villagermarket.neoforge.gametest.NeoForgeMarketGameTests;
import net.modcrafters.minecraft.villagermarket.network.AutomatonPayloads;
import net.modcrafters.minecraft.villagermarket.network.MarketPayloads;
import net.minecraft.core.registries.Registries;
import net.minecraft.world.item.CreativeModeTab;
Expand Down Expand Up @@ -35,19 +36,48 @@ private static void registerContent(RegisterEvent event) {
event.register(Registries.BLOCK, ModContent.MARKET_ID.identifier(), ModContent::marketBlock);
event.register(Registries.ITEM, ModContent.MARKET_ITEM_ID.identifier(), ModContent::marketItem);
event.register(Registries.MENU, ModContent.MENU_ID.identifier(), ModContent::menu);
event.register(Registries.BLOCK, ModContent.AUTOMATON_ID.identifier(), ModContent::automatonBlock);
event.register(Registries.ITEM, ModContent.AUTOMATON_ITEM_ID.identifier(), ModContent::automatonItem);
event.register(Registries.BLOCK_ENTITY_TYPE, ModContent.AUTOMATON_ID.identifier(), ModContent::automatonType);
event.register(Registries.MENU, ModContent.AUTOMATON_MENU_ID.identifier(), ModContent::automatonMenu);
event.register(Registries.CREATIVE_MODE_TAB, ModContent.TAB_ID.identifier(), () ->
ModContent.titled(CreativeModeTab.builder())
.displayItems((parameters, output) -> output.accept(ModContent.MARKET_ITEM))
.displayItems((parameters, output) -> {
output.accept(ModContent.MARKET_ITEM);
output.accept(ModContent.AUTOMATON_ITEM);
})
.build());
}

private static void registerPayloads(RegisterPayloadHandlersEvent event) {
PayloadRegistrar registrar = event.registrar("1");
//? if >=1.21.6 {
registrar.playToClient(MarketPayloads.Offers.TYPE, MarketPayloads.Offers.STREAM_CODEC);
//?} else
//registrar.playToClient(MarketPayloads.Offers.TYPE, MarketPayloads.Offers.STREAM_CODEC, (payload, context) -> net.modcrafters.minecraft.villagermarket.client.VillagerMarketScreen.apply(payload));
//?} else {
/*registrar.playToClient(MarketPayloads.Offers.TYPE, MarketPayloads.Offers.STREAM_CODEC, (payload, context) -> {
net.modcrafters.minecraft.villagermarket.client.VillagerMarketScreen.apply(payload);
net.modcrafters.minecraft.villagermarket.client.MarketAutomatonScreen.applyOffers(payload);
});
*///?}
registrar.playToServer(MarketPayloads.Trade.TYPE, MarketPayloads.Trade.STREAM_CODEC,
(payload, context) -> context.enqueueWork(() -> MarketPayloads.handleTrade((net.minecraft.server.level.ServerPlayer) context.player(), payload)));
//? if >=1.21.6 {
registrar.playToClient(AutomatonPayloads.Recipe.TYPE, AutomatonPayloads.Recipe.STREAM_CODEC);
//?} else
//registrar.playToClient(AutomatonPayloads.Recipe.TYPE, AutomatonPayloads.Recipe.STREAM_CODEC, (payload, context) -> net.modcrafters.minecraft.villagermarket.client.MarketAutomatonScreen.apply(payload));
registrar.playToServer(AutomatonPayloads.Select.TYPE, AutomatonPayloads.Select.STREAM_CODEC,
(payload, context) -> context.enqueueWork(() -> AutomatonPayloads.handleSelect((net.minecraft.server.level.ServerPlayer) context.player(), payload)));
//? if >=1.21.6 {
registrar.playToClient(AutomatonPayloads.Mode.TYPE, AutomatonPayloads.Mode.STREAM_CODEC);
//?} else
//registrar.playToClient(AutomatonPayloads.Mode.TYPE, AutomatonPayloads.Mode.STREAM_CODEC, (payload, context) -> net.modcrafters.minecraft.villagermarket.client.MarketAutomatonScreen.applyMode(payload));
registrar.playToServer(AutomatonPayloads.Redstone.TYPE, AutomatonPayloads.Redstone.STREAM_CODEC,
(payload, context) -> context.enqueueWork(() -> AutomatonPayloads.handleRedstone((net.minecraft.server.level.ServerPlayer) context.player(), payload)));
//? if >=1.21.6 {
registrar.playToClient(AutomatonPayloads.SimilarState.TYPE, AutomatonPayloads.SimilarState.STREAM_CODEC);
//?} else
//registrar.playToClient(AutomatonPayloads.SimilarState.TYPE, AutomatonPayloads.SimilarState.STREAM_CODEC, (payload, context) -> net.modcrafters.minecraft.villagermarket.client.MarketAutomatonScreen.applySimilar(payload));
registrar.playToServer(AutomatonPayloads.Similar.TYPE, AutomatonPayloads.Similar.STREAM_CODEC,
(payload, context) -> context.enqueueWork(() -> AutomatonPayloads.handleSimilar((net.minecraft.server.level.ServerPlayer) context.player(), payload)));
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package net.modcrafters.minecraft.villagermarket.neoforge.client;

import net.modcrafters.minecraft.villagermarket.ModContent;
import net.modcrafters.minecraft.villagermarket.client.MarketAutomatonScreen;
import net.modcrafters.minecraft.villagermarket.client.VillagerMarketScreen;
import net.modcrafters.minecraft.villagermarket.network.AutomatonPayloads;
import net.modcrafters.minecraft.villagermarket.network.MarketPayloads;
import net.neoforged.bus.api.IEventBus;
import net.neoforged.neoforge.client.event.RegisterMenuScreensEvent;
Expand All @@ -20,11 +22,18 @@ public static void register(IEventBus modBus) {

private static void registerScreens(RegisterMenuScreensEvent event) {
event.register(ModContent.menu(), VillagerMarketScreen::new);
event.register(ModContent.automatonMenu(), MarketAutomatonScreen::new);
}

//? if >=1.21.6 {
private static void registerPayloads(RegisterClientPayloadHandlersEvent event) {
event.register(MarketPayloads.Offers.TYPE, (payload, context) -> VillagerMarketScreen.apply(payload));
event.register(MarketPayloads.Offers.TYPE, (payload, context) -> {
VillagerMarketScreen.apply(payload);
MarketAutomatonScreen.applyOffers(payload);
});
event.register(AutomatonPayloads.Recipe.TYPE, (payload, context) -> MarketAutomatonScreen.apply(payload));
event.register(AutomatonPayloads.Mode.TYPE, (payload, context) -> MarketAutomatonScreen.applyMode(payload));
event.register(AutomatonPayloads.SimilarState.TYPE, (payload, context) -> MarketAutomatonScreen.applySimilar(payload));
}
//?}
}
Loading
Loading