简体   繁体   中英

Adding a multi-node ArrayList to Firebase Realtime-Database

I have been working on this multiplayer lobby for a few days now and I am STRUGGLING let me tell ya.

Anyways, I am reworking the problem on paper so I can have a step by step way to achieve what I am trying to do. The simple steps to my lobby are as follows:

  1. A user clicks a 'create game' button
  2. That button calls a createGame method which adds a node into my openGames root in my RealTime Database
  3. That node has more children such as the username, the user id, the index of the game in a list, and some other things.
  4. This game which is indexed in an arraylist, is drawn onto the screen through RecyclerView

So that is my current process but I am not sure if I am missing any steps. Of course there is a lot of code behind these steps but this is just the jist of things.

My question is how can I make it so that when a user creates a game, how can I have this properly added into an ArrayList that is in realtime and accessible to all users. Right now the ArrayList is not the same for all users. So if one person makes a game, that game is added to the realtime database, but it is not added to the list for each user.

How can I make a dynamic realtime ArrayList?

This is currently the method that is adding the game to the database and to the list at the same time.

    gameMaker = new GameMaker(hp.uid, userName, wagerD, numGames);

    cgRef.child(Integer.toString(numGames))
            .setValue(gameMaker).addOnCompleteListener(new OnCompleteListener<Void>() {
        @Override
        public void onComplete(@NonNull Task<Void> task) {
                if (task.isSuccessful()) {
                Toast.makeText(FlipCoinLobby.this, "Game creation successful.", Toast.LENGTH_SHORT).show();
                uName = gameRef.child("Username").toString();

                openGames.add(numGames, uName);

                adapter.notifyDataSetChanged();
            } else {
                Toast.makeText(FlipCoinLobby.this, task.getException().getMessage(), Toast.LENGTH_SHORT).show();
            }
        }
    });

And my ArrayList is simply defined as this in the top of the class:

ArrayList<String> openGames = new ArrayList<>();

I have done a similar game in Firebase. I usually use onDataChange() method to verify that every client knows that the list of gameRoom has changed.

  • user click Multiplayer

  • If already exist an empty room, the client get in.

  • If the other rooms are full or there isn't empty room, the client create a room

  • when the matchmaking is done, let's start the match

  • when the match is closed, the first client modify the user variables and the second delete the entire room child.

If u want to make a dynamic arraylist, you have to use onDataChange() method and use it every arraylist change. If the arraylist has changed, u have to update each client arraylist.

There is a lot of problem of competition.


My firebase database is this : General : 在此处输入图片说明

Account is for the log-in info and is a must for who wanna play multiplayer在此处输入图片说明

Every_Flag is a list of flags avaible for the multiplayer and downloaded by each client. Inside the client code, there are every flag with specific multiplayer ID - 0,1,2 etc Easy to update. 在此处输入图片说明

And finally the match :

在此处输入图片说明

Database reference :

 private DatabaseReference Matchmaking, Room,Accounts;

If the player logged in, the multiplayer button become accessible. This method is useful to get every match avaible or not inside the db :

//metodo per la gestione dei cambiamenti della tabella Matchmaking
        Matchmaking.addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                //nel caso ci siano delle modifiche alla tabella
                //se il match non è stato trovato e Account è stato verificato
                if(accountFound){
                    //azzero la Lista
                    List_party = new ArrayList<>();
                    Id_rooms = new ArrayList<>();
                for (DataSnapshot ds : dataSnapshot.getChildren()) {
                    //sempre se matchfound non è stato trovato
                    if (!currentwithID.getMatch_found()) {
                        //prendo la quantità di figli presenti
                        size = Integer.parseInt(String.valueOf(dataSnapshot.getChildrenCount()));
                        //inserisco all'interno della variabile CT il valore di ogni figlio
                        ct = ds.getValue(Class_party.class);
                        // e se è il numero di giocatori == 1 e la partita non è iniziata e finita in modo anomalo
                        if (ct.getNumberOfPlayers() == 1 && !ct.getStart() && ct.getCorrect_end() && ct.getAccessible()) {
                            //aggiungo l'id della room alla lista
                            //Inserisco alla list_party l'intero oggetto
                            Id_rooms.add(ct.getId());
                            List_party.add(ct);
                        }

                        Toast.makeText(HomeActivity.this, "Lista ROOMs aggiornata", Toast.LENGTH_SHORT).show();
                    }
                }
                }
            }

And this is how i generate the match :

