package j6;
// 「海ゲーム」サーバプログラム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.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Random;
import java.util.StringTokenizer;
public class SeaGameServer {
static final int DEFAULT_PORT = 9999;
//SeaGameServer接続用ポート番号
static ServerSocket serverSocket;
static ArrayList<ClientProc> clientprocs;
//クライアントとのコネクションを保持するVectorオブジェクト
static ArrayList<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 synchronized void calculation(){
if (userTable != null && energy_v != null){
// すべてのクライアントについて判定します
for (String user : userTable.keySet()) {
// 判定するクライアントの名前と船の位置を取り出します
Ship ship = (Ship) userTable.get(user);
// 燃料タンクすべてについて,船との位置関係を調べます
ArrayList<int[]> toberemoved = new ArrayList<int[]>();
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) {
toberemoved.add(e);
ship.point++;
}
}
for(int[] rme : toberemoved) {
energy_v.remove(rme);
}
}
}
}
//---------------------------------------------------------------------
// 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 ArrayList<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.add(e);
} // end of putEnergy
//---------------------------------------------------------------------
// サーバソケットの作成とクライアント接続の処理
// および適当なタイミングでの燃料タンクの逐次追加処理
public static void main(String[] arg){
try {// サーバソケットの作成
serverSocket = new ServerSocket();
serverSocket.bind(new InetSocketAddress("0.0.0.0", 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ミリ秒(=10秒)休止させます
}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;
try {
while ((line = in.readLine()) != null) {
// クライアントからの入力を読み取ります
// 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が空でない場合はログイン済み.コマンドを受け付ける.
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;
}
}
}
} 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