好吧,也许没有腐败。
所以,有点背景。我最近将我的Red5动力游戏从red5的windows版本转移到了Debian Squeeze上运行的版本。我有一个游戏大厅,它使用共享对象来维护各种可用游戏的列表。
单个游戏作为HashMap [String,Object]存储在所述SharedObject中的game_id中。 HashMap的一些属性是ArrayLists,特别是 players (连接的玩家id的ArrayList [Integer])和投票(另一个拥有玩家的ArrayList [Integer]提交投票)
每当我对这些ArrayLists中的任何一个进行更改时,出现问题,我就不能将HashMap写入SharedObject(setAttribute返回false)
创建新游戏(服务器端):
HashMap<String, Object> game = new HashMap<String, Object>();
game.put("id", PendingGameManager.GAME_IDX);
game.put("difficulty", difficulty);
game.put("type", type);
game.put("description", this.getDescription(type, difficulty));
game.put("players", new ArrayList<Integer>());
game.put("coords", coords);
game.put("created", Calendar.getInstance().getTimeInMillis());
game.put("votes", new ArrayList<Integer>());
boolean success = this.gamesSO.setAttribute(Integer.toString(PendingGameManager.GAME_IDX), game);
执行没有问题,成功返回true。
稍后我检索播放器数组并进行修改:
HashMap<String, Object> game = (HashMap<String, Object>)this.gamesSO.getMapAttribute(Integer.toString(game_id));
ArrayList<Integer> players = (ArrayList<Integer>) game.get("players");
players.add(new Integer(Integer.parseInt((user_id))));
boolean success = this.gamesSO.setAttribute(Integer.toString(game_id), game);
此处成功始终返回false。如果为游戏创建一个新的HashMap并从旧版本复制每个属性但省略玩家和投票它很好,但是什么都试过,我无法得到它维护一个数组。我也尝试使用List和Vector,结果相同。这是我第一次接触Java,我一直小心地只添加Integer的类实例而不是原始的int,但是对于我所有的努力,我已经用完了想法。
在Windows上运行完美时,我的原始实现使用了ArrayList [String]而不是ArrayList [Integer]
环境: Debian Squeeze 6.0.6 jre 1.7 Red5 1.0RC2
非常感谢任何帮助或建议!
答案 0 :(得分:2)
根据您的red5版本信息,这是“setAttribute”方法的实现:
@Override
public boolean setAttribute(String name, Object value) {
log.debug("setAttribute - name: {} value: {}", name, value);
boolean result = true;
ownerMessage.addEvent(Type.CLIENT_UPDATE_ATTRIBUTE, name, null);
if (value == null && super.removeAttribute(name)) {
// Setting a null value removes the attribute
modified = true;
syncEvents.add(new SharedObjectEvent(Type.CLIENT_DELETE_DATA, name, null));
deleteStats.incrementAndGet();
} else if (value != null && super.setAttribute(name, value)) {
// only sync if the attribute changed
modified = true;
syncEvents.add(new SharedObjectEvent(Type.CLIENT_UPDATE_DATA, name, value));
changeStats.incrementAndGet();
} else {
result = false;
}
notifyModified();
return result;
}
我猜值是!= null(但我可能错了)。但在我看来,它会通过“super.setAttribute”调用将该调用转发给它的父类, 这是父/超类的实现:
/**
* Set an attribute on this object.
*
* @param name the name of the attribute to change
* @param value the new value of the attribute
* @return true if the attribute value was added or changed, otherwise false
*/
public boolean setAttribute(String name, Object value) {
if (name != null) {
if (value != null) {
// update with new value
Object previous = attributes.put(name, value);
// previous will be null if the attribute didn't exist
return (previous == null || !value.equals(previous));
}
}
return false;
}
这里的重要路线(恕我直言):
return (previous == null || !value.equals(previous));
=&GT;无法找到“previous”,然后返回false。
问题是我想:你正在做的演员:
HashMap<String, Object> game = (HashMap<String, Object>)this.gamesSO.getMapAttribute(Integer.toString(game_id));
我不认为“this.gamesSO.getMapAttribute(Integer.toString(game_id));”将返回HashMap,我想我可以记住Red5有自己的Map类型。
如果您只是调试并添加:
System.out.println(this.gamesSO.getMapAttribute(Integer.toString(game_id)));
和/或添加一些调试断点并验证它究竟是什么类型。 然后真正投入到这一个。
我认为您还应该指定更详细的地图。 类似的东西:
HashMap<String, MyPlayerBean>
创建一个MyPlayerBean类,其中包含您真正需要的属性。 制作这些Map / List对象可能很方便快速入门,但如果您的应用程序开始增长,它可能会非常难看。
塞巴斯蒂安
答案 1 :(得分:0)
因此,为了澄清,我应该把我的发现作为答案。
由于我正在抓取存储在SharedObject中的HashMap(是一种复杂数据类型而不是原始数据)的引用,所以我所做的任何更改也会反映在该槽的当前值中,以便当它出现时再次设置,Red5无法发现差异。
new_val.equals(old_val); // returns true since they both reference the same instance of HashMap
如果在更改HashMap中包含的ArrayList之前克隆HashMap,则上述语句仍然计算为true,因为ArrayList与存储在当前槽中的实例相同,并且java在评估.equals时也会考虑每个对象的哈希码。 ()(实际上,比较两个HashMaps的内容)
因此,我必须在进行更改之前克隆HashMap和ArrayList ,以便检测更改并与客户端同步。或者(当我最终实现时)克隆HashMap,进行更改并另外更新基本属性,在我的例子中 int changeCount
如果Red5有一种机制来强制同步这种情况(例如在客户端的ActionScript中使用 setDirty 而不是 setProperty ),那就太棒了
答案 2 :(得分:0)
在更新作为ArrayList的共享对象属性时,我遇到了类似的问题。我最终删除了该属性,然后再次设置它。
ArrayList<String> userlist = (ArrayList<String>) so.getAttribute("userlist");
userlist.remove(username);
so.beginUpdate();
so.removeAttribute("userlist");
so.setAttribute("userlist", userlist);
so.endUpdate();