Red5 - SharedObject中的ArrayList已损坏

时间:2012-10-15 15:45:39

标签: java actionscript-3 arraylist red5 shared-objects

好吧,也许没有腐败。

所以,有点背景。我最近将我的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

非常感谢任何帮助或建议!

3 个答案:

答案 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();