每个游戏周期,服务器希望使用
sendGameWorldSections()
方法向每个连接的客户端发送一条消息。它在一个新线程中执行此操作,以便游戏代码保持运行并且不会阻塞线程。当调用静态方法 Server.sendMessageToPlayer(playerID,gameWorldSectionString);
时,该方法旨在获取 Socket
的 HashMap<String,Socket> playerIDToSocket
,然后获取其 OutputStream
,并将消息发送到该客户端。 playerIDToSocket
映射将 playerID
(作为字符串)映射到 Socket
。它存储playerID和Socket的方式是:服务器接受客户端的连接,客户端单独发送一条消息作为JSONObject
,只说明刚刚连接的玩家的PlayerID,然后服务器将其存储起来,使用 Socket
进入哈希映射。客户端应该将收到的每条消息打印到控制台上。当服务器将消息发送到客户端(游戏世界部分)时,客户端永远不会收到该消息。但有时,当我停止客户端程序时,它只会批量打印自连接以来收到的每一条消息。是不是某个地方堵住了?我似乎无法解决这个问题。
GameTicker 类:
package GameEngine;
import GameEngine.Game.PlayerManager;
import GameEngine.Game.World;
import GameEngine.Network.Server;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
public class GameTicker extends Thread{
public final long GAME_TICK = 2000000000; // The smallest unit of time in the game. (0.2s)
public int ticksPassed = 0;
/**
* This is the beating heart of the game engine. It controls the game state, receives inputs, and informs the outputter
* to send stuff to clients.
*
* Takes inputs of the current tick and then processes them (same thread)
*
* After each tick end, send messages to all clients informing them of the new sections.
*
*
*/
@Override
public void run() {
long gameStartTime = System.nanoTime();
long lastTickEnd = System.nanoTime();
while(true){
if(System.nanoTime() - lastTickEnd >= GAME_TICK){
//GameStateManager.performPlayerActions();
//GameStateManager.performGameWorldActions(); //
new Thread(new Runnable() {
@Override
public void run() {
try {
sendGameWorldSections();
} catch (JSONException e) {
throw new RuntimeException(e);
}
}
}).start();
lastTickEnd = System.nanoTime();
ticksPassed++;
System.out.println(">>GAME TICKER: Tick passed. #" + ticksPassed );
}
}
}
/**
* Sends sections of the game to each player that is logged in.
* @throws JSONException
*/
public void sendGameWorldSections() throws JSONException {
System.out.println("Sending game world sections..");
for(int i = 0; i < PlayerManager.loggedInPlayerIDs.size(); i++){
String playerID = PlayerManager.loggedInPlayerIDs.get(i);
int xP = PlayerManager.players.get(playerID).getX(); // Position x of player
int yP = PlayerManager.players.get(playerID).getY(); // Position y of player
int zP = PlayerManager.players.get(playerID).getZ();
long start = System.currentTimeMillis();
JSONObject gameWorldSectionJSON = null;
try {
gameWorldSectionJSON = World.getGameWorldSection(zP,xP,yP);
} catch (JSONException e) {
throw new RuntimeException(e);
}
System.out.println(System.currentTimeMillis() - start + "MS to get JSON of section.");
try {
gameWorldSectionJSON.put("RESULT","GAME_WORLD_SECTION");
} catch (JSONException e) {
throw new RuntimeException(e);
}
String gameWorldSectionString = gameWorldSectionJSON.toString();
try {
Server.sendMessageToPlayer(playerID,gameWorldSectionString);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
服务器类:
package GameEngine.Network;
import GameEngine.Game.Player;
import GameEngine.Game.PlayerManager;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.*;
import java.net.*;
import java.util.ArrayList;
import java.util.HashMap;
// Server class
public class Server extends Thread{
/**
* We must have:
* - Player ID -> InetAddress
*/
/**
* STEP 1: Client connects to the server.
* STEP 2: Client sends its user ID. The server then adds its inetaddress to the list. This is called Logging in.
*
* STEP 3..n:
* - Client makes requests to the server, which the ClientHandler handles the message then replies
* - Server can send message to a client or multiple clients.
*/
public static HashMap<String, Socket> playerIDToSocket = new HashMap<>(); // THESE ARE LOGGED IN.
public void run()
{
listenForConnections();
}
private void listenForConnections(){
ServerSocket server = null;
try {
server = new ServerSocket(1234);
server.setReuseAddress(true);
while (true) {
Thread.sleep(1);
Socket client = server.accept();
System.out.println("New client connected"
+ client.getInetAddress()
.getHostAddress());
ClientHandler connectedClient = new ClientHandler(client);
connectedClient.start();
}
}
catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
// public void sendMessagesToPlayers() throws InterruptedException, IOException {
// while(true){
// Thread.sleep(1);
// for(int i = 0; i < messagesToPlayers.size(); i++){
// ClientMessage clientMessage = messagesToPlayers.get(i);
// PrintWriter out = new PrintWriter(clientMessage.getSocket().getOutputStream(), true);
// out.print(clientMessage.getMessage());
// out.flush();
// }
// }
// }
public static void sendMessageToPlayer(String playerID, String message) throws IOException {
System.out.println("Sending message..");
Socket socket = playerIDToSocket.get(playerID);
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
out.print(message);
}
private class ClientHandler extends Thread {
private final Socket clientSocket;
// Constructor
public ClientHandler(Socket socket)
{
this.clientSocket = socket;
}
public void run()
{
try {
PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
String line;
while(true) {
Thread.sleep(1);
while ((line = in.readLine()) != null) {
// writing the received message from
// client
System.out.printf(" Sent from client "+ clientSocket +": %s\n", line);
JSONObject request = new JSONObject(line);
switch (request.getString("COMMAND")) {
case "SEND_SOCKET_DATA":
String playerID = request.getString("PLAYER_ID");
playerIDToSocket.put(playerID, clientSocket);
PlayerManager.loggedInPlayerIDs.add(playerID);
break;
}
out.println("You are now logged in.");
}
}
}
catch (IOException e) {
e.printStackTrace();
} catch (JSONException e) {
throw new RuntimeException(e);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
客户端类:
package Network;
import Game.Entities.Doorway;
import Game.Entities.EmptyBlock;
import Game.Entities.GraphicEntity;
import Game.GameWorldSection;
import Game.Player;
import Game.Tile;
import Graphics.SpriteData;
import jdk.jshell.execution.Util;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import javax.swing.text.Utilities;
import java.io.*;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Scanner;
public class Client extends Thread{
// driver code
private static Socket socket;
public static boolean isJSONValid(String test) {
try {
new JSONObject(test);
} catch (JSONException ex) {
// edited, to include @Arthur's comment
// e.g. in case JSONArray is valid as well...
try {
new JSONArray(test);
} catch (JSONException ex1) {
return false;
}
}
return true;
}
public void listen(BufferedReader in){
// Listening for server messages
try {
String message = in.readLine();
System.out.println("[SERVER]: "
+message);
if(isJSONValid(message)){
JSONObject json = new JSONObject(message);
switch(json.getString("RESULT")) {
case "GAME_WORLD_SECTION":
// Clear the tiles
GameWorldSection.clearTiles();
Tile[][] tiles = GameWorldSection.getTiles();
JSONObject coordinates = json.getJSONObject("COORDINATES");
Iterator<String> coordinatesIterator = coordinates.keys();
while (coordinatesIterator.hasNext()) {
String coordinateKey = coordinatesIterator.next();
JSONObject coordinateJSON = coordinates.getJSONObject(coordinateKey);
int real_x = Integer.parseInt(coordinateKey.split(",")[0]);
int real_y = Integer.parseInt(coordinateKey.split(",")[1]);
int painted_x = real_x - json.getInt("LOWEST_X");
int painted_y = real_y - json.getInt("LOWEST_Y");
JSONArray entities = coordinateJSON.getJSONArray("ENTITIES");
for (int i = 0; i < entities.length(); i++) {
JSONObject playerObj = entities.getJSONObject(i);
switch (playerObj.getString("NAME")) {
case "EMPTY_BLOCK":
tiles[painted_x][painted_y].addEntity(new EmptyBlock());
break;
case "TUTORIAL_FLOOR":
tiles[painted_x][painted_y].addEntity(new GraphicEntity(SpriteData.SPRITE_NAME.TUTORIAL_GROUND));
break;
case "DOORWAY":
tiles[painted_x][painted_y].addEntity(new Doorway(SpriteData.SPRITE_NAME.DOORWAY));
break;
case "PLAYER":
SpriteData.SPRITE_NAME spriteName = null;
boolean isMoving = playerObj.getBoolean("IS_MOVING");
switch (playerObj.getString("DIRECTION_FACING")) {
case "NORTH":
if (isMoving) {
spriteName = (SpriteData.SPRITE_NAME.CHARACTER_WALKING_NORTH);
} else {
spriteName = (SpriteData.SPRITE_NAME.CHARACTER_FACING_NORTH);
}
break;
case "WEST":
if (isMoving) {
spriteName = (SpriteData.SPRITE_NAME.CHARACTER_WALKING_WEST);
} else {
spriteName = (SpriteData.SPRITE_NAME.CHARACTER_FACING_WEST);
}
break;
case "SOUTH":
if (isMoving) {
spriteName = (SpriteData.SPRITE_NAME.CHARACTER_WALKING_SOUTH);
} else {
spriteName = (SpriteData.SPRITE_NAME.CHARACTER_FACING_SOUTH);
}
break;
case "EAST":
if (isMoving) {
spriteName = (SpriteData.SPRITE_NAME.CHARACTER_WALKING_EAST);
} else {
spriteName = (SpriteData.SPRITE_NAME.CHARACTER_FACING_EAST);
}
break;
}
tiles[painted_x][painted_y].addEntity(new Player(spriteName));
break;
}
}
}
}
}
else{
//
}
} catch (IOException e) {
throw new RuntimeException(e);
} catch (JSONException e) {
throw new RuntimeException(e);
}
}
public void run()
{
// establish a connection by providing host and port
// number
try {
socket = new Socket("localhost", 1234);
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
// reading from server
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
JSONObject clientData = new JSONObject();
clientData.put("COMMAND","SEND_SOCKET_DATA");
clientData.put("PLAYER_ID","123");
out.println(clientData.toString());
out.flush();
while(true) {
listen(in);
}
} catch (IOException e) {
throw new RuntimeException(e);
}catch(JSONException e) {
}
// writing to server
}
}
在此方法中:您正在使用 out.print (message) ,但这不会刷新数据,因为当您使用“true”初始化它时,刷新仅发生在 PrintWriter 中的 printf、format 或 println 上。
您应该添加“out.flush();”就在out.print之后。
public static void sendMessageToPlayer(String playerID, String message) throws IOException {
System.out.println("Sending message..");
Socket socket = playerIDToSocket.get(playerID);
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
out.print(message);
out.flush();
}