Newer
Older
NWP / src / j7 / SeaGameServer.java
@Motoki Miura Motoki Miura on 22 Aug 2020 11 KB run7-2 client
package j7;

// 「海ゲーム」サーバプログラムSeaGameServer.java
// このプログラムは,海ゲームのサーバプログラムです
// 使い方java SeaGameServer
// 起動すると,ポート番号10000 番に対するクライアントからの接続を待ちます
// プログラムを停止するにはコントロールC を入力してください
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Random;
import java.util.StringTokenizer;
import java.util.Vector;

public class SeaGameServer {
	static final int DEFAULT_PORT = 9999;
	//SeaGameServer接続用ポート番号
	static ServerSocket serverSocket;
	static ArrayList<ClientProc> clientprocs;
	//クライアントとのコネクションを保持するVectorオブジェクト
	static Vector<int[]> energy_v; // 燃料タンクの位置情報リスト
	static Hashtable<String,Ship> userTable = null;
	// クライアント関連情報登録用テーブル
	static Random random = null;

	// addConnectionメソッド
	// クライアントとの接続をVectorオブジェクトconnectionsに登録します
	public static void addConnection(ClientProc s){
		if (clientprocs == null){//初めてのコネクションの場合は,
			clientprocs = new ArrayList<ClientProc>();// connectionsを作成します
		}
		clientprocs.add(s);
	}

	// deleteConnectionメソッド
	// クライアントとの接続をconnectionsから削除します
	public static void deleteConnection(ClientProc s){
		if (clientprocs != null){
			clientprocs.remove(s);
		}
	}

	// loginUserメソッド
	// loginコマンドの処理として,利用者の名前や船の位置を登録します
	public static void loginUser(String name){
		if (userTable == null){// 登録用テーブルがなければ作成します
			userTable = new Hashtable<String,Ship>();
		}
		if (random == null){// 乱数の準備をします
			random = new Random();
		}
		// 船の初期位置を乱数で決定します
		int ix = Math.abs(random.nextInt()) % 256;
		int iy = Math.abs(random.nextInt()) % 256;

		// クライアントの名前や船の位置を表に登録します
		userTable.put(name, new Ship(ix, iy));
		// サーバ側の画面にもクライアントの名前を表示します
		System.out.println("login:" + name);
		System.out.flush();
	}

	// logoutUserメソッド
	// クライアントのログアウトを処理します
	public static void logoutUser(String name){
		// サーバ側画面にログアウトするクライアントの名前を表示します
		System.out.println("logout:" + name);
		System.out.flush();
		// 登録用テーブルから項目を削除します
		userTable.remove(name);
	}

	// leftメソッド
	// ある特定の船を左に動かして,燃料タンクが拾えるかどうか判定します
	// 判定にはcalculationメソッドを使います
	public static void left(String name){
		Ship ship = (Ship) userTable.get(name);
		ship.left();
		calculation();
	}

	// rightメソッド
	// ある特定の船を右に動かして,燃料タンクが拾えるかどうか判定します
	// 判定にはcalculationメソッドを使います
	public static void right(String name){
		Ship ship = (Ship) userTable.get(name);
		ship.right();
		calculation();
	}

	// upメソッド
	// ある特定の船を上に動かして,燃料タンクが拾えるかどうか判定します
	// 判定にはcalculationメソッドを使います
	public static void up(String name){
		Ship ship = (Ship) userTable.get(name);
		ship.up();
		calculation();
	}

	// downメソッド
	// ある特定の船を下に動かして,燃料タンクが拾えるかどうか判定します
	// 判定にはcalculationメソッドを使います
	public static void down(String name){
		Ship ship = (Ship) userTable.get(name);
		ship.down();
		calculation();
	}

