Inventories! (Part 1)

This commit is contained in:
LOOHP 2022-12-09 02:40:16 +00:00
parent ca186938f4
commit 949d2f34d7
48 changed files with 10960 additions and 6652 deletions

View File

@ -24,7 +24,7 @@
<groupId>com.loohp</groupId> <groupId>com.loohp</groupId>
<artifactId>Limbo</artifactId> <artifactId>Limbo</artifactId>
<name>Limbo</name> <name>Limbo</name>
<version>0.6.21-ALPHA</version> <version>0.7.0-ALPHA</version>
<description>Standalone Limbo Minecraft Server.</description> <description>Standalone Limbo Minecraft Server.</description>
<url>https://github.com/LOOHP/Limbo</url> <url>https://github.com/LOOHP/Limbo</url>

View File

@ -27,6 +27,10 @@ import com.loohp.limbo.commands.DefaultCommands;
import com.loohp.limbo.consolegui.GUI; import com.loohp.limbo.consolegui.GUI;
import com.loohp.limbo.events.EventsManager; import com.loohp.limbo.events.EventsManager;
import com.loohp.limbo.file.ServerProperties; import com.loohp.limbo.file.ServerProperties;
import com.loohp.limbo.inventory.CustomInventory;
import com.loohp.limbo.inventory.Inventory;
import com.loohp.limbo.inventory.InventoryHolder;
import com.loohp.limbo.inventory.InventoryType;
import com.loohp.limbo.location.Location; import com.loohp.limbo.location.Location;
import com.loohp.limbo.metrics.Metrics; import com.loohp.limbo.metrics.Metrics;
import com.loohp.limbo.network.ServerConnection; import com.loohp.limbo.network.ServerConnection;
@ -435,15 +439,15 @@ public final class Limbo {
} }
public KeyedBossBar createBossBar(Key Key, Component name, float progress, BossBar.Color color, BossBar.Overlay overlay, BossBar.Flag... flags) { public KeyedBossBar createBossBar(Key Key, Component name, float progress, BossBar.Color color, BossBar.Overlay overlay, BossBar.Flag... flags) {
KeyedBossBar keyedBossBar = com.loohp.limbo.bossbar.Unsafe.create(Key, BossBar.bossBar(name, progress, color, overlay, new HashSet<>(Arrays.asList(flags)))); KeyedBossBar keyedBossBar = com.loohp.limbo.bossbar.Unsafe.a(Key, BossBar.bossBar(name, progress, color, overlay, new HashSet<>(Arrays.asList(flags))));
bossBars.put(Key, keyedBossBar); bossBars.put(Key, keyedBossBar);
return keyedBossBar; return keyedBossBar;
} }
public void removeBossBar(Key Key) { public void removeBossBar(Key Key) {
KeyedBossBar keyedBossBar = bossBars.remove(Key); KeyedBossBar keyedBossBar = bossBars.remove(Key);
keyedBossBar.getProperties().removeListener(keyedBossBar.getUnsafe().getLimboListener()); keyedBossBar.getProperties().removeListener(keyedBossBar.getUnsafe().a());
keyedBossBar.getUnsafe().invalidate(); keyedBossBar.getUnsafe().b();
PacketPlayOutBoss packetPlayOutBoss = new PacketPlayOutBoss(keyedBossBar, PacketPlayOutBoss.BossBarAction.REMOVE); PacketPlayOutBoss packetPlayOutBoss = new PacketPlayOutBoss(keyedBossBar, PacketPlayOutBoss.BossBarAction.REMOVE);
for (Player player : keyedBossBar.getPlayers()) { for (Player player : keyedBossBar.getPlayers()) {
try { try {
@ -578,11 +582,7 @@ public final class Limbo {
} }
public int getNextEntityId() { public int getNextEntityId() {
if (entityIdCount.get() == Integer.MAX_VALUE) { return entityIdCount.getAndUpdate(i -> i == Integer.MAX_VALUE ? 0 : ++i);
return entityIdCount.getAndSet(0);
} else {
return entityIdCount.getAndIncrement();
}
} }
public void dispatchCommand(CommandSender sender, String str) { public void dispatchCommand(CommandSender sender, String str) {
@ -607,14 +607,26 @@ public final class Limbo {
Enumeration<URL> manifests = getClass().getClassLoader().getResources("META-INF/MANIFEST.MF"); Enumeration<URL> manifests = getClass().getClassLoader().getResources("META-INF/MANIFEST.MF");
while (manifests.hasMoreElements()) { while (manifests.hasMoreElements()) {
URL url = manifests.nextElement(); URL url = manifests.nextElement();
BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream())); try (BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream()))) {
Optional<String> line = br.lines().filter(each -> each.startsWith("Limbo-Version:")).findFirst(); Optional<String> line = br.lines().filter(each -> each.startsWith("Limbo-Version:")).findFirst();
br.close(); if (line.isPresent()) {
if (line.isPresent()) { return line.get().substring(14).trim();
return line.get().substring(14).trim(); }
} }
} }
return "Unknown"; return "Unknown";
} }
public Inventory createInventory(Component title, int slots, InventoryHolder holder) {
return CustomInventory.create(title, slots, holder);
}
public Inventory createInventory(InventoryType type, InventoryHolder holder) {
return createInventory(null, type, holder);
}
public Inventory createInventory(Component title, InventoryType type, InventoryHolder holder) {
throw new UnsupportedOperationException("This function has not been implemented yet.");
}
} }

View File

