Mirror von
https://github.com/GeyserMC/Geyser.git
synchronisiert 2024-12-27 00:23:03 +01:00
WIP. Autocrafting without java recipe book
Dieser Commit ist enthalten in:
Ursprung
c8016647f2
Commit
d3cb069594
@ -27,12 +27,12 @@ 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.entity.metadata.ItemStack;
|
||||||
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
|
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
|
||||||
|
import com.github.steveice10.mc.protocol.data.game.recipe.Ingredient;
|
||||||
import com.github.steveice10.mc.protocol.data.game.recipe.Recipe;
|
import com.github.steveice10.mc.protocol.data.game.recipe.Recipe;
|
||||||
import com.github.steveice10.mc.protocol.data.game.recipe.data.ShapedRecipeData;
|
import com.github.steveice10.mc.protocol.data.game.recipe.data.ShapedRecipeData;
|
||||||
import com.github.steveice10.mc.protocol.data.game.recipe.data.ShapelessRecipeData;
|
import com.github.steveice10.mc.protocol.data.game.recipe.data.ShapelessRecipeData;
|
||||||
import com.github.steveice10.mc.protocol.data.game.window.WindowType;
|
import com.github.steveice10.mc.protocol.data.game.window.WindowType;
|
||||||
import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientCreativeInventoryActionPacket;
|
import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientCreativeInventoryActionPacket;
|
||||||
import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientPrepareCraftingGridPacket;
|
|
||||||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType;
|
import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType;
|
||||||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerType;
|
import com.nukkitx.protocol.bedrock.data.inventory.ContainerType;
|
||||||
import com.nukkitx.protocol.bedrock.data.inventory.ItemData;
|
import com.nukkitx.protocol.bedrock.data.inventory.ItemData;
|
||||||
@ -40,8 +40,7 @@ import com.nukkitx.protocol.bedrock.data.inventory.StackRequestSlotInfoData;
|
|||||||
import com.nukkitx.protocol.bedrock.data.inventory.stackrequestactions.*;
|
import com.nukkitx.protocol.bedrock.data.inventory.stackrequestactions.*;
|
||||||
import com.nukkitx.protocol.bedrock.packet.ItemStackRequestPacket;
|
import com.nukkitx.protocol.bedrock.packet.ItemStackRequestPacket;
|
||||||
import com.nukkitx.protocol.bedrock.packet.ItemStackResponsePacket;
|
import com.nukkitx.protocol.bedrock.packet.ItemStackResponsePacket;
|
||||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
import it.unimi.dsi.fastutil.ints.*;
|
||||||
import it.unimi.dsi.fastutil.ints.IntSet;
|
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import org.geysermc.connector.inventory.CartographyContainer;
|
import org.geysermc.connector.inventory.CartographyContainer;
|
||||||
import org.geysermc.connector.inventory.GeyserItemStack;
|
import org.geysermc.connector.inventory.GeyserItemStack;
|
||||||
@ -159,8 +158,10 @@ public abstract class InventoryTranslator {
|
|||||||
if (shouldHandleRequestFirst(firstAction, inventory)) {
|
if (shouldHandleRequestFirst(firstAction, inventory)) {
|
||||||
// Some special request that shouldn't be processed normally
|
// Some special request that shouldn't be processed normally
|
||||||
responsePacket.getEntries().add(translateSpecialRequest(session, inventory, request));
|
responsePacket.getEntries().add(translateSpecialRequest(session, inventory, request));
|
||||||
} else if (firstAction.getType() == StackRequestActionType.CRAFT_RECIPE || firstAction.getType() == StackRequestActionType.CRAFT_RECIPE_AUTO) {
|
} else if (firstAction.getType() == StackRequestActionType.CRAFT_RECIPE) {
|
||||||
responsePacket.getEntries().add(translateCraftingRequest(session, inventory, request));
|
responsePacket.getEntries().add(translateCraftingRequest(session, inventory, request));
|
||||||
|
} else if (firstAction.getType() == StackRequestActionType.CRAFT_RECIPE_AUTO) {
|
||||||
|
responsePacket.getEntries().add(translateAutoCraftingRequest(session, inventory, request));
|
||||||
} else if (firstAction.getType() == StackRequestActionType.CRAFT_CREATIVE) {
|
} else if (firstAction.getType() == StackRequestActionType.CRAFT_CREATIVE) {
|
||||||
// This is also used for pulling items out of creative
|
// This is also used for pulling items out of creative
|
||||||
responsePacket.getEntries().add(translateCreativeRequest(session, inventory, request));
|
responsePacket.getEntries().add(translateCreativeRequest(session, inventory, request));
|
||||||
@ -329,40 +330,16 @@ public abstract class InventoryTranslator {
|
|||||||
}
|
}
|
||||||
} else { //transfer from one slot to another
|
} else { //transfer from one slot to another
|
||||||
int tempSlot = -1;
|
int tempSlot = -1;
|
||||||
if (!cursor.isEmpty()) {
|
if (!plan.getCursor().isEmpty()) {
|
||||||
tempSlot = findTempSlot(inventory, cursor, false, sourceSlot, destSlot);
|
tempSlot = findTempSlot(inventory, cursor, false, sourceSlot, destSlot);
|
||||||
if (tempSlot == -1) {
|
if (tempSlot == -1) {
|
||||||
return rejectRequest(request);
|
return rejectRequest(request);
|
||||||
}
|
}
|
||||||
plan.add(Click.LEFT, tempSlot); //place cursor into temp slot
|
plan.add(Click.LEFT, tempSlot); //place cursor into temp slot
|
||||||
}
|
}
|
||||||
int sourceAmount = plan.getItem(sourceSlot).getAmount();
|
|
||||||
if (transferAction.getCount() == sourceAmount) { //transfer all
|
transferSlot(plan, sourceSlot, destSlot, transferAction.getCount());
|
||||||
plan.add(Click.LEFT, sourceSlot); //pickup source
|
|
||||||
plan.add(Click.LEFT, destSlot); //let go of all items and done
|
|
||||||
} else { //transfer some
|
|
||||||
//try to transfer items with least clicks possible
|
|
||||||
int halfSource = sourceAmount - (sourceAmount / 2); //larger half
|
|
||||||
int holding;
|
|
||||||
if (plan.getCursor().isEmpty() && transferAction.getCount() <= halfSource) { //faster to take only half. CURSOR MUST BE EMPTY
|
|
||||||
plan.add(Click.RIGHT, sourceSlot);
|
|
||||||
holding = halfSource;
|
|
||||||
} else { //need all
|
|
||||||
plan.add(Click.LEFT, sourceSlot);
|
|
||||||
holding = sourceAmount;
|
|
||||||
}
|
|
||||||
if (transferAction.getCount() > holding / 2) { //faster to release extra items onto source or dest slot?
|
|
||||||
for (int i = 0; i < holding - transferAction.getCount(); i++) {
|
|
||||||
plan.add(Click.RIGHT, sourceSlot); //prepare cursor
|
|
||||||
}
|
|
||||||
plan.add(Click.LEFT, destSlot); //release cursor onto dest slot
|
|
||||||
} else {
|
|
||||||
for (int i = 0; i < transferAction.getCount(); i++) {
|
|
||||||
plan.add(Click.RIGHT, destSlot); //right click until transfer goal is met
|
|
||||||
}
|
|
||||||
plan.add(Click.LEFT, sourceSlot); //return extra items to source slot
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (tempSlot != -1) {
|
if (tempSlot != -1) {
|
||||||
plan.add(Click.LEFT, tempSlot); //retrieve original cursor
|
plan.add(Click.LEFT, tempSlot); //retrieve original cursor
|
||||||
}
|
}
|
||||||
@ -557,7 +534,6 @@ public abstract class InventoryTranslator {
|
|||||||
int recipeId = 0;
|
int recipeId = 0;
|
||||||
int resultSize = 0;
|
int resultSize = 0;
|
||||||
int timesCrafted = 0;
|
int timesCrafted = 0;
|
||||||
boolean autoCraft = false;
|
|
||||||
CraftState craftState = CraftState.START;
|
CraftState craftState = CraftState.START;
|
||||||
|
|
||||||
int leftover = 0;
|
int leftover = 0;
|
||||||
@ -571,28 +547,6 @@ public abstract class InventoryTranslator {
|
|||||||
}
|
}
|
||||||
craftState = CraftState.RECIPE_ID;
|
craftState = CraftState.RECIPE_ID;
|
||||||
recipeId = craftAction.getRecipeNetworkId();
|
recipeId = craftAction.getRecipeNetworkId();
|
||||||
autoCraft = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case CRAFT_RECIPE_AUTO: {
|
|
||||||
AutoCraftRecipeStackRequestActionData autoCraftAction = (AutoCraftRecipeStackRequestActionData) action;
|
|
||||||
if (craftState != CraftState.START) {
|
|
||||||
return rejectRequest(request);
|
|
||||||
}
|
|
||||||
craftState = CraftState.RECIPE_ID;
|
|
||||||
|
|
||||||
recipeId = autoCraftAction.getRecipeNetworkId();
|
|
||||||
if (!plan.getCursor().isEmpty()) {
|
|
||||||
return rejectRequest(request);
|
|
||||||
}
|
|
||||||
//reject if crafting grid is not clear
|
|
||||||
int gridSize = inventory.getId() == 0 ? 4 : 9;
|
|
||||||
for (int i = 1; i <= gridSize; i++) {
|
|
||||||
if (!inventory.getItem(i).isEmpty()) {
|
|
||||||
return rejectRequest(request);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
autoCraft = true;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CRAFT_RESULTS_DEPRECATED: {
|
case CRAFT_RESULTS_DEPRECATED: {
|
||||||
@ -638,39 +592,6 @@ public abstract class InventoryTranslator {
|
|||||||
int sourceSlot = bedrockSlotToJava(transferAction.getSource());
|
int sourceSlot = bedrockSlotToJava(transferAction.getSource());
|
||||||
int destSlot = bedrockSlotToJava(transferAction.getDestination());
|
int destSlot = bedrockSlotToJava(transferAction.getDestination());
|
||||||
|
|
||||||
if (autoCraft) {
|
|
||||||
Recipe recipe = session.getCraftingRecipes().get(recipeId);
|
|
||||||
//cannot use java recipe book if recipe is locked
|
|
||||||
if (recipe == null || !session.getUnlockedRecipes().contains(recipe.getIdentifier())) {
|
|
||||||
return rejectRequest(request);
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean cursorDest = isCursor(transferAction.getDestination());
|
|
||||||
boolean makeAll = timesCrafted > 1;
|
|
||||||
if (cursorDest) {
|
|
||||||
makeAll = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ClientPrepareCraftingGridPacket prepareCraftingPacket = new ClientPrepareCraftingGridPacket(inventory.getId(), recipe.getIdentifier(), makeAll);
|
|
||||||
session.sendDownstreamPacket(prepareCraftingPacket);
|
|
||||||
|
|
||||||
ItemStack output = null;
|
|
||||||
switch (recipe.getType()) {
|
|
||||||
case CRAFTING_SHAPED:
|
|
||||||
output = ((ShapedRecipeData)recipe.getData()).getResult();
|
|
||||||
break;
|
|
||||||
case CRAFTING_SHAPELESS:
|
|
||||||
output = ((ShapelessRecipeData)recipe.getData()).getResult();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
inventory.setItem(0, GeyserItemStack.from(output), session);
|
|
||||||
|
|
||||||
plan.add(cursorDest ? Click.LEFT : Click.LEFT_SHIFT, 0);
|
|
||||||
plan.execute(true);
|
|
||||||
|
|
||||||
return acceptRequest(request, makeContainerEntries(session, inventory, Collections.emptySet()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isCursor(transferAction.getDestination())) {
|
if (isCursor(transferAction.getDestination())) {
|
||||||
plan.add(Click.LEFT, sourceSlot);
|
plan.add(Click.LEFT, sourceSlot);
|
||||||
craftState = CraftState.DONE;
|
craftState = CraftState.DONE;
|
||||||
@ -711,9 +632,211 @@ public abstract class InventoryTranslator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
plan.execute(false);
|
plan.execute(false);
|
||||||
Set<Integer> affectedSlots = plan.getAffectedSlots();
|
return acceptRequest(request, makeContainerEntries(session, inventory, plan.getAffectedSlots()));
|
||||||
affectedSlots.addAll(Arrays.asList(1, 2, 3, 4)); //TODO: crafting grid
|
}
|
||||||
return acceptRequest(request, makeContainerEntries(session, inventory, affectedSlots));
|
|
||||||
|
public ItemStackResponsePacket.Response translateAutoCraftingRequest(GeyserSession session, Inventory inventory, ItemStackRequestPacket.Request request) {
|
||||||
|
int gridSize;
|
||||||
|
int gridDimensions;
|
||||||
|
if (this instanceof PlayerInventoryTranslator) {
|
||||||
|
gridSize = 4;
|
||||||
|
gridDimensions = 2;
|
||||||
|
} else if (this instanceof CraftingInventoryTranslator) {
|
||||||
|
gridSize = 9;
|
||||||
|
gridDimensions = 3;
|
||||||
|
} else {
|
||||||
|
return rejectRequest(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
Recipe recipe;
|
||||||
|
Ingredient[] ingredients = new Ingredient[0];
|
||||||
|
ItemStack output = null;
|
||||||
|
int recipeWidth = 0;
|
||||||
|
int ingRemaining = 0;
|
||||||
|
int ingredientIndex = -1;
|
||||||
|
|
||||||
|
Int2IntMap consumedSlots = new Int2IntOpenHashMap();
|
||||||
|
int prioritySlot = -1;
|
||||||
|
int secondarySlot = -1;
|
||||||
|
int tempSlot = -1;
|
||||||
|
boolean intoCursor = false;
|
||||||
|
|
||||||
|
int resultSize;
|
||||||
|
int timesCrafted = 0;
|
||||||
|
Int2ObjectMap<Int2IntMap> ingredientMap = new Int2ObjectOpenHashMap<>();
|
||||||
|
CraftState craftState = CraftState.START;
|
||||||
|
|
||||||
|
ClickPlan plan = new ClickPlan(session, this, inventory);
|
||||||
|
requestLoop:
|
||||||
|
for (StackRequestActionData action : request.getActions()) {
|
||||||
|
switch (action.getType()) {
|
||||||
|
case CRAFT_RECIPE_AUTO: {
|
||||||
|
AutoCraftRecipeStackRequestActionData autoCraftAction = (AutoCraftRecipeStackRequestActionData) action;
|
||||||
|
if (craftState != CraftState.START) {
|
||||||
|
return rejectRequest(request);
|
||||||
|
}
|
||||||
|
craftState = CraftState.RECIPE_ID;
|
||||||
|
|
||||||
|
int recipeId = autoCraftAction.getRecipeNetworkId();
|
||||||
|
recipe = session.getCraftingRecipes().get(recipeId);
|
||||||
|
if (recipe == null) {
|
||||||
|
return rejectRequest(request);
|
||||||
|
}
|
||||||
|
if (!plan.getCursor().isEmpty()) {
|
||||||
|
return rejectRequest(request);
|
||||||
|
}
|
||||||
|
//reject if crafting grid is not clear
|
||||||
|
for (int i = 1; i <= gridSize; i++) {
|
||||||
|
if (!inventory.getItem(i).isEmpty()) {
|
||||||
|
return rejectRequest(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (recipe.getType()) {
|
||||||
|
case CRAFTING_SHAPED:
|
||||||
|
ShapedRecipeData shapedData = (ShapedRecipeData) recipe.getData();
|
||||||
|
ingredients = shapedData.getIngredients();
|
||||||
|
recipeWidth = shapedData.getWidth();
|
||||||
|
output = shapedData.getResult();
|
||||||
|
if (shapedData.getWidth() > gridDimensions || shapedData.getHeight() > gridDimensions) {
|
||||||
|
return rejectRequest(request);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CRAFTING_SHAPELESS:
|
||||||
|
ShapelessRecipeData shapelessData = (ShapelessRecipeData) recipe.getData();
|
||||||
|
ingredients = shapelessData.getIngredients();
|
||||||
|
recipeWidth = gridDimensions;
|
||||||
|
output = shapelessData.getResult();
|
||||||
|
if (ingredients.length > gridSize) {
|
||||||
|
return rejectRequest(request);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CRAFT_RESULTS_DEPRECATED: {
|
||||||
|
CraftResultsDeprecatedStackRequestActionData deprecatedCraftAction = (CraftResultsDeprecatedStackRequestActionData) action;
|
||||||
|
if (craftState != CraftState.RECIPE_ID) {
|
||||||
|
return rejectRequest(request);
|
||||||
|
}
|
||||||
|
craftState = CraftState.DEPRECATED;
|
||||||
|
|
||||||
|
if (deprecatedCraftAction.getResultItems().length != 1) {
|
||||||
|
return rejectRequest(request);
|
||||||
|
}
|
||||||
|
resultSize = deprecatedCraftAction.getResultItems()[0].getCount();
|
||||||
|
timesCrafted = deprecatedCraftAction.getTimesCrafted();
|
||||||
|
if (resultSize <= 0 || timesCrafted <= 0) {
|
||||||
|
return rejectRequest(request);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CONSUME: {
|
||||||
|
ConsumeStackRequestActionData consumeAction = (ConsumeStackRequestActionData) action;
|
||||||
|
if (craftState != CraftState.DEPRECATED && craftState != CraftState.INGREDIENTS) {
|
||||||
|
return rejectRequest(request);
|
||||||
|
}
|
||||||
|
craftState = CraftState.INGREDIENTS;
|
||||||
|
|
||||||
|
if (ingRemaining == 0) {
|
||||||
|
while (++ingredientIndex < ingredients.length) {
|
||||||
|
if (ingredients[ingredientIndex].getOptions().length != 0) {
|
||||||
|
ingRemaining = timesCrafted;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ingRemaining -= consumeAction.getCount();
|
||||||
|
if (ingRemaining < 0)
|
||||||
|
return rejectRequest(request);
|
||||||
|
|
||||||
|
int javaSlot = bedrockSlotToJava(consumeAction.getSource());
|
||||||
|
consumedSlots.merge(javaSlot, consumeAction.getCount(), Integer::sum);
|
||||||
|
|
||||||
|
int gridSlot = 1 + ingredientIndex + ((ingredientIndex / recipeWidth) * (gridDimensions - recipeWidth));
|
||||||
|
Int2IntMap sources = ingredientMap.computeIfAbsent(gridSlot, k -> new Int2IntOpenHashMap());
|
||||||
|
sources.put(javaSlot, consumeAction.getCount());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TAKE:
|
||||||
|
case PLACE: {
|
||||||
|
TransferStackRequestActionData transferAction = (TransferStackRequestActionData) action;
|
||||||
|
if (craftState != CraftState.INGREDIENTS && craftState != CraftState.TRANSFER) {
|
||||||
|
return rejectRequest(request);
|
||||||
|
}
|
||||||
|
craftState = CraftState.TRANSFER;
|
||||||
|
|
||||||
|
if (transferAction.getSource().getContainer() != ContainerSlotType.CREATIVE_OUTPUT) {
|
||||||
|
return rejectRequest(request);
|
||||||
|
}
|
||||||
|
if (transferAction.getCount() <= 0) {
|
||||||
|
return rejectRequest(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
int javaSlot = bedrockSlotToJava(transferAction.getDestination());
|
||||||
|
if (isCursor(transferAction.getDestination())) { //TODO
|
||||||
|
intoCursor = true;
|
||||||
|
if (timesCrafted > 1) {
|
||||||
|
tempSlot = findTempSlot(inventory, GeyserItemStack.from(output), true);
|
||||||
|
if (tempSlot == -1) {
|
||||||
|
return rejectRequest(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break requestLoop;
|
||||||
|
} else if (inventory.getItem(javaSlot).getAmount() == consumedSlots.get(javaSlot)) {
|
||||||
|
prioritySlot = bedrockSlotToJava(transferAction.getDestination());
|
||||||
|
break requestLoop;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return rejectRequest(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final int maxLoops = Math.min(64, timesCrafted);
|
||||||
|
for (int loops = 0; loops < maxLoops; loops++) {
|
||||||
|
boolean done = true;
|
||||||
|
for (Int2ObjectMap.Entry<Int2IntMap> entry : ingredientMap.int2ObjectEntrySet()) {
|
||||||
|
Int2IntMap sources = entry.getValue();
|
||||||
|
System.out.println("Grid slot: " + entry.getIntKey());
|
||||||
|
System.out.println(sources);
|
||||||
|
if (sources.isEmpty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
done = false;
|
||||||
|
int gridSlot = entry.getIntKey();
|
||||||
|
if (!plan.getItem(gridSlot).isEmpty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
int sourceSlot;
|
||||||
|
if (loops == 0 && sources.containsKey(prioritySlot)) {
|
||||||
|
sourceSlot = prioritySlot;
|
||||||
|
} else {
|
||||||
|
sourceSlot = sources.keySet().iterator().nextInt();
|
||||||
|
}
|
||||||
|
int transferAmount = sources.remove(sourceSlot);
|
||||||
|
transferSlot(plan, sourceSlot, gridSlot, transferAmount);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int x = 0; x < 3; x++) {
|
||||||
|
int offset = x * 3;
|
||||||
|
System.out.println(plan.getItem(1 + offset).getAmount() + " " + plan.getItem(2 + offset).getAmount() + " " + plan.getItem(3 + offset).getAmount());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!done) {
|
||||||
|
//TODO: sometimes the server does not agree on this slot?
|
||||||
|
plan.add(Click.LEFT_SHIFT, 0, true);
|
||||||
|
} else {
|
||||||
|
System.out.println("Times looped: " + loops);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inventory.setItem(0, GeyserItemStack.from(output), session);
|
||||||
|
plan.execute(true);
|
||||||
|
return acceptRequest(request, makeContainerEntries(session, inventory, plan.getAffectedSlots()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public ItemStackResponsePacket.Response translateCreativeRequest(GeyserSession session, Inventory inventory, ItemStackRequestPacket.Request request) {
|
public ItemStackResponsePacket.Response translateCreativeRequest(GeyserSession session, Inventory inventory, ItemStackRequestPacket.Request request) {
|
||||||
@ -788,6 +911,37 @@ public abstract class InventoryTranslator {
|
|||||||
return rejectRequest(request);
|
return rejectRequest(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void transferSlot(ClickPlan plan, int sourceSlot, int destSlot, int transferAmount) {
|
||||||
|
boolean tempSwap = !plan.getCursor().isEmpty();
|
||||||
|
int sourceAmount = plan.getItem(sourceSlot).getAmount();
|
||||||
|
if (transferAmount == sourceAmount) { //transfer all
|
||||||
|
plan.add(Click.LEFT, sourceSlot); //pickup source
|
||||||
|
plan.add(Click.LEFT, destSlot); //let go of all items and done
|
||||||
|
} else { //transfer some
|
||||||
|
//try to transfer items with least clicks possible
|
||||||
|
int halfSource = sourceAmount - (sourceAmount / 2); //larger half
|
||||||
|
int holding;
|
||||||
|
if (!tempSwap && transferAmount <= halfSource) { //faster to take only half. CURSOR MUST BE EMPTY
|
||||||
|
plan.add(Click.RIGHT, sourceSlot);
|
||||||
|
holding = halfSource;
|
||||||
|
} else { //need all
|
||||||
|
plan.add(Click.LEFT, sourceSlot);
|
||||||
|
holding = sourceAmount;
|
||||||
|
}
|
||||||
|
if (!tempSwap && transferAmount > holding / 2) { //faster to release extra items onto source or dest slot?
|
||||||
|
for (int i = 0; i < holding - transferAmount; i++) {
|
||||||
|
plan.add(Click.RIGHT, sourceSlot); //prepare cursor
|
||||||
|
}
|
||||||
|
plan.add(Click.LEFT, destSlot); //release cursor onto dest slot
|
||||||
|
} else {
|
||||||
|
for (int i = 0; i < transferAmount; i++) {
|
||||||
|
plan.add(Click.RIGHT, destSlot); //right click until transfer goal is met
|
||||||
|
}
|
||||||
|
plan.add(Click.LEFT, sourceSlot); //return extra items to source slot
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static ItemStackResponsePacket.Response acceptRequest(ItemStackRequestPacket.Request request, List<ItemStackResponsePacket.ContainerEntry> containerEntries) {
|
public static ItemStackResponsePacket.Response acceptRequest(ItemStackRequestPacket.Request request, List<ItemStackResponsePacket.ContainerEntry> containerEntries) {
|
||||||
return new ItemStackResponsePacket.Response(ItemStackResponsePacket.ResponseStatus.OK, request.getRequestId(), containerEntries);
|
return new ItemStackResponsePacket.Response(ItemStackResponsePacket.ResponseStatus.OK, request.getRequestId(), containerEntries);
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,8 @@ 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.inventory.InventoryTranslator;
|
import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
|
||||||
import org.geysermc.connector.network.translators.inventory.SlotType;
|
import org.geysermc.connector.network.translators.inventory.SlotType;
|
||||||
|
import org.geysermc.connector.network.translators.inventory.translators.CraftingInventoryTranslator;
|
||||||
|
import org.geysermc.connector.network.translators.inventory.translators.PlayerInventoryTranslator;
|
||||||
import org.geysermc.connector.utils.InventoryUtils;
|
import org.geysermc.connector.utils.InventoryUtils;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@ -52,6 +54,7 @@ public class ClickPlan {
|
|||||||
private final GeyserSession session;
|
private final GeyserSession session;
|
||||||
private final InventoryTranslator translator;
|
private final InventoryTranslator translator;
|
||||||
private final Inventory inventory;
|
private final Inventory inventory;
|
||||||
|
private final int gridSize;
|
||||||
|
|
||||||
public ClickPlan(GeyserSession session, InventoryTranslator translator, Inventory inventory) {
|
public ClickPlan(GeyserSession session, InventoryTranslator translator, Inventory inventory) {
|
||||||
this.session = session;
|
this.session = session;
|
||||||
@ -61,6 +64,14 @@ public class ClickPlan {
|
|||||||
this.simulatedItems = new Int2ObjectOpenHashMap<>(inventory.getSize());
|
this.simulatedItems = new Int2ObjectOpenHashMap<>(inventory.getSize());
|
||||||
this.simulatedCursor = session.getPlayerInventory().getCursor().copy();
|
this.simulatedCursor = session.getPlayerInventory().getCursor().copy();
|
||||||
this.simulating = true;
|
this.simulating = true;
|
||||||
|
|
||||||
|
if (translator instanceof PlayerInventoryTranslator) {
|
||||||
|
gridSize = 4;
|
||||||
|
} else if (translator instanceof CraftingInventoryTranslator) {
|
||||||
|
gridSize = 9;
|
||||||
|
} else {
|
||||||
|
gridSize = -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void resetSimulation() {
|
private void resetSimulation() {
|
||||||
@ -69,6 +80,10 @@ public class ClickPlan {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void add(Click click, int slot) {
|
public void add(Click click, int slot) {
|
||||||
|
add(click, slot, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(Click click, int slot, boolean force) {
|
||||||
if (!simulating)
|
if (!simulating)
|
||||||
throw new UnsupportedOperationException("ClickPlan already executed");
|
throw new UnsupportedOperationException("ClickPlan already executed");
|
||||||
|
|
||||||
@ -76,7 +91,7 @@ public class ClickPlan {
|
|||||||
slot = Click.OUTSIDE_SLOT;
|
slot = Click.OUTSIDE_SLOT;
|
||||||
}
|
}
|
||||||
|
|
||||||
ClickAction action = new ClickAction(click, slot);
|
ClickAction action = new ClickAction(click, slot, force);
|
||||||
plan.add(action);
|
plan.add(action);
|
||||||
simulateAction(action);
|
simulateAction(action);
|
||||||
}
|
}
|
||||||
@ -114,7 +129,7 @@ public class ClickPlan {
|
|||||||
simulateAction(action);
|
simulateAction(action);
|
||||||
|
|
||||||
session.sendDownstreamPacket(clickPacket);
|
session.sendDownstreamPacket(clickPacket);
|
||||||
if (clickedItemStack == InventoryUtils.REFRESH_ITEM) {
|
if (clickedItemStack == InventoryUtils.REFRESH_ITEM || action.force) {
|
||||||
session.sendDownstreamPacket(new ClientConfirmTransactionPacket(inventory.getId(), actionId, true));
|
session.sendDownstreamPacket(new ClientConfirmTransactionPacket(inventory.getId(), actionId, true));
|
||||||
}
|
}
|
||||||
System.out.println(clickPacket);
|
System.out.println(clickPacket);
|
||||||
@ -128,7 +143,15 @@ public class ClickPlan {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public GeyserItemStack getItem(int slot) {
|
public GeyserItemStack getItem(int slot) {
|
||||||
return simulatedItems.computeIfAbsent(slot, k -> inventory.getItem(slot).copy());
|
return getItem(slot, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GeyserItemStack getItem(int slot, boolean generate) {
|
||||||
|
if (generate) {
|
||||||
|
return simulatedItems.computeIfAbsent(slot, k -> inventory.getItem(slot).copy());
|
||||||
|
} else {
|
||||||
|
return simulatedItems.getOrDefault(slot, inventory.getItem(slot));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public GeyserItemStack getCursor() {
|
public GeyserItemStack getCursor() {
|
||||||
@ -174,6 +197,10 @@ public class ClickPlan {
|
|||||||
} else if (InventoryUtils.canStack(cursor, clicked)) {
|
} else if (InventoryUtils.canStack(cursor, clicked)) {
|
||||||
cursor.add(clicked.getAmount());
|
cursor.add(clicked.getAmount());
|
||||||
}
|
}
|
||||||
|
reduceCraftingGrid(false);
|
||||||
|
break;
|
||||||
|
case LEFT_SHIFT:
|
||||||
|
reduceCraftingGrid(true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -215,6 +242,35 @@ public class ClickPlan {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO
|
||||||
|
private void reduceCraftingGrid(boolean makeAll) {
|
||||||
|
if (gridSize == -1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int crafted;
|
||||||
|
if (!makeAll) {
|
||||||
|
crafted = 1;
|
||||||
|
} else {
|
||||||
|
crafted = 0;
|
||||||
|
for (int i = 0; i < gridSize; i++) {
|
||||||
|
GeyserItemStack item = getItem(i + 1);
|
||||||
|
if (!item.isEmpty()) {
|
||||||
|
if (crafted == 0) {
|
||||||
|
crafted = item.getAmount();
|
||||||
|
}
|
||||||
|
crafted = Math.min(crafted, item.getAmount());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < gridSize; i++) {
|
||||||
|
GeyserItemStack item = getItem(i + 1);
|
||||||
|
if (!item.isEmpty())
|
||||||
|
item.sub(crafted);
|
||||||
|
}
|
||||||
|
System.out.println("REDUCED GRID BY " + crafted);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return a new set of all affected slots. This isn't a constant variable; it's newly generated each time it is run.
|
* @return a new set of all affected slots. This isn't a constant variable; it's newly generated each time it is run.
|
||||||
*/
|
*/
|
||||||
@ -235,5 +291,6 @@ public class ClickPlan {
|
|||||||
* Java slot
|
* Java slot
|
||||||
*/
|
*/
|
||||||
int slot;
|
int slot;
|
||||||
|
boolean force;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren