글을 읽기 전 이 블로그는 반말과 존댓말이 왔다 갔다 합니다.
아마 2000년대 이후에 태어난 사람이라면 거의 다 마인크래프트라는 게임을 접해보았을 것이다.
1.5.2 버전을 플레이 하던 시절 멀티플레이 서버에서는 생야생, 약탈, 건축, 미니게임 등 여러 종류의 서버가 있었다.
야생 서버에서는 플레이어를 아무 이유 없이 죽이거나 남의 집을 터는 일이 자주 발생하였고,
강제오피 라는 것을 사용하여 OP를 얻어 서버를 테러하는 사람도 있었다.
시간이 흘러, 고등학생이 된 나는 마인크래프트와 강제오피는 하나의 추억이 되었고 옛날 이야기가 되었다.
하지만 그 추억은 여전히 마음 한편이 남아있었고, Java를 배우게 된 나는 마인크래프트 1.5.2에서 구동되던 강제오피를 최신버전에서 사용할 수 있도록 만들기로 결심했다.
먼저 강제오피 플러그인과 프로그램에 대해 알아보기 위해 강제오피 플러그인의 이름인 SocketCommander를 구글에 검색해보았다.

2016년에 작성 된 옛날 블로그에서 소켓커맨드.zip 이라는 파일을 받았고 여기에는 바이러스가 심어져 있을지도 모르는 ForceOP.exe 와 SocketCommander.jar 이 있었다.
SocketCommander.jar 를 마인크래프트 1.20.2(Paper) 버킷에 넣고 구동하니


SocketCommander v1 이라는 플러그인은 Legacy 플러그인이라는 경고와 함께 Port 5959에서 서버 실행 중 이라는 안내 문구가 출력되었다.
ForceOP.exe 는 바이러스가 포함되어 있을 수도 있어 직접 제작한 ReForceOpClient.exe 으로 localhost:5959 으로 연결을 시도하니

SocketCommander이름에 맞게 Socket을 사용하여 통신하므로 ReForceOpClient.exe 에서도 연결이 되는 것을 확인하였다.
다만 Client로부터 명령어를 받고 콘솔에서 실행하는 로직은 ReForceOp와 다른지 명령어는 실행되지 않았다.
SocketCommander 플러그인이 Socket으로 통신하고 Client로 받은 명령어를 콘솔에서 실행하는 것만 구현하면 ReForceOp는 완성이다.

Intellij IDE에서 Minecraft Development 를 사용하여 프로젝트를 생성합니다.
private ServerSocket serverSocket;
private boolean running;
private JavaPlugin plugin;
public SocketServer(JavaPlugin plugin, int port) {
this.plugin = plugin;
try {
serverSocket = new ServerSocket(port);
running = true;
startServer();
} catch (Exception e) {
e.printStackTrace();
}
}
private void startServer() {
new Thread(() -> {
try {
while (!serverSocket.isClosed()) {
try {
Socket clientSocket = serverSocket.accept();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()))) {
String command;
while ((command = reader.readLine()) != null) {
final String cmd = command;
Bukkit.getScheduler().runTask(this.plugin, () ->
Bukkit.getServer().dispatchCommand(Bukkit.getServer().getConsoleSender(), cmd)
);
}
}
} catch (SocketException e) {
if (serverSocket.isClosed()) {
break;
} else {
throw e;
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
while (!serverSocket.isClosed())를 사용하여 서버 소켓이 닫히지 않는 한 반복 실행됩니다.
Socket clientSocket = serverSocket.accept() 클라이언트의 연결을 수락하고, 클라이언트와의 통신을 위한 소켓 객체 clientSocket을 생성합니다. 이 소켓을 사용하여 클라이언트와 통신합니다.
try (BufferedReader reader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()))) { ... } 클라이언트 소켓으로부터 입력 스트림을 얻어와서 BufferedReader로 래핑합니다. 이를 통해 클라이언트가 보낸 명령어를 읽을 수 있습니다.
String command; while ((command = reader.readLine()) != null) { ... } 클라이언트가 보낸 명령어를 한 줄씩 읽어옵니다.
Bukkit.getScheduler().runTask(this.plugin, () -> Bukkit.getServer().dispatchCommand(Bukkit.getServer().getConsoleSender(), cmd)) 명령어를 Bukkit Scheduler를 사용하여 메인 스레드에서 실행합니다. 이를 통해 마인크래프트 서버 콘솔에서 명령어를 실행할 수 있습니다.
catch (SocketException e) { ... } SocketException이 발생하여 콘솔에 Exception Message가 출력되지 않도록 따로 예외 처리합니다.
public void stopServer() {
try {
running = false;
if (serverSocket != null && !serverSocket.isClosed()) {
serverSocket.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
플러그인이 종료될 때 serverSocket를 종료하는 역할을 합니다.
running = false 으로 실행 상태를 false로 저장합니다.
if (serverSocket != null && !serverSocket.isClosed()) { ... } serverSocket이 null이 아니고 아직 닫히지 않았을 때 조건을 충족합니다.
serverSocket.close() 서버 소켓을 닫습니다. 이를 통해 서버 소켓이 더 이상 클라이언트의 연결을 받지 않으며 이미 연결된 클라이언트의 연결은 끊깁니다.
@Override
public void onEnable() {
socketServer = new SocketServer(this, 1234);
}
@Override
public void onDisable() {
if (socketServer != null) {
socketServer.stopServer();
}
}
onEnable 메서드는 플러그인이 활성화 되었을 때 SocketServer 객체를 생성하여 서버 소켓을 활성화 시킵니다.
onDisable 메서드는 플러그인이 비활성화 되었을 때 생성된 SocketServer 객체의 stopServer()를 실행시켜 소켓을 비활성화 시킵니다.
플러그인을 다 만들었으니 Socket 통신을 할 클라이언트(프로그램)을 만들어야 한다.
Python으로 간단하게 Socket 통신을 할 수 있는 클라이언트를 만들까 고민했지만 강제오피 프로그램인 ForceOP.exe를 떠올리며 Java에서 JFrame을 사용하여 제작하기로 하였다.

기존 ForceOP.exe의 디자인이다.
아직 JFrame에 대해서 잘 모르고 한번도 사용해본적이 없지만 열심히 만들어보았다.


ReForceOp 클라이언트의 디자인이다.
추후 여러가지 기능을 추가해 편리성을 높일 예정이다.
홍보하는것은 아니고요 사용법을 모르는 사람이 있을가봐 https://blog.naver.com/dogpower910/223541969574 에 올렸습니다. 삭제를 원하시면 답글 부탁드려요
안녕하세요 저는 평소에 코딩에 관심이 있어 github에서 사용법을 읽고 해봤더니 진짜 되서 신기했네요 ㅋㅋㅋ 하지만 보통 대형 서버들은 직접 플러그인을 만들어 사용하거나 검증된 플러그인만 사용해서 잘 될거같진 않네요 ㅋㅋㅋ