@ -52,43 +52,43 @@ public class Unsafe {
} }
@Deprecated @Deprecated
public void setPlayerGameModeSilently(Player player, GameMode mode) { public void a(Player player, GameMode mode) {
playerUnsafe.a(player, mode); playerUnsafe.a(player, mode);
} }
@Deprecated @Deprecated
public void setSelectedSlotSilently(Player player, byte slot) { public void a(Player player, byte slot) {
playerUnsafe.a(player, slot); playerUnsafe.a(player, slot);
} }
@Deprecated @Deprecated
public void setPlayerEntityId(Player player, int entityId) { public void a(Player player, int entityId) {
playerUnsafe.a(player, entityId); playerUnsafe.a(player, entityId);
} }
@Deprecated @Deprecated
public void removeEntity(World world, Entity entity) { public void a(World world, Entity entity) {
worldUnsafe.a(world, entity); worldUnsafe.a(world, entity);
} }
@Deprecated @Deprecated
public DataWatcher getDataWatcher(World world, Entity entity) { public DataWatcher b(World world, Entity entity) {
return worldUnsafe.b(world, entity); return worldUnsafe.b(world, entity);
} }
@Deprecated @Deprecated
public void setPlayerLocationSilently(Player player, Location location) { public void a(Player player, Location location) {
playerUnsafe.a(player, location); playerUnsafe.a(player, location);
} }
@Deprecated @Deprecated
public void addPlayer(Player player) { public void a(Player player) {
instance.playersByName.put(player.getName(), player); instance.playersByName.put(player.getName(), player);
instance.playersByUUID.put(player.getUniqueId(), player); instance.playersByUUID.put(player.getUniqueId(), player);
} }
@Deprecated @Deprecated
public void removePlayer(Player player) { public void b(Player player) {
instance.getBossBars().values().forEach(each -> each.hidePlayer(player)); instance.getBossBars().values().forEach(each -> each.hidePlayer(player));
instance.playersByName.remove(player.getName()); instance.playersByName.remove(player.getName());
instance.playersByUUID.remove(player.getUniqueId()); instance.playersByUUID.remove(player.getUniqueId());

View File

@ -27,7 +27,7 @@ import net.kyori.adventure.key.Key;
public class Unsafe { public class Unsafe {
@Deprecated @Deprecated
public static KeyedBossBar create(Key key, BossBar properties) { public static KeyedBossBar a(Key key, BossBar properties) {
return new KeyedBossBar(key, properties); return new KeyedBossBar(key, properties);
} }
@ -38,12 +38,12 @@ public class Unsafe {
} }
@Deprecated @Deprecated
public KeyedBossBar.LimboBossBarHandler getLimboListener() { public KeyedBossBar.LimboBossBarHandler a() {
return instance.listener; return instance.listener;
} }
@Deprecated @Deprecated
public void invalidate() { public void b() {
instance.valid.set(false); instance.valid.set(false);
} }

View File

@ -150,5 +150,4 @@ public class ArmorStand extends LivingEntity {
public void setRightLegRotation(Rotation3f rightLegRotation) { public void setRightLegRotation(Rotation3f rightLegRotation) {
this.rightLegRotation = rightLegRotation; this.rightLegRotation = rightLegRotation;
} }
} }

View File

@ -301,12 +301,12 @@ public abstract class Entity implements Sound.Emitter {
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
public void remove() { public void remove() {
Limbo.getInstance().getUnsafe().removeEntity(world, this); Limbo.getInstance().getUnsafe().a(world, this);
} }
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
public DataWatcher getDataWatcher() { public DataWatcher getDataWatcher() {
return Limbo.getInstance().getUnsafe().getDataWatcher(world, this); return Limbo.getInstance().getUnsafe().b(world, this);
} }
} }

View File

@ -0,0 +1,158 @@
/*
* This file is part of Limbo.
*
* Copyright (C) 2022. LoohpJames <jamesloohp@gmail.com>
* Copyright (C) 2022. Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.loohp.limbo.entity;
import com.loohp.limbo.inventory.EquipmentSlot;
import com.loohp.limbo.inventory.ItemStack;
/**
* An interface to a creatures inventory
*/
public interface EntityEquipment {
/**
* Stores the ItemStack at the given equipment slot in the inventory.
*
* @param slot the slot to put the ItemStack
* @param item the ItemStack to set
*/
void setItem(EquipmentSlot slot, ItemStack item);
/**
* Gets the ItemStack at the given equipment slot in the inventory.
*
* @param slot the slot to get the ItemStack
* @return the ItemStack in the given slot
*/
ItemStack getItem(EquipmentSlot slot);
/**
* Gets a copy of the item the entity is currently holding
* in their main hand.
*
* @return the currently held item
*/
ItemStack getItemInMainHand();
/**
* Sets the item the entity is holding in their main hand.
*
* @param item The item to put into the entities hand
*/
void setItemInMainHand(ItemStack item);
/**
* Gets a copy of the item the entity is currently holding
* in their off hand.
*
* @return the currently held item
*/
ItemStack getItemInOffHand();
/**
* Sets the item the entity is holding in their off hand.
*
* @param item The item to put into the entities hand
*/
void setItemInOffHand(ItemStack item);
/**
* Gets a copy of the helmet currently being worn by the entity
*
* @return The helmet being worn
*/
ItemStack getHelmet();
/**
* Sets the helmet worn by the entity
*
* @param helmet The helmet to put on the entity
*/
void setHelmet(ItemStack helmet);
/**
* Gets a copy of the chest plate currently being worn by the entity
*
* @return The chest plate being worn
*/
ItemStack getChestplate();
/**
* Sets the chest plate worn by the entity
*
* @param chestplate The chest plate to put on the entity
*/
void setChestplate(ItemStack chestplate);
/**
* Gets a copy of the leggings currently being worn by the entity
*
* @return The leggings being worn
*/
ItemStack getLeggings();
/**
* Sets the leggings worn by the entity
*
* @param leggings The leggings to put on the entity
*/
void setLeggings(ItemStack leggings);
/**
* Gets a copy of the boots currently being worn by the entity
*
* @return The boots being worn
*/
ItemStack getBoots();
/**
* Sets the boots worn by the entity
*
* @param boots The boots to put on the entity
*/
void setBoots(ItemStack boots);
/**
* Gets a copy of all worn armor
*
* @return The array of worn armor. Individual items may be null.
*/
ItemStack[] getArmorContents();
/**
* Sets the entities armor to the provided array of ItemStacks
*
* @param items The items to set the armor as. Individual items may be null.
*/
void setArmorContents(ItemStack[] items);
/**
* Clears the entity of all armor and held items
*/
void clear();
/**
* Get the entity this EntityEquipment belongs to
*
* @return the entity this EntityEquipment belongs to
*/
Entity getHolder();
}

View File

@ -0,0 +1,135 @@
/*
* This file is part of Limbo.
*
* Copyright (C) 2022. LoohpJames <jamesloohp@gmail.com>
* Copyright (C) 2022. Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.loohp.limbo.entity;
import com.loohp.limbo.inventory.EquipmentSlot;
import com.loohp.limbo.inventory.ItemStack;
import java.util.EnumMap;
public class StandardEntityEquipment implements EntityEquipment {
private final Entity entity;
private final EnumMap<EquipmentSlot, ItemStack> itemStacks;
public StandardEntityEquipment(Entity entity) {
this.entity = entity;
this.itemStacks = new EnumMap<>(EquipmentSlot.class);
}
@Override
public void setItem(EquipmentSlot slot, ItemStack item) {
}
@Override
public ItemStack getItem(EquipmentSlot slot) {
return itemStacks.get(slot);
}
@Override
public ItemStack getItemInMainHand() {
return itemStacks.get(EquipmentSlot.MAINHAND);
}
@Override
public void setItemInMainHand(ItemStack item) {
}
@Override
public ItemStack getItemInOffHand() {
return getItem(EquipmentSlot.OFFHAND);
}
@Override
public void setItemInOffHand(ItemStack item) {
}
@Override
public ItemStack getHelmet() {
return getItem(EquipmentSlot.HELMET);
}
@Override
public void setHelmet(ItemStack helmet) {
}
@Override
public ItemStack getChestplate() {
return getItem(EquipmentSlot.CHESTPLATE);
}
@Override
public void setChestplate(ItemStack chestplate) {
}
@Override
public ItemStack getLeggings() {
return getItem(EquipmentSlot.LEGGINGS);
}
@Override
public void setLeggings(ItemStack leggings) {
}
@Override
public ItemStack getBoots() {
return getItem(EquipmentSlot.BOOTS);
}
@Override
public void setBoots(ItemStack boots) {
}
@Override
public ItemStack[] getArmorContents() {
return itemStacks.values().toArray(new ItemStack[0]);
}
@Override
public void setArmorContents(ItemStack[] items) {
if (items.length != 6) {
throw new IllegalArgumentException("items must have a length of 6");
}
EquipmentSlot[] equipmentSlots = EquipmentSlot.values();
int i = 0;
for (EquipmentSlot equipmentSlot : equipmentSlots) {
setItem(equipmentSlot, items[i++]);
}
}
@Override
public void clear() {
for (EquipmentSlot equipmentSlot : itemStacks.keySet()) {
setItem(equipmentSlot, null);
}
}
@Override
public Entity getHolder() {
return null;
}
}

View File

@ -0,0 +1,45 @@
/*
* This file is part of Limbo.
*
* Copyright (C) 2022. LoohpJames <jamesloohp@gmail.com>
* Copyright (C) 2022. Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.loohp.limbo.events.inventory;
import com.loohp.limbo.events.Cancellable;
import com.loohp.limbo.inventory.Inventory;
import com.loohp.limbo.inventory.InventoryView;
import com.loohp.limbo.player.Player;
public class InventoryClickEvent extends InventoryEvent implements Cancellable {
private boolean cancelled;
public InventoryClickEvent(Player player, InventoryView inventoryView, Inventory clickedInventory) {
super(player, inventoryView, clickedInventory);
}
@Override
public boolean isCancelled() {
return cancelled;
}
@Override
public void setCancelled(boolean cancelled) {
this.cancelled = cancelled;
}
}

View File

@ -0,0 +1,31 @@
/*
* This file is part of Limbo.
*
* Copyright (C) 2022. LoohpJames <jamesloohp@gmail.com>
* Copyright (C) 2022. Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.loohp.limbo.events.inventory;
import com.loohp.limbo.inventory.InventoryView;
import com.loohp.limbo.player.Player;
public class InventoryCloseEvent extends InventoryEvent {
public InventoryCloseEvent(Player player, InventoryView inventoryView) {
super(player, inventoryView, inventoryView.getTopInventory());
}
}

View File

@ -0,0 +1,62 @@
/*
* This file is part of Limbo.
*
* Copyright (C) 2022. LoohpJames <jamesloohp@gmail.com>
* Copyright (C) 2022. Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.loohp.limbo.events.inventory;
import com.loohp.limbo.events.Cancellable;
import com.loohp.limbo.inventory.InventoryView;
import com.loohp.limbo.inventory.ItemStack;
import com.loohp.limbo.player.Player;
import com.loohp.limbo.player.PlayerInventory;
public class InventoryCreativeEvent extends InventoryEvent implements Cancellable {
private boolean cancelled;
private final int slot;
private ItemStack newItem;
public InventoryCreativeEvent(Player player, InventoryView inventoryView, PlayerInventory playerInventory, int slot, ItemStack newItem) {
super(player, inventoryView, playerInventory);
this.slot = slot;
this.newItem = newItem;
this.cancelled = false;
}
public int getSlot() {
return slot;
}
@Override
public boolean isCancelled() {
return cancelled;
}
@Override
public void setCancelled(boolean cancelled) {
this.cancelled = cancelled;
}
public ItemStack getNewItem() {
return newItem == null ? null : newItem.clone();
}
public void setNewItem(ItemStack newItem) {
this.newItem = newItem == null ? null : newItem.clone();
}
}

View File

@ -0,0 +1,50 @@
/*
* This file is part of Limbo.
*
* Copyright (C) 2022. LoohpJames <jamesloohp@gmail.com>
* Copyright (C) 2022. Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.loohp.limbo.events.inventory;
import com.loohp.limbo.events.Event;
import com.loohp.limbo.inventory.Inventory;
import com.loohp.limbo.inventory.InventoryView;
import com.loohp.limbo.player.Player;
public class InventoryEvent extends Event {
private final Player player;
private final InventoryView inventoryView;
private final Inventory clickedInventory;
public InventoryEvent(Player player, InventoryView inventoryView, Inventory clickedInventory) {
this.player = player;
this.inventoryView = inventoryView;
this.clickedInventory = clickedInventory;
}
public Player getPlayer() {
return player;
}
public InventoryView getInventoryView() {
return inventoryView;
}
public Inventory getClickedInventory() {
return clickedInventory;
}
}

View File

@ -0,0 +1,45 @@
/*
* This file is part of Limbo.
*
* Copyright (C) 2022. LoohpJames <jamesloohp@gmail.com>
* Copyright (C) 2022. Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.loohp.limbo.events.inventory;
import com.loohp.limbo.events.Cancellable;
import com.loohp.limbo.inventory.InventoryView;
import com.loohp.limbo.player.Player;
public class InventoryOpenEvent extends InventoryEvent implements Cancellable {
private boolean cancelled;
public InventoryOpenEvent(Player player, InventoryView inventoryView) {
super(player, inventoryView, inventoryView.getTopInventory());
this.cancelled = false;
}
@Override
public boolean isCancelled() {
return cancelled;
}
@Override
public void setCancelled(boolean cancelled) {
this.cancelled = cancelled;
}
}

View File

@ -0,0 +1,87 @@
/*
* This file is part of Limbo.
*
* Copyright (C) 2022. LoohpJames <jamesloohp@gmail.com>
* Copyright (C) 2022. Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.loohp.limbo.events.player;
import com.loohp.limbo.events.Cancellable;
import com.loohp.limbo.inventory.EquipmentSlot;
import com.loohp.limbo.inventory.ItemStack;
import com.loohp.limbo.location.BlockFace;
import com.loohp.limbo.player.Player;
import com.loohp.limbo.world.BlockState;
public class PlayerInteractEvent extends PlayerEvent implements Cancellable {
public enum Action {
LEFT_CLICK_AIR,
LEFT_CLICK_BLOCK,
PHYSICAL,
RIGHT_CLICK_AIR,
RIGHT_CLICK_BLOCK;
}
private boolean cancelled = false;
private final Action action;
private final ItemStack item;
private final BlockState clickedBlock;
private final BlockFace clickedFace;
private final EquipmentSlot hand;
public PlayerInteractEvent(Player player, Action action, ItemStack item, BlockState clickedBlock, BlockFace clickedFace, EquipmentSlot hand) {
super(player);
this.action = action;
this.item = item;
this.clickedBlock = clickedBlock;
this.clickedFace = clickedFace;
this.hand = hand;
}
public Action getAction() {
return action;
}
public ItemStack getItem() {
return item;
}
public BlockState getClickedBlock() {
return clickedBlock;
}
public BlockFace getClickedFace() {
return clickedFace;
}
public EquipmentSlot getHand() {
return hand;
}
@Override
public void setCancelled(boolean cancelled) {
this.cancelled = cancelled;
}
@Override
public boolean isCancelled() {
return cancelled;
}
}

View File

@ -0,0 +1,552 @@
/*
* This file is part of Limbo.
*
* Copyright (C) 2022. LoohpJames <jamesloohp@gmail.com>
* Copyright (C) 2022. Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.loohp.limbo.inventory;
import com.loohp.limbo.network.protocol.packets.PacketPlayOutSetSlot;
import com.loohp.limbo.network.protocol.packets.PacketPlayOutWindowItems;
import com.loohp.limbo.player.Player;
import net.kyori.adventure.key.Key;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicReferenceArray;
import java.util.function.IntUnaryOperator;
import java.util.stream.IntStream;
import java.util.stream.StreamSupport;
public abstract class AbstractInventory implements Inventory {
protected final InventoryHolder inventoryHolder;
protected final Map<Player, Integer> viewers;
protected final InventoryType inventoryType;
protected final AtomicReferenceArray<ItemStack> inventory;
protected final InventoryUpdateListener listener;
protected final IntUnaryOperator slotConvertor;
protected final IntUnaryOperator inverseSlotConvertor;
private final Unsafe unsafe;
protected int maxStackSize;
public AbstractInventory(int size, InventoryHolder inventoryHolder, InventoryType inventoryType, IntUnaryOperator slotConvertor, IntUnaryOperator inverseSlotConvertor) {
this.inventoryHolder = inventoryHolder;
this.viewers = new ConcurrentHashMap<>();
this.inventoryType = inventoryType;
this.inventory = new AtomicReferenceArray<>(size);
this.slotConvertor = slotConvertor == null ? IntUnaryOperator.identity() : slotConvertor;
this.inverseSlotConvertor = inverseSlotConvertor == null ? IntUnaryOperator.identity() : inverseSlotConvertor;
this.listener = (inventory, slot, oldItem, newItem) -> {
for (Map.Entry<Player, Integer> entry : viewers.entrySet()) {
try {
PacketPlayOutSetSlot packet = new PacketPlayOutSetSlot(entry.getValue(), 0, this.slotConvertor.applyAsInt(slot), newItem);
entry.getKey().clientConnection.sendPacket(packet);
} catch (IOException e) {
e.printStackTrace();
}
}
};
this.maxStackSize = 64;
this.unsafe = new Unsafe(this);
}
@Override
public void updateInventory(Player player) {
Integer windowId = viewers.get(player);
if (windowId == null) {
return;
}
ItemStack[] itemStackArray = new ItemStack[IntStream.range(0, inventory.length()).map(slotConvertor).max().orElse(-1) + 1];
for (int i = 0; i < inventory.length(); i++) {
itemStackArray[slotConvertor.applyAsInt(i)] = getItem(i);
}
try {
PacketPlayOutWindowItems packet = new PacketPlayOutWindowItems(windowId, 0, Arrays.asList(itemStackArray), ItemStack.AIR);
player.clientConnection.sendPacket(packet);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void updateInventory() {
ItemStack[] itemStackArray = new ItemStack[IntStream.range(0, inventory.length()).map(slotConvertor).max().orElse(0)];
for (int i = 0; i < inventory.length(); i++) {
itemStackArray[slotConvertor.applyAsInt(i)] = getItem(i);
}
List<ItemStack> itemStacks = Arrays.asList(itemStackArray);
for (Map.Entry<Player, Integer> entry : viewers.entrySet()) {
try {
PacketPlayOutWindowItems packet = new PacketPlayOutWindowItems(entry.getValue(), 0, itemStacks, ItemStack.AIR);
entry.getKey().clientConnection.sendPacket(packet);
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override
public int getSize() {
return inventory.length();
}
@Override
public int getMaxStackSize() {
return maxStackSize;
}
@Override
public void setMaxStackSize(int size) {
this.maxStackSize = size;
}
@Override
public ItemStack getItem(int index) {
return inventory.get(index);
}
@Override
public void setItem(int index, ItemStack item) {
if (item != null && item.type().equals(ItemStack.AIR.type())) {
item = null;
}
ItemStack oldItem = getItem(index);
if (!Objects.equals(item, oldItem)) {
inventory.set(index, item);
listener.slotChanged(this, index, oldItem, item);
}
}
public int firstPartial(Key material) {
for (int i = 0; i < inventory.length(); i++) {
ItemStack item = getItem(i);
if (item != null && item.type().equals(material) && item.amount() < item.getMaxStackSize()) {
return i;
}
}
return -1;
}
private int firstPartial(ItemStack item) {
if (item == null) {
return -1;
}
for (int i = 0; i < inventory.length(); i++) {
ItemStack cItem = getItem(i);
if (cItem != null && cItem.amount() < cItem.getMaxStackSize() && cItem.isSimilar(item)) {
return i;
}
}
return -1;
}
@Override
public HashMap<Integer, ItemStack> addItem(ItemStack... items) throws IllegalArgumentException {
HashMap<Integer, ItemStack> leftover = new HashMap<>();
for (int i = 0; i < items.length; i++) {
ItemStack item = items[i];
while (true) {
// Do we already have a stack of it?
int firstPartial = firstPartial(item);
// Drat! no partial stack
if (firstPartial == -1) {
// Find a free spot!
int firstFree = firstEmpty();
if (firstFree == -1) {
// No space at all!
leftover.put(i, item);
break;
} else {
// More than a single stack!
if (item.amount() > getMaxStackSize()) {
ItemStack stack = item.clone();
stack = stack.amount(getMaxStackSize());
setItem(firstFree, stack);
item = item.amount(item.amount() - getMaxStackSize());
items[i] = item;
} else {
// Just store it
setItem(firstFree, item);
break;
}
}
} else {
// So, apparently it might only partially fit, well lets do just that
ItemStack partialItem = getItem(firstPartial);
int amount = item.amount();
int partialAmount = partialItem.amount();
int maxAmount = partialItem.getMaxStackSize();
// Check if it fully fits
if (amount + partialAmount <= maxAmount) {
partialItem = partialItem.amount(amount + partialAmount);
// To make sure the packet is sent to the client
setItem(firstPartial, partialItem);
break;
}
// It fits partially
partialItem = partialItem.amount(maxAmount);
// To make sure the packet is sent to the client
setItem(firstPartial, partialItem);
item = item.amount(amount + partialAmount - maxAmount);
items[i] = item;
}
}
}
return leftover;
}
@Override
public HashMap<Integer, ItemStack> removeItem(ItemStack... items) throws IllegalArgumentException {
HashMap<Integer, ItemStack> leftover = new HashMap<>();
for (int i = 0; i < items.length; i++) {
ItemStack item = items[i];
int toDelete = item.amount();
while (true) {
int first = first(item, false);
// Drat! we don't have this type in the inventory
if (first == -1) {
item = item.amount(toDelete);
items[i] = item;
leftover.put(i, item);
break;
} else {
ItemStack itemStack = getItem(first);
int amount = itemStack.amount();
if (amount <= toDelete) {
toDelete -= amount;
// clear the slot, all used up
clear(first);
} else {
// split the stack and store
itemStack = itemStack.amount(amount - toDelete);
setItem(first, itemStack);
toDelete = 0;
}
}
// Bail when done
if (toDelete <= 0) {
break;
}
}
}
return leftover;
}
@Override
public ItemStack[] getContents() {
return StreamSupport.stream(spliterator(), false).toArray(ItemStack[]::new);
}
@Override
public void setContents(ItemStack[] items) throws IllegalArgumentException {
if (getSize() < items.length) {
throw new IllegalArgumentException("Invalid inventory size; expected " + getSize() + " or less");
}
for (int i = 0; i < getSize(); i++) {
if (i >= items.length) {
setItem(i, null);
} else {
setItem(i, items[i]);
}
}
}
@Override
public ItemStack[] getStorageContents() {
return getContents();
}
@Override
public void setStorageContents(ItemStack[] items) throws IllegalArgumentException {
setContents(items);
}
@Override
public boolean contains(Key material) throws IllegalArgumentException {
for (int i = 0; i < inventory.length(); i++) {
ItemStack itemStack = getItem(i);
if (itemStack != null && itemStack.type().equals(material)) {
return true;
}
}
return false;
}
@Override
public boolean contains(ItemStack item) {
for (int i = 0; i < inventory.length(); i++) {
ItemStack itemStack = getItem(i);
if (itemStack.equals(item)) {
return true;
}
}
return false;
}
@Override
public boolean contains(Key material, int amount) throws IllegalArgumentException {
if (amount <= 0) {
return true;
}
for (int i = 0; i < inventory.length(); i++) {
ItemStack itemStack = getItem(i);
if (itemStack != null && itemStack.type().equals(material)) {
if ((amount -= itemStack.amount()) <= 0) {
return true;
}
}
}
return false;
}
@Override
public boolean contains(ItemStack item, int amount) {
if (item == null) {
return false;
}
if (amount <= 0) {
return true;
}
for (int i = 0; i < inventory.length(); i++) {
ItemStack itemStack = getItem(i);
if (item.equals(itemStack) && --amount <= 0) {
return true;
}
}
return false;
}
@Override
public boolean containsAtLeast(ItemStack item, int amount) {
if (item == null) {
return false;
}
if (amount <= 0) {
return true;
}
for (int i = 0; i < inventory.length(); i++) {
ItemStack itemStack = getItem(i);
if (item.isSimilar(itemStack) && (amount -= itemStack.amount()) <= 0) {
return true;
}
}
return false;
}
@Override
public HashMap<Integer, ? extends ItemStack> all(Key material) throws IllegalArgumentException {
HashMap<Integer, ItemStack> slots = new HashMap<>();
ItemStack[] inventory = getStorageContents();
for (int i = 0; i < inventory.length; i++) {
ItemStack item = inventory[i];
if (item != null && item.type().equals(material)) {
slots.put(i, item);
}
}
return slots;
}
@Override
public HashMap<Integer, ? extends ItemStack> all(ItemStack item) {
HashMap<Integer, ItemStack> slots = new HashMap<>();
if (item != null) {
ItemStack[] inventory = getStorageContents();
for (int i = 0; i < inventory.length; i++) {
if (item.equals(inventory[i])) {
slots.put(i, inventory[i]);
}
}
}
return slots;
}
@Override
public int first(Key material) throws IllegalArgumentException {
for (int i = 0; i < inventory.length(); i++) {
ItemStack item = getItem(i);
if (item != null && item.type().equals(material)) {
return i;
}
}
return -1;
}
@Override
public int first(ItemStack item) {
return first(item, true);
}
private int first(ItemStack item, boolean withAmount) {
if (item == null) {
return -1;
}
for (int i = 0; i < inventory.length(); i++) {
ItemStack itemStack = inventory.get(i);
if (itemStack == null) continue;
if (withAmount ? item.equals(itemStack) : item.isSimilar(itemStack)) {
return i;
}
}
return -1;
}
@Override
public int firstEmpty() {
for (int i = 0; i < inventory.length(); i++) {
if (getItem(i) == null) {
return i;
}
}
return -1;
}
@Override
public boolean isEmpty() {
for (int i = 0; i < inventory.length(); i++) {
if (getItem(i) != null) {
return false;
}
}
return true;
}
@Override
public void remove(Key material) throws IllegalArgumentException {
for (int i = 0; i < inventory.length(); i++) {
ItemStack itemStack = getItem(i);
if (itemStack != null && itemStack.type().equals(material)) {
clear(i);
}
}
}
@Override
public void remove(ItemStack item) {
for (int i = 0; i < inventory.length(); i++) {
ItemStack itemStack = getItem(i);
if (itemStack != null && itemStack.equals(item)) {
clear(i);
}
}
}
@Override
public void clear(int index) {
setItem(index, null);
}
@Override
public void clear() {
for (int i = 0; i < inventory.length(); i++) {
setItem(i, null);
}
}
@Override
public Set<Player> getViewers() {
return Collections.unmodifiableSet(viewers.keySet());
}
@Override
public InventoryType getType() {
return inventoryType;
}
@Override
public InventoryHolder getHolder() {
return inventoryHolder;
}
@Override
public ListIterator<ItemStack> iterator() {
return new InventoryIterator(this);
}
@Override
public ListIterator<ItemStack> iterator(int index) {
if (index < 0) {
index += getSize() + 1; // ie, with -1, previous() will return the last element
}
return new InventoryIterator(this, index);
}
@SuppressWarnings("DeprecatedIsStillUsed")
@Deprecated
public Unsafe getUnsafe() {
return unsafe;
}
@SuppressWarnings("DeprecatedIsStillUsed")
@Deprecated
public static class Unsafe implements Inventory.Unsafe {
private final AbstractInventory inventory;
@Deprecated
public Unsafe(AbstractInventory inventory) {
this.inventory = inventory;
}
@Deprecated
public void a(int index, ItemStack itemStack) {
inventory.inventory.set(index, itemStack);
}
@Deprecated
public void b(int index, ItemStack itemStack) {
inventory.inventory.set(inventory.inverseSlotConvertor.applyAsInt(index), itemStack);
}
@Deprecated
public IntUnaryOperator a() {
return inventory.slotConvertor;
}
@Deprecated
public IntUnaryOperator b() {
return inventory.inverseSlotConvertor;
}
@Override
public Map<Player, Integer> c() {
return inventory.viewers;
}
}
}

View File

@ -0,0 +1,57 @@
/*
* This file is part of Limbo.
*
* Copyright (C) 2022. LoohpJames <jamesloohp@gmail.com>
* Copyright (C) 2022. Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.loohp.limbo.inventory;
import com.loohp.limbo.location.Location;
import net.kyori.adventure.text.Component;
public class CustomInventory extends AbstractInventory implements TitledInventory {
@SuppressWarnings("DeprecatedIsStillUsed")
@Deprecated
public static CustomInventory create(Component title, int size, InventoryHolder inventoryHolder) {
if (size % 9 != 0 || size > 54 || size < 9) {
throw new IllegalArgumentException("size must be a multiple of 9 and within 9 - 54");
}
return new CustomInventory(title, size, inventoryHolder);
}
private Component title;
private CustomInventory(Component title, int size, InventoryHolder inventoryHolder) {
super(size, inventoryHolder, InventoryType.CHEST, null, null);
this.title = title;
}
@Override
public Component getTitle() {
return title;
}
public void setTitle(Component title) {
this.title = title;
}
@Override
public Location getLocation() {
return null;
}
}

View File

@ -28,4 +28,24 @@ public enum EquipmentSlot {
LEGGINGS, LEGGINGS,
BOOTS; BOOTS;
public boolean isHandSlot() {
switch (this) {
case MAINHAND:
case OFFHAND:
return true;
}
return false;
}
public boolean isArmorSlot() {
switch (this) {
case HELMET:
case CHESTPLATE:
case LEGGINGS:
case BOOTS:
return true;
}
return false;
}
} }

View File

@ -0,0 +1,403 @@
/*
* This file is part of Limbo.
*
* Copyright (C) 2022. LoohpJames <jamesloohp@gmail.com>
* Copyright (C) 2022. Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.loohp.limbo.inventory;
import com.loohp.limbo.location.Location;
import com.loohp.limbo.player.Player;
import net.kyori.adventure.key.Key;
import org.jetbrains.annotations.Contract;
import java.util.HashMap;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import java.util.function.IntUnaryOperator;
public interface Inventory extends Iterable<ItemStack> {
/**
* Returns the size of the inventory
*
* @return The size of the inventory
*/
int getSize();
/**
* Returns the maximum stack size for an ItemStack in this inventory.
*
* @return The maximum size for an ItemStack in this inventory.
*/
int getMaxStackSize();
/**
* This method allows you to change the maximum stack size for an
* inventory.
* <p>
* <b>Caveats:</b>
* <ul>
* <li>Not all inventories respect this value.
* <li>Stacks larger than 127 may be clipped when the world is saved.
* <li>This value is not guaranteed to be preserved; be sure to set it
* before every time you want to set a slot over the max stack size.
* <li>Stacks larger than the default max size for this type of inventory
* may not display correctly in the client.
* </ul>
*
* @param size The new maximum stack size for items in this inventory.
*/
void setMaxStackSize(int size);
/**
* Returns the ItemStack found in the slot at the given index
*
* @param index The index of the Slot's ItemStack to return
* @return The ItemStack in the slot
*/
ItemStack getItem(int index);
/**
* Stores the ItemStack at the given index of the inventory.
*
* @param index The index where to put the ItemStack
* @param item The ItemStack to set
*/
void setItem(int index, ItemStack item);
/**
* Stores the given ItemStacks in the inventory. This will try to fill
* existing stacks and empty slots as well as it can.
* <p>
* The returned HashMap contains what it couldn't store, where the key is
* the index of the parameter, and the value is the ItemStack at that
* index of the varargs parameter. If all items are stored, it will return
* an empty HashMap.
* <p>
* If you pass in ItemStacks which exceed the maximum stack size for the
* Material, first they will be added to partial stacks where
* Material.getMaxStackSize() is not exceeded, up to
* Material.getMaxStackSize(). When there are no partial stacks left
* stacks will be split on Inventory.getMaxStackSize() allowing you to
* exceed the maximum stack size for that material.
* <p>
* It is known that in some implementations this method will also set
* the inputted argument amount to the number of that item not placed in
* slots.
*
* @param items The ItemStacks to add
* @return A HashMap containing items that didn't fit.
* @throws IllegalArgumentException if items or any element in it is null
*/
HashMap<Integer, ItemStack> addItem(ItemStack... items) throws IllegalArgumentException;
/**
* Removes the given ItemStacks from the inventory.
* <p>
* It will try to remove 'as much as possible' from the types and amounts
* you give as arguments.
* <p>
* The returned HashMap contains what it couldn't remove, where the key is
* the index of the parameter, and the value is the ItemStack at that
* index of the varargs parameter. If all the given ItemStacks are
* removed, it will return an empty HashMap.
* <p>
* It is known that in some implementations this method will also set the
* inputted argument amount to the number of that item not removed from
* slots.
*
* @param items The ItemStacks to remove
* @return A HashMap containing items that couldn't be removed.
* @throws IllegalArgumentException if items is null
*/
HashMap<Integer, ItemStack> removeItem(ItemStack... items) throws IllegalArgumentException;
/**
* Returns all ItemStacks from the inventory
*
* @return An array of ItemStacks from the inventory. Individual items may be null.
*/
ItemStack[] getContents();
/**
* Completely replaces the inventory's contents. Removes all existing
* contents and replaces it with the ItemStacks given in the array.
*
* @param items A complete replacement for the contents; the length must
* be less than or equal to {@link #getSize()}.
* @throws IllegalArgumentException If the array has more items than the
* inventory.
*/
void setContents(ItemStack[] items) throws IllegalArgumentException;
/**
* Return the contents from the section of the inventory where items can
* reasonably be expected to be stored. In most cases this will represent
* the entire inventory, but in some cases it may exclude armor or result
* slots.
* <br>
* It is these contents which will be used for add / contains / remove
* methods which look for a specific stack.
*
* @return inventory storage contents. Individual items may be null.
*/
ItemStack[] getStorageContents();
/**
* Put the given ItemStacks into the storage slots
*
* @param items The ItemStacks to use as storage contents
* @throws IllegalArgumentException If the array has more items than the
* inventory.
*/
void setStorageContents(ItemStack[] items) throws IllegalArgumentException;
/**
* Checks if the inventory contains any ItemStacks with the given
* material.
*
* @param material The material to check for
* @return true if an ItemStack is found with the given Material
* @throws IllegalArgumentException if material is null
*/
boolean contains(Key material) throws IllegalArgumentException;
/**
* Checks if the inventory contains any ItemStacks matching the given
* ItemStack.
* <p>
* This will only return true if both the type and the amount of the stack
* match.
*
* @param item The ItemStack to match against
* @return false if item is null, true if any exactly matching ItemStacks
* were found
*/
@Contract("null -> false")
boolean contains(ItemStack item);
/**
* Checks if the inventory contains any ItemStacks with the given
* material, adding to at least the minimum amount specified.
*
* @param material The material to check for
* @param amount The minimum amount
* @return true if amount is less than 1, true if enough ItemStacks were
* found to add to the given amount
* @throws IllegalArgumentException if material is null
*/
boolean contains(Key material, int amount) throws IllegalArgumentException;
/**
* Checks if the inventory contains at least the minimum amount specified
* of exactly matching ItemStacks.
* <p>
* An ItemStack only counts if both the type and the amount of the stack
* match.
*
* @param item the ItemStack to match against
* @param amount how many identical stacks to check for
* @return false if item is null, true if amount less than 1, true if
* amount of exactly matching ItemStacks were found
* @see #containsAtLeast(ItemStack, int)
*/
@Contract("null, _ -> false")
boolean contains(ItemStack item, int amount);
/**
* Checks if the inventory contains ItemStacks matching the given
* ItemStack whose amounts sum to at least the minimum amount specified.
*
* @param item the ItemStack to match against
* @param amount the minimum amount
* @return false if item is null, true if amount less than 1, true if
* enough ItemStacks were found to add to the given amount
*/
@Contract("null, _ -> false")
boolean containsAtLeast(ItemStack item, int amount);
/**
* Returns a HashMap with all slots and ItemStacks in the inventory with
* the given Material.
* <p>
* The HashMap contains entries where, the key is the slot index, and the
* value is the ItemStack in that slot. If no matching ItemStack with the
* given Material is found, an empty map is returned.
*
* @param material The material to look for
* @return A HashMap containing the slot index, ItemStack pairs
* @throws IllegalArgumentException if material is null
*/
HashMap<Integer, ? extends ItemStack> all(Key material) throws IllegalArgumentException;
/**
* Finds all slots in the inventory containing any ItemStacks with the
* given ItemStack. This will only match slots if both the type and the
* amount of the stack match
* <p>
* The HashMap contains entries where, the key is the slot index, and the
* value is the ItemStack in that slot. If no matching ItemStack with the
* given Material is found, an empty map is returned.
*
* @param item The ItemStack to match against
* @return A map from slot indexes to item at index
*/
HashMap<Integer, ? extends ItemStack> all(ItemStack item);
/**
* Finds the first slot in the inventory containing an ItemStack with the
* given material
*
* @param material The material to look for
* @return The slot index of the given Material or -1 if not found
* @throws IllegalArgumentException if material is null
*/
int first(Key material) throws IllegalArgumentException;
/**
* Returns the first slot in the inventory containing an ItemStack with
* the given stack. This will only match a slot if both the type and the
* amount of the stack match
*
* @param item The ItemStack to match against
* @return The slot index of the given ItemStack or -1 if not found
*/
int first(ItemStack item);
/**
* Returns the first empty Slot.
*
* @return The first empty Slot found, or -1 if no empty slots.
*/
int firstEmpty();
/**
* Check whether or not this inventory is empty. An inventory is considered
* to be empty if there are no ItemStacks in any slot of this inventory.
*
* @return true if empty, false otherwise
*/
boolean isEmpty();
/**
* Removes all stacks in the inventory matching the given material.
*
* @param material The material to remove
* @throws IllegalArgumentException if material is null
*/
void remove(Key material) throws IllegalArgumentException;
/**
* Removes all stacks in the inventory matching the given stack.
* <p>
* This will only match a slot if both the type and the amount of the
* stack match
*
* @param item The ItemStack to match against
*/
void remove(ItemStack item);
/**
* Clears out a particular slot in the index.
*
* @param index The index to empty.
*/
void clear(int index);
/**
* Clears out the whole Inventory.
*/
void clear();
/**
* Gets a list of players viewing the inventory. Note that a player is
* considered to be viewing their own inventory and internal crafting
* screen even when said inventory is not open. They will normally be
* considered to be viewing their inventory even when they have a
* different inventory screen open, but it's possible for customized
* inventory screens to exclude the viewer's inventory, so this should
* never be assumed to be non-empty.
*
* @return A set of HumanEntities who are viewing this Inventory.
*/
Set<Player> getViewers();
/**
* Returns what type of inventory this is.
*
* @return The InventoryType representing the type of inventory.
*/
InventoryType getType();
/**
* Gets the block or entity belonging to the open inventory
*
* @return The holder of the inventory; null if it has no holder.
*/
InventoryHolder getHolder();
@Override
ListIterator<ItemStack> iterator();
/**
* Returns an iterator starting at the given index. If the index is
* positive, then the first call to next() will return the item at that
* index; if it is negative, the first call to previous will return the
* item at index (getSize() + index).
*
* @param index The index.
* @return An iterator.
*/
ListIterator<ItemStack> iterator(int index);
/**
* Get the location of the block or entity which corresponds to this inventory. May return null if this container
* was custom created or is a virtual / subcontainer.
*
* @return location or null if not applicable.
*/
Location getLocation();
void updateInventory();
void updateInventory(Player player);
@Deprecated
Unsafe getUnsafe();
@Deprecated
interface Unsafe {
@Deprecated
void a(int index, ItemStack itemStack);
@Deprecated
void b(int index, ItemStack itemStack);
@Deprecated
IntUnaryOperator a();
@Deprecated
IntUnaryOperator b();
@Deprecated
Map<Player, Integer> c();
}
}

