Matchmaking for a multiplayer game
Clash Royale CLAN TAG#URR8PPP
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;
up vote
3
down vote
favorite
I have working code that needs some optimization for scalability and overall performance. This code basically creates a lobby where users wait for match to be created and link users for a multiplayer game match.
PlayerWait player = GameMatchEngine.getInstance().createMatch(userId);
if (player.getPlayer().getUserId() == userId)
while (player.isAvailable())
Thread.sleep(100);
if (player.getMatchBegan())
if (!hasUser(userId))
.... failed to find a match ....
else
multiplayer = getMultiplayer(userId);
//if
The code above is called when a user request for a match. if (player.getPlayer().getUserId() == userId)
happens when a first user requests a match, and there is no player in the lobby.
private Map<Integer, Multiplayer> users =
new ConcurrentHashMap<Integer, Multiplayer>();
/* removes user who was waiting and retrieves Multiplayer Session */
private boolean hasUser(Integer userId)
return this.users.containsKey(userId);
PlayerWait
class:
private Player player;
private boolean availablility = true;
private boolean matchBegan = true;
Timer timer;
public PlayerWait(Player player)
setPlayer(player);
waitTimeExpired(10);
private void waitTimeExpired(int seconds)
this.timer = new Timer();
timer.schedule(new scheduledTask(), seconds * 1000);
//waitTimeExpired()
class scheduledTask extends TimerTask
@Override
public void run()
synchronized(this)
if(availablility)
setAvailable(false);
if(matchBegan)
GameMatchEngine.getInstance().
matchWithAI(getPlayerWait());
//if
timer.cancel();
//class scheduledTask
public PlayerWait createMatch(int user_id) throws SQLException
String rank = getRank(user_id);
PlayerWait playerFound = findOpponentUser(rank, user_id);
if (playerFound == null)
playerFound = addUser(new Player(user_id, rank));
else
try
.... found an opponent; create a match in db ....
catch (SQLException e)
LOGGER.error("Error in creating a match: ", e);
//try
LOGGER.error("PlayerWait: " + playerFound);
return playerFound;
//createMatch()
private PlayerWait findOpponentUser(String rank, int userId)
boolean found = false;
PlayerWait player = null;
if (hasUser(rank))
player = getUser(rank);
if (player.getPlayer().getUserId() != userId)
synchronized (player)
found = player.isAvailable();
player.setAvailable(false);
//synchronized()
else
found = true;
if (found)
removeUser(rank);
else
player = null;
//if
//if
return player;
//findOpponentUser()
private PlayerWait addUser(Player player)
PlayerWait playerWait = null;
String rank = player.getUserRank();
if(!this.users.containsKey(rank))
playerWait = new PlayerWait(player);
this.users.put(rank,playerWait);
return playerWait;
//addUser()
So when a user A requests a match and found no one, user A is put into the concurrent hash map "users" and waits for ten seconds before expiration. While user is in the wait list, request is held onto the server until being expired. When user B requests a match, user A is released from the concurrent hash map and matched against user B. Multiplayer Session is used for holding these two players' data.
Here's my concern:
I believe the timer is a separate thread that runs on the background while request is also being held on the server. In addition, while user is waiting for the match, the
Thread.sleep
is called every 0.1 second. Is there any better way to implement/optimize this? There should be a scalability issue here.I think the overall performance soars as this is not really a real time match making method. Sharing any ideas on how to create matches with better performance would be greatly appreciated.
java performance multithreading thread-safety
add a comment |Â
up vote
3
down vote
favorite
I have working code that needs some optimization for scalability and overall performance. This code basically creates a lobby where users wait for match to be created and link users for a multiplayer game match.
PlayerWait player = GameMatchEngine.getInstance().createMatch(userId);
if (player.getPlayer().getUserId() == userId)
while (player.isAvailable())
Thread.sleep(100);
if (player.getMatchBegan())
if (!hasUser(userId))
.... failed to find a match ....
else
multiplayer = getMultiplayer(userId);
//if
The code above is called when a user request for a match. if (player.getPlayer().getUserId() == userId)
happens when a first user requests a match, and there is no player in the lobby.
private Map<Integer, Multiplayer> users =
new ConcurrentHashMap<Integer, Multiplayer>();
/* removes user who was waiting and retrieves Multiplayer Session */
private boolean hasUser(Integer userId)
return this.users.containsKey(userId);
PlayerWait
class:
private Player player;
private boolean availablility = true;
private boolean matchBegan = true;
Timer timer;
public PlayerWait(Player player)
setPlayer(player);
waitTimeExpired(10);
private void waitTimeExpired(int seconds)
this.timer = new Timer();
timer.schedule(new scheduledTask(), seconds * 1000);
//waitTimeExpired()
class scheduledTask extends TimerTask
@Override
public void run()
synchronized(this)
if(availablility)
setAvailable(false);
if(matchBegan)
GameMatchEngine.getInstance().
matchWithAI(getPlayerWait());
//if
timer.cancel();
//class scheduledTask
public PlayerWait createMatch(int user_id) throws SQLException
String rank = getRank(user_id);
PlayerWait playerFound = findOpponentUser(rank, user_id);
if (playerFound == null)
playerFound = addUser(new Player(user_id, rank));
else
try
.... found an opponent; create a match in db ....
catch (SQLException e)
LOGGER.error("Error in creating a match: ", e);
//try
LOGGER.error("PlayerWait: " + playerFound);
return playerFound;
//createMatch()
private PlayerWait findOpponentUser(String rank, int userId)
boolean found = false;
PlayerWait player = null;
if (hasUser(rank))
player = getUser(rank);
if (player.getPlayer().getUserId() != userId)
synchronized (player)
found = player.isAvailable();
player.setAvailable(false);
//synchronized()
else
found = true;
if (found)
removeUser(rank);
else
player = null;
//if
//if
return player;
//findOpponentUser()
private PlayerWait addUser(Player player)
PlayerWait playerWait = null;
String rank = player.getUserRank();
if(!this.users.containsKey(rank))
playerWait = new PlayerWait(player);
this.users.put(rank,playerWait);
return playerWait;
//addUser()
So when a user A requests a match and found no one, user A is put into the concurrent hash map "users" and waits for ten seconds before expiration. While user is in the wait list, request is held onto the server until being expired. When user B requests a match, user A is released from the concurrent hash map and matched against user B. Multiplayer Session is used for holding these two players' data.
Here's my concern:
I believe the timer is a separate thread that runs on the background while request is also being held on the server. In addition, while user is waiting for the match, the
Thread.sleep
is called every 0.1 second. Is there any better way to implement/optimize this? There should be a scalability issue here.I think the overall performance soars as this is not really a real time match making method. Sharing any ideas on how to create matches with better performance would be greatly appreciated.
java performance multithreading thread-safety
add a comment |Â
up vote
3
down vote
favorite
up vote
3
down vote
favorite
I have working code that needs some optimization for scalability and overall performance. This code basically creates a lobby where users wait for match to be created and link users for a multiplayer game match.
PlayerWait player = GameMatchEngine.getInstance().createMatch(userId);
if (player.getPlayer().getUserId() == userId)
while (player.isAvailable())
Thread.sleep(100);
if (player.getMatchBegan())
if (!hasUser(userId))
.... failed to find a match ....
else
multiplayer = getMultiplayer(userId);
//if
The code above is called when a user request for a match. if (player.getPlayer().getUserId() == userId)
happens when a first user requests a match, and there is no player in the lobby.
private Map<Integer, Multiplayer> users =
new ConcurrentHashMap<Integer, Multiplayer>();
/* removes user who was waiting and retrieves Multiplayer Session */
private boolean hasUser(Integer userId)
return this.users.containsKey(userId);
PlayerWait
class:
private Player player;
private boolean availablility = true;
private boolean matchBegan = true;
Timer timer;
public PlayerWait(Player player)
setPlayer(player);
waitTimeExpired(10);
private void waitTimeExpired(int seconds)
this.timer = new Timer();
timer.schedule(new scheduledTask(), seconds * 1000);
//waitTimeExpired()
class scheduledTask extends TimerTask
@Override
public void run()
synchronized(this)
if(availablility)
setAvailable(false);
if(matchBegan)
GameMatchEngine.getInstance().
matchWithAI(getPlayerWait());
//if
timer.cancel();
//class scheduledTask
public PlayerWait createMatch(int user_id) throws SQLException
String rank = getRank(user_id);
PlayerWait playerFound = findOpponentUser(rank, user_id);
if (playerFound == null)
playerFound = addUser(new Player(user_id, rank));
else
try
.... found an opponent; create a match in db ....
catch (SQLException e)
LOGGER.error("Error in creating a match: ", e);
//try
LOGGER.error("PlayerWait: " + playerFound);
return playerFound;
//createMatch()
private PlayerWait findOpponentUser(String rank, int userId)
boolean found = false;
PlayerWait player = null;
if (hasUser(rank))
player = getUser(rank);
if (player.getPlayer().getUserId() != userId)
synchronized (player)
found = player.isAvailable();
player.setAvailable(false);
//synchronized()
else
found = true;
if (found)
removeUser(rank);
else
player = null;
//if
//if
return player;
//findOpponentUser()
private PlayerWait addUser(Player player)
PlayerWait playerWait = null;
String rank = player.getUserRank();
if(!this.users.containsKey(rank))
playerWait = new PlayerWait(player);
this.users.put(rank,playerWait);
return playerWait;
//addUser()
So when a user A requests a match and found no one, user A is put into the concurrent hash map "users" and waits for ten seconds before expiration. While user is in the wait list, request is held onto the server until being expired. When user B requests a match, user A is released from the concurrent hash map and matched against user B. Multiplayer Session is used for holding these two players' data.
Here's my concern:
I believe the timer is a separate thread that runs on the background while request is also being held on the server. In addition, while user is waiting for the match, the
Thread.sleep
is called every 0.1 second. Is there any better way to implement/optimize this? There should be a scalability issue here.I think the overall performance soars as this is not really a real time match making method. Sharing any ideas on how to create matches with better performance would be greatly appreciated.
java performance multithreading thread-safety
I have working code that needs some optimization for scalability and overall performance. This code basically creates a lobby where users wait for match to be created and link users for a multiplayer game match.
PlayerWait player = GameMatchEngine.getInstance().createMatch(userId);
if (player.getPlayer().getUserId() == userId)
while (player.isAvailable())
Thread.sleep(100);
if (player.getMatchBegan())
if (!hasUser(userId))
.... failed to find a match ....
else
multiplayer = getMultiplayer(userId);
//if
The code above is called when a user request for a match. if (player.getPlayer().getUserId() == userId)
happens when a first user requests a match, and there is no player in the lobby.
private Map<Integer, Multiplayer> users =
new ConcurrentHashMap<Integer, Multiplayer>();
/* removes user who was waiting and retrieves Multiplayer Session */
private boolean hasUser(Integer userId)
return this.users.containsKey(userId);
PlayerWait
class:
private Player player;
private boolean availablility = true;
private boolean matchBegan = true;
Timer timer;
public PlayerWait(Player player)
setPlayer(player);
waitTimeExpired(10);
private void waitTimeExpired(int seconds)
this.timer = new Timer();
timer.schedule(new scheduledTask(), seconds * 1000);
//waitTimeExpired()
class scheduledTask extends TimerTask
@Override
public void run()
synchronized(this)
if(availablility)
setAvailable(false);
if(matchBegan)
GameMatchEngine.getInstance().
matchWithAI(getPlayerWait());
//if
timer.cancel();
//class scheduledTask
public PlayerWait createMatch(int user_id) throws SQLException
String rank = getRank(user_id);
PlayerWait playerFound = findOpponentUser(rank, user_id);
if (playerFound == null)
playerFound = addUser(new Player(user_id, rank));
else
try
.... found an opponent; create a match in db ....
catch (SQLException e)
LOGGER.error("Error in creating a match: ", e);
//try
LOGGER.error("PlayerWait: " + playerFound);
return playerFound;
//createMatch()
private PlayerWait findOpponentUser(String rank, int userId)
boolean found = false;
PlayerWait player = null;
if (hasUser(rank))
player = getUser(rank);
if (player.getPlayer().getUserId() != userId)
synchronized (player)
found = player.isAvailable();
player.setAvailable(false);
//synchronized()
else
found = true;
if (found)
removeUser(rank);
else
player = null;
//if
//if
return player;
//findOpponentUser()
private PlayerWait addUser(Player player)
PlayerWait playerWait = null;
String rank = player.getUserRank();
if(!this.users.containsKey(rank))
playerWait = new PlayerWait(player);
this.users.put(rank,playerWait);
return playerWait;
//addUser()
So when a user A requests a match and found no one, user A is put into the concurrent hash map "users" and waits for ten seconds before expiration. While user is in the wait list, request is held onto the server until being expired. When user B requests a match, user A is released from the concurrent hash map and matched against user B. Multiplayer Session is used for holding these two players' data.
Here's my concern:
I believe the timer is a separate thread that runs on the background while request is also being held on the server. In addition, while user is waiting for the match, the
Thread.sleep
is called every 0.1 second. Is there any better way to implement/optimize this? There should be a scalability issue here.I think the overall performance soars as this is not really a real time match making method. Sharing any ideas on how to create matches with better performance would be greatly appreciated.
java performance multithreading thread-safety
edited May 22 at 23:03
Jamalâ¦
30.1k11114225
30.1k11114225
asked May 22 at 22:59
John
161
161
add a comment |Â
add a comment |Â
1 Answer
1
active
oldest
votes
up vote
1
down vote
I believe what you describe fits the producer-consumer pattern: A player who wishes to be matched is a consumer of another player from the lobby. when players join the lobby, it should produce them to consumers. Therefore I think a better data structure for your requirements is BlockingQueue
. The most advantageous feature is the blocking of consumers. this eliminates the need for the timer. you can have consumer wait forever for an item (another player) or have it timeout after a certain time. regarding rank, you can still use a Map
with rank as key, and the queue as value. which brings up the question: in its current definition, the Map
can hold only one game per rank?
However, as long as the solution is confined within one JVM, you will have a scalability concern. To fix that, you will need to externalize the queue and use a messaging system (like RabbitMQ) and then rank becomes a topic where consumer register for. This is a much more complex solution but it has the potential to grow to social-network scale.
One more comment regarding readability:
if (player.getPlayer().getUserId() == userId)
happens when a first
user requests a match, and there is no player in the lobby.
First of all, I do not understand how this may happen, since in findOpponentUser()
you ask on unequality of user ids. anyway, at the very least, you should make a method like isOnlyPlayerInLobby()
.
First of all, great thanks to your comment. They seem to be great answers. Yes, it is producer-consumer pattern. How would you go about implementing this BlockingQueue instead PlayerWait class that I use? I am not really familiar with BlockingQueue, so before I dive into it, I just want to have a general idea of how to approach this. Can you elaborate more on the use of external messaging system? Every user has rank and I retrieve it from the database. What difference would this RabbitMQ make? How would I approach about using this? For the readability part, I totally agree with you on this.
â John
May 23 at 14:19
add a comment |Â
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
1
down vote
I believe what you describe fits the producer-consumer pattern: A player who wishes to be matched is a consumer of another player from the lobby. when players join the lobby, it should produce them to consumers. Therefore I think a better data structure for your requirements is BlockingQueue
. The most advantageous feature is the blocking of consumers. this eliminates the need for the timer. you can have consumer wait forever for an item (another player) or have it timeout after a certain time. regarding rank, you can still use a Map
with rank as key, and the queue as value. which brings up the question: in its current definition, the Map
can hold only one game per rank?
However, as long as the solution is confined within one JVM, you will have a scalability concern. To fix that, you will need to externalize the queue and use a messaging system (like RabbitMQ) and then rank becomes a topic where consumer register for. This is a much more complex solution but it has the potential to grow to social-network scale.
One more comment regarding readability:
if (player.getPlayer().getUserId() == userId)
happens when a first
user requests a match, and there is no player in the lobby.
First of all, I do not understand how this may happen, since in findOpponentUser()
you ask on unequality of user ids. anyway, at the very least, you should make a method like isOnlyPlayerInLobby()
.
First of all, great thanks to your comment. They seem to be great answers. Yes, it is producer-consumer pattern. How would you go about implementing this BlockingQueue instead PlayerWait class that I use? I am not really familiar with BlockingQueue, so before I dive into it, I just want to have a general idea of how to approach this. Can you elaborate more on the use of external messaging system? Every user has rank and I retrieve it from the database. What difference would this RabbitMQ make? How would I approach about using this? For the readability part, I totally agree with you on this.
â John
May 23 at 14:19
add a comment |Â
up vote
1
down vote
I believe what you describe fits the producer-consumer pattern: A player who wishes to be matched is a consumer of another player from the lobby. when players join the lobby, it should produce them to consumers. Therefore I think a better data structure for your requirements is BlockingQueue
. The most advantageous feature is the blocking of consumers. this eliminates the need for the timer. you can have consumer wait forever for an item (another player) or have it timeout after a certain time. regarding rank, you can still use a Map
with rank as key, and the queue as value. which brings up the question: in its current definition, the Map
can hold only one game per rank?
However, as long as the solution is confined within one JVM, you will have a scalability concern. To fix that, you will need to externalize the queue and use a messaging system (like RabbitMQ) and then rank becomes a topic where consumer register for. This is a much more complex solution but it has the potential to grow to social-network scale.
One more comment regarding readability:
if (player.getPlayer().getUserId() == userId)
happens when a first
user requests a match, and there is no player in the lobby.
First of all, I do not understand how this may happen, since in findOpponentUser()
you ask on unequality of user ids. anyway, at the very least, you should make a method like isOnlyPlayerInLobby()
.
First of all, great thanks to your comment. They seem to be great answers. Yes, it is producer-consumer pattern. How would you go about implementing this BlockingQueue instead PlayerWait class that I use? I am not really familiar with BlockingQueue, so before I dive into it, I just want to have a general idea of how to approach this. Can you elaborate more on the use of external messaging system? Every user has rank and I retrieve it from the database. What difference would this RabbitMQ make? How would I approach about using this? For the readability part, I totally agree with you on this.
â John
May 23 at 14:19
add a comment |Â
up vote
1
down vote
up vote
1
down vote
I believe what you describe fits the producer-consumer pattern: A player who wishes to be matched is a consumer of another player from the lobby. when players join the lobby, it should produce them to consumers. Therefore I think a better data structure for your requirements is BlockingQueue
. The most advantageous feature is the blocking of consumers. this eliminates the need for the timer. you can have consumer wait forever for an item (another player) or have it timeout after a certain time. regarding rank, you can still use a Map
with rank as key, and the queue as value. which brings up the question: in its current definition, the Map
can hold only one game per rank?
However, as long as the solution is confined within one JVM, you will have a scalability concern. To fix that, you will need to externalize the queue and use a messaging system (like RabbitMQ) and then rank becomes a topic where consumer register for. This is a much more complex solution but it has the potential to grow to social-network scale.
One more comment regarding readability:
if (player.getPlayer().getUserId() == userId)
happens when a first
user requests a match, and there is no player in the lobby.
First of all, I do not understand how this may happen, since in findOpponentUser()
you ask on unequality of user ids. anyway, at the very least, you should make a method like isOnlyPlayerInLobby()
.
I believe what you describe fits the producer-consumer pattern: A player who wishes to be matched is a consumer of another player from the lobby. when players join the lobby, it should produce them to consumers. Therefore I think a better data structure for your requirements is BlockingQueue
. The most advantageous feature is the blocking of consumers. this eliminates the need for the timer. you can have consumer wait forever for an item (another player) or have it timeout after a certain time. regarding rank, you can still use a Map
with rank as key, and the queue as value. which brings up the question: in its current definition, the Map
can hold only one game per rank?
However, as long as the solution is confined within one JVM, you will have a scalability concern. To fix that, you will need to externalize the queue and use a messaging system (like RabbitMQ) and then rank becomes a topic where consumer register for. This is a much more complex solution but it has the potential to grow to social-network scale.
One more comment regarding readability:
if (player.getPlayer().getUserId() == userId)
happens when a first
user requests a match, and there is no player in the lobby.
First of all, I do not understand how this may happen, since in findOpponentUser()
you ask on unequality of user ids. anyway, at the very least, you should make a method like isOnlyPlayerInLobby()
.
answered May 23 at 6:44
Sharon Ben Asher
2,038512
2,038512
First of all, great thanks to your comment. They seem to be great answers. Yes, it is producer-consumer pattern. How would you go about implementing this BlockingQueue instead PlayerWait class that I use? I am not really familiar with BlockingQueue, so before I dive into it, I just want to have a general idea of how to approach this. Can you elaborate more on the use of external messaging system? Every user has rank and I retrieve it from the database. What difference would this RabbitMQ make? How would I approach about using this? For the readability part, I totally agree with you on this.
â John
May 23 at 14:19
add a comment |Â
First of all, great thanks to your comment. They seem to be great answers. Yes, it is producer-consumer pattern. How would you go about implementing this BlockingQueue instead PlayerWait class that I use? I am not really familiar with BlockingQueue, so before I dive into it, I just want to have a general idea of how to approach this. Can you elaborate more on the use of external messaging system? Every user has rank and I retrieve it from the database. What difference would this RabbitMQ make? How would I approach about using this? For the readability part, I totally agree with you on this.
â John
May 23 at 14:19
First of all, great thanks to your comment. They seem to be great answers. Yes, it is producer-consumer pattern. How would you go about implementing this BlockingQueue instead PlayerWait class that I use? I am not really familiar with BlockingQueue, so before I dive into it, I just want to have a general idea of how to approach this. Can you elaborate more on the use of external messaging system? Every user has rank and I retrieve it from the database. What difference would this RabbitMQ make? How would I approach about using this? For the readability part, I totally agree with you on this.
â John
May 23 at 14:19
First of all, great thanks to your comment. They seem to be great answers. Yes, it is producer-consumer pattern. How would you go about implementing this BlockingQueue instead PlayerWait class that I use? I am not really familiar with BlockingQueue, so before I dive into it, I just want to have a general idea of how to approach this. Can you elaborate more on the use of external messaging system? Every user has rank and I retrieve it from the database. What difference would this RabbitMQ make? How would I approach about using this? For the readability part, I totally agree with you on this.
â John
May 23 at 14:19
add a comment |Â
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f194978%2fmatchmaking-for-a-multiplayer-game%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password