//MATCHMAKING
public void Create(View view)
{
    boolean start = false;
    boolean correct_end = true;
    boolean accessible = true;
    uSer = user.getDisplayName();
    //se la lunghezza delle partite disponibili a cui accedere è ==0
    if(size==0||Id_rooms.size()==0)
    {
        //creo la chiave univoca per il match
        String id = Matchmaking.push().getKey();
        //creo un utente corrente con nome e punteggio
        current=new Class_user(uSer,0);
        //un utente con ID utente, nome e se il match è stato trovato
        //genero le domande che verranno utilizzato solo per quel determinato match
        list_game= Generation();
        //ora creo il match vero e proprio e lo inserisco nella tabella matchmaking
        second = new Class_party(id, current, list_game,start,correct_end,accessible);
        Matchmaking.child(id).setValue(second); }
    else{
        accessible = false;
        // se invece ci sono delle partite disponibili, le randomizzo
        Collections.shuffle(Id_rooms);
        for(int i=0; i<List_party.size();i++)
        {
            //le passo tutte finché non trovo quella con l'ID corrispondente alla prima che ho randomizzato
            if(List_party.get(i).getId()==Id_rooms.get(0))
            {
                // popolo la variabile class_party
                second=new Class_party(List_party.get(i).getId(),List_party.get(i).getUser1(),List_party.get(i).getFlag(),start,correct_end,accessible);
            }
        }
        matchFound = false;
        //inserisco l'utente e il numero di giocatori = 2
        currentwithID = new Class_user(uID,uSer,matchFound);
        current=new Class_user(uSer,0);
        second.setUser2(current);
        second.setNumberOfPlayers(2);
        //ora reinserisco tutto nel figlio che ha come ID, quello della stanza selezionata
        Matchmaking.child(second.getId()).setValue(second);
        Accounts.child(uID).setValue(currentwithID);
    }

}

public ArrayList<Integer> Generation()
{
    ArrayList<Integer> tmp = new ArrayList<>();
    Collections.shuffle(mFlag);
    for(int i=0; i<=10; i++) //3 momentaneamente
    {
        tmp.add(mFlag.get(i));
    }
    Toast.makeText(HomeActivity.this,"Domande generate",Toast.LENGTH_LONG).show();

    return tmp;
}

First of all, i set all the variables. uSer is the name that i get from google auth. If the room avaible is 0, i create the empty ROOM, generate the list of flags ( random 10 flags) and insert the child that it is a class :

public class Class_party {

public Class_user user1, user2;
public int numberOfPlayers;
public String id;
public ArrayList<Integer> value;
public boolean start,correct_end,accessible;

public Class_party(String id,Class_user user1, ArrayList<Integer> value, Boolean start,Boolean correct_end, Boolean accessible) {
    this.value = value;
    this.user1 = user1;
    this.numberOfPlayers = 1;
    this.id = id;
    this.start = start;
    this.accessible = accessible;
    this.correct_end = correct_end;
    user2 = null;

}


public Class_party() {
}

public Class_user getUser1() {
    return user1;
}

public void setUser1(Class_user user1) {
    this.user1 = user1;
}

public Class_user getUser2() {
    return user2;
}

public void setUser2(Class_user user2) {
    this.user2 = user2;
}

public int getNumberOfPlayers() {
    return numberOfPlayers;
}

public void setNumberOfPlayers(int numberOfPlayers) {
    this.numberOfPlayers = numberOfPlayers;
}

public String getId() {
    return id;
}

public void setId(String id) {
    this.id = id;
}

public ArrayList<Integer> getFlag() { return value; }

public void setValue(ArrayList<Integer> value) {
    this.value=value;

If i found a match or a list of match, i choose a random match i find it and i set the second user inside the FirebaseDatabase with this particular string : Matchmaking.child(second.getId()).setValue(second);

Now, we get a new activity called MULTIPLAYER :

In this activity i create the multiplayer game, but let's see how I get all the information and set all the variables. First of all i use onDataChange to pickup every change inside the match.

 Matchmaking.addValueEventListener(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {
            //prendo tutte le tabelle e le aggiungo a list_party
                for (DataSnapshot ds : dataSnapshot.getChildren()) {
                    ct = ds.getValue(Class_party.class);
                    List_party.add(ct);
                }
                // mi prendo la partita che mi interessa
                for (int i = 0; i < List_party.size(); i++) {
                    if (List_party.get(i).getId() == intent_id) {
                        ct = List_party.get(i);
                    }
                }
                //controllo che gli utenti collegati siano due e che la partita non sia cominciata ne finita in modo anomalo
            if (ct.getNumberOfPlayers() == 2 && !ct.getAccessible() && ct.getCorrect_end() && !ct.getStart()) {
                    //mi scarico le bandiere che sono state scelte alla creazione della ROOM
                FlagsFromDB = ct.getFlag();
                //passo dal Layout di Waiting a quello iniziale
                switchToScreen(R.id.screen_intro);
                matchFound=true;
                Toast.makeText(MpMultiplayer.this, "Match Found", Toast.LENGTH_SHORT).show();
                //imposto il match trovato = true
                currentwithID.setMatch_found(true);
                //aggiorno la tabella
                Accounts.child(uID).setValue(currentwithID);
            }
            //se la partita è cominciata e il numero di giocatori è uguale a due e match_found = true
            if (ct.getStart() && ct.getNumberOfPlayers()==2 && matchFound) {
                    //dico che la room è stata creata
                roomcreated = true;
                //ora il gioco comincia e cambio di layout
                switchToScreen(R.id.screen_game);
                Popolate();
            }
            //se qualcuno esce prima del previsto e la partita è già cominciata
            if(!ct.getCorrect_end() && roomcreated)
            {
                //assegno automaticamente all'utente 500 punti
                points += 500;
                timer1.cancel();
                mName.setText(intent_user + " You Won!");
                mName.setTextSize(25);
                mPoints.setText(new Integer(points).toString());
                switchToScreen(R.id.screen_score);
                Toast.makeText(MpMultiplayer.this, "The player LEFT the game", Toast.LENGTH_SHORT).show();
            }
            //se l'utente si è scollegato e la partita non è ancora cominciata
            if(!ct.getCorrect_end() && !roomcreated)
            {  //assegno automaticamente 100 punti
                points += 100;
                mName.setText(intent_user + "You Won!");
                mName.setTextSize(25);
                mPoints.setText(new Integer(points).toString());
                switchToScreen(R.id.screen_score);
                Toast.makeText(MpMultiplayer.this, "The player LEFT the game", Toast.LENGTH_SHORT).show();
            }
        }

        @Override
        public void onCancelled(DatabaseError databaseError) {
        }
    });

When one of the two user press the button start, i change the database variable with this method :