	// calculationメソッド
	// 燃料タンクと船の位置関係を調べて,燃料タンクが拾えるかどうか判定します
	static void calculation(){
		if (userTable != null && energy_v != null){
			// すべてのクライアントについて判定します
			for (String user : userTable.keySet()) {
				// 判定するクライアントの名前と船の位置を取り出します
				Ship ship = (Ship) userTable.get(user);
				// 燃料タンクすべてについて,船との位置関係を調べます
				for (int[] e : energy_v) {
					// 燃料タンクの位置と船の位置を調べ,距離を計算します
					int x = e[0] - ship.x;
					int y = e[1] - ship.y;
					double r = Math.sqrt(x * x + y * y);
					// 距離"10"以内なら燃料タンクを取り込みます
					if (r < 10) {
						energy_v.removeElement(e);
						ship.point++;
					}
				}
			}
		}
	}
	//---------------------------------------------------------------------
	// STATコマンドを処理
	// クライアントに船の情報(ship_info)と,
	// 海上を漂流している燃料タンクの情報を(energy_info)を送信
	public static void statInfo(DataOutputStream pw) throws IOException{
		// 船の情報(ship_info)の送信
		pw.writeBytes("ship_info\n");
		if (userTable != null){
			for (String user : userTable.keySet()) {
				Ship ship = (Ship) userTable.get(user);
				pw.writeBytes(user + " " + ship.x + " "
						+ ship.y + " " + ship.point+"\n");
			}
		}
		pw.writeBytes(".\n");// ship_infoの終了
		// 燃料タンクの情報(energy_info)の送信
		pw.writeBytes("energy_info\n");
		if (energy_v != null){
			// すべての燃料タンクの位置情報をクライアントに送信します
			for (int[] e : energy_v) {
				pw.writeBytes(e[0] + " " + e[1] + "\n");
			}
		}
		pw.writeBytes(".\n");// enegy_infoの終了
		pw.flush();
	}
	//---------------------------------------------------------------------
	// 燃料タンクを1つだけ海上にランダムに配置
	public static void putEnergy(){
		if (energy_v == null){// 初めて配置する場合の処理
			energy_v = new Vector<int[]>();
		}
		if (random == null){// 初めて乱数を使う場合の処理
			random = new Random();
		}
		// 乱数で位置を決めて海上に配置します
		int[] e = new int[2];
		e[0] = Math.abs(random.nextInt()) % 256;
		e[1] = Math.abs(random.nextInt()) % 256;

		energy_v.addElement(e);
	} // end of putEnergy
	//---------------------------------------------------------------------
	// サーバソケットの作成とクライアント接続の処理
	// および適当なタイミングでの燃料タンクの逐次追加処理
	public static void main(String[] arg){
		try {// サーバソケットの作成
			serverSocket = new ServerSocket(DEFAULT_PORT);
		}catch (IOException e){
			System.err.println("can't create server socket.");
			System.exit(1);
		}
		// 燃料タンクを順に追加するスレッドetを作ります
		Thread et = new Thread(){
			public void run(){
				while(true){
					try {
						sleep(10000);// スレッドetを10000ミリ秒休止させます
					}catch(InterruptedException e){
						break;
					}
					// 海上に1つ燃料タンクを配置します
					SeaGameServer.putEnergy();
				}
			}
		};
		// etをスタートします
		et.start();
		// ソケットの受付と,クライアント処理プログラムの開始処理を行います
		while (true) {// 無限ループ
			try {
				Socket cs = serverSocket.accept();
				// クライアント処理スレッドを作成します
				ClientProc cp = new ClientProc(cs);
				addConnection(cp);// ClientProcを登録します
			}catch (IOException e){
				System.err.println("client socket or accept error.");
			}
		}
	} // end of main
} // end of class putEnergy
//=============================================================================
class ClientProc implements Runnable {
	Thread thread;
	Socket s; 		// クライアント接続用ソケット
	BufferedReader in;	// 入力ストリーム
	DataOutputStream out;	// 出力ストリーム
	String name = null;	// クライアントの名前

	//--------------------------------------------------------------------
	// ソケットを使って入出力ストリームを作成します
	public ClientProc(Socket s) throws IOException {
		this.s = s;
		in = new BufferedReader(new InputStreamReader(s.getInputStream()));
		out = new DataOutputStream(s.getOutputStream());
		thread = new Thread(this);
		thread.start();
	}
	//--------------------------------------------------------------------
	public void run(){
		//LOGOUTコマンド受信まで繰り返します
//		while(thread != null) {
			String line;
			System.out.println("running " + name);
			try {
				while ((line = in.readLine()) != null) {
					// クライアントからの入力を読み取ります
					System.out.println(line);
					// nameが空の場合にはLOGINコマンドのみを受け付けます
					if (name == null){
						StringTokenizer st = new StringTokenizer(line);
						String cmd = st.nextToken();
						if ("login".equalsIgnoreCase(cmd)){
							name = st.nextToken();
							System.out.println(name);
							SeaGameServer.loginUser(name);
						}else{
							// LOGINコマンド以外は無視
						}
					} else {
						// nameが空でない場合はログイン済み.コマンドを受け付ける.
						System.out.println("command: "+line);
						StringTokenizer st = new StringTokenizer(line);
						String cmd = st.nextToken();// コマンドの取り出し
						// コマンドに対応する処理
						if ("STAT".equalsIgnoreCase(cmd)){
							SeaGameServer.statInfo(out);
						} else if ("UP".equalsIgnoreCase(cmd)){
							SeaGameServer.up(name);
						} else if ("DOWN".equalsIgnoreCase(cmd)){
							SeaGameServer.down(name);
						} else if ("LEFT".equalsIgnoreCase(cmd)){
							SeaGameServer.left(name);
						} else if ("RIGHT".equalsIgnoreCase(cmd)){
							SeaGameServer.right(name);
						} else if ("LOGOUT".equalsIgnoreCase(cmd)){
							SeaGameServer.logoutUser(name);
							// LOGOUTコマンドの場合には繰り返しを終了
							break;
						}
					}	
					System.out.println("end");
				}
			} catch (IOException e) {
				e.printStackTrace();
			} finally {
				// 登録情報を削除し,接続を切断
				SeaGameServer.deleteConnection(this);
//				s.close();
			}
//		}
	} // end of run
} // enf of class clientProc
//=============================================================================
// 船の位置と,獲得した燃料タンクの数を管理
class Ship {

	int x, y;	// 船の位置座標
	int point = 0;	// 獲得した燃料タンクの個数

	//-------------------------------------------------------------------
	// 初期位置をセットします
	public Ship(int x, int y){
		this.x = x;
		this.y = y;
	}
	//-------------------------------------------------------------------
	// 船を左に動かす
	public void left(){
		x -= 10;
		if (x < 0) x += 256;	// 左の辺は右の辺につながっている
	}
	//-------------------------------------------------------------------
	// 船を右に動かす
	public void right(){
		x += 10;
		x %= 256;		// 右の辺は左の辺につながっている
	}
	//-------------------------------------------------------------------
	// 船を上に動かす
	public void up(){
		y += 10;
		y %= 256;		// 上の辺は下の辺につながっている
	}
	//-------------------------------------------------------------------
	// 船を下に動かす
	public void down(){
		y -= 10;
		if (y < 0) y += 256;	// 下の辺は上の辺につながっている

	}
} // end of class Ship