diff --git a/core/pom.xml b/core/pom.xml index 5c1bdbd..b2d281c 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -41,6 +41,12 @@ 1.21.1-R0.1-SNAPSHOT compile + + io.papermc.paper + paper-api + 1.21.1-R0.1-SNAPSHOT + provided + com.comphenix.protocol ProtocolLib @@ -66,6 +72,11 @@ fastboard 2.1.3 + + net.kyori + adventure-api + 4.17.0 + com.google.guava guava diff --git a/core/src/main/java/org/ef3d0c3e/sheepwars/events/TeamChangeEvent.java b/core/src/main/java/org/ef3d0c3e/sheepwars/events/TeamChangeEvent.java new file mode 100644 index 0000000..fd61027 --- /dev/null +++ b/core/src/main/java/org/ef3d0c3e/sheepwars/events/TeamChangeEvent.java @@ -0,0 +1,35 @@ +package org.ef3d0c3e.sheepwars.events; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; +import org.ef3d0c3e.sheepwars.player.CPlayer; +import org.ef3d0c3e.sheepwars.teams.Team; + +import javax.annotation.Nullable; + + +@AllArgsConstructor +public class TeamChangeEvent extends Event +{ + private static final HandlerList HANDLERS_LIST = new HandlerList(); + + public static HandlerList getHandlerList() + { + return HANDLERS_LIST; + } + @Override + public HandlerList getHandlers() + { + return HANDLERS_LIST; + } + + @Getter + final private CPlayer player; + @Getter + final private @Nullable Team oldTeam; + @Getter + final private @Nullable Team newTeam; +} + diff --git a/core/src/main/java/org/ef3d0c3e/sheepwars/game/Game.java b/core/src/main/java/org/ef3d0c3e/sheepwars/game/Game.java index fdd1e68..ed8f322 100644 --- a/core/src/main/java/org/ef3d0c3e/sheepwars/game/Game.java +++ b/core/src/main/java/org/ef3d0c3e/sheepwars/game/Game.java @@ -9,6 +9,8 @@ import org.ef3d0c3e.sheepwars.level.LevelFactory; import org.ef3d0c3e.sheepwars.level.lobby.LobbyLevel; import org.ef3d0c3e.sheepwars.packets.PacketListenerFactory; +import java.util.Random; + public class Game { private static void changePhase(WantsListen.Target phase) { @@ -28,6 +30,12 @@ public class Game { //@Getter //private static GameLevel level; + private static final Random random = new Random(); + public static int nextInt() + { + return random.nextInt(); + } + /** * Sets default phase to lobby diff --git a/core/src/main/java/org/ef3d0c3e/sheepwars/hologram/HologramItemComponent.java b/core/src/main/java/org/ef3d0c3e/sheepwars/hologram/HologramItemComponent.java new file mode 100644 index 0000000..7fd3641 --- /dev/null +++ b/core/src/main/java/org/ef3d0c3e/sheepwars/hologram/HologramItemComponent.java @@ -0,0 +1,61 @@ +package org.ef3d0c3e.sheepwars.hologram; + +import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes; +import com.github.retrooper.packetevents.util.Vector3d; +import com.github.retrooper.packetevents.wrapper.PacketWrapper; +import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerEntityMetadata; +import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerSpawnEntity; +import lombok.NonNull; +import org.bukkit.Location; +import org.bukkit.inventory.ItemStack; +import org.bukkit.util.Vector; +import org.ef3d0c3e.sheepwars.packets.EntityMetadata; +import org.ef3d0c3e.sheepwars.packets.ItemProjectileMetadata; +import org.ef3d0c3e.sheepwars.player.CPlayer; + +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +/** + * Represent a text component + */ +public abstract class HologramItemComponent extends HologramComponent +{ + protected HologramItemComponent(@NonNull Vector offset) + { + super(offset); + } + + protected abstract @NonNull ItemStack getItem(final @NonNull CPlayer cp); + + protected int getNetworkOffset() { return 1; } + + protected @NonNull List> build(final @NonNull Location location, final int networkId, final @NonNull CPlayer cp) + { + // Spawn + final Location loc = location.clone().add(getOffset()); + final WrapperPlayServerSpawnEntity spawn = new WrapperPlayServerSpawnEntity( + networkId, Optional.of(UUID.randomUUID()), + EntityTypes.ITEM, + new Vector3d(loc.getX(), loc.getY(), loc.getZ()), + loc.getYaw(), 0.f, loc.getPitch(), + 0, + Optional.empty() + ); + + // Metadata + final WrapperPlayServerEntityMetadata meta = new WrapperPlayServerEntityMetadata( + networkId, + Arrays.asList( + new EntityMetadata.NoGravity(true).into(), + new EntityMetadata.Silent(true).into(), + new ItemProjectileMetadata.Item(getItem(cp)).into() + ) + ); + + + return List.of(spawn, meta); + } +} diff --git a/core/src/main/java/org/ef3d0c3e/sheepwars/hologram/HologramTextComponent.java b/core/src/main/java/org/ef3d0c3e/sheepwars/hologram/HologramTextComponent.java index a4ddfda..029b40d 100644 --- a/core/src/main/java/org/ef3d0c3e/sheepwars/hologram/HologramTextComponent.java +++ b/core/src/main/java/org/ef3d0c3e/sheepwars/hologram/HologramTextComponent.java @@ -14,6 +14,7 @@ import lombok.NonNull; import net.kyori.adventure.text.Component; import org.bukkit.Location; import org.bukkit.util.Vector; +import org.ef3d0c3e.sheepwars.packets.ArmorStandMetadata; import org.ef3d0c3e.sheepwars.packets.EntityMetadata; import org.ef3d0c3e.sheepwars.player.CPlayer; @@ -54,7 +55,10 @@ public abstract class HologramTextComponent extends HologramComponent .into(), new EntityMetadata.NoGravity(true).into(), new EntityMetadata.CustomNameVisible(true).into(), - new EntityMetadata.CustomName(getText(cp)).into() + new EntityMetadata.CustomName(getText(cp)).into(), + new ArmorStandMetadata.Status() + .isMarker(true) + .into() ) ); diff --git a/core/src/main/java/org/ef3d0c3e/sheepwars/level/lobby/LobbyEvents.java b/core/src/main/java/org/ef3d0c3e/sheepwars/level/lobby/LobbyEvents.java index ca34cf2..f56f725 100644 --- a/core/src/main/java/org/ef3d0c3e/sheepwars/level/lobby/LobbyEvents.java +++ b/core/src/main/java/org/ef3d0c3e/sheepwars/level/lobby/LobbyEvents.java @@ -15,11 +15,18 @@ import org.bukkit.event.entity.*; import org.bukkit.event.entity.EntityDismountEvent; import org.bukkit.event.entity.EntityMountEvent; import org.bukkit.event.inventory.CraftItemEvent; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryDragEvent; +import org.bukkit.event.inventory.InventoryInteractEvent; import org.bukkit.event.player.PlayerDropItemEvent; +import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerMoveEvent; +import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.PlayerInventory; import org.ef3d0c3e.sheepwars.events.*; import org.ef3d0c3e.sheepwars.game.Game; +import org.ef3d0c3e.sheepwars.items.IItem; +import org.ef3d0c3e.sheepwars.player.skin.SkinItem; import java.util.UUID; @@ -33,10 +40,11 @@ public class LobbyEvents implements Listener ev.getPlayer().getHandle().teleport(Game.getLobby().getSpawn()); final PlayerInventory inv = ev.getPlayer().getHandle().getInventory(); + inv.clear(); //inv.setItem(0, TeamItem.getItem(ev.getPlayer())); //inv.setItem(1, KitItem.getItem(ev.getPlayer())); - //inv.setItem(4, RocketItem.getItem(ev.getPlayer())); - //inv.setItem(7, SkinItem.getItem(ev.getPlayer())); + inv.setItem(4, RocketItem.getItem(ev.getPlayer())); + inv.setItem(7, SkinItem.getItem(ev.getPlayer())); } /* @@ -54,15 +62,15 @@ public class LobbyEvents implements Listener final ItemStack replace = KitItem.getItem(ev.getPlayer()); if (!ItemBase.replace(ev.getPlayer().getHandle().getInventory(), KitItem.ITEM, replace)) ev.getPlayer().getHandle().getInventory().setItem(1, replace); - } + }*/ @EventHandler public void onSkinChange(final SkinChangeEvent ev) { final ItemStack replace = SkinItem.getItem(ev.getPlayer()); - if (!ItemBase.replace(ev.getPlayer().getHandle().getInventory(), SkinItem.ITEM, replace)) + if (!IItem.replace(ev.getPlayer().getHandle().getInventory(), SkinItem.ITEM, replace)) ev.getPlayer().getHandle().getInventory().setItem(7, replace); - }*/ + } // Cancel all unwanted events @EventHandler @@ -74,6 +82,20 @@ public class LobbyEvents implements Listener ev.setCancelled(true); } + @EventHandler + public void onInventoryDrag(final InventoryDragEvent ev) + { + if (ev.getInventory().equals(ev.getWhoClicked().getInventory())) + ev.setCancelled(true); + } + + @EventHandler + public void onInventoryClick(final InventoryClickEvent ev) + { + if (ev.getInventory().equals(ev.getWhoClicked().getInventory())) + ev.setCancelled(true); + } + @EventHandler public void onFoodLevelChange(final FoodLevelChangeEvent ev) { diff --git a/core/src/main/java/org/ef3d0c3e/sheepwars/level/lobby/LobbyHologram.java b/core/src/main/java/org/ef3d0c3e/sheepwars/level/lobby/LobbyHologram.java new file mode 100644 index 0000000..dd8c420 --- /dev/null +++ b/core/src/main/java/org/ef3d0c3e/sheepwars/level/lobby/LobbyHologram.java @@ -0,0 +1,82 @@ +package org.ef3d0c3e.sheepwars.level.lobby; + +import lombok.NonNull; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.TextComponent; +import net.kyori.adventure.text.format.TextColor; +import net.kyori.adventure.text.format.TextDecoration; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.event.EventHandler; +import org.bukkit.util.Vector; +import org.bukkit.inventory.ItemStack; +import org.ef3d0c3e.sheepwars.events.WantsListen; +import org.ef3d0c3e.sheepwars.hologram.Hologram; +import org.ef3d0c3e.sheepwars.hologram.HologramComponent; +import org.ef3d0c3e.sheepwars.hologram.HologramItemComponent; +import org.ef3d0c3e.sheepwars.hologram.HologramTextComponent; +import org.ef3d0c3e.sheepwars.player.CPlayer; + +public class LobbyHologram extends Hologram { + private static final int NETWORK_ID = 0xFF877710; + private final Location location; + + public LobbyHologram(final Location location) + { + super(NETWORK_ID); + this.location = location; + + // Center item + addComponent(new HologramItemComponent(new Vector(0, 0, 0)) + { + final ItemStack item = new ItemStack(Material.WHITE_WOOL); + + @Override + protected @NonNull ItemStack getItem(@NonNull CPlayer cp) + { + return item; + } + }); + + // Left item + addComponent(new HologramItemComponent(new Vector(0.9, 0, 0)) + { + final ItemStack item = new ItemStack(Material.IRON_SWORD); + + @Override + protected @NonNull ItemStack getItem(@NonNull CPlayer cp) + { + return item; + } + }); + + // Right item + addComponent(new HologramItemComponent(new Vector(-0.9, 0, 0)) + { + final ItemStack item = new ItemStack(Material.BOW); + + @Override + protected @NonNull ItemStack getItem(@NonNull CPlayer cp) + { + return item; + } + }); + + // Title + addComponent(new HologramTextComponent(new Vector(0, 0.4, 0)) { + final static Component title = Component.text("SheepWars") + .color(TextColor.color(140, 187, 64)) + .decorate(TextDecoration.BOLD); + + @Override @NonNull + protected Component getText(@NonNull CPlayer cp) { + return title; + } + }); + } + + @Override + public @NonNull Location getLocation(@NonNull CPlayer cp) { + return location; + } +} diff --git a/core/src/main/java/org/ef3d0c3e/sheepwars/level/lobby/LobbyLevel.java b/core/src/main/java/org/ef3d0c3e/sheepwars/level/lobby/LobbyLevel.java index a297ba6..609b0de 100644 --- a/core/src/main/java/org/ef3d0c3e/sheepwars/level/lobby/LobbyLevel.java +++ b/core/src/main/java/org/ef3d0c3e/sheepwars/level/lobby/LobbyLevel.java @@ -16,11 +16,13 @@ import lombok.Getter; import lombok.NonNull; import org.bukkit.*; import org.ef3d0c3e.sheepwars.SheepWars; +import org.ef3d0c3e.sheepwars.hologram.HologramFactory; import org.ef3d0c3e.sheepwars.level.Level; import org.ef3d0c3e.sheepwars.level.VoidBiomeProvider; import org.ef3d0c3e.sheepwars.level.VoidChunkGenerator; import org.ef3d0c3e.sheepwars.npc.NPCFactory; import org.ef3d0c3e.sheepwars.player.skin.SkinNPC; +import org.ef3d0c3e.sheepwars.teams.TeamNPC; import java.io.*; @@ -32,9 +34,9 @@ public class LobbyLevel extends Level private Location spawn; //@Getter - //private LobbyHologram hologram; + private LobbyHologram hologram; private SkinNPC skinNpc; - //private TeamNPC teamNpc; + private TeamNPC teamNpc; //private KitNPC kitNpc; /** @@ -51,18 +53,16 @@ public class LobbyLevel extends Level { spawn = config.SPAWN.getLocation(getHandle()); - /* hologram = new LobbyHologram(config.INFO.getLocation(getHandle())); HologramFactory.register(hologram); - */ skinNpc = new SkinNPC(config.SKIN.getLocation(getHandle())); NPCFactory.register(skinNpc); - /* teamNpc = new TeamNPC(config.TEAM.getLocation(getHandle())); NPCFactory.register(teamNpc); + /* kitNpc = new KitNPC(config.KIT.getLocation(getHandle())); NPCFactory.register(kitNpc); */ diff --git a/core/src/main/java/org/ef3d0c3e/sheepwars/level/lobby/RocketItem.java b/core/src/main/java/org/ef3d0c3e/sheepwars/level/lobby/RocketItem.java new file mode 100644 index 0000000..7aa241a --- /dev/null +++ b/core/src/main/java/org/ef3d0c3e/sheepwars/level/lobby/RocketItem.java @@ -0,0 +1,59 @@ +package org.ef3d0c3e.sheepwars.level.lobby; + +import lombok.NonNull; +import org.bukkit.Material; +import org.bukkit.Sound; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.entity.Player; +import org.bukkit.event.block.Action; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.util.Vector; +import org.ef3d0c3e.sheepwars.items.IItem; +import org.ef3d0c3e.sheepwars.items.ItemFactory; +import org.ef3d0c3e.sheepwars.player.CPlayer; + +import java.text.MessageFormat; + +public class RocketItem extends IItem +{ + public RocketItem() + { + super(); + } + + @Override + protected boolean onInteract(final Player p, final ItemStack item, final Action action, final EquipmentSlot hand, final Block clicked, final BlockFace face) + { + if (p.getCooldown(item.getType()) != 0) return true; + + p.getLocation().getWorld().playSound(p.getLocation(), Sound.ENTITY_FIREWORK_ROCKET_LAUNCH, 12.f, 1.f); + p.setVelocity(p.getVelocity() + .clone().add(new Vector(0.0, 0.8, 0.0))); + + p.setCooldown(item.getType(), 30); + return true; + } + + @Override + protected boolean onDrop(final Player p, final ItemStack item) { return true; } + + static final public RocketItem ITEM = new RocketItem(); + /** + * Gets item for player + * @param cp Player to get item for + * @return Item + */ + public static @NonNull ItemStack getItem(final CPlayer cp) + { + final ItemStack item = new ItemStack(Material.FIREWORK_ROCKET); + final ItemMeta meta = item.getItemMeta(); + meta.setDisplayName(MessageFormat.format("§a{0} §7{1}", cp.getLocale().ITEMS_ROCKET, cp.getLocale().ITEMS_RIGHTCLICK)); + item.setItemMeta(meta); + + ItemFactory.registerItem(ITEM); + return ITEM.apply(item); + } +} diff --git a/core/src/main/java/org/ef3d0c3e/sheepwars/locale/Locale.java b/core/src/main/java/org/ef3d0c3e/sheepwars/locale/Locale.java index fbb7523..0d29f48 100644 --- a/core/src/main/java/org/ef3d0c3e/sheepwars/locale/Locale.java +++ b/core/src/main/java/org/ef3d0c3e/sheepwars/locale/Locale.java @@ -69,6 +69,8 @@ public class Locale public String TEAM_PICKER; public String TEAM_NPCNAME; public String TEAM_NPCCURRENT; + public String TEAM_RED; + public String TEAM_BLUE; // Kits public String KIT_PICKER; diff --git a/core/src/main/java/org/ef3d0c3e/sheepwars/npc/PlayerNPC.java b/core/src/main/java/org/ef3d0c3e/sheepwars/npc/PlayerNPC.java index 7095bbe..f178206 100644 --- a/core/src/main/java/org/ef3d0c3e/sheepwars/npc/PlayerNPC.java +++ b/core/src/main/java/org/ef3d0c3e/sheepwars/npc/PlayerNPC.java @@ -4,7 +4,6 @@ import com.comphenix.protocol.wrappers.EnumWrappers; import com.github.retrooper.packetevents.PacketEvents; import com.github.retrooper.packetevents.protocol.entity.data.EntityData; import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes; -import com.github.retrooper.packetevents.protocol.npc.NPC; import com.github.retrooper.packetevents.protocol.player.GameMode; import com.github.retrooper.packetevents.protocol.player.TextureProperty; import com.github.retrooper.packetevents.protocol.player.UserProfile; @@ -15,8 +14,8 @@ import lombok.Getter; import lombok.NonNull; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; -import org.bukkit.Bukkit; import org.bukkit.Location; +import org.ef3d0c3e.sheepwars.packets.ArmorStandMetadata; import org.ef3d0c3e.sheepwars.packets.EntityMetadata; import org.ef3d0c3e.sheepwars.packets.PlayerMetadata; import org.ef3d0c3e.sheepwars.player.CPlayer; @@ -139,7 +138,7 @@ public abstract class PlayerNPC final WrapperPlayServerSpawnEntity spawn = new WrapperPlayServerSpawnEntity( networkId+i+1, Optional.of(UUID.randomUUID()), EntityTypes.ARMOR_STAND, - new Vector3d(loc.getX(), loc.getY()+(tags.size()-i-1)*0.3, loc.getZ()), + new Vector3d(loc.getX(), loc.getY()+(tags.size()-i-1)*0.3+1.80, loc.getZ()), 0.f, 0.f, 0.f, 0, Optional.empty() @@ -153,7 +152,10 @@ public abstract class PlayerNPC .into(), new EntityMetadata.NoGravity(true).into(), new EntityMetadata.CustomNameVisible(true).into(), - new EntityMetadata.CustomName(tag).into() + new EntityMetadata.CustomName(tag).into(), + new ArmorStandMetadata.Status() + .isMarker(true) + .into() ) ); diff --git a/core/src/main/java/org/ef3d0c3e/sheepwars/packets/ArmorStandMetadata.java b/core/src/main/java/org/ef3d0c3e/sheepwars/packets/ArmorStandMetadata.java index a7dc408..c98815f 100644 --- a/core/src/main/java/org/ef3d0c3e/sheepwars/packets/ArmorStandMetadata.java +++ b/core/src/main/java/org/ef3d0c3e/sheepwars/packets/ArmorStandMetadata.java @@ -28,18 +28,18 @@ public class ArmorStandMetadata { } public Status hasArms(boolean v) { - value = (byte)(value | (v ? 0b10 : 0b0)); - return this; - } - - public Status hasBasePlate(boolean v) { value = (byte)(value | (v ? 0b100 : 0b0)); return this; } - public Status isMarker(boolean v) { + public Status hasBasePlate(boolean v) { value = (byte)(value | (v ? 0b1000 : 0b0)); return this; } + + public Status isMarker(boolean v) { + value = (byte)(value | (v ? 0b10000 : 0b0)); + return this; + } } } diff --git a/core/src/main/java/org/ef3d0c3e/sheepwars/packets/ItemProjectileMetadata.java b/core/src/main/java/org/ef3d0c3e/sheepwars/packets/ItemProjectileMetadata.java new file mode 100644 index 0000000..4de1f19 --- /dev/null +++ b/core/src/main/java/org/ef3d0c3e/sheepwars/packets/ItemProjectileMetadata.java @@ -0,0 +1,25 @@ +package org.ef3d0c3e.sheepwars.packets; + +import com.github.retrooper.packetevents.protocol.entity.data.EntityData; +import com.github.retrooper.packetevents.protocol.entity.data.EntityDataTypes; +import io.github.retrooper.packetevents.util.SpigotConversionUtil; +import org.bukkit.inventory.ItemStack; + +public class ItemProjectileMetadata { + public static class Item implements IntoEntityData { + @Override + public EntityData into() { + return new EntityData( + 8, + EntityDataTypes.ITEMSTACK, + SpigotConversionUtil.fromBukkitItemStack(value) + ); + } + + ItemStack value; + + public Item(final ItemStack value) { + this.value = value; + } + } +} diff --git a/core/src/main/java/org/ef3d0c3e/sheepwars/player/CPlayer.java b/core/src/main/java/org/ef3d0c3e/sheepwars/player/CPlayer.java index 98a62f9..5a2410a 100644 --- a/core/src/main/java/org/ef3d0c3e/sheepwars/player/CPlayer.java +++ b/core/src/main/java/org/ef3d0c3e/sheepwars/player/CPlayer.java @@ -13,6 +13,7 @@ import org.ef3d0c3e.sheepwars.events.CPlayerJoinEvent; import org.ef3d0c3e.sheepwars.events.CPlayerQuitEvent; import org.ef3d0c3e.sheepwars.events.WantsListen; import org.ef3d0c3e.sheepwars.locale.Locale; +import org.ef3d0c3e.sheepwars.teams.Team; import javax.annotation.Nullable; import java.text.MessageFormat; @@ -95,17 +96,6 @@ public class CPlayer { if (cp.isOnline()) f.operation(cp); } - /** - * The player handle - */ - @Getter - private Player handle; - /** - * The OfflinePlayer handle - */ - @Getter - private OfflinePlayer offlinePlayer; - /** * The locale configured for the player */ @@ -117,6 +107,17 @@ public class CPlayer { @Getter private CosmeticManager cosmetics = new CosmeticManager(this); + /** + * The player handle + */ + @Getter + private Player handle; + /** + * The OfflinePlayer handle + */ + @Getter + private OfflinePlayer offlinePlayer; + /** * Updates the player handle * @param handle New handle @@ -143,6 +144,21 @@ public class CPlayer { return offlinePlayer.isOnline(); } + /** + * The player's team + * May not be null in lobby phase, null in game phase means spectator i.e. joined after the game started + */ + @Getter + private Team team = null; + + /** + * @note Don't call! Use {@link Team.setPlayerTeam(cp, team)} + * @param team New team + */ + public void setTeam(Team team) { + this.team = team; + } + /** * Events for the player wrapper * When a player joins or quits diff --git a/core/src/main/java/org/ef3d0c3e/sheepwars/player/CosmeticManager.java b/core/src/main/java/org/ef3d0c3e/sheepwars/player/CosmeticManager.java index f1d9a4b..0c42f73 100644 --- a/core/src/main/java/org/ef3d0c3e/sheepwars/player/CosmeticManager.java +++ b/core/src/main/java/org/ef3d0c3e/sheepwars/player/CosmeticManager.java @@ -3,6 +3,7 @@ package org.ef3d0c3e.sheepwars.player; import lombok.Getter; import lombok.NonNull; import lombok.Setter; +import net.kyori.adventure.text.Component; import org.bukkit.Bukkit; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; @@ -61,10 +62,11 @@ public class CosmeticManager { final CPlayer cp = CPlayer.get(ev.getPlayer()); String message; - //if (cp.getTeam() == null) + if (cp.getTeam() == null) message = MessageFormat.format("§f{0}§8:§7 {1}", cp.getHandle().getName(), ev.getMessage()); - //else - //message = MessageFormat.format("{0} | {1}§8:§7 {2}", cp.getTeam().getColoredName(), cp.getHandle().getName(), ev.getMessage()); + else { + message = MessageFormat.format("{0} | {1}§8:§7 {2}", cp.getTeam().getName(cp), cp.getHandle().getName(), ev.getMessage()); + } Bukkit.broadcastMessage(message); } diff --git a/core/src/main/java/org/ef3d0c3e/sheepwars/player/skin/SkinItem.java b/core/src/main/java/org/ef3d0c3e/sheepwars/player/skin/SkinItem.java new file mode 100644 index 0000000..fe694b3 --- /dev/null +++ b/core/src/main/java/org/ef3d0c3e/sheepwars/player/skin/SkinItem.java @@ -0,0 +1,58 @@ +package org.ef3d0c3e.sheepwars.player.skin; + +import lombok.NonNull; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.entity.Player; +import org.bukkit.event.block.Action; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.ef3d0c3e.sheepwars.Util; +import org.ef3d0c3e.sheepwars.items.IItem; +import org.ef3d0c3e.sheepwars.items.ItemFactory; +import org.ef3d0c3e.sheepwars.player.CPlayer; + +import java.text.MessageFormat; + +public class SkinItem extends IItem +{ + public SkinItem() + { + super(); + } + + @Override + protected boolean onInteract(final Player p, final ItemStack item, final Action action, final EquipmentSlot hand, final Block clicked, final BlockFace face) + { + p.openInventory(new SkinMenu(CPlayer.get(p)).getInventory()); + return true; + } + + @Override + protected boolean onDrop(final Player p, final ItemStack item) { return true; } + + static final public SkinItem ITEM = new SkinItem(); + /** + * Gets item for player + * @param cp Player to get item for + * @return Item + */ + public static @NonNull ItemStack getItem(final CPlayer cp) + { + final ItemStack item = (cp.getCosmetics().getCurrentSkin() == null) + ? cp.getCosmetics().getOriginalSkin().getDisplayItem() + : cp.getCosmetics().getCurrentSkin().getDisplayItem(); + final ItemMeta meta = item.getItemMeta(); + if (cp.getCosmetics().getCurrentSkin() == null) + meta.setDisplayName(MessageFormat.format("§a{0} §7{1}", cp.getLocale().ITEMS_SKIN, cp.getLocale().ITEMS_RIGHTCLICK)); + else + meta.setDisplayName(MessageFormat.format("§a{0} §8: §6{1} §7{2}", cp.getLocale().ITEMS_SKIN, cp.getCosmetics().getCurrentSkin().getName(), cp.getLocale().ITEMS_RIGHTCLICK)); + meta.setLore(Util.coloredLore("§7", cp.getLocale().ITEMS_SKINLORE)); + item.setItemMeta(meta); + + ItemFactory.registerItem(ITEM); + return ITEM.apply(item); + } +} + diff --git a/core/src/main/java/org/ef3d0c3e/sheepwars/teams/Team.java b/core/src/main/java/org/ef3d0c3e/sheepwars/teams/Team.java new file mode 100644 index 0000000..249ecd0 --- /dev/null +++ b/core/src/main/java/org/ef3d0c3e/sheepwars/teams/Team.java @@ -0,0 +1,120 @@ +package org.ef3d0c3e.sheepwars.teams; + +import com.comphenix.protocol.wrappers.WrappedChatComponent; +import io.github.retrooper.packetevents.util.SpigotConversionUtil; +import jline.internal.Nullable; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NonNull; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.TextColor; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.ef3d0c3e.sheepwars.events.CPlayerJoinEvent; +import org.ef3d0c3e.sheepwars.events.TeamChangeEvent; +import org.ef3d0c3e.sheepwars.events.WantsListen; +import org.ef3d0c3e.sheepwars.player.CPlayer; +import org.ef3d0c3e.sheepwars.game.Game; + +import java.util.HashSet; + +@AllArgsConstructor +public abstract class Team { + @Getter + private final ChatColor chatColor; + @Getter + private final TextColor color; + private HashSet players; + + private Team(final ChatColor chatColor, final TextColor color) + { + this.chatColor = chatColor; + this.color = color; + players = new HashSet<>(); + } + + + public abstract String getName(final CPlayer cp); + + public Component getColoredName(final CPlayer cp) + { + return Component.text(getName(cp)) + .color(TextColor.color(color)); + } + + /** + * Executes lambda for each member of the team + * @param f Lambda to execute for each member + */ + public void forEachMember(final CPlayer.ForEachPlayer f) + { + for (final CPlayer cp : players) + f.operation(cp); + } + + /** + * Executes lambda for each member of the team + * @param pre Player predicate + * @param f Lambda to execute for each member + */ + public void forEachMember(final CPlayer.PlayerPredicate pre, final CPlayer.ForEachPlayer f) + { + for (final CPlayer cp : players) + if (pre.operation(cp)) f.operation(cp); + } + + public int count() { + return players.size(); + } + + /** + * Sets the player's team + * @param cp The player to change the team of + * @param team The new team + */ + public static void setPlayerTeam(final @NonNull CPlayer cp, final @Nullable Team team) { + final Team oldTeam = cp.getTeam(); + if (oldTeam != null) oldTeam.players.remove(cp); + + cp.setTeam(team); + if (team != null) team.players.add(cp); + if (team != oldTeam) + Bukkit.getPluginManager().callEvent(new TeamChangeEvent(cp, oldTeam, team)); + } + + public static Team RED = new Team(ChatColor.RED, TextColor.color(255, 0, 0)) { + @Override + public String getName(CPlayer cp) { + return cp.getLocale().TEAM_RED; + } + }; + + public static Team BLUE = new Team(ChatColor.BLUE, TextColor.color(0, 0, 255)) { + @Override + public String getName(CPlayer cp) { + return cp.getLocale().TEAM_BLUE; + } + }; + + @WantsListen(phase = WantsListen.Target.Lobby) + public static class Events implements Listener + { + @EventHandler + public void onJoin(final CPlayerJoinEvent ev) + { + if (RED.count() < BLUE.count()) + { + Team.setPlayerTeam(ev.getPlayer(), RED); + } + else if (BLUE.count() < RED.count()) { + Team.setPlayerTeam(ev.getPlayer(), BLUE); + } + // Random team + else { + Team.setPlayerTeam(ev.getPlayer(), Game.nextInt() % 2 == 0 ? RED : BLUE); + } + } + } +} diff --git a/core/src/main/java/org/ef3d0c3e/sheepwars/teams/TeamNPC.java b/core/src/main/java/org/ef3d0c3e/sheepwars/teams/TeamNPC.java new file mode 100644 index 0000000..8464a3f --- /dev/null +++ b/core/src/main/java/org/ef3d0c3e/sheepwars/teams/TeamNPC.java @@ -0,0 +1,108 @@ +package org.ef3d0c3e.sheepwars.teams; + +import com.comphenix.protocol.wrappers.EnumWrappers; +import com.google.common.collect.Lists; +import com.mojang.authlib.properties.Property; +import lombok.NonNull; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.TextColor; +import net.kyori.adventure.text.format.TextDecoration; +import org.bukkit.Location; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.scheduler.BukkitRunnable; +import org.ef3d0c3e.sheepwars.SheepWars; +import org.ef3d0c3e.sheepwars.events.TeamChangeEvent; +import org.ef3d0c3e.sheepwars.events.WantsListen; +import org.ef3d0c3e.sheepwars.npc.NPCFactory; +import org.ef3d0c3e.sheepwars.npc.PlayerNPC; +import org.ef3d0c3e.sheepwars.player.CPlayer; + +import java.util.List; + +public class TeamNPC extends PlayerNPC { + private static final int NETWORK_ID = 0xFF777720; + private final Location location; + + public TeamNPC(Location location) { + super(NETWORK_ID); + this.location = location; + } + + @Override + protected @NonNull String getName() { + return "skin"; + } + + @Override + protected @NonNull List getNametag(@NonNull CPlayer cp) { + if (cp.getCosmetics().getCurrentSkin() == null) + return Lists.newArrayList( + Component.text(cp.getLocale().TEAM_NPCNAME) + .color(TextColor.color(207, 50, 200)) + .decorate(TextDecoration.BOLD)); + else + return Lists.newArrayList( + Component.text(cp.getLocale().TEAM_NPCNAME) + .color(TextColor.color(207, 50, 200)) + .decorate(TextDecoration.BOLD), + Component.text(cp.getLocale().TEAM_NPCCURRENT) + .color(TextColor.color(85, 85, 127)) + .decorate(TextDecoration.UNDERLINED), + Component.text(cp.getCosmetics().getCurrentSkin().getName()) + .color(TextColor.color(180, 85, 120)) + ); + } + + @Override + protected @NonNull Property getTextures(@NonNull CPlayer cp) { + return new Property("textures", + "ewogICJ0aW1lc3RhbXAiIDogMTcwMTk1OTQ2MjQ4NSwKICAicHJvZmlsZUlkIiA6ICJmODY0ZjY3ZGJlN2Y0OTBlYTZlODQzMjg2M2NkZWMxOCIsCiAgInByb2ZpbGVOYW1lIiA6ICJCb3lmcmllbmQ1MDY1IiwKICAic2lnbmF0dXJlUmVxdWlyZWQiIDogdHJ1ZSwKICAidGV4dHVyZXMiIDogewogICAgIlNLSU4iIDogewogICAgICAidXJsIiA6ICJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlLzIzMDgwZWZlMzdkNTk0ZjU0ZjNjMTgxYmRlNTBlMTI4ZWQyZjQyYmVkOGQzNjk3N2Y4ZTQwNmRmZDY0ZWZkZmQiCiAgICB9CiAgfQp9", + "hB0+1tCYxTvdjYlYS7wtwqbf30CNa/8BmkrpWv7mA0MFiQIikInm+rZEZbyHFFOba8Dc5Ee9hC/pu4rHwBqXN4XjsFELFqUHymKnhOSNmUfY7aA0rn+CaNrJOP7LSrajSN+r7qgRsXUph/7yEaTpMhWwu+nfzbxtaS7e8WqjqHtMYdjWg30HSTqrSYzmobo9wh3twbuEFil8dCTdG3A9YkICfhYeuAgwDENGe220ThPC4HJsyPS1NCAkuwfGHKFyjqomUkPm0o6ijnb4I5naSvGFFLqvjJDFQ2dwui8TacykoLj+Mou3NnSTawcutBD10HiMF/mgssZTsINim1Da4uOZR9FsShiAk5Z4nq7unh0vPdH+lgCoTaN5tD0DCmrZt5OLSEqpzx62EoYRWM5nUXRISVHKKADpra424O9zSytOCwjGGvYVg6uB6lOOb+Gm1+VDEU+7QzwhpYiMFaqiofZDJw7LNQ0EZcbbbTUFfUE7/d/X4sb2AGQno7RGVAdWrz/Kszf6/ri+Wru4GRHZBaS3LVnxXU4FUL7P9yF3ZPrpNZIt14f1WSWmk1ltGnwNwK8HfSRfo7uWZtDVZZWOeJA1bqTYMDP9n1hIHYgrLdIEmXpGK3RVytYeKbQVLWbTtPKTzPMhDaDyQkwGint0HgFm3jS29sRejdAMF81Hjt4="); + } + + @Override + protected @NonNull Location getLocation(@NonNull CPlayer cp) { + return location; + } + + @Override + protected boolean sendPredicate(@NonNull CPlayer cp) { + return cp.getHandle().getWorld() == location.getWorld(); + } + + @Override + protected void onInteract(@NonNull CPlayer cp, EnumWrappers.Hand hand, boolean sneaking) { + new BukkitRunnable() { + @Override + public void run() { + if (!cp.isOnline()) return; + + if (cp.getTeam() == Team.RED) { + Team.setPlayerTeam(cp, Team.BLUE); + } else { + Team.setPlayerTeam(cp, Team.RED); + } + } + }.runTask(SheepWars.getPlugin()); + } + + @Override + protected void update(final @NonNull CPlayer cp) { + // Resend nametag + removeNametag(cp, 3); + sendNametag(cp); + + // Resend skin + sendInfo(cp, true); + } + + @WantsListen(phase = WantsListen.Target.Lobby) + public static class Events implements Listener { + @EventHandler + public void onTeamChange(final TeamChangeEvent ev) { + ((TeamNPC) NPCFactory.get(NETWORK_ID)).update(ev.getPlayer()); + } + } + +} diff --git a/core/src/main/resources/exports/lobby.yml b/core/src/main/resources/exports/lobby.yml index e6a7089..f063462 100644 --- a/core/src/main/resources/exports/lobby.yml +++ b/core/src/main/resources/exports/lobby.yml @@ -1,7 +1,7 @@ offset: [0, 64, 0] spawn: [0.5, 65, 0.5, 0, 0] limbo: 55 -info: [0.5, 59.5, 20.5, 0, 0] +info: [0.5, 60.5, 20.5, 0, 0] skin: [2.5, 58, 40.5, -179.9, 0] team: [6.5, 58, 39.5, -179.9, 0] kit: [-1.5, 58, 40.5, -179.9, 0] diff --git a/core/src/main/resources/exports/locales/fr.yml b/core/src/main/resources/exports/locales/fr.yml index 4a1fcc8..22728f5 100644 --- a/core/src/main/resources/exports/locales/fr.yml +++ b/core/src/main/resources/exports/locales/fr.yml @@ -55,7 +55,8 @@ team: picker: "Choisissez une équipe" npcname: "Choisissez une équipe" npccurrent: "Votre équipe:" - + red: "Rouge" + blue: "Bleue" kit: picker: "Choisissez un kit" npcname: "Choisissez un kit" diff --git a/pom.xml b/pom.xml index d80ef7d..9880b6c 100644 --- a/pom.xml +++ b/pom.xml @@ -40,6 +40,10 @@ sonatype https://oss.sonatype.org/content/groups/public/ + + papermc + https://repo.papermc.io/repository/maven-public/ + diff --git a/spigot-1.21.0/src/main/java/org/ef3d0c3e/sheepwars/v1_21_R0/Skin.java b/spigot-1.21.0/src/main/java/org/ef3d0c3e/sheepwars/v1_21_R0/Skin.java index 7a4a3ac..a226386 100644 --- a/spigot-1.21.0/src/main/java/org/ef3d0c3e/sheepwars/v1_21_R0/Skin.java +++ b/spigot-1.21.0/src/main/java/org/ef3d0c3e/sheepwars/v1_21_R0/Skin.java @@ -1,7 +1,9 @@ package org.ef3d0c3e.sheepwars.v1_21_R0; import com.github.retrooper.packetevents.PacketEvents; +import com.github.retrooper.packetevents.protocol.chat.RemoteChatSession; import com.github.retrooper.packetevents.protocol.player.GameMode; +import com.github.retrooper.packetevents.protocol.player.PublicProfileKey; import com.github.retrooper.packetevents.protocol.player.TextureProperty; import com.github.retrooper.packetevents.protocol.player.UserProfile; import com.github.retrooper.packetevents.protocol.world.Difficulty; @@ -14,7 +16,9 @@ import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerPl import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerRespawn; import com.mojang.authlib.properties.Property; import com.mojang.authlib.properties.PropertyMap; +import io.github.retrooper.packetevents.util.SpigotConversionUtil; import lombok.NonNull; +import net.kyori.adventure.text.Component; import net.minecraft.core.GlobalPos; import net.minecraft.core.Holder; import net.minecraft.network.protocol.game.ClientboundRespawnPacket; @@ -67,6 +71,9 @@ public class Skin implements SkinVersionWrapper cp.getHandle().getUniqueId() ); + final ServerPlayer p = ((CraftPlayer)cp.getHandle()).getHandle(); + final net.minecraft.network.chat.RemoteChatSession chatSession = p.getChatSession(); + // Add info packet final WrapperPlayServerPlayerInfoUpdate info = new WrapperPlayServerPlayerInfoUpdate( WrapperPlayServerPlayerInfoUpdate.Action.ADD_PLAYER, @@ -85,14 +92,19 @@ public class Skin implements SkinVersionWrapper true, cp.getHandle().getPing(), GameMode.getById(cp.getHandle().getGameMode().getValue()), - null, - null + Component.text(cp.getHandle().getName()), + // FIXME: This is not correct as the player chat session is still invalid + new RemoteChatSession(chatSession.sessionId(), new PublicProfileKey( + chatSession.profilePublicKey().data().expiresAt(), + chatSession.profilePublicKey().data().key(), + chatSession.profilePublicKey().data().keySignature() + ) + ) ) ); // Respawn packet final ServerLevel level = ((CraftWorld)loc.getWorld()).getHandle(); - final ServerPlayer p = ((CraftPlayer)cp.getHandle()).getHandle(); final WrapperPlayServerRespawn respawn = new WrapperPlayServerRespawn( new Dimension(level.getLevel().dimensionType().hashCode()),