   void ChangeStartValue(){

        Room = FirebaseDatabase.getInstance().getReference("Matchmaking");
        try {
            ct.setStart(true);
            Room.child(ct.getId()).setValue(ct);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    void switchToScreen(int screenId) {
        // make the requested screen visible; hide all others.
        for (int id : SCREENS) {
            findViewById(id).setVisibility(screenId == id ? View.VISIBLE : View.GONE);
        }
    }

And this is when the user finish the match and press the backToMainMenu button :

 private View.OnClickListener backtomainmenu = new View.OnClickListener() {
    @Override
    public void onClick(final View view) {
        try {
            if(ct.getNumberOfPlayers()==2){
            ct.setNumberOfPlayers(ct.getNumberOfPlayers()-1);
            Room.child(ct.getId()).setValue(ct);
            Accounts.child(uID).child("match_found").setValue(false);
            }
            else
                if (ct.getNumberOfPlayers()==1 && ct.getStart()&& ct.getCorrect_end()){
                    Room.child(ct.getId()).removeValue();
                    Accounts.child(uID).child("match_found").setValue(false); }
                    else
                        if(ct.getNumberOfPlayers()==1 && ct.getStart() && !ct.getCorrect_end()){
                            Room.child(ct.getId()).removeValue();
                            Accounts.child(uID).child("match_found").setValue(false);}
                            else
                                if(ct.getNumberOfPlayers()==1 && !ct.getStart() && !ct.getCorrect_end()) {
                                    Room.child(ct.getId()).removeValue();
                                    Accounts.child(uID).child("match_found").setValue(false);}


        } catch (Exception e) {
            e.printStackTrace();
        }

        finish();

    }
};

And if the user wanna go out without finish the match :

 @Override
    //in caso l'utente voglia uscire
    public void onBackPressed() {
        backpress = (backpress + 1);
        // se preme una volta compare il toast
        if (backpress >= 1 && backpress < 2)
            Toast.makeText(getApplicationContext(), " Press Back again to Exit ", Toast.LENGTH_SHORT).show();
        //se preme per più di una volta esce
        if (backpress > 1) {
            try {
                //se i giocatori sono più di due, setto i giocatori ad 1 e Correct End a false
                if(ct.getNumberOfPlayers()>=2){
                    ct.setNumberOfPlayers(ct.getNumberOfPlayers()-1);
                    ct.setCorrect_end(false);
                    //aggiorno poi le tabelle
                    Room.child(ct.getId()).setValue(ct);
                    Accounts.child(uID).child("match_found").setValue(false);
                }
                else
                    //se il giocatore è da solo
                if(ct.getNumberOfPlayers()<2)
                {
                    //cancello il figlio dalla tabella matchmaking
                    Room.child(ct.getId()).removeValue();
                    Accounts.child(uID).child("match_found").setValue(false);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            //se poi la partita era già cominciata, cancello il timer per evitare errori
            if(roomcreated)
            timer1.cancel();
            //chiudo l'activity
            this.finish();
        }
    }

I hope that this will help you :D If u have any questions, I'll answer to you :D

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM