從0開始 3.真正的聊天室 (singleton) - MUD Game
By Charlie
at 2019-12-05T15:53
at 2019-12-05T15:53
Table of Contents
大家好! 歡迎收看從0開始的 MUD 開發第 3 集
在上一集我們已經實作了多執行緒的 telnet echo server
雖然我們的 server 可以支援複數使用者同時連線
但是使用者之間並沒有辦法互相溝通
所以這一集我們要講的是如何透過 java 內建的資料結構(HashMap)收集, 管理使用者
同時建立一個 PlayerManager, 藉由 PlayerManager 提供一個統一的操作介面
// PlayerManager.class
// ✂--------------請沿虛線剪下--------------
package core;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiConsumer;
public class PlayerManager {
// 宣告 static 的變數儲存 PlayerManager 的 instance
// 這個寫法可以讓 PlayerManager 這個 class 被載入時, 立刻執行自己的建構子
private static PlayerManager instance = new PlayerManager();
// 用來儲存所有使用者的 Map
private static Map<String, Player> players;
// 重要! 一個 private 的建構子, 確保沒有其他程式可以 new PlayerManager()
private PlayerManager() {
players = new ConcurrentHashMap<>();
}
// 一個 static 的 getInstance() 方法取代原有的建構子
// 原本常見的 PlayerManager pm = new PlayerManager();
// 寫法改為 PlayerManager pm = PlayerManager.getInstance();
public static PlayerManager getInstance() {
return instance;
}
// 以下是真正提供給其他程式使用的方法
// 包括了新增、移除使用者的 addPlayer, removePlayer
// 檢查使用者是否上線的 isPlayerOnline
// 對所有使用者下指令的 forEach
public void addPlayer(String name, Player player) {
players.put(name, player);
}
public void removePlayer(String name) {
players.remove(name);
}
public boolean isPlayerOnline(String name) {
return players.containsKey(name);
}
public void forEach(BiConsumer<? super String, ? super Player> biConsumer) {
players.forEach(biConsumer);
}
}
// ✂--------------請沿虛線剪下--------------
有了 PlayerManager 的支援, 原本的 Player.run() 功能也得到了進一步的擴充
// 在原有的 Player class 中新增一個 String playerName 儲存使用者的 ID
private String playerName;
public void run() {
try {
// 剛寫好的 PlayerManager 在這邊派上用場, 讓他來負責處理關於使用者的事務
PlayerManager pm = PlayerManager.getInstance();
write("歡迎來到 telnet chat server! (多執行緒版本)");
write("請問您的大名?");
// 一個簡易判斷線上是否有同名使用者的小程式
// 如果有同名使用者則一直執行無限迴圈, 直到使用者輸入了不衝突的新 ID
String name;
while (pm.isPlayerOnline(name = read())) {
write("線上已經有一個叫做" + name + "的使用者了! 請重新輸入");
}
// 當使用者輸入了獨一無二的 ID 後, 先對所有線上使用者廣播
// 再將目前的使用者加入 players
playerName = name;
pm.forEach((k, v) -> v.write(playerName + "上線囉"));
pm.addPlayer(playerName, this);
write(playerName + "你好! 歡迎進入聊天室");
while (true) {
String input = read();
if ("quit".equalsIgnoreCase(input)) {
socket.close();
// 別忘了使用者下線後必須把他移除
pm.removePlayer(playerName);
pm.forEach((k, v) -> v.write(playerName + "離開了"));
break;
}
// 原本只是呼叫個別使用者的 write()
// 現在改為使用 forEach 呼叫所有使用者的 write() 達到 chat 的效果
pm.forEach((k, v) -> v.write("【閒聊】" + playerName + "說:" + input));
}
} catch (IOException ignored) {}
}
以下是三開連線的畫面
如何? MUD 的味道是不是開始慢慢出現了? XD
https://i.imgur.com/9l27OV9.png
--
╔═◢ ◣═╦╦═════╦═════╗
║ ◤◤◤ ◥ ╠╣飛鳥ももこ╠═╗ ║
║ ▇ ▇ ║╚═════╝ ╚═╦═╣
║ ▌ ● ● ▌ ║╔══════╗╔═╩═╣
║◤ ◥ ︺█◤ ◥╠╣Momoko Asuka╠╝ ║
╚◣◢ ▄▂▄ ◣◢╩╩══════╩════╝
--
在上一集我們已經實作了多執行緒的 telnet echo server
雖然我們的 server 可以支援複數使用者同時連線
但是使用者之間並沒有辦法互相溝通
所以這一集我們要講的是如何透過 java 內建的資料結構(HashMap)收集, 管理使用者
同時建立一個 PlayerManager, 藉由 PlayerManager 提供一個統一的操作介面
// PlayerManager.class
// ✂--------------請沿虛線剪下--------------
package core;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiConsumer;
public class PlayerManager {
// 宣告 static 的變數儲存 PlayerManager 的 instance
// 這個寫法可以讓 PlayerManager 這個 class 被載入時, 立刻執行自己的建構子
private static PlayerManager instance = new PlayerManager();
// 用來儲存所有使用者的 Map
private static Map<String, Player> players;
// 重要! 一個 private 的建構子, 確保沒有其他程式可以 new PlayerManager()
private PlayerManager() {
players = new ConcurrentHashMap<>();
}
// 一個 static 的 getInstance() 方法取代原有的建構子
// 原本常見的 PlayerManager pm = new PlayerManager();
// 寫法改為 PlayerManager pm = PlayerManager.getInstance();
public static PlayerManager getInstance() {
return instance;
}
// 以下是真正提供給其他程式使用的方法
// 包括了新增、移除使用者的 addPlayer, removePlayer
// 檢查使用者是否上線的 isPlayerOnline
// 對所有使用者下指令的 forEach
public void addPlayer(String name, Player player) {
players.put(name, player);
}
public void removePlayer(String name) {
players.remove(name);
}
public boolean isPlayerOnline(String name) {
return players.containsKey(name);
}
public void forEach(BiConsumer<? super String, ? super Player> biConsumer) {
players.forEach(biConsumer);
}
}
// ✂--------------請沿虛線剪下--------------
有了 PlayerManager 的支援, 原本的 Player.run() 功能也得到了進一步的擴充
// 在原有的 Player class 中新增一個 String playerName 儲存使用者的 ID
private String playerName;
public void run() {
try {
// 剛寫好的 PlayerManager 在這邊派上用場, 讓他來負責處理關於使用者的事務
PlayerManager pm = PlayerManager.getInstance();
write("歡迎來到 telnet chat server! (多執行緒版本)");
write("請問您的大名?");
// 一個簡易判斷線上是否有同名使用者的小程式
// 如果有同名使用者則一直執行無限迴圈, 直到使用者輸入了不衝突的新 ID
String name;
while (pm.isPlayerOnline(name = read())) {
write("線上已經有一個叫做" + name + "的使用者了! 請重新輸入");
}
// 當使用者輸入了獨一無二的 ID 後, 先對所有線上使用者廣播
// 再將目前的使用者加入 players
playerName = name;
pm.forEach((k, v) -> v.write(playerName + "上線囉"));
pm.addPlayer(playerName, this);
write(playerName + "你好! 歡迎進入聊天室");
while (true) {
String input = read();
if ("quit".equalsIgnoreCase(input)) {
socket.close();
// 別忘了使用者下線後必須把他移除
pm.removePlayer(playerName);
pm.forEach((k, v) -> v.write(playerName + "離開了"));
break;
}
// 原本只是呼叫個別使用者的 write()
// 現在改為使用 forEach 呼叫所有使用者的 write() 達到 chat 的效果
pm.forEach((k, v) -> v.write("【閒聊】" + playerName + "說:" + input));
}
} catch (IOException ignored) {}
}
以下是三開連線的畫面
如何? MUD 的味道是不是開始慢慢出現了? XD
https://i.imgur.com/9l27OV9.png
--
╔═◢ ◣═╦╦═════╦═════╗
║ ◤◤◤ ◥ ╠╣飛鳥ももこ╠═╗ ║
║ ▇ ▇ ║╚═════╝ ╚═╦═╣
║ ▌ ● ● ▌ ║╔══════╗╔═╩═╣
║◤ ◥ ︺█◤ ◥╠╣Momoko Asuka╠╝ ║
╚◣◢ ▄▂▄ ◣◢╩╩══════╩════╝
--
Tags:
線上
All Comments
Related Posts
從0開始 2.多執行緒
By Gary
at 2019-12-05T13:36
at 2019-12-05T13:36
請問該如何賺寶石最划算
By Daph Bay
at 2019-12-05T13:08
at 2019-12-05T13:08
OWL 2020 隊伍不精確總移動里程數
By Dorothy
at 2019-12-04T23:16
at 2019-12-04T23:16
馬爾杜克道具店
By Dorothy
at 2019-12-04T22:18
at 2019-12-04T22:18
這次修正控球
By Hamiltion
at 2019-12-04T19:41
at 2019-12-04T19:41