KUDONG's Blog

Aldar-Player-Manage 문서입니다.

1. Features

  • 각 서버별 플레이어 캐시 데이터 분산저장 & 동기화
  • 플레이어의 입장/퇴장/서버이동 옵저버 제공
  • Permissons(권한), Chat Message(메세지), Server Switch(서버이동) 관련 API 제공
  • 프록시 또는 마인크래프트 서버의 서버다운 핸들링 , 2개이상의 프록시 호환

2. Mechanism

2.1 서버 연결 흐름도

  • 플레이어 접속시 시나리오 (Player Server Join)

  • 플레이어 퇴장시 시나리오 (Player Proxy Quit)

빠른 퇴장후 바로 접속시 핸들링

  • 플레이어 서버이동시 시나리오 (Player Server Switch Join)

2.2 Proxy/Minecraft 서버 다운시

  • 프록시서버 다운시 게임서버는 해당 Proxy를 경유해서 접속한 플레이어의 캐시데이터(IAldarPlayer)를 삭제한다.
  • 프록시 서버 재개시 다른 프록시 서버로부터 캐시데이터를 전송받는다. (단일 프록시 사용시 문제되지않음)

  • 마인크래프트 서버 다운시 게임서버와 프록시서버는 해당 서버의 플레이어의 캐시데이터(IAldarPlayer) 를 삭제한다.
  • 마인크래프트 서버 재개시 프록시 서버로 부터 캐시데이터를 전송받는다.

3. AldarPlayerManageAPI

PlayerManageAPI를 사용하기위해 Paper(Bukkit)환경에서 다음과 같은 방식으로 가져올수 있습니다.

try
{
    IAldarPlayerManageAPI playerApi = DependencyLoader
                .<PlayerManagerCore>getPluginInst("aldar-player-manage-v2")
                .getAldarPlayerManageAPI();

} catch (Exception e)
{
    this.logger.log(Level.SEVERE, "플러그인 의존성 로드 실패:");
}

Aldar-Player-Manage 를 로드하기 위해서는 Aldar-framework 를 디펜던시로 필요합니다.

해당 API는 다음과 같은 기능을 제공합니다.

public interface IAldarPlayerManageAPI
{
    /**
     * UUID 통해 플레이어의 정보를 가지고 옵니다.
     * @param uuid
     * @return
     */
    public IAldarPlayer getAldarPlayer(UUID uuid); 
    /***
     * username 을 통해 플레이어의 정보를 가지고 옵니다.
     * @param userName
     * @return
     */
    public IAldarPlayer getAldarPlayer(String userName); 
    /**
     *  글로벌 전체 플레이어의 목록을 가져옵니다.
     * @return
     */
    public Map<UUID, IAldarPlayer> getAldarPlayerMap();
    /**
     * 
     * @return
     */
    public List<UUID> getAldarPlayerUUIDs();
    /**
     * 글로벌 서버의 옵저버를 등록합니다 (다른 서버들의 플레이어 입장/퇴장/변경 을 알수있습니다.)
     * @param observer
     */
    public void addGlobalPlayerConnObserver(GlobalPlayerConnectionObserver observer);
    /**
     * 글로벌 서버의 옵저버를 삭제합니다.
     * @param observer
     */
    public void removeGlobalPlayerConnObserver(GlobalPlayerConnectionObserver observer);

    /**
     * 로컬 서버의 옵저버를 삭제합니다.
     * 현재 서버내에 있는 플레이어의 입장/퇴장/변경 만을 알수있습니다.
     * @param observer
     */
    public void addLocalPlayerConnObserver(LocalPlayerConnectionObserver observer);

    /**
     * 로컬 서버의 옵저버를 삭제합니다.
     * @param observer
     */
    public void removeLocalPlayerConnObserver(LocalPlayerConnectionObserver observer); 

    /**
     * 플레이어를 다른 서버로 이동시킵니다.
     * @param uuid
     * @param targetServer
     * @return 
     */
    public void jumpServer(UUID uuid, String targetServer);
    public void jumpServer(UUID uuid, AldarLocation location);
    public void jumpServer(UUID uuid, AldarLocation location, Consumer<Boolean> callback);
    public void jumpServer(UUID uuid, String targetServer, String worldname , double x , double y , double z);
    public void jumpServer(UUID uuid, String targetServer, String worldname , double x , double y , double z , float yaw , float pitch);
    public void jumpServer(UUID uuid, String targetServer, Consumer<Boolean> callback);
    public void jumpServer(UUID uuid, String targetServer, String worldname , double x , double y , double z, Consumer<Boolean> callback);
    public void jumpServer(UUID uuid, String targetServer, String worldname , double x , double y , double z , float yaw , float pitch , Consumer<Boolean> callback);
    /***
     * 해당 플레이어들에게 동일한 메세지를 보냅니다. 
     * @param players
     * @param message
     */
    public void sendMessageMulticast(List<IAldarPlayer> players, String message);
    public void sendMessageMulticastUUID(List<UUID> players, String message );
    public void sendMessageMulticastPlayerName(List<String> players, String message);
    public void sendMessageBroadCast(String message);

    /**
     * 로비 좌표를 가져옵니다.
     * @return
     */
    public AldarLocation getLobbyLocation();
    /**
     * 처음 접속한 유저가 접속하는 장소를 가져옵니다. 
     * @return
     */
    public AldarLocation getFirstJoinLocation();

}

4. Observer

다른서버 또는 같은서버내에서 플레이어의 접속/퇴장/서버이동을 처리하기위해 사용합니다.

4.1 Local Observer

public interface LocalPlayerConnectionObserver
{

