作者gasbomb (虚空雷神兽)
看板mud
标题[心得] 从0开始 3.真正的聊天室 (singleton)
时间Thu Dec 5 15:53:31 2019
大家好! 欢迎收看从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╠╝ ║
╚◣◢ ▄▂▄ ◣◢╩╩══════╩════╝
--
※ 发信站: 批踢踢实业坊(ptt.cc), 来自: 211.72.253.40 (台湾)
※ 文章网址: https://webptt.com/cn.aspx?n=bbs/mud/M.1575532413.A.3C7.html