Work on villager trading

This commit is contained in:
AJ Ferguson 2020-05-18 21:15:29 -08:00
parent 30e38b3a2f
commit e2d46c3d49
4 changed files with 72 additions and 85 deletions

View file

@ -156,12 +156,6 @@ public class GeyserSession implements CommandSender {
@Setter @Setter
private Vector3i lastInteractionPosition; private Vector3i lastInteractionPosition;
@Setter
private ItemStack firstTradeSlot;
@Setter
private ItemStack secondTradeSlot;
@Setter @Setter
private boolean switchingDimension = false; private boolean switchingDimension = false;
private boolean manyDimPackets = false; private boolean manyDimPackets = false;

View file

@ -26,7 +26,12 @@
package org.geysermc.connector.network.translators.bedrock; package org.geysermc.connector.network.translators.bedrock;
import com.github.steveice10.mc.protocol.data.game.window.VillagerTrade;
import com.github.steveice10.mc.protocol.data.game.window.WindowType;
import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientSelectTradePacket;
import com.nukkitx.protocol.bedrock.packet.EntityEventPacket; import com.nukkitx.protocol.bedrock.packet.EntityEventPacket;
import org.geysermc.connector.entity.living.merchant.VillagerEntity;
import org.geysermc.connector.inventory.Inventory;
import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator; import org.geysermc.connector.network.translators.PacketTranslator;
import org.geysermc.connector.network.translators.Translator; import org.geysermc.connector.network.translators.Translator;
@ -41,6 +46,23 @@ public class BedrockEntityEventTranslator extends PacketTranslator<EntityEventPa
case EATING_ITEM: case EATING_ITEM:
session.sendUpstreamPacket(packet); session.sendUpstreamPacket(packet);
return; return;
case COMPLETE_TRADE:
ClientSelectTradePacket selectTradePacket = new ClientSelectTradePacket(packet.getData());
session.getDownstream().getSession().send(selectTradePacket);
VillagerEntity villager = (VillagerEntity) session.getEntityCache().getEntityByGeyserId(session.getLastInteractedVillagerEid());
if (villager == null) {
session.getConnector().getLogger().debug("Could not find villager with entity id: " + session.getLastInteractedVillagerEid());
return;
}
Inventory openInventory = session.getInventoryCache().getOpenInventory();
if (openInventory != null && openInventory.getWindowType() == WindowType.MERCHANT) {
if (packet.getData() > 0 && packet.getData() < villager.getVillagerTrades().length) {
VillagerTrade trade = villager.getVillagerTrades()[packet.getData()];
openInventory.setItem(2, trade.getOutput());
}
}
return;
} }
session.getConnector().getLogger().debug("Did not translate incoming EntityEventPacket: " + packet.toString()); session.getConnector().getLogger().debug("Did not translate incoming EntityEventPacket: " + packet.toString());
} }

View file