    /**
     * AldarLocalPlayerAsyncPreLoginEvent
     * 플레이어가 접속하기 전에 호출됩니다.
     * 
     * 보통 DB에서 데이터를 읽어올때 사용합니다.
     * 
     * <Default Join & First Join>
     * 현재서버에서 읽어온 값을 JsonObject에 저장하면 다른서버의 Global Observer에서  JsonObject가 브로드캐스트됩니다.
     * <ServerSwitch Join>
     * 이전 서버에서 보낸 JsonObject를 가져올수 있습니다.
     * 
     * Disallow 메서드호출시 해당플레이어의 서버접속이 취소 됩니다.
     * 
     * @param e
     */
    public void onPlayerJoin(AldarLocalPlayerAsyncPreLoginEvent e);
    /**
     * AldarLocalPlayerJoinEvent
     * 플레이어가 현재서버에 접속했을때 호출됩니다.
     * 
     * <Default Join & First Join>
     * AldarLocalPlayerAsyncPreLoginEvent 에서 저장한 JsonObject를 가져올수 있습니다.
     * <ServerSwitch Join>
     * 이전 서버에서 보낸 JsonObject를 가져올수 있습니다.
     * 
     * @param e
     */
    public void onPlayerJoin(AldarLocalPlayerJoinEvent e);
    /**
     * AldarLocalPlayerQuitEvent
     * 플레이어가 현재서버를 나갈때 호출됩니다.
     * 
     * <Proxy Quit & Server Switch Quit>
     * 임시로 JsonObject에  데이터를 저장하고 이후
     * public void saveAsyncDB(Consumer<JsonObject> task)
     * 를 통해서 DB작업을 따로 수행할수 있습니다.
     * 
     * @param e
     */
    public void onPlayerLeave(AldarLocalPlayerQuitEvent e);
    /**
     * AldarLocalPlayerTeleportEvent
     * 플레이어가 텔레포트할때 호출됩니다.
     * @param e
     */
    public void onPlayerTeleport(AldarLocalPlayerTeleportEvent e);
}

<옵저버 등록>

this.playerApi.addLocalPlayerConnObserver(/*LocalPlayerConnObserver 을 구현한 클래스*/);

<예제>

public class LocalObserverExample implements LocalPlayerConnectionObserver
{

    /**
     * AldarLocalPlayerAsyncPreLoginEvent 에서는 DB에서 데이터를 가지고 온다 
     * 저장한 데이터를 임시로 JsonObject 에 담는다.
     * DB에서 가져온 데이터는 AldarLocalPlayerJoinEvent 에서 수행할것
     */
    @Override
    public void onPlayerJoin(AldarLocalPlayerAsyncPreLoginEvent e)
    {
        JoinResult r = e.getResult();
        IAldarPlayer info = e.getPlayer();

        switch(r)
        {
            case DEFAULT_JOIN: 
                //do something
                break;
            case FIRST_JOIN: 
                //do something
                break;
            case SERVER_SWITCH_JOIN: 
                String beforeServer = e.getBeforeServer();
                JsonObject obj = e.getJsonObject(); //커스텀 메세지
                break;
        }

        //접속하기 전에 차단 가능
        e.disallow("<테스트> 에러발생");

    }

    @Override
    public void onPlayerJoin(AldarLocalPlayerJoinEvent e)
    {
        JoinResult r = e.getResult();
        IAldarPlayer info = e.getPlayer();
        Player player = e.getBukkitPlayer();
        AldarLocation loc = e.getPreDeterminedLocation();

        switch(r)
        {
            case DEFAULT_JOIN: 
                //do something
                break;
            case FIRST_JOIN: 
                //do something
                break;
            case SERVER_SWITCH_JOIN: 
                //커스텀 메세지
                JsonObject obj = e.getJsonObject();
                break;
        }

    }

    /**
     * 구문안에서 동기로 DB처리 할것 
     */
    @Override
    public void onPlayerLeave(AldarLocalPlayerQuitEvent e)
    {
        QuitResult r = e.getResult();

        switch(r)
        {
            case PROXY_QUIT:
                Player p1 = e.getBukkitPlayer();
                JsonObject obj = e.getMessage();
                obj.addProperty("player_status", "dead");

                // 버킷스레드에서 동기화할 데이터를 JSON 으로 임시 저장 <Bukkit Thread>

                e.saveAsyncDB(json -> {

                    //DB 작업 수행 <Async Thread>
                    String status = obj.get("player_status").getAsString();

                });
                break;
            case SERVER_SWITCH_SAVE: //DB작업 수행
                Player p2 = e.getBukkitPlayer();

                e.saveAsyncDB(json -> {

                    //DB 작업 수행 <Async Thread>
                    //TargetServer로 실을 커스텀 메세지 JSON으로 저장
                    json.addProperty("hello", "good");

                });
                break;
            case SERVER_SWITCH_QUIT: //내부 캐시 지울때 사용
                //remove local cache 
                break;
        }
    }

    @Override
    public void onPlayerTeleport(AldarLocalPlayerTeleportEvent e)
    {
        TeleportResult r = e.getResult();

        switch(r)
        {
            case TELEPORT_AFTER:
                //do something
                break;
            case TELEPORT_BEFORE:
                //do something
                break;
        }
    }

}

4.2 Global Observer

public interface GlobalPlayerConnectionObserver
{
     public void onPlayerJoin(AldarGlobalPlayerJoinEvent e);
     public void onPlayerQuit(AldarGlobalPlayerQuitEvent e);
}

<옵저버 등록>

this.playerApi.addGlobalPlayerConnObserver(/*GlobalPlayerConnObserver 을 구현한 클래스*/);