View File

@ -0,0 +1,26 @@
/*
* This file is part of Limbo.
*
* Copyright (C) 2022. LoohpJames <jamesloohp@gmail.com>
* Copyright (C) 2022. Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.loohp.limbo.inventory;
public enum InventoryClickType {
PICKUP, QUICK_MOVE, SWAP, CLONE, THROW, QUICK_CRAFT, PICKUP_ALL;
}

View File

@ -0,0 +1,28 @@
/*
* This file is part of Limbo.
*
* Copyright (C) 2022. LoohpJames <jamesloohp@gmail.com>
* Copyright (C) 2022. Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.loohp.limbo.inventory;
public interface InventoryHolder {
Inventory getInventory();
InventoryHolder getHolder();
}

View File

@ -0,0 +1,90 @@
/*
* This file is part of Limbo.
*
* Copyright (C) 2022. LoohpJames <jamesloohp@gmail.com>
* Copyright (C) 2022. Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.loohp.limbo.inventory;
import java.util.ListIterator;
public class InventoryIterator implements ListIterator<ItemStack> {
private final Inventory inventory;
private int nextIndex;
private Boolean lastDirection; // true = forward, false = backward, null = haven't moved yet
InventoryIterator(Inventory inventory) {
this.inventory = inventory;
this.nextIndex = 0;
}
InventoryIterator(Inventory inventory, int index) {
this.inventory = inventory;
this.nextIndex = index;
}
@Override
public boolean hasNext() {
return nextIndex < inventory.getSize();
}
@Override
public ItemStack next() {
lastDirection = true;
return inventory.getItem(nextIndex++);
}
@Override
public int nextIndex() {
return nextIndex;
}
@Override
public boolean hasPrevious() {
return nextIndex > 0;
}
@Override
public ItemStack previous() {
lastDirection = false;
return inventory.getItem(--nextIndex);
}
@Override
public int previousIndex() {
return nextIndex - 1;
}
@Override
public void set(ItemStack item) {
if (lastDirection == null) {
throw new IllegalStateException("No current item!");
}
int i = lastDirection ? nextIndex - 1 : nextIndex;
inventory.setItem(i, item);
}
@Override
public void add(ItemStack item) {
throw new UnsupportedOperationException("Can't change the size of an inventory!");
}
@Override
public void remove() {
throw new UnsupportedOperationException("Can't change the size of an inventory!");
}
}

View File

@ -0,0 +1,263 @@
/*
* This file is part of Limbo.
*
* Copyright (C) 2022. LoohpJames <jamesloohp@gmail.com>
* Copyright (C) 2022. Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.loohp.limbo.inventory;
import net.kyori.adventure.key.Key;
public enum InventoryType {
/**
* A chest inventory, with 0, 9, 18, 27, 36, 45, or 54 slots of type
* CONTAINER.
*/
CHEST(27, "Chest"),
/**
* A dispenser inventory, with 9 slots of type CONTAINER.
*/
DISPENSER(9, "Dispenser"),
/**
* A dropper inventory, with 9 slots of type CONTAINER.
*/
DROPPER(9, "Dropper"),
/**
* A furnace inventory, with a RESULT slot, a CRAFTING slot, and a FUEL
* slot.
*/
FURNACE(3, "Furnace"),
/**
* A workbench inventory, with 9 CRAFTING slots and a RESULT slot.
*/
WORKBENCH(10, "Crafting"),
/**
* A player's crafting inventory, with 4 CRAFTING slots and a RESULT slot.
* Also implies that the 4 ARMOR slots are accessible.
*/
CRAFTING(5, "Crafting", false),
/**
* An enchantment table inventory, with two CRAFTING slots and three
* enchanting buttons.
*/
ENCHANTING(2, "Enchanting"),
/**
* A brewing stand inventory, with one FUEL slot and four CRAFTING slots.
*/
BREWING(5, "Brewing"),
/**
* A player's inventory, with 9 QUICKBAR slots, 27 CONTAINER slots, 4 ARMOR
* slots and 1 offhand slot. The ARMOR and offhand slots may not be visible
* to the player, though.
*/
PLAYER(41, "Player"),
/**
* The creative mode inventory, with only 9 QUICKBAR slots and nothing
* else. (The actual creative interface with the items is client-side and
* cannot be altered by the server.)
*/
CREATIVE(9, "Creative", false),
/**
* The merchant inventory, with 2 CRAFTING slots, and 1 RESULT slot.
*/
MERCHANT(3, "Villager", false),
/**
* The ender chest inventory, with 27 slots.
*/
ENDER_CHEST(27, "Ender Chest"),
/**
* An anvil inventory, with 2 CRAFTING slots and 1 RESULT slot
*/
ANVIL(3, "Repairing"),
/**
* A smithing inventory, with 2 CRAFTING slots and 1 RESULT slot
*/
SMITHING(3, "Upgrade Gear"),
/**
* A beacon inventory, with 1 CRAFTING slot
*/
BEACON(1, "container.beacon"),
/**
* A hopper inventory, with 5 slots of type CONTAINER.
*/
HOPPER(5, "Item Hopper"),
/**
* A shulker box inventory, with 27 slots of type CONTAINER.
*/
SHULKER_BOX(27, "Shulker Box"),
/**
* A barrel box inventory, with 27 slots of type CONTAINER.
*/
BARREL(27, "Barrel"),
/**
* A blast furnace inventory, with a RESULT slot, a CRAFTING slot, and a
* FUEL slot.
*/
BLAST_FURNACE(3, "Blast Furnace"),
/**
* A lectern inventory, with 1 BOOK slot.
*/
LECTERN(1, "Lectern"),
/**
* A smoker inventory, with a RESULT slot, a CRAFTING slot, and a FUEL slot.
*/
SMOKER(3, "Smoker"),
/**
* Loom inventory, with 3 CRAFTING slots, and 1 RESULT slot.
*/
LOOM(4, "Loom"),
/**
* Cartography inventory with 2 CRAFTING slots, and 1 RESULT slot.
*/
CARTOGRAPHY(3, "Cartography Table"),
/**
* Grindstone inventory with 2 CRAFTING slots, and 1 RESULT slot.
*/
GRINDSTONE(3, "Repair & Disenchant"),
/**
* Stonecutter inventory with 1 CRAFTING slot, and 1 RESULT slot.
*/
STONECUTTER(2, "Stonecutter"),
/**
* Pseudo composter inventory with 0 or 1 slots of undefined type.
*/
COMPOSTER(1, "Composter"),
/**
* Pseudo chiseled bookshelf inventory, with 6 slots of undefined type.
*/
CHISELED_BOOKSHELF(6, "Chiseled Bookshelf"),
;
private final int size;
private final String title;
private final boolean isCreatable;
InventoryType(int defaultSize, /*@NotNull*/ String defaultTitle) {
this(defaultSize, defaultTitle, true);
}
InventoryType(int defaultSize, /*@NotNull*/ String defaultTitle, boolean isCreatable) {
size = defaultSize;
title = defaultTitle;
this.isCreatable = isCreatable;
}
public int getDefaultSize() {
return size;
}
public String getDefaultTitle() {
return title;
}
/**
* Denotes that this InventoryType can be created via the normal
* {@link org.bukkit.Bukkit#createInventory} methods.
*
* @return if this InventoryType can be created and shown to a player
*/
public boolean isCreatable() {
return isCreatable;
}
public Key getRawType(int slots) {
switch (this) {
case CHEST:
return Key.key("minecraft:generic_9x" + (slots / 9));
case DISPENSER:
case DROPPER:
return Key.key("minecraft:generic_3x3");
case FURNACE:
return Key.key("minecraft:furnace");
case WORKBENCH:
return Key.key("minecraft:crafting");
case ENCHANTING:
return Key.key("minecraft:enchantment");
case BREWING:
return Key.key("minecraft:brewing_stand");
case MERCHANT:
return Key.key("minecraft:merchant");
case ENDER_CHEST:
case BARREL:
return Key.key("minecraft:generic_9x3");
case ANVIL:
return Key.key("minecraft:anvil");
case SMITHING:
return Key.key("minecraft:smithing");
case BEACON:
return Key.key("minecraft:beacon");
case HOPPER:
return Key.key("minecraft:hopper");
case SHULKER_BOX:
return Key.key("minecraft:shulker_box");
case BLAST_FURNACE:
return Key.key("minecraft:blast_furnace");
case LECTERN:
return Key.key("minecraft:lectern");
case SMOKER:
return Key.key("minecraft:smoker");
case LOOM:
return Key.key("minecraft:loom");
case CARTOGRAPHY:
return Key.key("minecraft:cartography_table");
case GRINDSTONE:
return Key.key("minecraft:grindstone");
case STONECUTTER:
return Key.key("minecraft:stonecutter");
case CRAFTING:
case PLAYER:
case CREATIVE:
case COMPOSTER:
case CHISELED_BOOKSHELF:
return null;
}
return null;
}
public enum SlotType {
/**
* A result slot in a furnace or crafting inventory.
*/
RESULT,
/**
* A slot in the crafting matrix, or an 'input' slot.
*/
CRAFTING,
/**
* An armour slot in the player's inventory.
*/
ARMOR,
/**
* A regular slot in the container or the player's inventory; anything
* not covered by the other enum values.
*/
CONTAINER,
/**
* A slot in the bottom row or quickbar.
*/
QUICKBAR,
/**
* A pseudo-slot representing the area outside the inventory window.
*/
OUTSIDE,
/**
* The fuel slot in a furnace inventory, or the ingredient slot in a
* brewing stand inventory.
*/
FUEL;
}
}