@ -26,18 +26,11 @@
package org.geysermc.connector.network.translators.inventory; package org.geysermc.connector.network.translators.inventory;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
import com.github.steveice10.mc.protocol.data.game.window.ClickItemParam;
import com.github.steveice10.mc.protocol.data.game.window.VillagerTrade;
import com.github.steveice10.mc.protocol.data.game.window.WindowAction;
import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientSelectTradePacket;
import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientWindowActionPacket;
import com.nukkitx.protocol.bedrock.data.ContainerId; import com.nukkitx.protocol.bedrock.data.ContainerId;
import com.nukkitx.protocol.bedrock.data.InventoryActionData; import com.nukkitx.protocol.bedrock.data.InventoryActionData;
import org.geysermc.connector.entity.living.merchant.VillagerEntity; import com.nukkitx.protocol.bedrock.data.InventorySource;
import org.geysermc.connector.inventory.Inventory; import org.geysermc.connector.inventory.Inventory;
import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.Translators;
import org.geysermc.connector.network.translators.inventory.updater.CursorInventoryUpdater; import org.geysermc.connector.network.translators.inventory.updater.CursorInventoryUpdater;
import org.geysermc.connector.network.translators.inventory.updater.InventoryUpdater; import org.geysermc.connector.network.translators.inventory.updater.InventoryUpdater;
@ -80,6 +73,14 @@ public class MerchantInventoryTranslator extends BaseInventoryTranslator {
return super.bedrockSlotToJava(action); return super.bedrockSlotToJava(action);
} }
@Override
public SlotType getSlotType(int javaSlot) {
if (javaSlot == 2) {
return SlotType.OUTPUT;
}
return SlotType.NORMAL;
}
@Override @Override
public void prepareInventory(GeyserSession session, Inventory inventory) { public void prepareInventory(GeyserSession session, Inventory inventory) {
@ -93,8 +94,6 @@ public class MerchantInventoryTranslator extends BaseInventoryTranslator {
@Override @Override
public void closeInventory(GeyserSession session, Inventory inventory) { public void closeInventory(GeyserSession session, Inventory inventory) {
session.setLastInteractedVillagerEid(-1); session.setLastInteractedVillagerEid(-1);
session.setFirstTradeSlot(null);
session.setSecondTradeSlot(null);
} }
@Override @Override
@ -109,60 +108,10 @@ public class MerchantInventoryTranslator extends BaseInventoryTranslator {
@Override @Override
public void translateActions(GeyserSession session, Inventory inventory, List<InventoryActionData> actions) { public void translateActions(GeyserSession session, Inventory inventory, List<InventoryActionData> actions) {
InventoryActionData result = null; for (InventoryActionData action : actions) {
if (action.getSource().getType() == InventorySource.Type.NON_IMPLEMENTED_TODO) {
VillagerEntity villager = (VillagerEntity) session.getEntityCache().getEntityByGeyserId(session.getLastInteractedVillagerEid());
if (villager == null) {
session.getConnector().getLogger().debug("Could not find villager with entity id: " + session.getLastInteractedVillagerEid());
return; return;
} }
// We need to store the trade slot data in the session itself as data
// needs to persist beyond this translateActions method since the client
// sends multiple packets for this
for (InventoryActionData data : actions) {
if (data.getSlot() == 4 && session.getFirstTradeSlot() == null && data.getSource().getContainerId() == ContainerId.CURSOR) {
session.setFirstTradeSlot(Translators.getItemTranslator().translateToJava(session, data.getToItem()));
}
if (data.getSlot() == 5 && session.getSecondTradeSlot() == null && data.getToItem() != null && data.getSource().getContainerId() == ContainerId.CURSOR) {
session.setSecondTradeSlot(Translators.getItemTranslator().translateToJava(session, data.getToItem()));
}
if (data.getSlot() == 50 && result == null) {
result = data;
}
}
if (result == null || session.getFirstTradeSlot() == null) {
super.translateActions(session, inventory, actions);
return;
}
ItemStack resultSlot = Translators.getItemTranslator().translateToJava(session, result.getToItem());
for (int i = 0; i < villager.getVillagerTrades().length; i++) {
VillagerTrade trade = villager.getVillagerTrades()[i];
if (!Translators.getItemTranslator().equals(session.getFirstTradeSlot(), trade.getFirstInput(), true, true, false) || !Translators.getItemTranslator().equals(resultSlot, trade.getOutput(), true, false, false)) {
continue;
}
if (session.getSecondTradeSlot() != null && trade.getSecondInput() != null && !Translators.getItemTranslator().equals(session.getSecondTradeSlot(), trade.getSecondInput(), true, false, false)) {
continue;
}
ClientSelectTradePacket selectTradePacket = new ClientSelectTradePacket(i);
session.sendDownstreamPacket(selectTradePacket);
ClientWindowActionPacket tradeAction = new ClientWindowActionPacket(
inventory.getId(),
inventory.getTransactionId().getAndIncrement(),
this.bedrockSlotToJava(result),
null,
WindowAction.CLICK_ITEM,
ClickItemParam.LEFT_CLICK
);
session.sendDownstreamPacket(tradeAction);
break;
} }
super.translateActions(session, inventory, actions); super.translateActions(session, inventory, actions);

View file

@ -56,12 +56,13 @@ public class JavaTradeListTranslator extends PacketTranslator<ServerTradeListPac
return; return;
} }
villager.setVillagerTrades(packet.getTrades()); villager.setVillagerTrades(packet.getTrades());
villager.getMetadata().put(EntityData.TRADE_XP, packet.getExperience());
villager.getMetadata().put(EntityData.TRADE_TIER, packet.getVillagerLevel() - 1); villager.getMetadata().put(EntityData.TRADE_TIER, packet.getVillagerLevel() - 1);
villager.getMetadata().put(EntityData.MAX_TRADE_TIER, 4);
villager.getMetadata().put(EntityData.TRADE_XP, packet.getExperience());
villager.updateBedrockMetadata(session); villager.updateBedrockMetadata(session);
UpdateTradePacket updateTradePacket = new UpdateTradePacket(); UpdateTradePacket updateTradePacket = new UpdateTradePacket();
updateTradePacket.setTradeTier(packet.getVillagerLevel() + 1); updateTradePacket.setTradeTier(packet.getVillagerLevel() - 1);
updateTradePacket.setWindowId((short) packet.getWindowId()); updateTradePacket.setWindowId((short) packet.getWindowId());
updateTradePacket.setWindowType((short) ContainerType.TRADING.id()); updateTradePacket.setWindowType((short) ContainerType.TRADING.id());
updateTradePacket.setDisplayName("Villager"); updateTradePacket.setDisplayName("Villager");
@ -75,41 +76,62 @@ public class JavaTradeListTranslator extends PacketTranslator<ServerTradeListPac
for (VillagerTrade trade : packet.getTrades()) { for (VillagerTrade trade : packet.getTrades()) {
CompoundTagBuilder recipe = CompoundTagBuilder.builder(); CompoundTagBuilder recipe = CompoundTagBuilder.builder();
recipe.intTag("maxUses", trade.getMaxUses()); recipe.intTag("maxUses", trade.getMaxUses());
recipe.intTag("traderExp", packet.getExperience()); recipe.intTag("traderExp", trade.getXp());
recipe.floatTag("priceMultiplierA", trade.getPriceMultiplier()); recipe.floatTag("priceMultiplierA", trade.getPriceMultiplier());
recipe.tag(getItemTag(session, trade.getOutput(), "sell")); recipe.tag(getItemTag(session, trade.getOutput(), "sell", 0));
recipe.floatTag("priceMultiplierB", 0.0f); recipe.floatTag("priceMultiplierB", 0.0f);
recipe.intTag("buyCountB", 0); recipe.intTag("buyCountB", trade.getSecondInput() != null ? trade.getSecondInput().getAmount() : 0);
recipe.intTag("buyCountA", trade.getOutput().getAmount()); recipe.intTag("buyCountA", trade.getFirstInput().getAmount());
recipe.intTag("demand", trade.getDemand()); recipe.intTag("demand", trade.getDemand());
recipe.intTag("tier", packet.getVillagerLevel() - 1); recipe.intTag("tier", packet.getVillagerLevel() - 1);
recipe.tag(getItemTag(session, trade.getFirstInput(), "buyA")); recipe.tag(getItemTag(session, trade.getFirstInput(), "buyA", trade.getSpecialPrice()));
if (trade.getSecondInput() != null) { if (trade.getSecondInput() != null) {
recipe.tag(getItemTag(session, trade.getSecondInput(), "buyB")); recipe.tag(getItemTag(session, trade.getSecondInput(), "buyB", 0));
} }
recipe.intTag("uses", trade.getNumUses()); recipe.intTag("uses", trade.getNumUses());
recipe.byteTag("rewardExp", (byte) trade.getXp()); recipe.byteTag("rewardExp", (byte) 1);
tags.add(recipe.buildRootTag()); tags.add(recipe.buildRootTag());
} }
//Hidden trade to fix visual experience bug
if (packet.getVillagerLevel() < 5) {
tags.add(CompoundTagBuilder.builder()
.intTag("maxUses", 0)
.intTag("traderExp", 0)
.floatTag("priceMultiplierA", 0.0f)
.floatTag("priceMultiplierB", 0.0f)
.intTag("buyCountB", 0)
.intTag("buyCountA", 0)
.intTag("demand", 0)
.intTag("tier", 5)
.intTag("uses", 0)
.byteTag("rewardExp", (byte) 0)
.buildRootTag());
}
builder.listTag("Recipes", CompoundTag.class, tags); builder.listTag("Recipes", CompoundTag.class, tags);
List<CompoundTag> expTags = new ArrayList<>(); List<CompoundTag> expTags = new ArrayList<>();
expTags.add(CompoundTagBuilder.builder().intTag("0", 0).buildRootTag()); expTags.add(CompoundTagBuilder.builder().intTag("0", 0).buildRootTag());
expTags.add(CompoundTagBuilder.builder().intTag("1", 10).buildRootTag()); expTags.add(CompoundTagBuilder.builder().intTag("1", 11).buildRootTag());
expTags.add(CompoundTagBuilder.builder().intTag("2", 60).buildRootTag()); expTags.add(CompoundTagBuilder.builder().intTag("2", 71).buildRootTag());
expTags.add(CompoundTagBuilder.builder().intTag("3", 160).buildRootTag()); expTags.add(CompoundTagBuilder.builder().intTag("3", 151).buildRootTag());
expTags.add(CompoundTagBuilder.builder().intTag("4", 310).buildRootTag()); expTags.add(CompoundTagBuilder.builder().intTag("4", 251).buildRootTag());
builder.listTag("TierExpRequirements", CompoundTag.class, expTags); builder.listTag("TierExpRequirements", CompoundTag.class, expTags);
updateTradePacket.setOffers(builder.buildRootTag()); updateTradePacket.setOffers(builder.buildRootTag());
session.sendUpstreamPacket(updateTradePacket); session.sendUpstreamPacket(updateTradePacket);
} }
private CompoundTag getItemTag(GeyserSession session, ItemStack stack, String name) { private CompoundTag getItemTag(GeyserSession session, ItemStack stack, String name, int specialPrice) {
ItemData itemData = Translators.getItemTranslator().translateToBedrock(session, stack); ItemData itemData = Translators.getItemTranslator().translateToBedrock(session, stack);
ItemEntry itemEntry = Translators.getItemTranslator().getItem(stack); ItemEntry itemEntry = Translators.getItemTranslator().getItem(stack);
CompoundTagBuilder builder = CompoundTagBuilder.builder(); CompoundTagBuilder builder = CompoundTagBuilder.builder();
builder.byteTag("Count", (byte) itemData.getCount()); builder.byteTag("Count", (byte) (Math.max(itemData.getCount() + specialPrice, 1)));
builder.shortTag("Damage", itemData.getDamage()); builder.shortTag("Damage", itemData.getDamage());
builder.stringTag("Name", itemEntry.getJavaIdentifier()); builder.shortTag("id", (short) itemEntry.getBedrockId());
if (itemData.getTag() != null) {
CompoundTag tag = itemData.getTag().toBuilder().build("tag");
builder.tag(tag);
}
return builder.build(name); return builder.build(name);
} }
} }