9
0
mirror of https://github.com/HibiscusMC/HMCCosmetics.git synced 2025-12-26 18:39:07 +00:00

Added citizens support

This commit is contained in:
MasterOfTheFish
2022-02-19 19:37:38 -05:00
parent a42730dc19
commit ffd15345d2
24 changed files with 663 additions and 113 deletions

View File

@@ -93,7 +93,7 @@ bukkit {
apiVersion = "1.16"
name = "HMCCosmetics"
authors = listOf("MasterOfTheFish")
softDepend = listOf("Multiverse", "PlaceholderAPI", "Oraxen", "ItemsAdder")
softDepend = listOf("Multiverse", "PlaceholderAPI", "Oraxen", "ItemsAdder", "Citizens")
depend = listOf("ProtocolLib")
permissions {
register("hmccosmetics.cmd.default") {

View File

@@ -36,3 +36,4 @@ softdepend:
- PlaceholderAPI
- Oraxen
- ItemsAdder
- Citizens

View File

@@ -36,3 +36,4 @@ softdepend:
- PlaceholderAPI
- Oraxen
- ItemsAdder
- Citizens

View File

@@ -11,6 +11,7 @@ import io.github.fisher2911.hmccosmetics.database.DatabaseFactory;
import io.github.fisher2911.hmccosmetics.gui.ArmorItem;
import io.github.fisher2911.hmccosmetics.gui.CosmeticsMenu;
import io.github.fisher2911.hmccosmetics.hook.HookManager;
import io.github.fisher2911.hmccosmetics.hook.item.CitizensHook;
import io.github.fisher2911.hmccosmetics.hook.item.ItemsAdderHook;
import io.github.fisher2911.hmccosmetics.listener.ClickListener;
import io.github.fisher2911.hmccosmetics.listener.CosmeticFixListener;
@@ -29,12 +30,14 @@ import io.github.fisher2911.hmccosmetics.user.UserManager;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;
import me.mattstudios.mf.base.CommandManager;
import me.mattstudios.mf.base.CompletionHandler;
import org.bstats.bukkit.Metrics;
import org.bukkit.Bukkit;
import org.bukkit.plugin.java.JavaPlugin;
@@ -102,10 +105,10 @@ public class HMCCosmetics extends JavaPlugin {
this.saveTask.cancel();
this.database.saveAll();
this.messageHandler.close();
this.userManager.cancelTeleportTask();
this.userManager.removeAll();
Threads.getInstance().onDisable();
this.database.close();
this.taskManager.end();
}
private void registerListeners() {
@@ -127,6 +130,7 @@ public class HMCCosmetics extends JavaPlugin {
private void registerCommands() {
this.commandManager = new CommandManager(this, true);
final HookManager hookManager = HookManager.getInstance();
this.commandManager.getMessageHandler().register(
"cmd.no.console", player ->
this.messageHandler.sendMessage(
@@ -134,17 +138,28 @@ public class HMCCosmetics extends JavaPlugin {
Messages.MUST_BE_PLAYER
)
);
this.commandManager.getCompletionHandler().register("#types",
final CompletionHandler completionHandler = this.commandManager.getCompletionHandler();
completionHandler.register("#types",
resolver ->
Arrays.stream(ArmorItem.Type.
values()).
map(ArmorItem.Type::toString).
collect(Collectors.toList())
);
this.commandManager.getCompletionHandler().register("#ids",
completionHandler.register("#ids",
resolver ->
this.cosmeticManager.getAll().stream().map(ArmorItem::getId)
.collect(Collectors.toList()));
completionHandler.register("#npc-args",
resolver -> List.of(CosmeticsCommand.NPC_REMOVE, CosmeticsCommand.NPC_APPLY));
completionHandler.register("#npcs", resolver -> {
final List<String> ids = new ArrayList<>();
if (!hookManager.isEnabled(CitizensHook.class)) return ids;
for (final int id : hookManager.getCitizensHook().getAllNPCS()) {
ids.add(String.valueOf(id));
}
return ids;
});
this.commandManager.register(new CosmeticsCommand(this));
}

View File

@@ -1,6 +1,7 @@
package io.github.fisher2911.hmccosmetics.api.event;
import io.github.fisher2911.hmccosmetics.api.CosmeticItem;
import io.github.fisher2911.hmccosmetics.user.BaseUser;
import io.github.fisher2911.hmccosmetics.user.User;
/**
@@ -8,17 +9,19 @@ import io.github.fisher2911.hmccosmetics.user.User;
*/
public class CosmeticChangeEvent extends CosmeticItemEvent {
private final User user;
private final BaseUser user;
private CosmeticItem removed;
public CosmeticChangeEvent(final CosmeticItem cosmeticItem, final CosmeticItem removed,
final User user) {
public CosmeticChangeEvent(
final CosmeticItem cosmeticItem,
final CosmeticItem removed,
final BaseUser user) {
super(cosmeticItem);
this.removed = removed;
this.user = user;
}
public User getUser() {
public BaseUser getUser() {
return user;
}

View File

@@ -5,6 +5,8 @@ import io.github.fisher2911.hmccosmetics.config.Settings;
import io.github.fisher2911.hmccosmetics.config.WardrobeSettings;
import io.github.fisher2911.hmccosmetics.gui.ArmorItem;
import io.github.fisher2911.hmccosmetics.gui.CosmeticsMenu;
import io.github.fisher2911.hmccosmetics.hook.HookManager;
import io.github.fisher2911.hmccosmetics.hook.item.CitizensHook;
import io.github.fisher2911.hmccosmetics.message.Message;
import io.github.fisher2911.hmccosmetics.message.MessageHandler;
import io.github.fisher2911.hmccosmetics.message.Messages;
@@ -14,10 +16,6 @@ import io.github.fisher2911.hmccosmetics.user.User;
import io.github.fisher2911.hmccosmetics.user.UserManager;
import io.github.fisher2911.hmccosmetics.user.Wardrobe;
import io.github.fisher2911.hmccosmetics.util.StringUtils;
import java.util.Map;
import java.util.Optional;
import me.mattstudios.mf.annotations.Command;
import me.mattstudios.mf.annotations.Completion;
import me.mattstudios.mf.annotations.Default;
@@ -25,11 +23,14 @@ import me.mattstudios.mf.annotations.Permission;
import me.mattstudios.mf.annotations.SubCommand;
import me.mattstudios.mf.base.CommandBase;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Color;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
@Command("cosmetics")
public class CosmeticsCommand extends CommandBase {
@@ -86,8 +87,11 @@ public class CosmeticsCommand extends CommandBase {
@SubCommand("dye")
@Permission(io.github.fisher2911.hmccosmetics.message.Permission.DYE_COMMAND)
public void dyeArmor(final Player player, @Completion("#types") String typeString,
final @me.mattstudios.mf.annotations.Optional String dyeColor) {
public void dyeArmor(
final Player player,
@Completion("#types") String typeString,
final @me.mattstudios.mf.annotations.Optional String dyeColor
) {
final Optional<User> optionalUser = this.userManager.get(player.getUniqueId());
@@ -141,7 +145,8 @@ public class CosmeticsCommand extends CommandBase {
final CommandSender sender,
@Completion("#players") final Player player,
@Completion("#ids") final String id,
final @me.mattstudios.mf.annotations.Optional String dyeColor) {
final @me.mattstudios.mf.annotations.Optional String dyeColor
) {
final Optional<User> userOptional = this.userManager.get(player.getUniqueId());
if (userOptional.isEmpty()) {
@@ -285,6 +290,75 @@ public class CosmeticsCommand extends CommandBase {
}).execute();
}
public static final String NPC_APPLY = "apply";
public static final String NPC_REMOVE = "remove";
@SubCommand("npc")
@Permission(io.github.fisher2911.hmccosmetics.message.Permission.SET_COSMETIC_COMMAND)
public void applyNpc(
final CommandSender sender,
@Completion("#npc-args") final String arg,
/*@Completion("#npcs")*/ final Integer npcId,
@Completion("#types") final String typeStr,
@me.mattstudios.mf.annotations.Optional @Completion("#ids") final String itemId
) {
final CitizensHook citizensHook = HookManager.getInstance().getCitizensHook();
if (citizensHook == null) {
this.messageHandler.sendMessage(
sender,
Messages.HOOK_NOT_ENABLED,
Map.of(Placeholder.TYPE, "Citizens")
);
return;
}
final ArmorItem armorItem = this.plugin.getCosmeticManager().getArmorItem(itemId);
if (armorItem == null) {
this.messageHandler.sendMessage(
sender,
Messages.ITEM_NOT_FOUND
);
return;
}
switch (arg.toLowerCase(Locale.ROOT)) {
case NPC_APPLY -> {
this.setNpcCosmetic(citizensHook, sender, npcId, armorItem);
}
case NPC_REMOVE -> {
try {
final ArmorItem.Type type = ArmorItem.Type.valueOf(typeStr);
this.setNpcCosmetic(citizensHook, sender, npcId, ArmorItem.empty(type, "none"));
} catch (final IllegalArgumentException exception) {
this.messageHandler.sendMessage(
sender,
Messages.INVALID_TYPE,
Map.of(Placeholder.TYPE, typeStr)
);
}
}
};
}
private void setNpcCosmetic(final CitizensHook hook, final CommandSender sender, final int npcId, final ArmorItem item) {
final boolean isSet = hook.setNpcCosmetic(npcId, item);
if (!isSet) {
this.messageHandler.sendMessage(
sender,
Messages.NPC_NOT_FOUND,
Map.of(Placeholder.ID, String.valueOf(npcId))
);
return;
}
this.messageHandler.sendMessage(
sender,
Messages.SET_NPC_COSMETIC,
Map.of(Placeholder.TYPE, item.getType().toString(),
Placeholder.ITEM, item.getId(),
Placeholder.ID, String.valueOf(npcId))
);
}
private void setDyeColor(final String dyeColor, final ArmorItem armorItem, final CommandSender sender) {
try {
final java.awt.Color awtColor = java.awt.Color.decode(dyeColor);

View File

@@ -7,9 +7,14 @@ import com.j256.ormlite.table.TableUtils;
import io.github.fisher2911.hmccosmetics.HMCCosmetics;
import io.github.fisher2911.hmccosmetics.concurrent.Threads;
import io.github.fisher2911.hmccosmetics.database.dao.ArmorItemDAO;
import io.github.fisher2911.hmccosmetics.database.dao.CitizenDAO;
import io.github.fisher2911.hmccosmetics.database.dao.UserDAO;
import io.github.fisher2911.hmccosmetics.gui.ArmorItem;
import io.github.fisher2911.hmccosmetics.hook.HookManager;
import io.github.fisher2911.hmccosmetics.hook.item.CitizensHook;
import io.github.fisher2911.hmccosmetics.inventory.PlayerArmor;
import io.github.fisher2911.hmccosmetics.user.BaseUser;
import io.github.fisher2911.hmccosmetics.user.NPCUser;
import io.github.fisher2911.hmccosmetics.user.User;
import java.sql.SQLException;
@@ -18,15 +23,18 @@ import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import io.github.fisher2911.hmccosmetics.user.UserFactory;
import io.github.fisher2911.hmccosmetics.user.Wardrobe;
import org.bukkit.Bukkit;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
public class Database {
protected final HMCCosmetics plugin;
final Dao<UserDAO, UUID> userDao;
final Dao<ArmorItemDAO, UUID> armorItemDao;
final Dao<CitizenDAO, Integer> citizenDao;
final Dao<ArmorItemDAO, String> armorItemDao;
private final ConnectionSource dataSource;
private final DatabaseType databaseType;
AtomicInteger FAKE_ENTITY_ID = new AtomicInteger(Integer.MAX_VALUE);
@@ -52,6 +60,7 @@ public class Database {
this.plugin = plugin;
this.dataSource = dataSource;
this.userDao = DaoManager.createDao(this.dataSource, UserDAO.class);
this.citizenDao = DaoManager.createDao(this.dataSource, CitizenDAO.class);
this.armorItemDao = DaoManager.createDao(this.dataSource, ArmorItemDAO.class);
this.databaseType = databaseType;
@@ -65,13 +74,14 @@ public class Database {
try {
TableUtils.createTableIfNotExists(this.dataSource, ArmorItemDAO.class);
TableUtils.createTableIfNotExists(this.dataSource, UserDAO.class);
TableUtils.createTableIfNotExists(this.dataSource, CitizenDAO.class);
} catch (final SQLException exception) {
exception.printStackTrace();
}
}
public void loadUser(final Player player, final Consumer<User> onComplete) {
final UUID uuid = player.getUniqueId();
public void loadUser(final Entity entity, final Consumer<User> onComplete) {
final UUID uuid = entity.getUniqueId();
final int armorStandId = FAKE_ENTITY_ID.getAndDecrement();
final Wardrobe wardrobe = this.createNewWardrobe(uuid);
Threads.getInstance().execute(
@@ -83,22 +93,54 @@ public class Database {
user = this.userDao.createIfNotExists(new UserDAO(uuid));
}
final List<ArmorItemDAO> armorItems = this.armorItemDao.queryForEq("uuid",
uuid.toString());
final List<ArmorItemDAO> armorItems = this.armorItemDao.queryForEq("uuid", uuid.toString());
final User actualUser = user.toUser(
this.plugin.getCosmeticManager(),
player.getEntityId(),
entity.getEntityId(),
armorItems,
wardrobe,
armorStandId);
armorStandId
);
Bukkit.getScheduler().runTask(this.plugin,
() -> {
this.plugin.getUserManager().add(
actualUser
);
onComplete.accept(actualUser);
}
() -> onComplete.accept(actualUser)
);
} catch (final SQLException exception) {
exception.printStackTrace();
}
});
onComplete.accept(new User(
uuid,
entity.getEntityId(),
PlayerArmor.empty(),
wardrobe,
armorStandId
));
}
public void loadNPCUser(final int id, final Entity entity, final Consumer<NPCUser> onComplete) {
final int armorStandId = FAKE_ENTITY_ID.getAndDecrement();
Threads.getInstance().execute(
() -> {
try {
CitizenDAO citizen = this.citizenDao.queryForId(id);
if (citizen == null) {
citizen = this.citizenDao.createIfNotExists(new CitizenDAO(id));
}
final List<ArmorItemDAO> armorItems = this.armorItemDao.queryForEq("uuid", String.valueOf(id));
final NPCUser actualUser = citizen.toUser(
this.plugin.getCosmeticManager(),
entity.getEntityId(),
armorItems,
armorStandId
);
Bukkit.getScheduler().runTask(this.plugin,
() -> onComplete.accept(actualUser)
);
} catch (final SQLException exception) {
@@ -106,17 +148,15 @@ public class Database {
}
});
final User user = new User(uuid, player.getEntityId(), PlayerArmor.empty(), wardrobe, armorStandId);
this.plugin.getUserManager().add(user);
onComplete.accept(user);
onComplete.accept(new NPCUser(id, entity.getEntityId(), PlayerArmor.empty(), armorStandId));
}
public void saveUser(final User user) {
try {
final UserDAO userDAO = new UserDAO(user.getUuid());
final UserDAO userDAO = new UserDAO(user.getId());
this.userDao.createOrUpdate(userDAO);
final String uuid = user.getUuid().toString();
final String uuid = user.getId().toString();
for (final ArmorItem armorItem : user.getPlayerArmor().getArmorItems()) {
final ArmorItemDAO dao = ArmorItemDAO.fromArmorItem(armorItem);
dao.setUuid(uuid);
@@ -128,6 +168,23 @@ public class Database {
}
}
public void saveNPCUser(final NPCUser user) {
try {
final CitizenDAO citizenDAO = new CitizenDAO(user.getId());
this.citizenDao.createOrUpdate(citizenDAO);
final String id = user.getId().toString();
for (final ArmorItem armorItem : user.getPlayerArmor().getArmorItems()) {
final ArmorItemDAO dao = ArmorItemDAO.fromArmorItem(armorItem);
dao.setUuid(id);
this.armorItemDao.createOrUpdate(dao);
}
} catch (final SQLException exception) {
exception.printStackTrace();
}
}
public void saveAll() {
for (final User user : this.plugin.getUserManager().getAll()) {
this.saveUser(user);
@@ -154,7 +211,7 @@ public class Database {
return userDao;
}
public Dao<ArmorItemDAO, UUID> getArmorItemDao() {
public Dao<ArmorItemDAO, String> getArmorItemDao() {
return armorItemDao;
}

View File

@@ -0,0 +1,66 @@
package io.github.fisher2911.hmccosmetics.database.dao;
import com.j256.ormlite.field.DatabaseField;
import com.j256.ormlite.table.DatabaseTable;
import io.github.fisher2911.hmccosmetics.cosmetic.CosmeticManager;
import io.github.fisher2911.hmccosmetics.gui.ArmorItem;
import io.github.fisher2911.hmccosmetics.inventory.PlayerArmor;
import io.github.fisher2911.hmccosmetics.user.BaseUser;
import io.github.fisher2911.hmccosmetics.user.NPCUser;
import io.github.fisher2911.hmccosmetics.user.User;
import io.github.fisher2911.hmccosmetics.user.Wardrobe;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.Objects;
@DatabaseTable(tableName = "citizen")
public class CitizenDAO {
@DatabaseField(id = true)
private int citizensId;
public CitizenDAO() {
}
public CitizenDAO(final int citizensId) {
this.citizensId = citizensId;
}
public void setCitizensId(final int citizensId) {
this.citizensId = citizensId;
}
@Nullable
public NPCUser toUser(
final CosmeticManager cosmeticManager,
final int entityId,
final List<ArmorItemDAO> armorItems,
final int armorStandId
) {
final PlayerArmor playerArmor = PlayerArmor.empty();
for (final ArmorItemDAO armorItemDao : armorItems) {
final ArmorItem armorItem = armorItemDao.toArmorItem(cosmeticManager);
if (armorItem == null) {
continue;
}
playerArmor.setItem(armorItem);
}
return new NPCUser(this.citizensId, entityId, playerArmor, armorStandId);
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final CitizenDAO that = (CitizenDAO) o;
return citizensId == that.citizensId;
}
@Override
public int hashCode() {
return Objects.hash(citizensId);
}
}

View File

@@ -5,8 +5,11 @@ import com.j256.ormlite.table.DatabaseTable;
import io.github.fisher2911.hmccosmetics.cosmetic.CosmeticManager;
import io.github.fisher2911.hmccosmetics.gui.ArmorItem;
import io.github.fisher2911.hmccosmetics.inventory.PlayerArmor;
import io.github.fisher2911.hmccosmetics.user.BaseUser;
import io.github.fisher2911.hmccosmetics.user.NPCUser;
import io.github.fisher2911.hmccosmetics.user.User;
import io.github.fisher2911.hmccosmetics.user.Wardrobe;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.Objects;
@@ -29,12 +32,14 @@ public class UserDAO {
this.uuid = uuid;
}
@Nullable
public User toUser(
final CosmeticManager cosmeticManager,
final int entityId,
final List<ArmorItemDAO> armorItems,
final Wardrobe wardrobe,
final int armorStandId) {
final int armorStandId
) {
final PlayerArmor playerArmor = PlayerArmor.empty();
for (final ArmorItemDAO armorItemDao : armorItems) {

View File

@@ -184,9 +184,13 @@ public class ArmorItem extends GuiItem {
}
public static ArmorItem empty(final Type type) {
return empty(type, "");
}
public static ArmorItem empty(final Type type, final String id) {
return new ArmorItem(
new ItemStack(Material.AIR),
"",
id,
new ArrayList<>(),
"",
type,

View File

@@ -90,8 +90,14 @@ public class CosmeticGui {
final ArmorItem.Type type = armorItem.getType();
final User setUser;
if (user.isWardrobeActive()) {
setUser = user.getWardrobe();
} else {
setUser = user;
}
final ArmorItem setTo = this.plugin.getUserManager().setOrUnset(
user,
setUser,
armorItem,
Messages.getRemovedMessage(type),
Messages.getSetMessage(type)

View File

@@ -8,6 +8,7 @@ import io.github.fisher2911.hmccosmetics.HMCCosmetics;
import io.github.fisher2911.hmccosmetics.inventory.PlayerArmor;
import io.github.fisher2911.hmccosmetics.message.Placeholder;
import io.github.fisher2911.hmccosmetics.user.User;
import io.github.fisher2911.hmccosmetics.user.Wardrobe;
import io.github.fisher2911.hmccosmetics.util.builder.ItemBuilder;
import java.util.HashMap;
import java.util.Map;
@@ -128,6 +129,7 @@ public class DyeSelectorGui extends CosmeticGui {
armorItem.setDye(colorItem.getColor().asRGB());
if (user.isWardrobeActive())
this.plugin.getUserManager().setItem(user, armorItem);
this.updateSelected(user, player);
});

View File

@@ -1,6 +1,7 @@
package io.github.fisher2911.hmccosmetics.hook;
import io.github.fisher2911.hmccosmetics.HMCCosmetics;
import io.github.fisher2911.hmccosmetics.hook.item.CitizensHook;
import io.github.fisher2911.hmccosmetics.hook.item.ItemHook;
import io.github.fisher2911.hmccosmetics.hook.item.ItemHooks;
import io.github.fisher2911.hmccosmetics.hook.item.ItemsAdderHook;
@@ -27,6 +28,7 @@ public class HookManager {
private final HMCCosmetics plugin;
private final ItemHooks itemHooks;
private final PAPIHook papiHook;
private final CitizensHook citizensHook;
private final Set<Class<? extends Hook>> registeredHooks;
private final Set<Listener> listeners;
@@ -45,13 +47,21 @@ public class HookManager {
final Map<String, ItemHook> itemHookMap = new HashMap<>();
final OraxenHook oraxenHook = new OraxenHook();
final ItemsAdderHook itemsAdderHook = new ItemsAdderHook();
final CitizensHook citizensHook = new CitizensHook(this.plugin);
if (pluginManager.getPlugin("Oraxen") != null) {
itemHookMap.put(oraxenHook.getIdentifier(), oraxenHook);
itemHookMap.put(oraxenHook.getId(), oraxenHook);
}
if (pluginManager.getPlugin("ItemsAdder") != null) {
itemHookMap.put(itemsAdderHook.getIdentifier(), itemsAdderHook);
itemHookMap.put(itemsAdderHook.getId(), itemsAdderHook);
this.listeners.add(itemsAdderHook);
}
if (pluginManager.getPlugin("Citizens") != null) {
this.registerHook(citizensHook.getClass());
this.listeners.add(citizensHook);
this.citizensHook = citizensHook;
} else {
this.citizensHook = null;
}
this.itemHooks = new ItemHooks(itemHookMap);
itemHookMap.values().forEach(hook -> this.registerHook(hook.getClass()));
@@ -87,6 +97,11 @@ public class HookManager {
return papiHook;
}
@Nullable
public CitizensHook getCitizensHook() {
return this.citizensHook;
}
public ItemHooks getItemHooks() {
return itemHooks;
}

View File

@@ -0,0 +1,181 @@
package io.github.fisher2911.hmccosmetics.hook.item;
import io.github.fisher2911.hmccosmetics.HMCCosmetics;
import io.github.fisher2911.hmccosmetics.concurrent.Threads;
import io.github.fisher2911.hmccosmetics.config.Settings;
import io.github.fisher2911.hmccosmetics.database.Database;
import io.github.fisher2911.hmccosmetics.gui.ArmorItem;
import io.github.fisher2911.hmccosmetics.hook.Hook;
import io.github.fisher2911.hmccosmetics.task.InfiniteTask;
import io.github.fisher2911.hmccosmetics.user.NPCUser;
import net.citizensnpcs.api.CitizensAPI;
import net.citizensnpcs.api.event.NPCDeathEvent;
import net.citizensnpcs.api.event.NPCDespawnEvent;
import net.citizensnpcs.api.event.NPCRemoveEvent;
import net.citizensnpcs.api.event.NPCSpawnEvent;
import net.citizensnpcs.api.event.PlayerCreateNPCEvent;
import net.citizensnpcs.api.npc.NPC;
import org.bukkit.Bukkit;
import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.UUID;
public class CitizensHook implements Hook, Listener {
private static final String IDENTIFIER = "citizens";
private final HMCCosmetics plugin;
private final Database database;
private final Map<Integer, NPCUser> npcs = new HashMap<>();
public CitizensHook(final HMCCosmetics plugin) {
this.plugin = plugin;
this.database = this.plugin.getDatabase();
final Settings settings = this.plugin.getSettings();
this.plugin.getTaskManager().submit(
new InfiniteTask(() -> {
for (final NPCUser user : this.npcs.values()) {
if (!user.isValid()) {
continue;
}
user.updateArmorStand(settings);
}
})
);
}
public List<Integer> getAllNPCS() {
final Iterator<NPC> iterator = CitizensAPI.getNPCRegistry().sorted().iterator();
final List<Integer> ids = new ArrayList<>();
while (iterator.hasNext()) {
ids.add(iterator.next().getId());
}
return ids;
}
public int getCitizensId(final Entity entity) {
final NPC npc = CitizensAPI.getNPCRegistry().getNPC(entity);
if (npc == null) return -1;
return npc.getId();
}
public boolean setNpcCosmetic(final int id, final ArmorItem armorItem) {
final NPC npc = CitizensAPI.getNPCRegistry().getById(id);
if (npc == null) return false;
final NPCUser user = this.npcs.get(npc.getId());
if (user == null) {
Threads.getInstance().execute(() -> {
this.database.loadNPCUser(
npc.getId(),
npc.getEntity(),
npcUser ->
Bukkit.getScheduler().runTask(
this.plugin,
() -> {
this.npcs.put(npc.getId(), npcUser);
this.setNpcCosmetic(npcUser, armorItem);
}
)
);
});
return true;
}
return this.setNpcCosmetic(user, armorItem);
}
public boolean setNpcCosmetic(final NPCUser user, final ArmorItem armorItem) {
if (user == null) return false;
final NPC npc = this.getNPC(user.getId());
if (npc == null) return false;
if (!(npc.getEntity() instanceof final LivingEntity entity)) return false;
user.getPlayerArmor().setItem(armorItem);
final ArmorItem.Type type = armorItem.getType();
if (type != ArmorItem.Type.BACKPACK) {
entity.getEquipment().setItem(
type.getSlot(),
armorItem.getItemStack(true)
);
}
return true;
}
@Nullable
public NPC getNPC(final UUID uuid) {
return CitizensAPI.getNPCRegistry().getByUniqueIdGlobal(uuid);
}
@Nullable
public NPC getNPC(final int id) {
return CitizensAPI.getNPCRegistry().getById(id);
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
public void onNpcLoad(final PlayerCreateNPCEvent event) {
this.loadNpc(event.getNPC());
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
public void onNpcLoad(final NPCSpawnEvent event) {
this.loadNpc(event.getNPC());
}
private void loadNpc(final NPC npc) {
if (Bukkit.getPlayer(npc.getUniqueId()) != null) return;
Bukkit.getScheduler().runTaskLater(this.plugin,
() -> Threads.getInstance().execute(() -> this.database.loadNPCUser(
npc.getId(),
npc.getEntity(),
user -> Bukkit.getScheduler().runTask(
this.plugin,
() -> {
this.npcs.put(npc.getId(), user);
for (final ArmorItem.Type type : ArmorItem.Type.values()) {
this.setNpcCosmetic(npc.getId(), user.getPlayerArmor().getItem(type));
}
}
)
)),
1);
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
public void onNpcUnload(final NPCDespawnEvent event) {
this.unloadNpc(event.getNPC());
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
public void onNpcUnload(final NPCRemoveEvent event) {
this.unloadNpc(event.getNPC());
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
public void onNpcUnload(final NPCDeathEvent event) {
this.unloadNpc(event.getNPC());
}
private void unloadNpc(final NPC npc) {
final NPCUser user = this.npcs.remove(npc.getId());
if (user == null) return;
user.despawnAttached();
Threads.getInstance().execute(() -> this.database.saveNPCUser(user));
}
@Override
public String getId() {
return IDENTIFIER;
}
}

View File

@@ -3,6 +3,7 @@ package io.github.fisher2911.hmccosmetics.listener;
import io.github.fisher2911.hmccosmetics.HMCCosmetics;
import io.github.fisher2911.hmccosmetics.config.WardrobeSettings;
import io.github.fisher2911.hmccosmetics.database.Database;
import io.github.fisher2911.hmccosmetics.task.TaskChain;
import io.github.fisher2911.hmccosmetics.user.User;
import io.github.fisher2911.hmccosmetics.user.UserManager;
import io.github.fisher2911.hmccosmetics.user.Wardrobe;
@@ -30,17 +31,20 @@ public class JoinListener implements Listener {
@EventHandler
public void onJoin(final PlayerJoinEvent event) {
final Player player = event.getPlayer();
this.database.loadUser(player,
user -> Bukkit.getScheduler().runTaskAsynchronously(this.plugin,
() -> {
this.userManager.resendCosmetics(player);
final WardrobeSettings settings = this.plugin.getSettings().getWardrobeSettings();
if (settings.isAlwaysDisplay() && settings.getWardrobeLocation() != null) {
final Wardrobe wardrobe = user.getWardrobe();
wardrobe.setCurrentLocation(settings.getWardrobeLocation());
wardrobe.spawnFakePlayer(player);
}
}));
this.database.loadUser(
player,
user -> new TaskChain(this.plugin).chain(
() -> this.userManager.add(user)
).chain(() -> {
this.userManager.resendCosmetics(player);
final WardrobeSettings settings = this.plugin.getSettings().getWardrobeSettings();
if (settings.isAlwaysDisplay() && settings.getWardrobeLocation() != null) {
final Wardrobe wardrobe = user.getWardrobe();
wardrobe.setCurrentLocation(settings.getWardrobeLocation());
wardrobe.spawnFakePlayer(player);
}
}, true).execute()
);
}
@EventHandler

View File

@@ -47,6 +47,13 @@ public class Messages {
new Message("invalid-user", ChatColor.RED + "That user's data cannot be found!");
public static final Message ITEM_NOT_FOUND =
new Message("item-not-found", ChatColor.RED + "That item could not be found!");
public static final Message HOOK_NOT_ENABLED =
new Message("hook-not-enabled", ChatColor.RED + Placeholder.TYPE + " is not enabled!");
public static final Message NPC_NOT_FOUND =
new Message("npc-not-found", ChatColor.RED + "NPC with id " + Placeholder.ID + " not found!");
public static final Message SET_NPC_COSMETIC =
new Message("set-npc-cosmetic", ChatColor.GREEN + "Set " + Placeholder.TYPE + " of " +
Placeholder.ID + " to " + Placeholder.ITEM);
public static final Message HELP_COMMAND =
new Message("help-command",
"""

View File

@@ -16,6 +16,7 @@ public class Placeholder {
public static final String PLAYER = "%player%";
public static final String ENABLED = "%enabled%";
public static final String ALLOWED = "%allowed%";
public static final String ID = "%id%";
/**
* @param message message being translated

View File

@@ -23,9 +23,9 @@ import java.util.Objects;
import java.util.Set;
import java.util.UUID;
public abstract class BaseUser {
public abstract class BaseUser<T> {
protected final UUID uuid;
protected final T id;
protected final int entityId;
protected final PlayerArmor playerArmor;
@@ -37,8 +37,8 @@ public abstract class BaseUser {
// List of players that are currently viewing the armor stand
protected final Set<UUID> viewing = new HashSet<>();
public BaseUser(final UUID uuid, final int entityId, final PlayerArmor playerArmor, final int armorStandId) {
this.uuid = uuid;
public BaseUser(final T id, final int entityId, final PlayerArmor playerArmor, final int armorStandId) {
this.id = id;
this.entityId = entityId;
this.playerArmor = playerArmor;
this.armorStandId = armorStandId;
@@ -47,8 +47,8 @@ public abstract class BaseUser {
@Nullable
public abstract Location getLocation();
public UUID getUuid() {
return this.uuid;
public T getId() {
return this.id;
}
public PlayerArmor getPlayerArmor() {
@@ -153,7 +153,7 @@ public abstract class BaseUser {
new ItemStack(Material.AIR)
));
if (!this.uuid.equals(other.getUniqueId())) return;
if (!this.id.equals(other.getUniqueId())) return;
PacketManager.sendPacket(other, PacketManager.getEquipmentPacket(equipmentList, this.armorStandId));
}
}
@@ -192,4 +192,8 @@ public abstract class BaseUser {
return this.entityId;
}
public abstract Equipment getEquipment();
public abstract boolean isWardrobeActive();
}

View File

@@ -32,4 +32,5 @@ public class Equipment {
public void setItem(final EquipmentSlot slot, @Nullable final ItemStack itemStack) {
this.equipment.put(slot, itemStack);
}
}

View File

@@ -0,0 +1,60 @@
package io.github.fisher2911.hmccosmetics.user;
import io.github.fisher2911.hmccosmetics.hook.HookManager;
import io.github.fisher2911.hmccosmetics.hook.item.CitizensHook;
import io.github.fisher2911.hmccosmetics.inventory.PlayerArmor;
import net.citizensnpcs.api.npc.NPC;
import org.bukkit.Location;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.Nullable;
public class NPCUser extends BaseUser<Integer> {
private final CitizensHook hook;
public NPCUser(final int id, final int entityId, final PlayerArmor playerArmor, final int armorStandId) {
super(id, entityId, playerArmor, armorStandId);
this.hook = HookManager.getInstance().getCitizensHook();
}
public NPCUser(final PlayerArmor playerArmor, final int armorStandId, final NPC npc) {
this(npc.getId(), npc.getId(), playerArmor, armorStandId);
}
@Nullable
public NPC getNpc() {
return this.hook.getNPC(this.getId());
}
@Override
@Nullable
public Location getLocation() {
final NPC npc = this.getNpc();
if (npc == null) return null;
return npc.getEntity().getLocation();
}
public boolean isValid() {
return this.getNpc() != null;
}
@Override
public boolean shouldShow(final Player other) {
return true;
}
@Override
public Equipment getEquipment() {
final NPC npc = this.getNpc();
if (npc == null) return new Equipment();
if (!(npc.getEntity() instanceof final LivingEntity entity)) return new Equipment();
return Equipment.fromEntityEquipment(entity.getEquipment());
}
@Override
public boolean isWardrobeActive() {
return false;
}
}

View File

@@ -3,7 +3,6 @@ package io.github.fisher2911.hmccosmetics.user;
import io.github.fisher2911.hmccosmetics.gui.ArmorItem;
import io.github.fisher2911.hmccosmetics.gui.CosmeticGui;
import io.github.fisher2911.hmccosmetics.inventory.PlayerArmor;
import net.minecraft.server.v1_16_R3.PacketPlayInAbilities;
import org.bukkit.Bukkit;
import org.bukkit.GameMode;
import org.bukkit.Location;
@@ -13,18 +12,22 @@ import org.jetbrains.annotations.Nullable;
import java.util.UUID;
public class User extends BaseUser {
public class User extends BaseUser<UUID> {
protected Wardrobe wardrobe;
private CosmeticGui openGui;
public User(final UUID uuid, final int entityId, final PlayerArmor playerArmor, final int armorStandId, final Wardrobe wardrobe) {
public User(final UUID uuid, final int entityId, final PlayerArmor playerArmor, final Wardrobe wardrobe, final int armorStandId) {
super(uuid, entityId, playerArmor, armorStandId);
this.wardrobe = wardrobe;
}
public User(final UUID uuid, final int entityId, final PlayerArmor playerArmor, final int armorStandId) {
super(uuid, entityId, playerArmor, armorStandId);
}
public @Nullable Player getPlayer() {
return Bukkit.getPlayer(this.uuid);
return Bukkit.getPlayer(this.getId());
}
public Wardrobe getWardrobe() {
@@ -62,4 +65,16 @@ public class User extends BaseUser {
if (player == null) return null;
return player.getLocation();
}
@Override
public Equipment getEquipment() {
final Player player = this.getPlayer();
if (player == null) return new Equipment();
return Equipment.fromEntityEquipment(player.getEquipment());
}
@Override
public boolean isWardrobeActive() {
return this.wardrobe.isActive();
}
}

View File

@@ -0,0 +1,55 @@
package io.github.fisher2911.hmccosmetics.user;
import io.github.fisher2911.hmccosmetics.HMCCosmetics;
import io.github.fisher2911.hmccosmetics.hook.HookManager;
import io.github.fisher2911.hmccosmetics.hook.item.CitizensHook;
import io.github.fisher2911.hmccosmetics.inventory.PlayerArmor;
import org.bukkit.entity.Entity;
import org.jetbrains.annotations.Nullable;
import java.util.UUID;
public class UserFactory {
private static final HMCCosmetics plugin;
private static final UserManager userManager;
static {
plugin = HMCCosmetics.getPlugin(HMCCosmetics.class);
userManager = plugin.getUserManager();
}
@SuppressWarnings("unchecked")
@Nullable
public static <T extends BaseUser> T createUser(
final Class<T> type,
final Entity entity,
final int armorStandId
) {
final UUID uuid = entity.getUniqueId();
final int entityId = entity.getEntityId();
if (type.equals(User.class)) {
return (T) new User(
uuid,
entityId,
PlayerArmor.empty(),
plugin.getDatabase().createNewWardrobe(uuid),
armorStandId
);
}
if (type.equals(NPCUser.class)) {
if (!HookManager.getInstance().isEnabled(CitizensHook.class)) return null;
final int citizensId = HookManager.getInstance().getCitizensHook().getCitizensId(entity);
return (T) new NPCUser(
citizensId,
entityId,
PlayerArmor.empty(),
armorStandId
);
}
return null;
}
}

View File

@@ -22,6 +22,7 @@ import io.github.fisher2911.hmccosmetics.message.MessageHandler;
import io.github.fisher2911.hmccosmetics.message.Placeholder;
import io.github.fisher2911.hmccosmetics.message.Translation;
import io.github.fisher2911.hmccosmetics.packet.PacketManager;
import io.github.fisher2911.hmccosmetics.task.InfiniteTask;
import io.github.fisher2911.hmccosmetics.util.builder.ItemBuilder;
import org.bukkit.Bukkit;
import org.bukkit.Material;
@@ -46,10 +47,6 @@ public class UserManager {
private final MessageHandler messageHandler;
private final Map<UUID, User> userMap = new HashMap<>();
private final Map<Integer, User> userEntityIdMap = new HashMap<>();
private final Map<Integer, User> armorStandIdMap = new HashMap<>();
private BukkitTask teleportTask;
public UserManager(final HMCCosmetics plugin) {
this.plugin = plugin;
@@ -58,12 +55,7 @@ public class UserManager {
}
public void add(final User user) {
this.userMap.put(user.getUuid(), user);
final Player player = user.getPlayer();
if (player != null) {
this.userEntityIdMap.put(user.getEntityId(), user);
}
this.armorStandIdMap.put(user.getArmorStandId(), user);
this.userMap.put(user.getId(), user);
this.updateCosmetics(user);
}
@@ -80,9 +72,6 @@ public class UserManager {
if (user == null) return;
this.armorStandIdMap.remove(user.getArmorStandId());
this.userEntityIdMap.remove(user.getEntityId());
final PlayerArmor copy = user.getPlayerArmor().copy();
user.removeAllCosmetics();
@@ -97,16 +86,13 @@ public class UserManager {
public void startTeleportTask() {
// throws an error on first load of registry if this isn't here
WrappedDataWatcher.Registry.get(Byte.class);
this.teleportTask = Bukkit.getScheduler().runTaskTimerAsynchronously(
this.plugin,
this.plugin.getTaskManager().submit(new InfiniteTask(
() -> {
for (final User user : this.userMap.values()) {
user.updateArmorStand(this.plugin.getSettings());
}
},
1,
1
);
}
));
}
public void resendCosmetics(final Player player) {
@@ -123,21 +109,15 @@ public class UserManager {
}
public void updateCosmetics(final User user) {
public void updateCosmetics(final BaseUser user) {
for (final Player player : Bukkit.getOnlinePlayers()) {
this.updateCosmetics(user, player);
}
}
public void updateCosmetics(final User user, final Player other) {
final Player player = user.getPlayer();
final Equipment equipment;
if (player == null) {
equipment = new Equipment();
} else {
equipment = Equipment.fromEntityEquipment(player.getEquipment());
}
public void updateCosmetics(final BaseUser user, final Player other) {
// final Player player = user.getPlayer();
final Equipment equipment = user.getEquipment();
for (final ArmorItem.Type type : ArmorItem.Type.values()) {
if (type.getSlot() == null) continue;
@@ -151,7 +131,7 @@ public class UserManager {
}
private void sendUpdatePacket(
final User user,
final BaseUser user,
final Player other,
final Equipment equipment,
final ArmorItem.Type type) {
@@ -171,7 +151,7 @@ public class UserManager {
}
private ItemStack getCosmeticItem(
final User user,
final BaseUser user,
final Equipment equipment,
final ArmorItem armorItem,
final EquipmentSlot slot) {
@@ -200,36 +180,29 @@ public class UserManager {
final ItemStack equipped = equipment.getItem(slot);
if (equipped != null && (equipped.getType() != Material.AIR && !user.getWardrobe().isActive())) {
if (equipped != null && (equipped.getType() != Material.AIR && !user.isWardrobeActive())) {
return equipped;
}
return itemStack;
}
public void setItem(final User user, final ArmorItem armorItem) {
final Wardrobe wardrobe = user.getWardrobe();
final User setUser;
if (wardrobe.isActive()) {
setUser = wardrobe;
} else {
setUser = user;
}
ArmorItem previous = setUser.getPlayerArmor().getItem(armorItem.getType());
public void setItem(final BaseUser user, final ArmorItem armorItem) {
ArmorItem previous = user.getPlayerArmor().getItem(armorItem.getType());
final CosmeticChangeEvent event =
new CosmeticChangeEvent(new CosmeticItem(armorItem.copy()), new CosmeticItem(previous.copy()), setUser);
new CosmeticChangeEvent(new CosmeticItem(armorItem.copy()), new CosmeticItem(previous.copy()), user);
Bukkit.getPluginManager().callEvent(event);
if (event.isCancelled()) return;
setUser.setItem(event.getCosmeticItem().getArmorItem());
user.setItem(event.getCosmeticItem().getArmorItem());
Bukkit.getScheduler().runTaskAsynchronously(this.plugin, () -> {
switch (armorItem.getType()) {
case HAT, OFF_HAND, CHEST_PLATE, PANTS, BOOTS -> {
this.updateCosmetics(setUser);
this.updateCosmetics(user);
}
case BACKPACK -> {
if (wardrobe.isActive()) setUser.updateArmorStand(settings);
if (user instanceof Wardrobe) user.updateArmorStand(settings);
}
}
});
@@ -290,10 +263,6 @@ public class UserManager {
this.userMap.clear();
}
public void cancelTeleportTask() {
this.teleportTask.cancel();
}
@Nullable
private EquipmentSlot slotFromInventorySlot(final int slot) {
return switch (slot) {

View File

@@ -94,12 +94,12 @@ public class Wardrobe extends User {
final PacketContainer playerSpawnPacket = PacketManager.getFakePlayerSpawnPacket(
this.currentLocation,
this.getUuid(),
this.getId(),
this.getEntityId()
);
final PacketContainer playerInfoPacket = PacketManager.getFakePlayerInfoPacket(
viewer,
this.getUuid()
this.getId()
);
@@ -246,4 +246,8 @@ public class Wardrobe extends User {
}
}
@Override
public Equipment getEquipment() {
return new Equipment();
}
}