View File

@ -0,0 +1,27 @@
/*
* This file is part of Limbo.
*
* Copyright (C) 2022. LoohpJames <jamesloohp@gmail.com>
* Copyright (C) 2022. Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.loohp.limbo.inventory;
@FunctionalInterface
public interface InventoryUpdateListener {
void slotChanged(Inventory inventory, int slot, ItemStack oldItem, ItemStack newItem);
}

View File

@ -0,0 +1,267 @@
/*
* This file is part of Limbo.
*
* Copyright (C) 2022. LoohpJames <jamesloohp@gmail.com>
* Copyright (C) 2022. Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.loohp.limbo.inventory;
import com.loohp.limbo.network.protocol.packets.PacketPlayOutWindowData;
import com.loohp.limbo.player.Player;
import java.io.IOException;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class InventoryView {
private final Player player;
private final String title;
private Inventory topInventory;
private final Inventory bottomInventory;
private final Map<Property, Integer> properties;
private ItemStack carriedItem;
private final Unsafe unsafe;
public InventoryView(Player player, String title, Inventory topInventory, Inventory bottomInventory) {
this.player = player;
this.title = title;
this.topInventory = topInventory;
this.bottomInventory = bottomInventory;
this.properties = new ConcurrentHashMap<>();
this.carriedItem = null;
this.unsafe = new Unsafe(this);
}
public ItemStack getCarriedItem() {
return carriedItem;
}
public void setCarriedItem(ItemStack carriedItem) {
this.carriedItem = carriedItem;
}
public InventoryType getType() {
return topInventory.getType();
}
public Player getPlayer() {
return player;
}
public String getTitle() {
return title;
}
public Inventory getTopInventory() {
return topInventory;
}
public Inventory getBottomInventory() {
return bottomInventory;
}
public Map<Property, Integer> getProperties() {
return Collections.unmodifiableMap(properties);
}
public int countSlots() {
return topInventory.getSize() + bottomInventory.getSize();
}
public ItemStack getItem(int index) {
if (topInventory != null) {
if (index < topInventory.getSize()) {
return topInventory.getItem(topInventory.getUnsafe().b().applyAsInt(index));
}
index -= topInventory.getSize();
}
return bottomInventory.getItem(bottomInventory.getUnsafe().b().applyAsInt(index));
}
public void setItem(int index, ItemStack itemStack) {
if (topInventory != null) {
if (index < topInventory.getSize()) {
topInventory.setItem(topInventory.getUnsafe().b().applyAsInt(index), itemStack);
return;
}
index -= topInventory.getSize();
}
bottomInventory.setItem(bottomInventory.getUnsafe().b().applyAsInt(index), itemStack);
}
public void setProperty(InventoryView.Property prop, int value) {
if (topInventory != null && prop.getType().equals(topInventory.getType())) {
Integer id = topInventory.getUnsafe().c().get(player);
if (id != null) {
properties.put(prop, value);
PacketPlayOutWindowData packet = new PacketPlayOutWindowData(id, prop.getId(), value);
try {
player.clientConnection.sendPacket(packet);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
@SuppressWarnings("DeprecatedIsStillUsed")
@Deprecated
public Unsafe getUnsafe() {
return unsafe;
}
@SuppressWarnings("DeprecatedIsStillUsed")
@Deprecated
public static class Unsafe {
private final InventoryView inventoryView;
@Deprecated
public Unsafe(InventoryView inventoryView) {
this.inventoryView = inventoryView;
}
@Deprecated
public void a(Inventory topInventory) {
inventoryView.topInventory = topInventory;
inventoryView.properties.clear();
}
}
/**
* Represents various extra properties of certain inventory windows.
*/
public enum Property {
/**
* The progress of the down-pointing arrow in a brewing inventory.
*/
BREW_TIME(0, InventoryType.BREWING),
/**
* The progress of the fuel slot in a brewing inventory.
* <p>
* This is a value between 0 and 20, with 0 making the bar empty, and 20
* making the bar full.
*/
FUEL_TIME(1, InventoryType.BREWING),
/**
* The progress of the flame in a furnace inventory.
*/
BURN_TIME(0, InventoryType.FURNACE),
/**
* How many total ticks the current fuel should last.
*/
TICKS_FOR_CURRENT_FUEL(1, InventoryType.FURNACE),
/**
* The progress of the right-pointing arrow in a furnace inventory.
*/
COOK_TIME(2, InventoryType.FURNACE),
/**
* How many total ticks the current smelting should last.
*/
TICKS_FOR_CURRENT_SMELTING(3, InventoryType.FURNACE),
/**
* In an enchanting inventory, the top button's experience level
* value.
*/
ENCHANT_BUTTON1(0, InventoryType.ENCHANTING),
/**
* In an enchanting inventory, the middle button's experience level
* value.
*/
ENCHANT_BUTTON2(1, InventoryType.ENCHANTING),
/**
* In an enchanting inventory, the bottom button's experience level
* value.
*/
ENCHANT_BUTTON3(2, InventoryType.ENCHANTING),
/**
* In an enchanting inventory, the first four bits of the player's xpSeed.
*/
ENCHANT_XP_SEED(3, InventoryType.ENCHANTING),
/**
* In an enchanting inventory, the top button's enchantment's id
*/
ENCHANT_ID1(4, InventoryType.ENCHANTING),
/**
* In an enchanting inventory, the middle button's enchantment's id
*/
ENCHANT_ID2(5, InventoryType.ENCHANTING),
/**
* In an enchanting inventory, the bottom button's enchantment's id
*/
ENCHANT_ID3(6, InventoryType.ENCHANTING),
/**
* In an enchanting inventory, the top button's level value.
*/
ENCHANT_LEVEL1(7, InventoryType.ENCHANTING),
/**
* In an enchanting inventory, the middle button's level value.
*/
ENCHANT_LEVEL2(8, InventoryType.ENCHANTING),
/**
* In an enchanting inventory, the bottom button's level value.
*/
ENCHANT_LEVEL3(9, InventoryType.ENCHANTING),
/**
* In an beacon inventory, the levels of the beacon
*/
LEVELS(0, InventoryType.BEACON),
/**
* In an beacon inventory, the primary potion effect
*/
PRIMARY_EFFECT(1, InventoryType.BEACON),
/**
* In an beacon inventory, the secondary potion effect
*/
SECONDARY_EFFECT(2, InventoryType.BEACON),
/**
* The repair's cost in xp levels
*/
REPAIR_COST(0, InventoryType.ANVIL),
/**
* The lectern's current open book page
*/
BOOK_PAGE(0, InventoryType.LECTERN);
private final int id;
private final InventoryType style;
Property(int id, InventoryType appliesTo) {
this.id = id;
style = appliesTo;
}
public InventoryType getType() {
return style;
}
/**
* Gets the id of this view.
*
* @return the id of this view
* @deprecated Magic value
*/
@Deprecated
public int getId() {
return id;
}
}
}

View File

