본문 바로가기

플러그인 제작 강의

[마인크래프트 플러그인 제작 강의] 4-2강 config파일 사용하지 않고 변수 사용하기

SMALL

 

 

 

안녕하세요. 

 

오늘은 3강에서 만들었던 플러그인을 이용하여 변수를 파일로 저장하고, 불러오는 방법에 대해서 알아보겠습니다.

 

이번 강좌에서는 config파일을 이용하지 않고 진행하겠습니다.

 

여기 클릭 하면 3강으로 ~

 

 

 

 

 

Main.java 의 코드는 아래와 같은 상태에서 강의를 시작하겠습니다.

import java.util.HashMap;
import java.util.UUID;

import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.plugin.java.JavaPlugin;

public class Main extends JavaPlugin implements Listener {

	private final File f = new File(getDataFolder(), "/data.txt");
	
	ConsoleCommandSender consol = Bukkit.getConsoleSender();
	HashMap<UUID, Integer> map = new HashMap<UUID, Integer>(); 
    
	@Override
	public void onEnable() {
		consol.sendMessage( ChatColor.AQUA + "[플러그인 활성화 중 입니다.]");
		getServer().getPluginManager().registerEvents(this, this);
	}
	
	@Override
	public void onDisable() {
		consol.sendMessage( ChatColor.AQUA + "[플러그인 비활성화 중 입니다.]");
	}
	
	@EventHandler
	public void join(PlayerJoinEvent e) {

		UUID uuid = e.getPlayer().getUniqueId();
		
		if(map.containsKey(uuid)) {
			map.put(uuid, map.get(uuid) + 1);
		} else {
			map.put(uuid, 1);
		}
		
		int count = map.get(uuid);
		
		e.setJoinMessage( count +"번째 입장했습니다!"  );
		
	}
}

 

 

3강의 내용에서 아래 코드가 추가되었습니다.

	private final File f = new File(getDataFolder(), "/data.txt");

 이 코드는 마인크래프트 서버 폴더 안의 plugins폴더 안의 cchLecture(플러그인이름) 안의 data.txt 파일을 선언하고 대입 하는 과정입니다.

 

 

먼저 data.txt 라는 파일을 만드는 makeFile() 함수를 작성해 보겠습니다.

 

파일의 제목은 마음대로 지으셔도 됩니다.

 

파일을 생성하는 코드는 아래와 같습니다.

 

