3

まあ、おそらくそのように破損していません。

だから、少し背景。最近、Red5を搭載したゲームをred5のWindowsビルドからDebianSqueezeで実行されているものに移動しました。共有オブジェクトを使用して、利用可能なさまざまなゲームのリストを維持するゲームロビーがあります。

単一のゲームは、SharedObjectのgame_idに対してHashMap [String、Object]として保存されます。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);

ここで成功すると常にfalseが返されます。ゲーム用に新しいHashMapを作成し、古いものから各プロパティをコピーして、プレーヤーを省略して投票する場合は問題ありませんが、何をしようとしても、配列を維持することはできません。ListとVectorでもこれを試しましたが、同じ結果になりました。これはJavaとの最初の接触であり、プリミティブintではなくIntegerのクラスインスタンスのみを追加するように注意しましたが、すべての努力のためにアイデアが不足しました。

Windowsで完全に実行された場合、元の実装ではArrayList[Integer]ではなくArrayList[String]を使用していました。

環境: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;
}

値は!= 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;
}

ここでの重要な行(IMHO):

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には独自のマップタイプがあることを思い出すことができると思います。

単にデバッグして追加する場合:

System.out.println(this.gamesSO.getMapAttribute(Integer.toString(game_id)));

および/またはデバッグブレークポイントを追加して、これが正確にどのタイプであるかを確認します。そして、これに本当にキャストします。

マップもより詳細に指定する必要があると思います。何かのようなもの:

 HashMap<String, MyPlayerBean>

そして、本当に必要な属性を使用して、クラスMyPlayerBeanを作成します。これらのマップ/リストオブジェクトを作成することは、すぐに始めるのに便利かもしれませんが、アプリケーションが成長し始めると、かなり醜くなる可能性があります。

セバスチャン

于 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のクローンを作成した場合、ArrayListは現在のスロットに格納されているのと同じインスタンスであり、javaも.equals()()を評価するときに各オブジェクトのハッシュコードを考慮するため、上記のステートメントはtrueと評価されます。実際には、両方のHashMapの内容を比較します)

したがって、変更を検出してクライアントと同期するには、変更を加える前にHashMapとArrayListの両方のクローンを作成する必要があります。または、(私が実装した結果として)HashMapのクローンを作成し、変更を加えて、プリミティブプロパティ(私の場合はint changeCount )を追加で更新します。

Red5にそのような場合に同期を強制するメカニズムがあれば素晴らしいでしょう(クライアント側のActionscriptでsetPropertyの代わりにsetDirtyを使用するなど)

于 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 に答える