Skip to content

The basics ‐ Player states

ImIllusion edited this page Jul 22, 2023 · 3 revisions

Player states are simple and short "states" that are applied to any given player. These states can be as simple as being a spectator, or you can put multiple states together to achieve more complex logic.

Let's start by semarating this into 3 classes:

  • A player state abstract class, which defines the logical structure
  • A player state registry, where we register all our states and event handlers
  • A player state tracker, which allows us to know what players have what states

Let's start with our abstract class

public abstract class AbstractPlayerState {

    private final PlayerStateTracker tracker;
    
    public AbstractPlayerState(PlayerStateTracker tracker) {
        this.tracker = tracker;
    }
    
    public abstract String getName(); // 
    public abstract void apply(Player player);
    public abstract void revert(Player player);
    
    public boolean isAppliedTo(Player player) {
        return tracker.hasState(getName());
    }
    
    public <EventType extends Event> registerEvents(Class<EventType> eventClass, Consumer<EventType> task) {
        // Up to you
    }
}

And a sample player state

public class SpectatorPlayerState extends AbstractPlayerState {
    
    public SpectatorPlayerState(PlayerStateTracker tracker) {
        super(tracker);
    }
    
    @Override
    public String getName() {
        return "spectator";
    }
    
    @Override
    public void apply(Player player) {
        player.setGameMode(GameMode.SPECTATOR);
    }
    
    @Override
    public void revert(Player player) {
        player.setGameMode(GameMode.ADVENTURE);
    }
}

Now that we have a player state, let's quickly do the registry and tracker

public class PlayerStateRegistry {

    private final Map<String, AbstractPlayerState> states = new ConcurrentHashMap<>();
    
    public void registerState(AbstractPlayerState state) {
        states.put(state.getName(), state); 
    }
    
    public AbstractPlayerState getState(String name) {
        return states.get(name);
    }
}
public class PlayerStateTracker implements Listener {

    private final Map<UUID, List<String>> playerStates = new ConcurrentHashMap<>();
    private final PlayerStateRegistry registry;
    
    public PlayerStateTracker(PlayerStateRegistry registry) {
        this.registry = registry;
        
        // Register events or something
    }
    
    public void addState(Player player, AbstractPlayerState state) {
        if(state == null) { // idiot proof
            return;
        }
    
        UUID playerId = player.getUniqueId();
        String stateName = state.getName();
        
        List<String> states = playerStates.computeIfAbsent(playerId, irrelevant -> new ArrayList<>());
        
        // Optional
        if(states.contains(stateName)) {
            return;
        }
        
        // Apply the state and add to the list
        states.add(stateName);
        state.apply(player);
        
        // Feel free to save this list to PDC in case an instance crashes
    }
    
    public void addState(Player player, String stateName) {
        addState(player, registry.getState(stateName));
    }
    
    public void removeState(Player player, AbstractPlayerState state) {
        if(state == null) { // idiot proof
            return;
        }
        
        UUID playerId = player.getUniqueId();
        String stateName = state.getName();
        
        List<String> states = playerStates.getOrDefault(playerId, Collections.EMPTY_LIST);
        
        if(states.isEmpty() || !states.contains(stateName)) {
            return;
        }
        
        states.remove(stateName);
        
        if(states.isEmpty()) {
            playerStates.remove(playerId);
        }
        
        state.revert(player);
    }
    
    public void removeState(Player player, String stateName) {
        removeState(player, registry.getState(stateName));
    }
    
    public boolean hasState(Player player, String state) {
        return playerStates.getOrDefault(player.getUniqueId(), Collections.EMPTY_LIST).contains(state);
    }
    
    public void removeAll(Player player) {
        UUID playerId = player.getUniqueId();
        List<String> states = this.playerStates.remove(playerId);
        
        if(states == null) {
            return;
        }
        
        // Apply reverse order just in case
        Collections.reverse(states);
        
        for(String state : states) {
            AbstractPlayerState abstractState = registry.getState(state);
    
            if(abstractState == null) { // ??
                continue;
            }
            
            abstractState.revert(player);
        }
    }
    
    @EventHandler
    public void onQuit(PlayerQuitEvent event) {
        removeAll(event.getPlayer());
    }
}

Have fun!

Clone this wiki locally