public void makeFile(File f) {
		if (!f.exists() || !f.isFile()) {
			try {
				f.createNewFile();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

 

작성한 makeFile() 함수를  onEnable() 함수 안에 작성하겠습니다.

 

@Override
	public void onEnable() {
		consol.sendMessage(ChatColor.AQUA + "[플러그인 활성화 중 입니다.]");
		getServer().getPluginManager().registerEvents(this, this);
		makeFile(f);
	}

 

위 코드는 아래와 같은 순서로 진행됩니다.

 

1. 마인크래프트 서버가 켜진다.

2. data.txt 파일이 있는지 없는지 확인한다.

3. data.txt 파일이 없다면 data.txt 파일을 생성한다.

 

 

마인크래프트 서버를 킨 후 plugin/cchLecture(플러그인 이름) 폴더를 확인해보시면

data.txt 파일이 생성 된 것을 확인하실수 있을겁니다.

 

 

파일 생성을 완료했으면 다음 단계로, 

map에 저장했던 변수들을 data.txt 파일에 저장하는 함수를 작성해 보겠습니다.

 

public void mapToFile(File f, HashMap<UUID, Integer> map) {
		try {
			FileWriter writer = new FileWriter(f, false);
			for(UUID uuid : map.keySet()){
				writer.write(uuid.toString()+"|"+map.get(uuid)+"\n");
			}
			writer.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

 

JAVA언어를 처음 접하는 분들의 경우에 이 코드의 작동 순서를 이해하기는 쉽지 않기 때문에

설명은 생략하도록 하겠습니다. 복사, 붙혀넣기를 잘 하시면 에러가 발생하지 않을 것 입니다.

 

 

 

그렇다면 우리는 언제 map에 있는 정보를 data.txt 파일에 저장하면 될까요?

 

1. map에 변수를 저장 했을 때

2. 마인크래프트 서버가 꺼지기 전에

3. 특정시간마다 반복

 

사실 정답은 없습니다. 1,2,3번 모두 장단점이 있습니다.

 

1번의 경우 map에 변수를 저장할때 바로 파일에 내용을 작성하기 때문에 데이터가 손실될 위험이 적습니다.

하지만 파일을 저장하는 횟수가 많기 때문에 서버과부화를 유발할 위험이 있습니다.

 

2번의 경우 onDisable() 함수에 작성하는 방법인데, onDisable() 함수가 실행되기 전, 즉 서버가 강제종료 되었을때

데이터가 날아가게 될 위험이 있습니다. (빽섭의 위험이 있습니다).

하지만 서버가 꺼질때 단 한번만 저장 하기때문에 과부화가 걸릴 일은 없을 것입니다.

 

3번의 경우가 1번 ,2번의 장단점을 타협한 것이라고 할 수 있겠습니다.

특정시간(30초 or 1분 or 2분 ...) 마다 파일을 저장한다면 서버가 강제로 종료되어 ondisable() 함수가 작동되지 않더라도 최소한의 데이터(최대 특정시간 만큼)만 손실되기 때문에 2번에 비해 데이터 손실 위험이 적습니다.

 그리고 특정시간마다 반복하기 때문에 1번에 비해 서버과부화의 위험이 낮습니다.

 

 

 

아래 코드는 3번의 경우를 적용해 30초마다 map에 있는 데이터가 data.txt 파일로 저장되는 코드입니다.

 

@SuppressWarnings("deprecation")
	public void mapToFile(File f, HashMap<UUID, Integer> map) {
		Bukkit.getScheduler().scheduleAsyncRepeatingTask( this , new Runnable() {
			
			@Override
			public void run() {
				try {
					consol.sendMessage(ChatColor.GREEN + "데이터를 저장 했습니다");
					FileWriter writer = new FileWriter(f, false);
					for(UUID uuid : map.keySet()){
						writer.write(uuid.toString()+"|"+map.get(uuid)+"\n");
					}
					writer.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}, 20 * 30, 20 * 30);
		
	}

 

 

작성한 mapToFile() 함수를  onEnable() 함수 안에 작성하겠습니다.

 

@Override
	public void onEnable() {
		consol.sendMessage(ChatColor.AQUA + "[플러그인 활성화 중 입니다.]");
		getServer().getPluginManager().registerEvents(this, this);
		makeFile(f);
		mapToFile(f, map);
	}

 

실행 결과는 아래와 같습니다. 

 

30초마다 정상적으로 작동되고, 데이터도 알맞게 저장됩니다.

 

 

map에 있는 파일을 data.txt 파일로 저장하는데 까지 성공을 했습니다.

 

만약 지금 서버를 종료한 후, 다시 서버를 키면 우리가 저장했던 변수들을 그대로 사용 할 수 있을까요?

 

정답은 아닙니다. 우리는 data.txt로 저장된 파일을 불러오는 과정을 아직 하지 않았습니다.

 

 

 

 

 

아까 했던 질문을 비슷하게 다시 한번 하겠습니다. 

 

우리는 언제 data.txt 파일을 불러와 map으로 저장해주면 될까요?

 

map -> data.txt 로 저장해주는 건 3가지 경우가 있었지만

 

data.txt -> map으로 저장하는 것은 마인크래프트 서버가 켜질때 한번만 해주면 됩니다.

 

그 이유는 쉽게 이해하실거라 생각합니다.

 

 

 

 

data.txt파일에서 map으로 데이터를 저장하는 fileToMap() 함수를 작성해보겠습니다.

 

코드는 아래와 같습니다.

 

	public void fileToMap(File f, HashMap<UUID, Integer> map) {
		try {
			BufferedReader reader = new BufferedReader(new FileReader(f));
			String fileLine = null;
			while ((fileLine = reader.readLine()) != null) {

				UUID uuid = UUID.fromString(fileLine.split("\\|")[0]);
				String str = fileLine.split("\\|")[1];

				map.put(uuid, Integer.parseInt(str));
			}
		} catch (FileNotFoundException e3) {
			e3.printStackTrace();
		} catch (IOException e4) {
			e4.printStackTrace();
		}
	}

이 코드가 실행되는 과정에 대한 설명은 위와 마찬가지로 생략하도록 하겠습니다.

 

 

 

마지막으로, 작성한 fileToMap() 함수를  onEnable() 함수 안에 작성하겠습니다.

 

@Override
	public void onEnable() {
		consol.sendMessage(ChatColor.AQUA + "[플러그인 활성화 중 입니다.]");
		getServer().getPluginManager().registerEvents(this, this);
		makeFile(f);
		mapToFile(f, map);
		fileToMap(f, map);
	}

 

실행을 해보시면 정상적으로 작동될 것입니다.

 

 

 

 

최종적으로 작성된 Main.java의 코드는 아래와 같습니다.

 

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.UUID;

import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.plugin.java.JavaPlugin;

public class Main extends JavaPlugin implements Listener {

	private final File f = new File(getDataFolder(), "/data.txt");

	ConsoleCommandSender consol = Bukkit.getConsoleSender();
	HashMap<UUID, Integer> map = new HashMap<UUID, Integer>();

	@Override
	public void onEnable() {
		consol.sendMessage(ChatColor.AQUA + "[플러그인 활성화 중 입니다.]");
		getServer().getPluginManager().registerEvents(this, this);
		makeFile(f);
		mapToFile(f, map);
		fileToMap(f, map);
	}

	@Override
	public void onDisable() {
		consol.sendMessage(ChatColor.AQUA + "[플러그인 비활성화 중 입니다.]");
	}

	@EventHandler
	public void join(PlayerJoinEvent e) {

		UUID uuid = e.getPlayer().getUniqueId();

		if (map.containsKey(uuid)) {
			map.put(uuid, map.get(uuid) + 1);
		} else {
			map.put(uuid, 1);
		}

		int count = map.get(uuid);

		e.setJoinMessage(count + "번째 입장했습니다!");

	}

	@SuppressWarnings("deprecation")
	public void mapToFile(File f, HashMap<UUID, Integer> map) {
		Bukkit.getScheduler().scheduleAsyncRepeatingTask(this, new Runnable() {

			@Override
			public void run() {
				try {
					consol.sendMessage(ChatColor.GREEN + "데이터를 저장 했습니다");
					FileWriter writer = new FileWriter(f, false);
					for (UUID uuid : map.keySet()) {
						writer.write(uuid.toString() + "|" + map.get(uuid) + "\n");
					}
					writer.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}, 20 * 30, 20 * 30);

	}

	public void fileToMap(File f, HashMap<UUID, Integer> map) {
		try {
			BufferedReader reader = new BufferedReader(new FileReader(f));
			String fileLine = null;
			while ((fileLine = reader.readLine()) != null) {

				UUID uuid = UUID.fromString(fileLine.split("\\|")[0]);
				String str = fileLine.split("\\|")[1];

				map.put(uuid, Integer.parseInt(str));
			}
		} catch (FileNotFoundException e3) {
			e3.printStackTrace();
		} catch (IOException e4) {
			e4.printStackTrace();
		}
	}

	public void makeFile(File f) {
		if (!f.exists() || !f.isFile()) {
			try {
				f.createNewFile();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}

 

이 코드를 수정하여 사용하는 걸 원하신다면
HashMap에 변수를 저장하는 과정에서 변수, 데이터타입에 대한 충분한 이해가 필요합니다.

 

LIST