【发布时间】:2021-12-05 22:16:40
【问题描述】:
我正在尝试在我的 Minecraft 服务器中开发一个统计/成就系统。
我做了一些研究,但仍然不能很好地做出决定,所以决定在堆栈溢出中发布我的第一个问题。
有多种类型的成就,例如破坏方块,收获作物,杀死动物......等等。表初始化程序如下所示。 (我故意将这些值设置为双精度)
public static void init() {
String query = "CREATE TABLE IF NOT EXISTS statistic ("
+ " uuid VARCHAR(255) PRIMARY KEY,"
+ " block_break double, crop_break double, ore_break double,"
+ " wood_break double, animal_kill double, monster_kill double, boss_kill double,"
+ " fish_natural double, fish_auto double "
+ ")";
try {
Connection conn = HikariPoolManager.getInstance().getConnection();
PreparedStatement ps = conn.prepareStatement(query);
ps.execute();
} catch (SQLException e) {
e.printStackTrace();
}
}
然后我用这个保存回来
public static void save(String uuidString, StatisticsType stat, double val) {
String query= "INSERT INTO statistics (uuid, {stat}) "
+" VALUE (?,?) ON DUPLICATE KEY UPDATE "
+" uuid=VALUES( uuid ), {stat}=VALUES( {stat} )"
.replace("{stat}", stat.name());
try (Connection conn = HikariPoolManager.getInstance().getConnection();
PreparedStatement ps = conn.prepareStatement(query)
){
ps.setString(1, uuidString);
ps.setDouble(2, val);
ps.execute();
} catch (SQLException e) {
e.printStackTrace();
}
}
PlayerCache.java
public class PlayerCache {
@Getter
private static final Map<UUID, PlayerCache> cacheMap = new HashMap<>();
private final UUID uuid;
@Getter
private HashMap<StatisticsType, Double> statistics;
@Getter
private HashSet<StatisticsType> changed;
public PlayerCache(UUID uuid) {
this.uuid= uuid;
}
public void init(HashMap<StatisticsType,Double> achievements) {
this.statistics = new HashMap<>();
this.changed = new HashSet<>();
this.statistics.putAll(achievements);
}
public void addValue(StatisticsType type, double addition) {
statistics.put(type, statistics.get(type) + addition);
changed.add(type);
}
public double getStatistic(StatisticsType type) {
return statistics.getOrDefault(type, 0.0D);
}
public void save() {
for (StatisticsType statisticsType : changed) {
StatisticDAO.save(uuid.toString(),statisticsType, getStatistic(statisticsType));
}
changed.clear();
}
public static PlayerCache get(final UUID uuid) {
PlayerCache playerCache = cacheMap.get(uuid);
if (playerCache == null) {
playerCache = new PlayerCache(uuid);
cacheMap.put(uuid, playerCache);
}
return playerCache;
}
}
我对编程的一般设计有疑问,而不是修复代码本身。
目前,事情就是这样发展的。 为简单起见,让我选择两个统计动作 - 碎石和杀怪。
-
玩家加入游戏,读取数据,对玩家进行缓存,并将统计信息放入缓存中。
-
玩家打破石头,它会增加玩家缓存中的统计数据。
-
如果玩家打碎了石头,它会切换一个布尔标志以显示他已经打碎了石头,因此需要在某个时候将此信息刷新到数据库中。
-
服务器循环所有玩家,并检查玩家是否做了任何事情。如果玩家做了什么,它会调用 sql save 方法,并切换回布尔标志。
不过,我这次遇到的问题很少。
-
玩家可以在写入数据库期间打破石头,杀死怪物。甚至更多不同的动作。这将导致从播放器调用多个保存函数。有没有更好的方法来解决这个问题?
-
我读取和写入数据的一般方法是否正确?我几乎使用相同的方法来处理其他功能的数据库内容,但不确定这是否是好方法。
【问题讨论】:
-
1) 您也应该在第一个代码中使用 try/resource/catch,就像在第二个代码中一样。 2)你这样做的方式,你有很多小写(在这种情况下不是问题,每秒查询仍然太少)。但是您可以将统计信息保存在 RAM 中,并使用“已更改”标志。然后运行一个计时器(带有 sleep 或 sth alik 的守护线程循环),每隔一秒左右检查一次,并将所有更改的数据集批量写入数据库(一次多个数据行,准备好的语句)调用。理论上,如果性能仍然存在问题,写入周期时间会自行增加。