@ -0,0 +1,122 @@
/*
* This file is part of Limbo.
*
* Copyright (C) 2022. LoohpJames <jamesloohp@gmail.com>
* Copyright (C) 2022. Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.loohp.limbo.inventory;
import net.kyori.adventure.key.Key;
import net.querz.nbt.tag.CompoundTag;
import java.util.Objects;
public class ItemStack implements Cloneable {
public static final ItemStack AIR = new ItemStack(Key.key("minecraft:air"));
private final Key material;
private final int amount;
private final CompoundTag nbt;
private CompoundTag fullTag;
public ItemStack(Key material) {
this(material, 1);
}
public ItemStack(Key material, int amount) {
this(material, amount, null);
}
public ItemStack(Key material, int amount, CompoundTag nbt) {
this.material = material;
this.amount = amount;
this.nbt = nbt;
this.fullTag = null;
}
public ItemStack(CompoundTag fullTag) {
this.material = Key.key(fullTag.getString("id"));
this.amount = fullTag.getInt("Count");
this.nbt = fullTag.containsKey("tag") ? fullTag.getCompoundTag("tag") : null;
this.fullTag = fullTag.clone();
}
@SuppressWarnings("MethodDoesntCallSuperMethod")
@Override
public ItemStack clone() {
return new ItemStack(material, amount, nbt == null ? null : nbt.clone());
}
public Key type() {
return material;
}
public ItemStack type(Key material) {
return new ItemStack(material, amount, nbt == null ? null : nbt.clone());
}
public int amount() {
return amount;
}
public ItemStack amount(int amount) {
return new ItemStack(material, amount, nbt == null ? null : nbt.clone());
}
public CompoundTag nbt() {
return nbt;
}
public ItemStack nbt(CompoundTag nbt) {
return new ItemStack(material, amount, nbt == null ? null : nbt.clone());
}
public int getMaxStackSize() {
return 64;
}
public CompoundTag getFullTag() {
if (fullTag != null) {
return fullTag;
}
CompoundTag compoundTag = new CompoundTag();
compoundTag.putString("id", material.toString());
compoundTag.putInt("Count", amount);
if (nbt != null) {
compoundTag.put("tag", nbt);
}
return fullTag = compoundTag;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ItemStack itemStack = (ItemStack) o;
return amount == itemStack.amount && material.equals(itemStack.material) && Objects.equals(nbt, itemStack.nbt);
}
public boolean isSimilar(ItemStack stack) {
return material.equals(stack.material) && Objects.equals(nbt, stack.nbt);
}
@Override
public int hashCode() {
return Objects.hash(material, amount, nbt);
}
}

View File

@ -0,0 +1,28 @@
/*
* This file is part of Limbo.
*
* Copyright (C) 2022. LoohpJames <jamesloohp@gmail.com>
* Copyright (C) 2022. Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.loohp.limbo.inventory;
import net.kyori.adventure.text.Component;
public interface TitledInventory extends Inventory {
Component getTitle();
}

View File

@ -0,0 +1,185 @@
/*
* This file is part of Limbo.
*
* Copyright (C) 2022. LoohpJames <jamesloohp@gmail.com>
* Copyright (C) 2022. Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.loohp.limbo.location;
/**
* Represents the face of a block
*/
public enum BlockFace {
NORTH(0, 0, -1),
EAST(1, 0, 0),
SOUTH(0, 0, 1),
WEST(-1, 0, 0),
UP(0, 1, 0),
DOWN(0, -1, 0),
NORTH_EAST(NORTH, EAST),
NORTH_WEST(NORTH, WEST),
SOUTH_EAST(SOUTH, EAST),
SOUTH_WEST(SOUTH, WEST),
WEST_NORTH_WEST(WEST, NORTH_WEST),
NORTH_NORTH_WEST(NORTH, NORTH_WEST),
NORTH_NORTH_EAST(NORTH, NORTH_EAST),
EAST_NORTH_EAST(EAST, NORTH_EAST),
EAST_SOUTH_EAST(EAST, SOUTH_EAST),
SOUTH_SOUTH_EAST(SOUTH, SOUTH_EAST),
SOUTH_SOUTH_WEST(SOUTH, SOUTH_WEST),
WEST_SOUTH_WEST(WEST, SOUTH_WEST),
SELF(0, 0, 0);
private final int modX;
private final int modY;
private final int modZ;
BlockFace(final int modX, final int modY, final int modZ) {
this.modX = modX;
this.modY = modY;
this.modZ = modZ;
}
BlockFace(final BlockFace face1, final BlockFace face2) {
this.modX = face1.getModX() + face2.getModX();
this.modY = face1.getModY() + face2.getModY();
this.modZ = face1.getModZ() + face2.getModZ();
}
/**
* Get the amount of X-coordinates to modify to get the represented block
*
* @return Amount of X-coordinates to modify
*/
public int getModX() {
return modX;
}
/**
* Get the amount of Y-coordinates to modify to get the represented block
*
* @return Amount of Y-coordinates to modify
*/
public int getModY() {
return modY;
}
/**
* Get the amount of Z-coordinates to modify to get the represented block
*
* @return Amount of Z-coordinates to modify
*/
public int getModZ() {
return modZ;
}
/**
* Gets the normal vector corresponding to this block face.
*
* @return the normal vector
*/
public Vector getDirection() {
Vector direction = new Vector(modX, modY, modZ);
if (modX != 0 || modY != 0 || modZ != 0) {
direction.normalize();
}
return direction;
}
/**
* Returns true if this face is aligned with one of the unit axes in 3D
* Cartesian space (ie NORTH, SOUTH, EAST, WEST, UP, DOWN).
*
* @return Cartesian status
*/
public boolean isCartesian() {
switch (this) {
case NORTH:
case SOUTH:
case EAST:
case WEST:
case UP:
case DOWN:
return true;
default:
return false;
}
}
public BlockFace getOppositeFace() {
switch (this) {
case NORTH:
return BlockFace.SOUTH;
case SOUTH:
return BlockFace.NORTH;
case EAST:
return BlockFace.WEST;
case WEST:
return BlockFace.EAST;
case UP:
return BlockFace.DOWN;
case DOWN:
return BlockFace.UP;
case NORTH_EAST:
return BlockFace.SOUTH_WEST;
case NORTH_WEST:
return BlockFace.SOUTH_EAST;
case SOUTH_EAST:
return BlockFace.NORTH_WEST;
case SOUTH_WEST:
return BlockFace.NORTH_EAST;
case WEST_NORTH_WEST:
return BlockFace.EAST_SOUTH_EAST;
case NORTH_NORTH_WEST:
return BlockFace.SOUTH_SOUTH_EAST;
case NORTH_NORTH_EAST:
return BlockFace.SOUTH_SOUTH_WEST;
case EAST_NORTH_EAST:
return BlockFace.WEST_SOUTH_WEST;
case EAST_SOUTH_EAST:
return BlockFace.WEST_NORTH_WEST;
case SOUTH_SOUTH_EAST:
return BlockFace.NORTH_NORTH_WEST;
case SOUTH_SOUTH_WEST:
return BlockFace.NORTH_NORTH_EAST;
case WEST_SOUTH_WEST:
return BlockFace.EAST_NORTH_EAST;
case SELF:
return BlockFace.SELF;
}
return BlockFace.SELF;
}
}

View File

