-
-
Save Namnodorel/fbf72c7ae871115d78c9a6aa76c693b6 to your computer and use it in GitHub Desktop.
import net.citizensnpcs.api.CitizensAPI; | |
import net.citizensnpcs.api.event.NPCSpawnEvent; | |
import net.citizensnpcs.api.npc.NPC; | |
import org.bukkit.Bukkit; | |
import org.bukkit.configuration.ConfigurationSection; | |
import org.bukkit.entity.Player; | |
import org.bukkit.event.EventHandler; | |
import org.bukkit.event.Listener; | |
import org.bukkit.event.player.PlayerJoinEvent; | |
import org.bukkit.plugin.Plugin; | |
import pl.betoncraft.betonquest.*; | |
import pl.betoncraft.betonquest.api.QuestEvent; | |
import pl.betoncraft.betonquest.config.Config; | |
import pl.betoncraft.betonquest.config.ConfigPackage; | |
import pl.betoncraft.betonquest.utils.PlayerConverter; | |
import java.util.HashMap; | |
import java.util.HashSet; | |
public class NPCHider implements Listener, Runnable{ | |
private static NPCHider instance; | |
private EntityHider hider; | |
private HashMap<Integer, HashSet<ConditionID>> npcs; | |
private ConfigurationSection configSection; | |
private Integer updateInterval; | |
public static void init(Plugin plugin){ | |
if(instance == null){ | |
instance = new NPCHider(plugin); | |
} | |
} | |
private NPCHider(Plugin plugin){ | |
configSection = BetonQuest.getInstance().getConfig().getConfigurationSection("hide_npcs"); | |
if (configSection == null){ | |
return; | |
} | |
hider = new EntityHider(plugin, EntityHider.Policy.BLACKLIST); | |
npcs = new HashMap<>(); | |
loadFromConfig(); | |
Bukkit.getScheduler().scheduleSyncRepeatingTask(plugin, this, 0L, 20 * updateInterval); | |
} | |
private void loadFromConfig(){ | |
for(ConfigPackage cfgPackage : Config.getPackages().values()){ | |
if(cfgPackage.getCustom().getConfig() == null || cfgPackage.getCustom().getConfig().getConfigurationSection("hide_npcs") == null){ | |
continue; | |
} | |
for (String npcID : cfgPackage.getCustom().getConfig().getConfigurationSection("hide_npcs").getKeys(false)) { | |
try { | |
int id = Integer.parseInt(npcID); | |
NPC npc = CitizensAPI.getNPCRegistry().getById(id); | |
if(npc != null){ | |
HashSet<ConditionID> conditions = new HashSet<>(); | |
String conditionsString = cfgPackage.getCustom().getConfig().getConfigurationSection("hide_npcs").getString(npcID); | |
for(String condition : conditionsString.split(",")){ | |
conditions.add(new ConditionID(cfgPackage, condition)); | |
} | |
if(npcs.containsKey(id)){ | |
npcs.get(id).addAll(conditions); | |
}else{ | |
npcs.put(id, conditions); | |
} | |
}else{ | |
System.out.println("[NPCHider]: An NPC with the id " + npcID + " does not exist!"); | |
} | |
} catch (NumberFormatException e) { | |
System.err.println("[NPCHider]: Given NPC ID " + npcID + " is not a valid number!"); | |
} catch (ObjectNotFoundException e){ | |
System.err.println("[NPCHider]: " + e.getMessage()); | |
} | |
} | |
} | |
updateInterval = configSection.getInt("updateInterval"); | |
} | |
//These methods check the conditions and apply according visibilities immediately, | |
//and need to go through more or less entities and conditions depending on how specific | |
//the arguments are | |
public void applyVisibility(Player p, Integer npcID){ | |
boolean hidden = true; | |
if(npcs.get(npcID).isEmpty()){ | |
hidden = false; | |
}else{ | |
for(ConditionID condition : npcs.get(npcID)){ | |
if(!BetonQuest.condition(PlayerConverter.getID(p), condition)){ | |
hidden = false; | |
break; | |
} | |
} | |
} | |
NPC npc = CitizensAPI.getNPCRegistry().getById(npcID); | |
if(npc.isSpawned()){ | |
if(hidden){ | |
hider.hideEntity(p, npc.getEntity()); | |
}else{ | |
hider.showEntity(p, npc.getEntity()); | |
} | |
} | |
} | |
public void applyVisibility(Player p){ | |
for(Integer npcID : npcs.keySet()){ | |
applyVisibility(p, npcID); | |
} | |
} | |
public void applyVisibility(NPC npc){ | |
for(Player p : Bukkit.getOnlinePlayers()){ | |
applyVisibility(p, npc.getId()); | |
} | |
} | |
public void applyVisibility(){ | |
for(Player p : Bukkit.getOnlinePlayers()){ | |
for(Integer npcID : npcs.keySet()){ | |
applyVisibility(p, npcID); | |
} | |
} | |
} | |
public static NPCHider getInstance() { | |
return instance; | |
} | |
@Override | |
public void run() { | |
applyVisibility(); | |
} | |
//We want to have the visibility applied immediately, NPCs shouldn't spawn, stay there for a second and only then disappear | |
@EventHandler | |
public void onNPCSpawn(NPCSpawnEvent event) { | |
applyVisibility(event.getNPC()); | |
} | |
@EventHandler | |
public void onPlayerJoin(PlayerJoinEvent event) { | |
applyVisibility(event.getPlayer()); | |
} | |
//Optional; in case something changed and the updated visibilities need to be applied immediately for that player | |
public static class UpdateVisibilityNowEvent extends QuestEvent { | |
public UpdateVisibilityNowEvent(Instruction instruction) throws InstructionParseException { | |
super(instruction); | |
} | |
@Override | |
public void run(String id) throws QuestRuntimeException { | |
NPCHider.getInstance().applyVisibility(PlayerConverter.getPlayer(id)); | |
} | |
} | |
//Optional; I implemented it because in some cases there are temporary NPCs which shouldn't be | |
//Visible to everyone | |
public static class WriteHideConditionEvent extends QuestEvent { | |
private Integer npcID; | |
private HashSet<ConditionID> conditions; | |
public WriteHideConditionEvent(Instruction instruction) throws InstructionParseException { | |
super(instruction); | |
super.staticness = true; | |
super.persistent = true; | |
npcID = instruction.getInt(); | |
conditions = new HashSet<>(); | |
conditions.addAll(instruction.getList(instruction.getOptional("cons"), instruction::getCondition)); | |
} | |
@Override | |
public void run(String s) throws QuestRuntimeException { | |
NPC npc = CitizensAPI.getNPCRegistry().getById(npcID); | |
if(npc != null){ | |
NPCHider.getInstance().npcs.put(npcID, conditions); | |
NPCHider.getInstance().applyVisibility(npc); | |
}else{ | |
System.out.println("[NPCHider]: An NPC with the id " + npcID + " does not exist!"); | |
} | |
} | |
} | |
} |
I find minecraft very interesting as base as the more difficult parts are already handled for you and you only have to concentrate on new features etc.
No more messing with sockets, rendering or textures (only if you want an own resourcepack, but thats purely optional). There are even ready plugins one can use/modify and all the limitations are only making it more interesting to play with in my opinion ;)
PS: Denizens scripts are just gibberish for me, thanks nonetheless!
PPS: I figured one would have massiv problems with citizens, but building it from the ground up doesn't sound to good either...
I'd rather go with speaking english, in case anyone wants to read the conversation who isn't German ;D Yes, stuff like sockets and rendering are done already, but Minecraft feels so much like a dirty hack in many "higher level" cases... An example I got really angry about is that the nametags of each and every entity in MC are stored in their nametag-attribute... Except for players. Players' nametags are stored on the scoreboard, whyever that is. Rendering works, but is extremely slow... With shaders installed, the FPS of MC can go really low, and that's not because of the used PC or that MC has been written in Java; it simply because MC was written so inefficiently. The selection of what needs a packet and which information is stored in them is also really weird. I don't necessarily mean it is better to make a whole new game, but rather base big projects off a different one. But we're really getting off-topic xD
Meh, boring :p (I translated my comment though)
What game do you have in mind? ^^
And I agree that minecraft/bukkit is sometimes really messy, but nothing too bad I guess. overdesigning a problem is sometimes just as bad :3
At least we are not spamming in the betonquest issues ;)
@MWFIAE I wouldn't mind a bit of interesting spam ๐
@Namnodorel You gave me a few ideas for making instances. We'll see if anything comes out of it. And thank you for sharing this, I'll include it in the plugin ๐
@MWFIAE Might be true, but I have yet to come across a problem that needs less thought put into it :P Messing with entities, packets and NMS has been a big headache for me multiple times.
Since I have no new big project planned currently nothing too specific, but Terasology looks promising to me if I want "Minecraft, just programmed better". If just sockets and rendering are the problem, I would love to try out libgdx some day. I mean, you can literally make a game in Java and it runs on anything from iPhone to Windows... A dream come true! :D And since it's an engine there's no need to mess with low-level stuff.
@Co0sh Great ^^ I'm glad to help. I've recently tried DungeonsXL (the description looked very good), but since it is so very very early access and more built for minigame-like dungeons that instances in an RPG I decided not to use it. You might still find it interesting though :) I especially liked the multiple (and random, if you want to) floors and signs to place stuff like spawners and checkpoints in the dungeon/instance.
@Namnodorel
I messed with Unity and Unreal-Engine a few times in the past which are even better equipped engines, but the problem with the whole moddeling/texturing/etc. still remains and I'm not very good with this artist stuff :p
@Co0sh
Nah, you don't have time for spam.
I already try to help all the other people in the issues so that you have more time to resolve my own issues and implementing features I could make good use of :3 (selfish me :3 )
@MWFIAE Me neither, but that can be learned. I found https://cgcookie.com/ to be a good resource for learning to model stuff. And you can still use the plenty of presets existing- in MC, you're using a number of "presets" as well, right? :D
@Namnodorel looks good, will take a look at it sometime if I have some real free time :)
Yeah, but it's not the same, as all presets are from the same source and don't look wild mixed... You know what I mean, right?
@MWFIAE Yes, kinda. You can get them to look quite similar if you are picky about which presets you use. Buut of course it takes significantly more time to find the right stuff.
@MWFIAE
As far as I know, Citizens saves the NPCs only when the server is shut down or you do
/citizens save
. What I did was just creating a new NPC and deleting it when the player leaves the server, goes away too far etc. So nothing really gets saved (well, the NPC ID counter increases, but I don't have a problem with that). And I agree with you that it is risky to mess with NPCs in that way. I actually think that when (if :D) I ever start a new bigger project, it won't be in MC because all the code is just so messy and buggy and in general hard to work with if you want to do something more advanced.I can't exactly share my code with you, since that has been my first thing that I tried to get done in Denizen - you can have that script though: https://gitlab.com/snippets/1679818
Buuut it still is more of a prototype; skin copying doesn't really work yet, items aren't transferred, there is a hardcoded German message to the player in it and it is in general not really ready for production. But it shows how the concept works. This specific script makes the NPC walk to a series of locations, wait at each for the player to catch up and destroy itself in case the player leaves or walks away.
I'll potentially rewrite the script in Java at some point; Denizen was really hard for me to learn the basics of and get something working. Unfortunaley right now I have other priorities.