3

好吧,也许没有像这样被破坏。

所以,有点背景。我最近将我的 Red5 驱动游戏从 red5 的 windows 版本转移到了在 Debian Squeeze 上运行的游戏。我有一个游戏大厅,它使用共享对象来维护各种可用游戏的列表。

单个游戏被存储为 HashMap[String, Object] 与它在所述 SharedObject 中的 game_id 相对应。HashMap 的几个属性是 ArrayLists,特别是玩家(连接玩家 id 的 ArrayList[Integer])和投票(提交投票的玩家的另一个 ArrayList[Integer])

每当我对这些 ArrayList 中的任何一个进行更改时,某处出现问题,我无法再将 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);

这里成功总是返回假。如果为游戏创建一个新的 HashMap 并从旧的属性中复制每个属性,但忽略玩家投票,这很好,但无论尝试什么,我都无法让它维护一个数组。我也用 List 和 Vector 尝试过,结果相同。这是我第一次接触 Java,我一直很小心地只添加 Integer 的类实例而不是原始的 int,但我所有的努力都已经没有想法了。

在 Windows 上运行完美时,我的原始实现使用 ArrayList[String] 而不是 ArrayList[Integer]

环境:Debian Squeeze 6.0.6 jre 1.7 Red5 1.0RC2

任何帮助或建议将不胜感激!

4

3 回答 3

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;
}

我猜 value 是 != 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));

=> "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 对象可能很方便快速入门,但如果您的应用程序开始增长,它可能会变得非常难看。

塞巴斯蒂安

于 2012-10-15T17:24:40.807 回答
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() 时也会考虑每个对象的哈希码(实际上,比较两个 HashMap 的内容)

因此,我必须在进行更改之前克隆 HashMap 和 ArrayList ,以便检测到更改并与客户端同步。或者(正如我最终实现的那样)克隆 HashMap,进行更改并另外更新原始属性,在我的情况下为int changeCount

如果 Red5 有一种机制可以针对这种情况强制同步(例如在客户端 Actionscript 中使用setDirty而不是setProperty ),那就太好了

于 2012-10-17T12:39:04.110 回答
0

更新共享对象属性时,我遇到了类似的问题,它是一个 ArrayList。我最终删除了该属性,然后再次设置它。

ArrayList<String> userlist = (ArrayList<String>) so.getAttribute("userlist");
userlist.remove(username);
so.beginUpdate();
so.removeAttribute("userlist");
so.setAttribute("userlist", userlist);
so.endUpdate();
于 2013-02-07T19:24:32.240 回答