@ -0,0 +1,51 @@
/*
* This file is part of Limbo.
*
* Copyright (C) 2022. LoohpJames <jamesloohp@gmail.com>
* Copyright (C) 2022. Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.loohp.limbo.location;
import com.loohp.limbo.entity.Entity;
public abstract class MovingObjectPosition {
protected final Vector location;
protected MovingObjectPosition(Vector vec3d) {
this.location = vec3d;
}
public double distanceTo(Entity entity) {
double d0 = this.location.x - entity.getX();
double d1 = this.location.y - entity.getY();
double d2 = this.location.z - entity.getZ();
return d0 * d0 + d1 * d1 + d2 * d2;
}
public abstract MovingObjectPosition.EnumMovingObjectType getType();
public Vector getLocation() {
return this.location;
}
public enum EnumMovingObjectType {
MISS, BLOCK, ENTITY;
}
}

View File

@ -0,0 +1,71 @@
/*
* This file is part of Limbo.
*
* Copyright (C) 2022. LoohpJames <jamesloohp@gmail.com>
* Copyright (C) 2022. Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.loohp.limbo.location;
import com.loohp.limbo.world.BlockPosition;
public class MovingObjectPositionBlock extends MovingObjectPosition {
private final BlockFace direction;
private final BlockPosition blockPos;
private final boolean miss;
private final boolean inside;
public static MovingObjectPositionBlock miss(Vector vec3d, BlockFace direction, BlockPosition blockposition) {
return new MovingObjectPositionBlock(true, vec3d, direction, blockposition, false);
}
public MovingObjectPositionBlock(Vector vec3d, BlockFace direction, BlockPosition blockposition, boolean flag) {
this(false, vec3d, direction, blockposition, flag);
}
private MovingObjectPositionBlock(boolean flag, Vector vec3d, BlockFace direction, BlockPosition blockposition, boolean flag1) {
super(vec3d);
this.miss = flag;
this.direction = direction;
this.blockPos = blockposition;
this.inside = flag1;
}
public MovingObjectPositionBlock withDirection(BlockFace direction) {
return new MovingObjectPositionBlock(this.miss, this.location, direction, this.blockPos, this.inside);
}
public MovingObjectPositionBlock withPosition(BlockPosition blockposition) {
return new MovingObjectPositionBlock(this.miss, this.location, this.direction, blockposition, this.inside);
}
public BlockPosition getBlockPos() {
return this.blockPos;
}
public BlockFace getDirection() {
return this.direction;
}
@Override
public MovingObjectPosition.EnumMovingObjectType getType() {
return this.miss ? MovingObjectPosition.EnumMovingObjectType.MISS : MovingObjectPosition.EnumMovingObjectType.BLOCK;
}
public boolean isInside() {
return this.inside;
}
}

View File

@ -20,6 +20,9 @@
package com.loohp.limbo.network; package com.loohp.limbo.network;
import com.loohp.limbo.Limbo; import com.loohp.limbo.Limbo;
import com.loohp.limbo.events.inventory.InventoryCloseEvent;
import com.loohp.limbo.events.inventory.InventoryCreativeEvent;
import com.loohp.limbo.events.player.PlayerInteractEvent;
import com.loohp.limbo.events.player.PlayerJoinEvent; import com.loohp.limbo.events.player.PlayerJoinEvent;
import com.loohp.limbo.events.player.PlayerLoginEvent; import com.loohp.limbo.events.player.PlayerLoginEvent;
import com.loohp.limbo.events.player.PlayerMoveEvent; import com.loohp.limbo.events.player.PlayerMoveEvent;
@ -30,6 +33,7 @@ import com.loohp.limbo.events.player.PlayerSpawnEvent;
import com.loohp.limbo.events.player.PluginMessageEvent; import com.loohp.limbo.events.player.PluginMessageEvent;
import com.loohp.limbo.events.status.StatusPingEvent; import com.loohp.limbo.events.status.StatusPingEvent;
import com.loohp.limbo.file.ServerProperties; import com.loohp.limbo.file.ServerProperties;
import com.loohp.limbo.inventory.Inventory;
import com.loohp.limbo.location.Location; import com.loohp.limbo.location.Location;
import com.loohp.limbo.network.protocol.packets.Packet; import com.loohp.limbo.network.protocol.packets.Packet;
import com.loohp.limbo.network.protocol.packets.PacketHandshakingIn; import com.loohp.limbo.network.protocol.packets.PacketHandshakingIn;
@ -40,7 +44,9 @@ import com.loohp.limbo.network.protocol.packets.PacketLoginOutDisconnect;
import com.loohp.limbo.network.protocol.packets.PacketLoginOutLoginSuccess; import com.loohp.limbo.network.protocol.packets.PacketLoginOutLoginSuccess;
import com.loohp.limbo.network.protocol.packets.PacketLoginOutPluginMessaging; import com.loohp.limbo.network.protocol.packets.PacketLoginOutPluginMessaging;
import com.loohp.limbo.network.protocol.packets.PacketOut; import com.loohp.limbo.network.protocol.packets.PacketOut;
import com.loohp.limbo.network.protocol.packets.PacketPlayInBlockPlace;
import com.loohp.limbo.network.protocol.packets.PacketPlayInChat; import com.loohp.limbo.network.protocol.packets.PacketPlayInChat;
import com.loohp.limbo.network.protocol.packets.PacketPlayInCloseWindow;
import com.loohp.limbo.network.protocol.packets.PacketPlayInHeldItemChange; import com.loohp.limbo.network.protocol.packets.PacketPlayInHeldItemChange;
import com.loohp.limbo.network.protocol.packets.PacketPlayInKeepAlive; import com.loohp.limbo.network.protocol.packets.PacketPlayInKeepAlive;
import com.loohp.limbo.network.protocol.packets.PacketPlayInPluginMessaging; import com.loohp.limbo.network.protocol.packets.PacketPlayInPluginMessaging;
@ -49,7 +55,10 @@ import com.loohp.limbo.network.protocol.packets.PacketPlayInPositionAndLook;
import com.loohp.limbo.network.protocol.packets.PacketPlayInResourcePackStatus; import com.loohp.limbo.network.protocol.packets.PacketPlayInResourcePackStatus;
import com.loohp.limbo.network.protocol.packets.PacketPlayInResourcePackStatus.EnumResourcePackStatus; import com.loohp.limbo.network.protocol.packets.PacketPlayInResourcePackStatus.EnumResourcePackStatus;
import com.loohp.limbo.network.protocol.packets.PacketPlayInRotation; import com.loohp.limbo.network.protocol.packets.PacketPlayInRotation;
import com.loohp.limbo.network.protocol.packets.PacketPlayInSetCreativeSlot;
import com.loohp.limbo.network.protocol.packets.PacketPlayInTabComplete; import com.loohp.limbo.network.protocol.packets.PacketPlayInTabComplete;
import com.loohp.limbo.network.protocol.packets.PacketPlayInUseItem;
import com.loohp.limbo.network.protocol.packets.PacketPlayInWindowClick;
import com.loohp.limbo.network.protocol.packets.PacketPlayOutDeclareCommands; import com.loohp.limbo.network.protocol.packets.PacketPlayOutDeclareCommands;
import com.loohp.limbo.network.protocol.packets.PacketPlayOutDisconnect; import com.loohp.limbo.network.protocol.packets.PacketPlayOutDisconnect;
import com.loohp.limbo.network.protocol.packets.PacketPlayOutEntityMetadata; import com.loohp.limbo.network.protocol.packets.PacketPlayOutEntityMetadata;
@ -86,6 +95,7 @@ import com.loohp.limbo.utils.GameMode;
import com.loohp.limbo.utils.MojangAPIUtils; import com.loohp.limbo.utils.MojangAPIUtils;
import com.loohp.limbo.utils.MojangAPIUtils.SkinResponse; import com.loohp.limbo.utils.MojangAPIUtils.SkinResponse;
import com.loohp.limbo.world.BlockPosition; import com.loohp.limbo.world.BlockPosition;
import com.loohp.limbo.world.BlockState;
import com.loohp.limbo.world.World; import com.loohp.limbo.world.World;
import net.kyori.adventure.key.Key; import net.kyori.adventure.key.Key;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
@ -114,6 +124,7 @@ import java.util.Arrays;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.Random; import java.util.Random;
import java.util.Set; import java.util.Set;
@ -491,7 +502,7 @@ public class ClientConnection extends Thread {
player = new Player(this, username, uuid, Limbo.getInstance().getNextEntityId(), Limbo.getInstance().getServerProperties().getWorldSpawn(), new PlayerInteractManager()); player = new Player(this, username, uuid, Limbo.getInstance().getNextEntityId(), Limbo.getInstance().getServerProperties().getWorldSpawn(), new PlayerInteractManager());
player.setSkinLayers((byte) (0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40)); player.setSkinLayers((byte) (0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40));
Limbo.getInstance().getUnsafe().addPlayer(player); Limbo.getInstance().getUnsafe().a(player);
break; break;
} else if (packetIn instanceof PacketLoginInPluginMessaging) { } else if (packetIn instanceof PacketLoginInPluginMessaging) {
PacketLoginInPluginMessaging response = (PacketLoginInPluginMessaging) packetIn; PacketLoginInPluginMessaging response = (PacketLoginInPluginMessaging) packetIn;
@ -519,7 +530,7 @@ public class ClientConnection extends Thread {
player = new Player(this, data.getUsername(), data.getUuid(), Limbo.getInstance().getNextEntityId(), Limbo.getInstance().getServerProperties().getWorldSpawn(), new PlayerInteractManager()); player = new Player(this, data.getUsername(), data.getUuid(), Limbo.getInstance().getNextEntityId(), Limbo.getInstance().getServerProperties().getWorldSpawn(), new PlayerInteractManager());
player.setSkinLayers((byte) (0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40)); player.setSkinLayers((byte) (0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40));
Limbo.getInstance().getUnsafe().addPlayer(player); Limbo.getInstance().getUnsafe().a(player);
break; break;
} }
@ -551,7 +562,7 @@ public class ClientConnection extends Thread {
PacketPlayOutLogin join = new PacketPlayOutLogin(player.getEntityId(), false, properties.getDefaultGamemode(), Limbo.getInstance().getWorlds(), Limbo.getInstance().getDimensionRegistry().getCodec(), world, 0, (byte) properties.getMaxPlayers(), 8, 8, properties.isReducedDebugInfo(), true, false, true); PacketPlayOutLogin join = new PacketPlayOutLogin(player.getEntityId(), false, properties.getDefaultGamemode(), Limbo.getInstance().getWorlds(), Limbo.getInstance().getDimensionRegistry().getCodec(), world, 0, (byte) properties.getMaxPlayers(), 8, 8, properties.isReducedDebugInfo(), true, false, true);
sendPacket(join); sendPacket(join);
Limbo.getInstance().getUnsafe().setPlayerGameModeSilently(player, properties.getDefaultGamemode()); Limbo.getInstance().getUnsafe().a(player, properties.getDefaultGamemode());
ByteArrayOutputStream brandOut = new ByteArrayOutputStream(); ByteArrayOutputStream brandOut = new ByteArrayOutputStream();
DataTypeIO.writeString(new DataOutputStream(brandOut), properties.getServerModName(), StandardCharsets.UTF_8); DataTypeIO.writeString(new DataOutputStream(brandOut), properties.getServerModName(), StandardCharsets.UTF_8);
@ -586,7 +597,7 @@ public class ClientConnection extends Thread {
sendPacket(spawnPos); sendPacket(spawnPos);
PacketPlayOutPositionAndLook positionLook = new PacketPlayOutPositionAndLook(worldSpawn.getX(), worldSpawn.getY(), worldSpawn.getZ(), worldSpawn.getYaw(), worldSpawn.getPitch(), 1, false); PacketPlayOutPositionAndLook positionLook = new PacketPlayOutPositionAndLook(worldSpawn.getX(), worldSpawn.getY(), worldSpawn.getZ(), worldSpawn.getYaw(), worldSpawn.getPitch(), 1, false);
Limbo.getInstance().getUnsafe().setPlayerLocationSilently(player, new Location(world, worldSpawn.getX(), worldSpawn.getY(), worldSpawn.getZ(), worldSpawn.getYaw(), worldSpawn.getPitch())); Limbo.getInstance().getUnsafe().a(player, new Location(world, worldSpawn.getX(), worldSpawn.getY(), worldSpawn.getZ(), worldSpawn.getYaw(), worldSpawn.getPitch()));
sendPacket(positionLook); sendPacket(positionLook);
player.getDataWatcher().update(); player.getDataWatcher().update();
@ -647,7 +658,7 @@ public class ClientConnection extends Thread {
sendPacket(cancel); sendPacket(cancel);
} else { } else {
Location to = event.getTo(); Location to = event.getTo();
Limbo.getInstance().getUnsafe().setPlayerLocationSilently(player, to); Limbo.getInstance().getUnsafe().a(player, to);
// If an event handler used setTo, let's make sure we tell the player about it. // If an event handler used setTo, let's make sure we tell the player about it.
if (!originalTo.equals(to)) { if (!originalTo.equals(to)) {
PacketPlayOutPositionAndLook pos = new PacketPlayOutPositionAndLook(to.getX(), to.getY(), to.getZ(), to.getYaw(), to.getPitch(), 1, false); PacketPlayOutPositionAndLook pos = new PacketPlayOutPositionAndLook(to.getX(), to.getY(), to.getZ(), to.getYaw(), to.getPitch(), 1, false);
@ -657,7 +668,9 @@ public class ClientConnection extends Thread {
sendPacket(response); sendPacket(response);
} }
}; };
System.out.println("Waiting");
PacketIn packetIn = channel.readPacket(); PacketIn packetIn = channel.readPacket();
System.out.println("Received " + packetIn.getClass());
if (packetIn instanceof PacketPlayInPositionAndLook) { if (packetIn instanceof PacketPlayInPositionAndLook) {
PacketPlayInPositionAndLook pos = (PacketPlayInPositionAndLook) packetIn; PacketPlayInPositionAndLook pos = (PacketPlayInPositionAndLook) packetIn;
Location from = player.getLocation(); Location from = player.getLocation();
@ -720,9 +733,9 @@ public class ClientConnection extends Thread {
} else if (change.getSlot() != event.getSlot()) { } else if (change.getSlot() != event.getSlot()) {
PacketPlayOutHeldItemChange changePacket = new PacketPlayOutHeldItemChange(event.getSlot()); PacketPlayOutHeldItemChange changePacket = new PacketPlayOutHeldItemChange(event.getSlot());
sendPacket(changePacket); sendPacket(changePacket);
Limbo.getInstance().getUnsafe().setSelectedSlotSilently(player, event.getSlot()); Limbo.getInstance().getUnsafe().a(player, event.getSlot());
} else { } else {
Limbo.getInstance().getUnsafe().setSelectedSlotSilently(player, event.getSlot()); Limbo.getInstance().getUnsafe().a(player, event.getSlot());
} }
} else if (packetIn instanceof PacketPlayInResourcePackStatus) { } else if (packetIn instanceof PacketPlayInResourcePackStatus) {
@ -735,6 +748,42 @@ public class ClientConnection extends Thread {
} else if (packetIn instanceof PacketPlayInPluginMessaging) { } else if (packetIn instanceof PacketPlayInPluginMessaging) {
PacketPlayInPluginMessaging inPluginMessaging = (PacketPlayInPluginMessaging) packetIn; PacketPlayInPluginMessaging inPluginMessaging = (PacketPlayInPluginMessaging) packetIn;
Limbo.getInstance().getEventsManager().callEvent(new PluginMessageEvent(player, inPluginMessaging.getChannel(), inPluginMessaging.getData())); Limbo.getInstance().getEventsManager().callEvent(new PluginMessageEvent(player, inPluginMessaging.getChannel(), inPluginMessaging.getData()));
} else if (packetIn instanceof PacketPlayInBlockPlace) {
PacketPlayInBlockPlace packet = (PacketPlayInBlockPlace) packetIn;
Limbo.getInstance().getEventsManager().callEvent(new PlayerInteractEvent(player, PlayerInteractEvent.Action.RIGHT_CLICK_AIR, player.getEquipment().getItem(packet.getHand()), null, null, packet.getHand()));
} else if (packetIn instanceof PacketPlayInUseItem) {
PacketPlayInUseItem packet = (PacketPlayInUseItem) packetIn;
BlockState block = player.getWorld().getBlock(packet.getBlockHit().getBlockPos());
Limbo.getInstance().getEventsManager().callEvent(new PlayerInteractEvent(player, PlayerInteractEvent.Action.RIGHT_CLICK_AIR, player.getEquipment().getItem(packet.getHand()), block, packet.getBlockHit().getDirection(), packet.getHand()));
} else if (packetIn instanceof PacketPlayInSetCreativeSlot) {
PacketPlayInSetCreativeSlot packet = (PacketPlayInSetCreativeSlot) packetIn;
InventoryCreativeEvent event = Limbo.getInstance().getEventsManager().callEvent(new InventoryCreativeEvent(player, player.getInventoryView(), player.getInventory(), player.getInventory().getUnsafe().b().applyAsInt(packet.getSlotNumber()), packet.getItemStack()));
if (event.isCancelled()) {
player.updateInventory();
} else {
player.getInventory().getUnsafe().b(packet.getSlotNumber(), event.getNewItem());
}
} else if (packetIn instanceof PacketPlayInWindowClick) {
PacketPlayInWindowClick packet = (PacketPlayInWindowClick) packetIn;
/*
InventoryCreativeEvent event = Limbo.getInstance().getEventsManager().callEvent(new InventoryCreativeEvent(player, player.getInventoryView(), player.getInventory(), player.getInventory().getUnsafe().b().applyAsInt(packet.getSlotNumber()), packet.getItemStack()));
if (event.isCancelled()) {
player.updateInventory();
} else {
player.getInventory().getUnsafe().b(packet.getSlotNumber(), event.getNewItem());
}
*/
} else if (packetIn instanceof PacketPlayInCloseWindow) {
PacketPlayInCloseWindow packet = (PacketPlayInCloseWindow) packetIn;
Inventory inventory = player.getInventoryView().getTopInventory();
if (inventory != null) {
Integer id = inventory.getUnsafe().c().get(player);
if (id != null) {
Limbo.getInstance().getEventsManager().callEvent(new InventoryCloseEvent(player, player.getInventoryView()));
player.getInventoryView().getUnsafe().a(null);
inventory.getUnsafe().c().remove(player);
}
}
} }
} catch (Exception e) { } catch (Exception e) {
break; break;
@ -757,7 +806,7 @@ public class ClientConnection extends Thread {
state = ClientState.DISCONNECTED; state = ClientState.DISCONNECTED;
if (player != null) { if (player != null) {
Limbo.getInstance().getUnsafe().removePlayer(player); Limbo.getInstance().getUnsafe().b(player);
} }
Limbo.getInstance().getServerConnection().getClients().remove(this); Limbo.getInstance().getServerConnection().getClients().remove(this);
running = false; running = false;

View File

@ -0,0 +1,49 @@
/*
* This file is part of Limbo.
*
* Copyright (C) 2022. LoohpJames <jamesloohp@gmail.com>
* Copyright (C) 2022. Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.loohp.limbo.network.protocol.packets;
import com.loohp.limbo.inventory.EquipmentSlot;
import com.loohp.limbo.utils.DataTypeIO;
import java.io.DataInputStream;
import java.io.IOException;
public class PacketPlayInBlockPlace extends PacketIn {
private EquipmentSlot hand;
private int sequence;
public PacketPlayInBlockPlace(EquipmentSlot hand, int sequence) {
this.hand = hand;
this.sequence = sequence;
}
public PacketPlayInBlockPlace(DataInputStream in) throws IOException {
this(EquipmentSlot.values()[DataTypeIO.readVarInt(in)], DataTypeIO.readVarInt(in));
}
public EquipmentSlot getHand() {
return hand;
}
public int getSequence() {
return sequence;
}
}

View File

@ -0,0 +1,40 @@
/*
* This file is part of Limbo.
*
* Copyright (C) 2022. LoohpJames <jamesloohp@gmail.com>
* Copyright (C) 2022. Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.loohp.limbo.network.protocol.packets;
import java.io.DataInputStream;
import java.io.IOException;
public class PacketPlayInCloseWindow extends PacketIn {
private int containerId;
public PacketPlayInCloseWindow(int containerId) {
this.containerId = containerId;
}
public PacketPlayInCloseWindow(DataInputStream in) throws IOException {
this(in.readByte());
}
public int getContainerId() {
return containerId;
}
}

View File

@ -0,0 +1,49 @@
/*
* This file is part of Limbo.
*
* Copyright (C) 2022. LoohpJames <jamesloohp@gmail.com>
* Copyright (C) 2022. Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.loohp.limbo.network.protocol.packets;
import com.loohp.limbo.inventory.ItemStack;
import com.loohp.limbo.utils.DataTypeIO;
import java.io.DataInputStream;
import java.io.IOException;
public class PacketPlayInSetCreativeSlot extends PacketIn {
private int slotNumber;
private ItemStack itemStack;
public PacketPlayInSetCreativeSlot(int slotNumber, ItemStack itemStack) {
this.slotNumber = slotNumber;
this.itemStack = itemStack;
}
public PacketPlayInSetCreativeSlot(DataInputStream in) throws IOException {
this(in.readShort(), DataTypeIO.readItemStack(in));
}
public int getSlotNumber() {
return slotNumber;
}
public ItemStack getItemStack() {
return itemStack;
}
}

View File

@ -0,0 +1,56 @@
/*
* This file is part of Limbo.
*
* Copyright (C) 2022. LoohpJames <jamesloohp@gmail.com>
* Copyright (C) 2022. Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.loohp.limbo.network.protocol.packets;
import com.loohp.limbo.inventory.EquipmentSlot;
import com.loohp.limbo.location.MovingObjectPositionBlock;
import com.loohp.limbo.utils.DataTypeIO;
import java.io.DataInputStream;
import java.io.IOException;
public class PacketPlayInUseItem extends PacketIn {
private EquipmentSlot hand;
private MovingObjectPositionBlock blockHit;
private int sequence;
public PacketPlayInUseItem(EquipmentSlot hand, MovingObjectPositionBlock blockHit, int sequence) {
this.hand = hand;
this.blockHit = blockHit;
this.sequence = sequence;
}
public PacketPlayInUseItem(DataInputStream in) throws IOException {
this(EquipmentSlot.values()[DataTypeIO.readVarInt(in)], DataTypeIO.readBlockHitResult(in), DataTypeIO.readVarInt(in));
}
public EquipmentSlot getHand() {
return hand;
}
public MovingObjectPositionBlock getBlockHit() {
return blockHit;
}
public int getSequence() {
return sequence;
}
}

View File

@ -0,0 +1,96 @@
/*
* This file is part of Limbo.
*
* Copyright (C) 2022. LoohpJames <jamesloohp@gmail.com>
* Copyright (C) 2022. Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.loohp.limbo.network.protocol.packets;
import com.loohp.limbo.inventory.InventoryClickType;
import com.loohp.limbo.inventory.ItemStack;
import com.loohp.limbo.utils.DataTypeIO;
import java.io.DataInputStream;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public class PacketPlayInWindowClick extends PacketIn {
private int containerId;
private int stateId;
private int slotNum;
private int buttonNum;
private InventoryClickType clickType;
private Map<Integer, ItemStack> changedSlots;
private ItemStack carriedItem;
public PacketPlayInWindowClick(int containerId, int stateId, int slotNum, int buttonNum, InventoryClickType clickType, Map<Integer, ItemStack> changedSlots, ItemStack carriedItem) {
this.containerId = containerId;
this.stateId = stateId;
this.slotNum = slotNum;
this.buttonNum = buttonNum;
this.clickType = clickType;
this.changedSlots = changedSlots;
this.carriedItem = carriedItem;
}
public PacketPlayInWindowClick(DataInputStream in) throws IOException {
this.containerId = in.readByte();
this.stateId = DataTypeIO.readVarInt(in);
this.slotNum = in.readShort();
this.buttonNum = in.readByte();
this.clickType = InventoryClickType.values()[DataTypeIO.readVarInt(in)];
Map<Integer, ItemStack> changedSlots = new HashMap<>();
int size = DataTypeIO.readVarInt(in);
for (int i = 0; i < size; i++) {
int slot = in.readShort();
ItemStack itemStack = DataTypeIO.readItemStack(in);
changedSlots.put(slot, itemStack);
}
this.changedSlots = Collections.unmodifiableMap(changedSlots);
this.carriedItem = DataTypeIO.readItemStack(in);
}
public int getContainerId() {
return containerId;
}
public int getStateId() {
return stateId;
}
public int getSlotNum() {
return slotNum;
}
public int getButtonNum() {
return buttonNum;
}
public InventoryClickType getClickType() {
return clickType;
}
public Map<Integer, ItemStack> getChangedSlots() {
return changedSlots;
}
public ItemStack getCarriedItem() {
return carriedItem;
}
}

View File

@ -0,0 +1,49 @@
/*
* This file is part of Limbo.
*
* Copyright (C) 2022. LoohpJames <jamesloohp@gmail.com>
* Copyright (C) 2022. Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.loohp.limbo.network.protocol.packets;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
public class PacketPlayOutCloseWindow extends PacketOut {
private int containerId;
public PacketPlayOutCloseWindow(int containerId) {
this.containerId = containerId;
}
public int getContainerId() {
return containerId;
}
@Override
public byte[] serializePacket() throws IOException {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
DataOutputStream output = new DataOutputStream(buffer);
output.writeByte(Packet.getPlayOut().get(getClass()));
output.writeByte(containerId);
return buffer.toByteArray();
}
}

View File

@ -0,0 +1,70 @@
/*
* This file is part of Limbo.
*
* Copyright (C) 2022. LoohpJames <jamesloohp@gmail.com>
* Copyright (C) 2022. Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.loohp.limbo.network.protocol.packets;
import com.loohp.limbo.registry.Registry;
import com.loohp.limbo.utils.DataTypeIO;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
public class PacketPlayOutOpenWindow extends PacketOut {
private int containerId;
private Key type;
private Component title;
public PacketPlayOutOpenWindow(int containerId, Key type, Component title) {
this.containerId = containerId;
this.type = type;
this.title = title;
}
public int getContainerId() {
return containerId;
}
public Key getType() {
return type;
}
public Component getTitle() {
return title;
}
@Override
public byte[] serializePacket() throws IOException {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
DataOutputStream output = new DataOutputStream(buffer);
output.writeByte(Packet.getPlayOut().get(getClass()));
DataTypeIO.writeVarInt(output, containerId);
DataTypeIO.writeVarInt(output, Registry.MENU_REGISTRY.getId(type));
DataTypeIO.writeString(output, GsonComponentSerializer.gson().serialize(title), StandardCharsets.UTF_8);
return buffer.toByteArray();
}
}

View File

@ -0,0 +1,74 @@
/*
* This file is part of Limbo.
*
* Copyright (C) 2022. LoohpJames <jamesloohp@gmail.com>
* Copyright (C) 2022. Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.loohp.limbo.network.protocol.packets;
import com.loohp.limbo.inventory.ItemStack;
import com.loohp.limbo.utils.DataTypeIO;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.List;
public class PacketPlayOutSetSlot extends PacketOut {
private int containerId;
private int stateId;
private int slot;
private ItemStack itemStack;
public PacketPlayOutSetSlot(int containerId, int stateId, int slot, ItemStack itemStack) {
this.containerId = containerId;
this.stateId = stateId;
this.slot = slot;
this.itemStack = itemStack;
}
public int getContainerId() {
return containerId;
}
public int getStateId() {
return stateId;
}
public int getSlot() {
return slot;
}
public ItemStack getItemStack() {
return itemStack;
}
@Override
public byte[] serializePacket() throws IOException {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
DataOutputStream output = new DataOutputStream(buffer);
output.writeByte(Packet.getPlayOut().get(getClass()));
output.writeByte(containerId);
DataTypeIO.writeVarInt(output, stateId);
output.writeShort(slot);
DataTypeIO.writeItemStack(output, itemStack);
return buffer.toByteArray();
}
}

View File

@ -0,0 +1,63 @@
/*
* This file is part of Limbo.
*
* Copyright (C) 2022. LoohpJames <jamesloohp@gmail.com>
* Copyright (C) 2022. Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.loohp.limbo.network.protocol.packets;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
public class PacketPlayOutWindowData extends PacketOut {
private int containerId;
private int id;
private int value;
public PacketPlayOutWindowData(int containerId, int id, int value) {
this.containerId = containerId;
this.id = id;
this.value = value;
}
public int getContainerId() {
return containerId;
}
public int getId() {
return id;
}
public int getValue() {
return value;
}
@Override
public byte[] serializePacket() throws IOException {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
DataOutputStream output = new DataOutputStream(buffer);
output.writeByte(Packet.getPlayOut().get(getClass()));
output.writeByte(containerId);
output.writeShort(id);
output.writeShort(value);
return buffer.toByteArray();
}
}

View File

@ -0,0 +1,77 @@
/*
* This file is part of Limbo.
*
* Copyright (C) 2022. LoohpJames <jamesloohp@gmail.com>
* Copyright (C) 2022. Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.loohp.limbo.network.protocol.packets;
import com.loohp.limbo.inventory.ItemStack;
import com.loohp.limbo.utils.DataTypeIO;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.List;
public class PacketPlayOutWindowItems extends PacketOut {
private int containerId;
private int stateId;
private List<ItemStack> items;
private ItemStack carriedItem;
public PacketPlayOutWindowItems(int containerId, int stateId, List<ItemStack> items, ItemStack carriedItem) {
this.containerId = containerId;
this.stateId = stateId;
this.items = items;
this.carriedItem = carriedItem;
}
public int getContainerId() {
return containerId;
}
public int getStateId() {
return stateId;
}
public List<ItemStack> getItems() {
return items;
}
public ItemStack getCarriedItem() {
return carriedItem;
}
@Override
public byte[] serializePacket() throws IOException {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
DataOutputStream output = new DataOutputStream(buffer);
output.writeByte(Packet.getPlayOut().get(getClass()));
output.writeByte(containerId);
DataTypeIO.writeVarInt(output, stateId);
DataTypeIO.writeVarInt(output, items.size());
for (ItemStack itemStack : items) {
DataTypeIO.writeItemStack(output, itemStack);
}
DataTypeIO.writeItemStack(output, carriedItem);
return buffer.toByteArray();
}
}

View File

@ -24,10 +24,17 @@ import com.loohp.limbo.commands.CommandSender;
import com.loohp.limbo.entity.DataWatcher; import com.loohp.limbo.entity.DataWatcher;
import com.loohp.limbo.entity.DataWatcher.WatchableField; import com.loohp.limbo.entity.DataWatcher.WatchableField;
import com.loohp.limbo.entity.DataWatcher.WatchableObjectType; import com.loohp.limbo.entity.DataWatcher.WatchableObjectType;
import com.loohp.limbo.entity.EntityEquipment;
import com.loohp.limbo.entity.EntityType; import com.loohp.limbo.entity.EntityType;
import com.loohp.limbo.entity.LivingEntity; import com.loohp.limbo.entity.LivingEntity;
import com.loohp.limbo.events.inventory.InventoryCloseEvent;
import com.loohp.limbo.events.inventory.InventoryOpenEvent;
import com.loohp.limbo.events.player.PlayerChatEvent; import com.loohp.limbo.events.player.PlayerChatEvent;
import com.loohp.limbo.events.player.PlayerTeleportEvent; import com.loohp.limbo.events.player.PlayerTeleportEvent;
import com.loohp.limbo.inventory.Inventory;
import com.loohp.limbo.inventory.InventoryHolder;
import com.loohp.limbo.inventory.InventoryView;
import com.loohp.limbo.inventory.TitledInventory;
import com.loohp.limbo.location.Location; import com.loohp.limbo.location.Location;
import com.loohp.limbo.network.ClientConnection; import com.loohp.limbo.network.ClientConnection;
import com.loohp.limbo.network.protocol.packets.ClientboundClearTitlesPacket; import com.loohp.limbo.network.protocol.packets.ClientboundClearTitlesPacket;
@ -37,9 +44,11 @@ import com.loohp.limbo.network.protocol.packets.ClientboundSetTitleTextPacket;
import com.loohp.limbo.network.protocol.packets.ClientboundSetTitlesAnimationPacket; import com.loohp.limbo.network.protocol.packets.ClientboundSetTitlesAnimationPacket;
import com.loohp.limbo.network.protocol.packets.ClientboundSystemChatPacket; import com.loohp.limbo.network.protocol.packets.ClientboundSystemChatPacket;
import com.loohp.limbo.network.protocol.packets.PacketOut; import com.loohp.limbo.network.protocol.packets.PacketOut;
import com.loohp.limbo.network.protocol.packets.PacketPlayOutCloseWindow;
import com.loohp.limbo.network.protocol.packets.PacketPlayOutGameState; import com.loohp.limbo.network.protocol.packets.PacketPlayOutGameState;
import com.loohp.limbo.network.protocol.packets.PacketPlayOutHeldItemChange; import com.loohp.limbo.network.protocol.packets.PacketPlayOutHeldItemChange;
import com.loohp.limbo.network.protocol.packets.PacketPlayOutNamedSoundEffect; import com.loohp.limbo.network.protocol.packets.PacketPlayOutNamedSoundEffect;
import com.loohp.limbo.network.protocol.packets.PacketPlayOutOpenWindow;
import com.loohp.limbo.network.protocol.packets.PacketPlayOutPlayerListHeaderFooter; import com.loohp.limbo.network.protocol.packets.PacketPlayOutPlayerListHeaderFooter;
import com.loohp.limbo.network.protocol.packets.PacketPlayOutPositionAndLook; import com.loohp.limbo.network.protocol.packets.PacketPlayOutPositionAndLook;
import com.loohp.limbo.network.protocol.packets.PacketPlayOutResourcePackSend; import com.loohp.limbo.network.protocol.packets.PacketPlayOutResourcePackSend;
@ -70,8 +79,9 @@ import java.time.Duration;
import java.time.Instant; import java.time.Instant;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicInteger;
public class Player extends LivingEntity implements CommandSender { public class Player extends LivingEntity implements CommandSender, InventoryHolder {
public static final String CHAT_DEFAULT_FORMAT = "<%name%> %message%"; public static final String CHAT_DEFAULT_FORMAT = "<%name%> %message%";
@ -82,6 +92,9 @@ public class Player extends LivingEntity implements CommandSender {
protected GameMode gamemode; protected GameMode gamemode;
protected DataWatcher watcher; protected DataWatcher watcher;
protected byte selectedSlot; protected byte selectedSlot;
protected final PlayerInventory playerInventory;
protected final InventoryView inventoryView;
private final AtomicInteger containerIdCounter;
@WatchableField(MetadataIndex = 15, WatchableObjectType = WatchableObjectType.FLOAT) @WatchableField(MetadataIndex = 15, WatchableObjectType = WatchableObjectType.FLOAT)
protected float additionalHearts = 0.0F; protected float additionalHearts = 0.0F;
@ -101,12 +114,19 @@ public class Player extends LivingEntity implements CommandSender {
this.clientConnection = clientConnection; this.clientConnection = clientConnection;
this.username = username; this.username = username;
this.entityId = entityId; this.entityId = entityId;
this.containerIdCounter = new AtomicInteger(1);
this.playerInventory = new PlayerInventory(this);
this.inventoryView = new InventoryView(this, null, null, playerInventory);
this.playerInteractManager = playerInteractManager; this.playerInteractManager = playerInteractManager;
this.playerInteractManager.setPlayer(this); this.playerInteractManager.setPlayer(this);
this.watcher = new DataWatcher(this); this.watcher = new DataWatcher(this);
this.watcher.update(); this.watcher.update();
} }
protected int nextContainerId() {
return containerIdCounter.updateAndGet(i -> ++i > Byte.MAX_VALUE ? 1 : i);
}
public byte getSelectedSlot() { public byte getSelectedSlot() {
return selectedSlot; return selectedSlot;
} }
@ -551,14 +571,81 @@ public class Player extends LivingEntity implements CommandSender {
} }
} }
/**
* Use {@link com.loohp.limbo.bossbar.KeyedBossBar#showPlayer(Player)} instead
*/
@Override @Override
@Deprecated
public void showBossBar(BossBar bar) { public void showBossBar(BossBar bar) {
throw new UnsupportedOperationException("This function has not been implemented yet."); Limbo.getInstance().getBossBars().values().stream().filter(each -> each.getProperties() == bar).findFirst().ifPresent(each -> each.showPlayer(this));
}
/**
* Use {@link com.loohp.limbo.bossbar.KeyedBossBar#hidePlayer(Player)} instead
*/
@Override
@Deprecated
public void hideBossBar(BossBar bar) {
Limbo.getInstance().getBossBars().values().stream().filter(each -> each.getProperties() == bar).findFirst().ifPresent(each -> each.hidePlayer(this));
} }
@Override @Override
public void hideBossBar(BossBar bar) { public PlayerInventory getInventory() {
throw new UnsupportedOperationException("This function has not been implemented yet."); return playerInventory;
}
public InventoryView getInventoryView() {
return inventoryView;
}
public void updateInventory() {
playerInventory.updateInventory(this);
}
public void openInventory(Inventory inventory) {
inventoryView.getUnsafe().a(inventory);
int id = nextContainerId();
inventory.getUnsafe().c().put(this, id);
InventoryOpenEvent event = Limbo.getInstance().getEventsManager().callEvent(new InventoryOpenEvent(this, inventoryView));
if (event.isCancelled()) {
inventoryView.getUnsafe().a(null);
inventory.getUnsafe().c().remove(this);
} else {
Component title = inventory instanceof TitledInventory ? ((TitledInventory) inventory).getTitle() : Component.translatable("container.chest");
PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, inventory.getType().getRawType(inventory.getSize()), title);
try {
clientConnection.sendPacket(packet);
} catch (IOException e) {
e.printStackTrace();
}
inventory.updateInventory(this);
}
}
public void closeInventory() {
Inventory inventory = inventoryView.getTopInventory();
if (inventory != null) {
Integer id = inventory.getUnsafe().c().get(this);
if (id != null) {
Limbo.getInstance().getEventsManager().callEvent(new InventoryCloseEvent(this, inventoryView));
inventoryView.getUnsafe().a(null);
inventory.getUnsafe().c().remove(this);
PacketPlayOutCloseWindow packet = new PacketPlayOutCloseWindow(id);
try {
clientConnection.sendPacket(packet);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public EntityEquipment getEquipment() {
return playerInventory;
}
@Override
public InventoryHolder getHolder() {
return this;
} }
} }

View File

@ -0,0 +1,173 @@
/*
* This file is part of Limbo.
*
* Copyright (C) 2022. LoohpJames <jamesloohp@gmail.com>
* Copyright (C) 2022. Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.loohp.limbo.player;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.ImmutableBiMap;
import com.loohp.limbo.entity.EntityEquipment;
import com.loohp.limbo.inventory.AbstractInventory;
import com.loohp.limbo.inventory.EquipmentSlot;
import com.loohp.limbo.inventory.InventoryType;
import com.loohp.limbo.inventory.ItemStack;
import com.loohp.limbo.location.Location;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumMap;
import java.util.Map;
import java.util.function.ToIntFunction;
public class PlayerInventory extends AbstractInventory implements EntityEquipment {
private static final Map<EquipmentSlot, ToIntFunction<PlayerInventory>> EQUIPMENT_SLOT_MAPPING;
private static final BiMap<Integer, Integer> SLOT_MAPPING;
static {
Map<EquipmentSlot, ToIntFunction<PlayerInventory>> equipmentSlotMapping = new EnumMap<>(EquipmentSlot.class);
equipmentSlotMapping.put(EquipmentSlot.MAINHAND, i -> i.getHolder().selectedSlot);
equipmentSlotMapping.put(EquipmentSlot.OFFHAND, i -> 40);
equipmentSlotMapping.put(EquipmentSlot.BOOTS, i -> 36);
equipmentSlotMapping.put(EquipmentSlot.LEGGINGS, i -> 37);
equipmentSlotMapping.put(EquipmentSlot.CHESTPLATE, i -> 38);
equipmentSlotMapping.put(EquipmentSlot.HELMET, i -> 39);
EQUIPMENT_SLOT_MAPPING = Collections.unmodifiableMap(equipmentSlotMapping);
BiMap<Integer, Integer> slotMapping = HashBiMap.create(41);
for (int i = 0; i < 9; i++) {
slotMapping.put(i, i + 36);
}
for (int i = 9; i < 36; i++) {
slotMapping.put(i, i);
}
for (int i = 36; i < 40; i++) {
slotMapping.put(i, i - 31);
}
slotMapping.put(40, 45);
SLOT_MAPPING = ImmutableBiMap.copyOf(slotMapping);
}
private final Player player;
public PlayerInventory(Player player) {
super(InventoryType.PLAYER.getDefaultSize(), player, InventoryType.PLAYER, i -> SLOT_MAPPING.getOrDefault(i, i), i -> SLOT_MAPPING.inverse().getOrDefault(i, i));
this.player = player;
this.viewers.put(player, 0);
}
@Override
public Player getHolder() {
return player;
}
@Override
public void setItem(EquipmentSlot slot, ItemStack item) {
setItem(EQUIPMENT_SLOT_MAPPING.get(slot).applyAsInt(this), item);
}
@Override
public ItemStack getItem(EquipmentSlot slot) {
return getItem(EQUIPMENT_SLOT_MAPPING.get(slot).applyAsInt(this));
}
@Override
public ItemStack getItemInMainHand() {
return getItem(EquipmentSlot.MAINHAND);
}
@Override
public void setItemInMainHand(ItemStack item) {
setItem(EquipmentSlot.MAINHAND, item);
}
@Override
public ItemStack getItemInOffHand() {
return getItem(EquipmentSlot.OFFHAND);
}
@Override
public void setItemInOffHand(ItemStack item) {
setItem(EquipmentSlot.OFFHAND, item);
}
@Override
public ItemStack getHelmet() {
return getItem(EquipmentSlot.HELMET);
}
@Override
public void setHelmet(ItemStack helmet) {
setItem(EquipmentSlot.HELMET, helmet);
}
@Override
public ItemStack getChestplate() {
return getItem(EquipmentSlot.CHESTPLATE);
}
@Override
public void setChestplate(ItemStack chestplate) {
setItem(EquipmentSlot.CHESTPLATE, chestplate);
}
@Override
public ItemStack getLeggings() {
return getItem(EquipmentSlot.LEGGINGS);
}
@Override
public void setLeggings(ItemStack leggings) {
setItem(EquipmentSlot.LEGGINGS, leggings);
}
@Override
public ItemStack getBoots() {
return getItem(EquipmentSlot.BOOTS);
}
@Override
public void setBoots(ItemStack boots) {
setItem(EquipmentSlot.BOOTS, boots);
}
@Override
public ItemStack[] getArmorContents() {
return Arrays.stream(EquipmentSlot.values()).filter(EquipmentSlot::isArmorSlot).map(this::getItem).toArray(ItemStack[]::new);
}
@Override
public void setArmorContents(ItemStack[] items) {
int i = 0;
for (EquipmentSlot equipmentSlot : EquipmentSlot.values()) {
if (equipmentSlot.isArmorSlot()) {
if (i < items.length) {
setItem(equipmentSlot, items[i]);
}
i++;
}
}
}
@Override
public Location getLocation() {
return player.getLocation();
}
}

View File

@ -19,6 +19,8 @@
package com.loohp.limbo.registry; package com.loohp.limbo.registry;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.loohp.limbo.Limbo; import com.loohp.limbo.Limbo;
import net.kyori.adventure.key.Key; import net.kyori.adventure.key.Key;
import org.json.simple.JSONObject; import org.json.simple.JSONObject;
@ -41,6 +43,8 @@ import java.util.Map.Entry;
public class Registry { public class Registry {
public static final BlockEntityRegistry BLOCK_ENTITY_TYPE; public static final BlockEntityRegistry BLOCK_ENTITY_TYPE;
public static final ItemRegistry ITEM_REGISTRY;
public static final MenuRegistry MENU_REGISTRY;
static { static {
String name = "registries.json"; String name = "registries.json";
@ -54,23 +58,44 @@ public class Registry {
} }
Map<Key, Integer> blockEntityType = new HashMap<>(); Map<Key, Integer> blockEntityType = new HashMap<>();
Key defaultItemKey = null;
BiMap<Key, Integer> itemIds = HashBiMap.create();
Map<Key, Integer> menuIds = new HashMap<>();
try (InputStreamReader reader = new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8)) { try (InputStreamReader reader = new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8)) {
JSONObject json = (JSONObject) new JSONParser().parse(reader); JSONObject json = (JSONObject) new JSONParser().parse(reader);
JSONObject blockEntityJson = (JSONObject) ((JSONObject) json.get("minecraft:block_entity_type")).get("entries"); JSONObject blockEntityJson = (JSONObject) ((JSONObject) json.get("minecraft:block_entity_type")).get("entries");
for (Object obj : blockEntityJson.keySet()) { for (Object obj : blockEntityJson.keySet()) {
String key = obj.toString(); String key = obj.toString();
int id = (int) (long) ((JSONObject) blockEntityJson.get(key)).get("protocol_id"); int id = ((Number) ((JSONObject) blockEntityJson.get(key)).get("protocol_id")).intValue();
blockEntityType.put(Key.key(key), id); blockEntityType.put(Key.key(key), id);
} }
JSONObject itemJson = (JSONObject) json.get("minecraft:item");
defaultItemKey = Key.key((String) itemJson.get("default"));
JSONObject itemEntriesJson = (JSONObject) itemJson.get("entries");
for (Object obj : itemEntriesJson.keySet()) {
String key = obj.toString();
int id = ((Number) ((JSONObject) itemEntriesJson.get(key)).get("protocol_id")).intValue();
itemIds.put(Key.key(key), id);
}
JSONObject menuEntriesJson = (JSONObject) ((JSONObject) json.get("minecraft:menu")).get("entries");
for (Object obj : menuEntriesJson.keySet()) {
String key = obj.toString();
int id = ((Number) ((JSONObject) menuEntriesJson.get(key)).get("protocol_id")).intValue();
menuIds.put(Key.key(key), id);
}
} catch (IOException | ParseException e) { } catch (IOException | ParseException e) {
e.printStackTrace(); e.printStackTrace();
} }
BLOCK_ENTITY_TYPE = new BlockEntityRegistry(blockEntityType); BLOCK_ENTITY_TYPE = new BlockEntityRegistry(blockEntityType);
ITEM_REGISTRY = new ItemRegistry(defaultItemKey, itemIds);
MENU_REGISTRY = new MenuRegistry(menuIds);
} }
public static class BlockEntityRegistry { public static class BlockEntityRegistry {
private Map<Key, Integer> blockEntityType; private Map<Key, Integer> blockEntityType;
private BlockEntityRegistry(Map<Key, Integer> blockEntityType) { private BlockEntityRegistry(Map<Key, Integer> blockEntityType) {
@ -99,4 +124,47 @@ public class Registry {
} }
} }
public static class ItemRegistry {
private final Key defaultKey;
private final BiMap<Key, Integer> itemIds;
private ItemRegistry(Key defaultKey, BiMap<Key, Integer> itemIds) {
this.defaultKey = defaultKey;
this.itemIds = itemIds;
}
public Key getDefaultKey() {
return defaultKey;
}
public int getId(Key key) {
Integer id = itemIds.get(key);
if (id != null) {
return id;
}
if (defaultKey == null) {
return 0;
}
return itemIds.getOrDefault(defaultKey, 0);
}
public Key fromId(int id) {
return itemIds.inverse().getOrDefault(id, defaultKey);
}
}
public static class MenuRegistry {
private final Map<Key, Integer> menuIds;
private MenuRegistry(Map<Key, Integer> menuIds) {
this.menuIds = menuIds;
}
public int getId(Key key) {
return menuIds.getOrDefault(key, -1);
}
}
} }

View File

@ -19,7 +19,14 @@
package com.loohp.limbo.utils; package com.loohp.limbo.utils;
import com.loohp.limbo.inventory.ItemStack;
import com.loohp.limbo.location.BlockFace;
import com.loohp.limbo.location.MovingObjectPositionBlock;
import com.loohp.limbo.location.Vector;
import com.loohp.limbo.registry.Registry;
import com.loohp.limbo.world.BlockPosition; import com.loohp.limbo.world.BlockPosition;
import net.kyori.adventure.key.Key;
import net.querz.nbt.io.NBTInputStream;
import net.querz.nbt.io.NBTOutputStream; import net.querz.nbt.io.NBTOutputStream;
import net.querz.nbt.tag.CompoundTag; import net.querz.nbt.tag.CompoundTag;
import net.querz.nbt.tag.Tag; import net.querz.nbt.tag.Tag;
@ -28,6 +35,7 @@ import java.io.ByteArrayOutputStream;
import java.io.DataInputStream; import java.io.DataInputStream;
import java.io.DataOutputStream; import java.io.DataOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.PushbackInputStream;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.Arrays; import java.util.Arrays;
import java.util.BitSet; import java.util.BitSet;
@ -36,6 +44,52 @@ import java.util.UUID;
public class DataTypeIO { public class DataTypeIO {
public static void writeItemStack(DataOutputStream out, ItemStack itemstack) throws IOException {
if (itemstack == null || itemstack.isSimilar(ItemStack.AIR) || itemstack.amount() == 0) {
out.writeBoolean(false);
} else {
out.writeBoolean(true);
writeVarInt(out, Registry.ITEM_REGISTRY.getId(itemstack.type()));
out.writeByte(itemstack.amount());
writeCompoundTag(out, itemstack.nbt());
}
}
public static ItemStack readItemStack(DataInputStream in) throws IOException {
if (!in.readBoolean()) {
return ItemStack.AIR;
} else {
Key key = Registry.ITEM_REGISTRY.fromId(readVarInt(in));
byte amount = in.readByte();
CompoundTag nbt = readCompoundTag(in);
return new ItemStack(key, amount, nbt);
}
}
public static void writeBlockHitResult(DataOutputStream out, MovingObjectPositionBlock movingobjectpositionblock) throws IOException {
BlockPosition blockposition = movingobjectpositionblock.getBlockPos();
writeBlockPosition(out, blockposition);
writeVarInt(out, movingobjectpositionblock.getDirection().ordinal());
Vector vector = movingobjectpositionblock.getLocation();
out.writeFloat((float) (vector.getX() - (double) blockposition.getX()));
out.writeFloat((float) (vector.getY() - (double) blockposition.getY()));
out.writeFloat((float) (vector.getZ() - (double) blockposition.getZ()));
out.writeBoolean(movingobjectpositionblock.isInside());
}
public static MovingObjectPositionBlock readBlockHitResult(DataInputStream in) throws IOException {
BlockPosition blockposition = readBlockPosition(in);
BlockFace direction = BlockFace.values()[readVarInt(in)];
float f = in.readFloat();
float f1 = in.readFloat();
float f2 = in.readFloat();
boolean flag = in.readBoolean();
return new MovingObjectPositionBlock(new Vector((double) blockposition.getX() + (double) f, (double) blockposition.getY() + (double) f1, (double) blockposition.getZ() + (double) f2), direction, blockposition, flag);
}
public static <E extends Enum<E>> void writeEnumSet(DataOutputStream out, EnumSet<E> enumset, Class<E> oclass) throws IOException { public static <E extends Enum<E>> void writeEnumSet(DataOutputStream out, EnumSet<E> enumset, Class<E> oclass) throws IOException {
E[] ae = oclass.getEnumConstants(); E[] ae = oclass.getEnumConstants();
BitSet bitset = new BitSet(ae.length); BitSet bitset = new BitSet(ae.length);
@ -80,6 +134,14 @@ public class DataTypeIO {
public static void writeBlockPosition(DataOutputStream out, BlockPosition position) throws IOException { public static void writeBlockPosition(DataOutputStream out, BlockPosition position) throws IOException {
out.writeLong(((position.getX() & 0x3FFFFFF) << 38) | ((position.getZ() & 0x3FFFFFF) << 12) | (position.getY() & 0xFFF)); out.writeLong(((position.getX() & 0x3FFFFFF) << 38) | ((position.getZ() & 0x3FFFFFF) << 12) | (position.getY() & 0xFFF));
} }
public static BlockPosition readBlockPosition(DataInputStream in) throws IOException {
long value = in.readLong();
int x = (int) (value >> 38);
int y = (int) (value << 52 >> 52);
int z = (int) (value << 26 >> 38);
return new BlockPosition(x, y, z);
}
public static void writeUUID(DataOutputStream out, UUID uuid) throws IOException { public static void writeUUID(DataOutputStream out, UUID uuid) throws IOException {
out.writeLong(uuid.getMostSignificantBits()); out.writeLong(uuid.getMostSignificantBits());
@ -91,13 +153,21 @@ public class DataTypeIO {
} }
public static void writeCompoundTag(DataOutputStream out, CompoundTag tag) throws IOException { public static void writeCompoundTag(DataOutputStream out, CompoundTag tag) throws IOException {
ByteArrayOutputStream buffer = new ByteArrayOutputStream(); if (tag == null) {
out.writeByte(0);
DataOutputStream output = new DataOutputStream(buffer); } else {
new NBTOutputStream(output).writeTag(tag, Tag.DEFAULT_MAX_DEPTH); new NBTOutputStream(out).writeTag(tag, Tag.DEFAULT_MAX_DEPTH);
}
byte[] b = buffer.toByteArray(); }
out.write(b);
public static CompoundTag readCompoundTag(DataInputStream in) throws IOException {
byte b = in.readByte();
if (b == 0) {
return null;
}
PushbackInputStream buffered = new PushbackInputStream(in);
buffered.unread(b);
return (CompoundTag) new NBTInputStream(buffered).readTag(Tag.DEFAULT_MAX_DEPTH).getTag();
} }
public static String readString(DataInputStream in, Charset charset) throws IOException { public static String readString(DataInputStream in, Charset charset) throws IOException {

View File

@ -27,6 +27,7 @@ import net.querz.nbt.tag.Tag;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Objects;
public class BlockState { public class BlockState {
@ -79,33 +80,15 @@ public class BlockState {
} }
@Override @Override
public int hashCode() { public boolean equals(Object o) {
final int prime = 31; if (this == o) return true;
int result = 1; if (o == null || getClass() != o.getClass()) return false;
result = prime * result + ((tag == null) ? 0 : tag.hashCode()); BlockState that = (BlockState) o;
return result; return Objects.equals(tag, that.tag);
} }
@Override @Override
public boolean equals(Object obj) { public int hashCode() {
if (this == obj) { return Objects.hash(tag);
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
BlockState other = (BlockState) obj;
if (tag == null) {
if (other.tag != null) {
return false;
}
} else if (!tag.equals(other.tag)) {
return false;
}
return true;
} }
} }

View File

@ -41,6 +41,7 @@ import java.util.Collections;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -91,7 +92,7 @@ public class World {
chunk.cleanupPalettesAndBlockStates(); chunk.cleanupPalettesAndBlockStates();
chunk.setHeightMaps(HEIGHT_MAP.clone()); chunk.setHeightMaps(HEIGHT_MAP.clone());
chunk.setBiomes(new int[256]); chunk.setBiomes(new int[256]);
chunk.setTileEntities(new ListTag<CompoundTag>(CompoundTag.class)); chunk.setTileEntities(new ListTag<>(CompoundTag.class));
} }
} }
@ -124,6 +125,10 @@ public class World {
CompoundTag block = SchematicConversionUtils.toBlockTag(blockdata); CompoundTag block = SchematicConversionUtils.toBlockTag(blockdata);
chunk.setBlockStateAt(x, y, z, block, false); chunk.setBlockStateAt(x, y, z, block, false);
} }
public BlockState getBlock(BlockPosition blockPosition) {
return getBlock(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ());
}
public BlockState getBlock(int x, int y, int z) { public BlockState getBlock(int x, int y, int z) {
Chunk chunk = this.chunks[(x >> 4)][(z >> 4)]; Chunk chunk = this.chunks[(x >> 4)][(z >> 4)];
@ -139,6 +144,10 @@ public class World {
} }
return new BlockState(tag); return new BlockState(tag);
} }
public void setBlock(BlockPosition blockPosition, BlockState state) {
setBlock(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ(), state);
}
public void setBlock(int x, int y, int z, BlockState state) { public void setBlock(int x, int y, int z, BlockState state) {
Chunk chunk = this.chunks[(x >> 4)][(z >> 4)]; Chunk chunk = this.chunks[(x >> 4)][(z >> 4)];
@ -300,40 +309,15 @@ public class World {
} }
@Override @Override
public int hashCode() { public boolean equals(Object o) {
final int prime = 31; if (this == o) return true;
int result = 1; if (o == null || getClass() != o.getClass()) return false;
result = prime * result + Arrays.deepHashCode(chunks); World world = (World) o;
result = prime * result + ((environment == null) ? 0 : environment.hashCode()); return Objects.equals(name, world.name);
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
} }
@Override @Override
public boolean equals(Object obj) { public int hashCode() {
if (this == obj) { return Objects.hash(name);
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
World other = (World) obj;
if (!Arrays.deepEquals(chunks, other.chunks)) {
return false;
}
if (environment != other.environment) {
return false;
}
if (name == null) {
if (other.name != null) {
return false;
}
} else if (!name.equals(other.name)) {
return false;
}
return true;
} }
} }

View File

@ -21,7 +21,12 @@
"0x0C": "PacketPlayInPluginMessaging", "0x0C": "PacketPlayInPluginMessaging",
"0x08": "PacketPlayInTabComplete", "0x08": "PacketPlayInTabComplete",
"0x28": "PacketPlayInHeldItemChange", "0x28": "PacketPlayInHeldItemChange",
"0x24": "PacketPlayInResourcePackStatus" "0x24": "PacketPlayInResourcePackStatus",
"0x32": "PacketPlayInBlockPlace",
"0x31": "PacketPlayInUseItem",
"0x2B": "PacketPlayInSetCreativeSlot",
"0x0A": "PacketPlayInWindowClick",
"0x0B": "PacketPlayInCloseWindow"
}, },
"PlayOut": { "PlayOut": {
"PacketPlayOutLogin": "0x24", "PacketPlayOutLogin": "0x24",
@ -53,7 +58,12 @@
"ClientboundClearTitlesPacket": "0x0C", "ClientboundClearTitlesPacket": "0x0C",
"PacketPlayOutBoss": "0x0A", "PacketPlayOutBoss": "0x0A",
"PacketPlayOutNamedSoundEffect": "0x5E", "PacketPlayOutNamedSoundEffect": "0x5E",
"PacketPlayOutStopSound": "0x5F" "PacketPlayOutStopSound": "0x5F",
"PacketPlayOutWindowItems": "0x10",
"PacketPlayOutSetSlot": "0x12",
"PacketPlayOutOpenWindow": "0x2C",
"PacketPlayOutCloseWindow": "0x0F",
"PacketPlayOutWindowData": "0x11"
}, },
"StatusIn": { "StatusIn": {
"0x01": "PacketStatusInPing", "0x01": "PacketStatusInPing",

File diff suppressed because it is too large Load Diff