我在android studios中创建了一个移动风险游戏(客户端),在intellij中创建了一个服务器,它利用java套接字和objectiostreams发送/接收字符串,在客户端完成轮次时更新每个客户端的游戏状态。
我目前的障碍是,当我启动我的应用程序(目前使用的是pixel 2 api r虚拟机)时,似乎创建了两个风险游戏,或者正在为客户端创建两个线程,第一个线程正在发送正确的风险游戏配置(显示在应用程序gui中的配置),然后第二个线程是垃圾。这里是我主要活动的代码:
package com.example.risk;
import androidx.appcompat.app.AppCompatActivity;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Color;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Objects;
import java.util.Random;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.ReentrantLock;
import static java.lang.Thread.sleep;
public class MainActivity extends AppCompatActivity {
RiskGame game; //game instance
Button next; //next button
Button Attack; // attack button
ArrayList<Button> countryButtons; // array list of country buttons
Random rand;
TextView army;
Client client;
@Override
protected void onCreate(Bundle savedInstanceState) {
getSupportActionBar().hide();
super.onCreate(savedInstanceState); //used by android
setContentView(R.layout.activity_main);
army = findViewById(R.id.armyLabel);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); //force landscape mode
rand = new Random(); //random object
countryButtons = new ArrayList<>(); //initialize arrayList
game = new RiskGame(); //new game instance
next = findViewById(R.id.nextButt); //next button
Attack = findViewById(R.id.attackBut); // attack button
int orentation = getResources().getConfiguration().orientation;
if (Configuration.ORIENTATION_LANDSCAPE == orentation) {
for (int i = 0; i < 16; i++) {
String name = "c" + (i + 1);
Button button = findViewById(getResources().getIdentifier(name, "id", getPackageName()));
button.setTag(game.getCountry(i + 1));
countryButtons.add(button);
}
countryButtonSetUp(countryButtons); //sets up button behavior
nextButtonSetup();// sets up next button
Attack.setOnClickListener(v -> { //sets attack button behavior
if (game.getPhase() == 2) {
game = game.attack();
} else if (game.getPhase() == 3) {
game = game.move();
}
updateButtons();
});
for (int i = 0; i < 12; i++) {
next.performClick();
}
} else {
}
}
/**
* Updates a country button's display features
*/
public void updateButtons(){
try {
for (Button button : this.countryButtons) {
Country c = (Country) button.getTag();
button.setTag(game.getCountry(c.getcID()));
button.setText(c.getcID() + ": " + c.getArmiesHeld()); //sets the text to proper number of armies
// System.out.println(c.getPlayerNum() + "Player Num"); // delete
int val = c.getPlayerNum(); //switch statement to set color
switch (val) {
case 1:
button.setBackgroundColor(Color.RED);
break;
case 2:
button.setBackgroundColor(Color.WHITE);
break;
case 3:
button.setBackgroundColor(Color.YELLOW);
break;
case 4:
button.setBackgroundColor(Color.BLUE);
break;
default:
break;
}
if(game.getPhase() == 1){
Attack.setVisibility(View.INVISIBLE);
}else if(game.getPhase() == 2){
Attack.setVisibility(View.VISIBLE);
Attack.setText("Attack");
}else if (game.getPhase() == 3){
Attack.setText("Move");
}
}
army.setText(game.getActivePlayer().getPlaceablearmies() + " ");
}catch (Exception e){
}
//Create the Client object and pass the RiskGame object to it
}
/**
* sets up country button behavior
* @param buttons
*/
public void countryButtonSetUp(ArrayList<Button> buttons){
for(Button button:buttons) { //iterate over each button
Country c = (Country) button.getTag(); //get country data
button.setOnClickListener(v ->{ //set on click behavior
if(game.getPhase() == 1) { //place troop phase of turn
if (game.getActivePlayer().getPlayerNum() == c.getPlayerNum()) { //if active player holds country
Player active = game.getActivePlayer();
if(active.getPlaceablearmies() >0 ) {
this.game = game.addArmy(c.getcID());
button.setTag(game.getCountry(((Country) button.getTag()).getcID())); //update buttons data
active.setPlaceablearmies(active.getPlaceablearmies() -1);
updateButtons(); //update display
}
}
}else if(game.getPhase() ==2){ // attack phase
if(game.getActivePlayer().getPlayerNum() == c.getPlayerNum()){ //player controls country
game.setAttackingCount(c); //sets the country as attacking
}else { //else set to defending
game.setDefendingCount(c);
}
}else if(game.getPhase() == 3){ //move phase
//sets countries that are being moved from and to, if reclicked deselect the country
if(game.getActivePlayer().getPlayerNum() == c.getPlayerNum()){
if(game.getMoveFromcID() == 0){
game.setMoveFromcID(c.getcID());
}else if(game.getMoveTocID() == 0){
game.setMoveTocID(c.getcID());
}else if(game.getMoveTocID() == c.getcID()){
game.setMoveTocID(0);
}else if(game.getMoveFromcID() == c.getcID()){
game.setMoveFromcID(0);
}
}
}
});
// System.out.println(c.getNeighborCIDs() +"");//delete
}
updateButtons(); //update the buttons display
}
public void nextButtonSetup(){//change game phase and player
next.setOnClickListener(v ->{
game.phaseChange();
updateButtons();
});
}
}
这里最相关的代码是调用我的riskgame构造函数的oncreate方法。riskgame构造函数在oncreate方法中只调用一次。以下是我的riskgame类,其中最相关的线程创建位置位于构造函数的第83行:
package com.example.risk;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Random;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class RiskGame implements Serializable {
Player activePlayer;
private int activePlayerID;
int phase;
ArrayList<Player> players;
Country attackingCount;
Country defendingCount;
ArrayList<Country> countries;
Random rand;
int moveFromcID;
int moveTocID;
int aDie1;
int aDie2;
int aDie3;
int dDie1;
int dDie2;
public RiskGame() {
players = new ArrayList<Player>();
for (int i = 1; i < 5; ++i) {
players.add(new Player(i));
}
activePlayer = players.get(0);
phase = 1;
attackingCount = new Country();
defendingCount = new Country();
countries = new ArrayList<>();
rand = new Random();
aDie1 =0;
aDie2 =0;
aDie3 =0;
dDie1 =0;
dDie2 =0;
moveFromcID =0;
moveTocID =0;
ArrayList<int []> neighbors = new ArrayList<int[]>(); // array of neighbor arrays
neighbors.add(new int[]{2});
neighbors.add( new int[]{1, 3, 14, 15});
neighbors.add(new int[]{2, 4, 16});
neighbors.add(new int[]{3, 5, 16});
neighbors.add(new int[]{4, 6, 16});
neighbors.add(new int[]{5, 7, 8});
neighbors.add( new int[]{6, 8});
neighbors.add(new int[]{6, 7, 9, 16, 15});
neighbors.add(new int[]{8, 10});
neighbors.add(new int[]{9, 11, 12});
neighbors.add(new int[]{10});
neighbors.add( new int[]{10, 13});
neighbors.add( new int[]{12, 14});
neighbors.add(new int[]{13, 2});
neighbors.add( new int[]{2, 8, 16});
neighbors.add(new int[]{3, 4, 5, 8, 15});
for(int i = 0; i < neighbors.size(); i++) { //sets up random gameboard
int j = 0;
do {
j = rand.nextInt(4) ;
}while (players.get(j).getHeldCountries().size() >= 4); //randomly selects a player that doesn't have 4 held countries
Country c = new Country(j + 1, neighbors.get(i).length+1 , i + 1, neighbors.get(i)); //makes the country
addCountry(c); //adds to the arraylist
players.get(j).addCounty(c); //gives country to player
}
//Add Client Here
ExecutorService worker = Executors.newSingleThreadExecutor();
Client client = null;
try {
client = new Client(this);
} catch (IOException e) {
e.printStackTrace();
}
Client finalClient = client;
worker.submit(() -> {
while (!Thread.interrupted())
finalClient.start();
});
}
public void addCountry(Country country){
countries.add(country);
}
public Player getActivePlayer() {
return activePlayer;
}
public void setActivePlayer(Player activePlayer) {
this.activePlayer = activePlayer;
}
public ArrayList<Player> getPlayers() {
return players;
}
public void setPlayers(ArrayList<Player> players) {
this.players = players;
}
public RiskGame nextPlayer(){
if(activePlayer.getPlayerNum() == 4){
activePlayer = players.get(0);
}else {
activePlayer = players.get(activePlayer.getPlayerNum());
}
return this;
}
public int getPhase() {
return phase;
}
public void setPhase(int phase) {
this.phase = phase;
}
public Country getAttackingCount() {
return attackingCount;
}
public void setAttackingCount(Country attackingCount) {
this.attackingCount = attackingCount;
}
public Country getDefendingCount() {
return defendingCount;
}
public void setDefendingCount(Country defendingCount) {
this.defendingCount = defendingCount;
}
public void setActivePlayerID(int activePlayerID){
this.activePlayerID = activePlayerID;
}
public int getActivePlayerID(){
return this.activePlayerID;
}
public RiskGame attack(){
if(phase == 2){
boolean validAttack = false;
for (int i =0 ; i < attackingCount.getNeighborCIDs().length; i++){
System.out.println(attackingCount.getNeighborCIDs()[i]);
if (attackingCount.getNeighborCIDs()[i] == defendingCount.getcID() && attackingCount.getArmiesHeld() >1){
validAttack = true;
}
}
if (!validAttack){
System.out.println("not vaild attack"); //delete
System.out.println(attackingCount.getcID() + "attack cid" + defendingCount.getcID()); //delete
System.out.println("attacking cids" + Arrays.asList(attackingCount.getNeighborCIDs()).toString() + " cid: "+ attackingCount.getcID());
for (int i =0 ; i < attackingCount.getNeighborCIDs().length; i++){
System.out.println(attackingCount.getNeighborCIDs()[i]);
}
System.out.println("defending cids" + Arrays.asList(defendingCount.getNeighborCIDs()).toString() + " cid: "+ defendingCount.getcID());
attackingCount = new Country();
defendingCount = new Country();
}else {
rollAttack();
rollDefence();
int cId;
if(aDie1 > dDie1 || aDie2 >dDie1 || aDie3 > aDie1){
cId = defendingCount.getcID();
countries.get(cId - 1).removeArmy();
}else if((dDie1 > aDie1 && dDie1 > aDie2 && dDie1 > aDie3)) {
cId = attackingCount.getcID();
countries.get(cId - 1).removeArmy();
}
if (dDie2 != 0 && attackingCount.getArmiesHeld() > 1){
if(aDie1 > dDie2 || aDie2 >dDie2 || aDie3 > aDie2){
cId = defendingCount.getcID();
countries.get(cId - 1).removeArmy();
}else if((dDie2 > aDie1 && dDie2 > aDie2 && dDie2 > aDie3)) {
cId = attackingCount.getcID();
countries.get(cId - 1).removeArmy();
}
}
if(defendingCount.getArmiesHeld() <= 0){
cId = defendingCount.getcID();
countries.get(cId - 1).setPlayerNum(attackingCount.getPlayerNum());
countries.get(cId -1).setArmiesHeld(1);
cId = attackingCount.getcID();
countries.get(cId -1).setArmiesHeld(countries.get(cId -1).getArmiesHeld() - 1);
attackingCount = new Country();
defendingCount = new Country();
}
diceToZero();
System.out.println( attackingCount.getcID() + " is attacking " + defendingCount.getcID()); //delete
}
}
return this;
}
private void diceToZero(){
aDie1 =0;
aDie2 =0;
aDie3 =0;
dDie1 =0;
dDie2 =0;
}
public RiskGame phaseChange(){
if(phase != 3){ //if not phase 3 increment phase
phase+= 1;
}else { //otherwise set phase to one and change active player
phase = 1;
nextPlayer();
activePlayer.setPlaceablearmies(0);
for(Country c: countries) {
if (c.getPlayerNum() == activePlayer.getPlayerNum() ) {
activePlayer.setPlaceablearmies(activePlayer.getPlaceablearmies() + c.getArmyValue());
}
}
moveTocID =0;
moveFromcID =0;
if(activePlayer.getPlaceablearmies() ==0){
phaseChange();
phaseChange();
phaseChange();
}
}
return this;
}
public RiskGame addArmy(int cid){
countries.get(cid - 1).addArmy();
return this;
}
//Rolls the attacking dice
private void rollAttack(){
if(attackingCount.getArmiesHeld() > 3){
aDie1 = rand.nextInt(7 )+1;
aDie2 = rand.nextInt(7 )+1;
aDie3 = rand.nextInt(7 )+1;
}else if(attackingCount.getArmiesHeld() > 2){
aDie1 = rand.nextInt(7 )+1;
aDie2 = rand.nextInt(7 )+1;
}else {
aDie1 = rand.nextInt(7 ) +1;
}
System.out.println(aDie1 +" " + aDie2 +" " +aDie3);
}
//rolls the defence dice
private void rollDefence(){
if(defendingCount.getArmiesHeld() > 1){
dDie1 = rand.nextInt(7 )+1;
dDie2 = rand.nextInt(7 )+1;
}else {
dDie1 = rand.nextInt(7 )+1;
}
System.out.println(dDie1 +" "+ dDie2);
}
public RiskGame move(){
if(phase == 3){
boolean validMove = false;
if(moveTocID != 0 && moveFromcID != 0){
for (int i = 0; i < countries.get(moveFromcID - 1).getNeighborCIDs().length; i++) {
if (countries.get(moveFromcID - 1).getNeighborCIDs()[i] == moveTocID && countries.get(moveFromcID - 1).getArmiesHeld() > 1) {
validMove = true;
}
}
if (moveTocID != 0 && moveTocID != moveFromcID && moveFromcID != 0 && validMove) {
addArmy(moveTocID);
countries.get(moveFromcID - 1).removeArmy();
}
}
}
moveFromcID =0;
moveTocID =0;
return this;
}
public Country getCountry(int cid){
return countries.get(cid - 1);
}
public int getMoveFromcID() {
return moveFromcID;
}
public void setMoveFromcID(int moveFromcID) {
this.moveFromcID = moveFromcID;
}
public int getMoveTocID() {
return moveTocID;
}
public void setMoveTocID(int moveTocID) {
this.moveTocID = moveTocID;
}
}
下面是我的“客户”课程:
package com.example.risk;
import android.util.Pair;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.time.Duration;
import java.time.Instant;
import java.time.ZonedDateTime;
import java.util.LinkedList;
import java.util.Objects;
import java.util.Scanner;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.ReentrantLock;
public class Client extends Thread{
RiskGame g;
int ID;
boolean playerExited = false;
ReentrantLock lock = new ReentrantLock();
AtomicBoolean started = new AtomicBoolean(false);
public Client(RiskGame g) throws IOException {
this.g = g;
this.ID = generateID();
}
public String writeGameStateToString(){
String gamestate = "";
gamestate += this.ID + ":";
for(Country countries: g.countries){
gamestate += countries.getcID() + " " + countries.getPlayerNum() + " " + countries.getArmyValue() + ":";
}
return gamestate;
}
@Override
public synchronized void run() {
try {
lock.lock();
this.sendData();
} catch (IOException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public synchronized static int generateID(){
ZonedDateTime nowZoned = ZonedDateTime.now();
Instant midnight = nowZoned.toLocalDate().atStartOfDay(nowZoned.getZone()).toInstant();
Duration duration = Duration.between(midnight, Instant.now());
long seconds = duration.getSeconds();
return (int) (seconds%(65535-4999))+4999;
}
public synchronized void sendData() throws IOException {
if(!started.getAndSet(true)) {
System.out.println(ID);
Socket sendingSocket = new Socket("10.0.2.2", 4999);
OutputStream os;
BufferedOutputStream bos;
ObjectOutputStream oos;
os = sendingSocket.getOutputStream();
bos = new BufferedOutputStream(os);
oos = new ObjectOutputStream(bos);
oos.writeObject(writeGameStateToString());
oos.flush();
while (!this.playerExited) {
//Get gamestate back from server
receiveData();
os = sendingSocket.getOutputStream();
bos = new BufferedOutputStream(os);
oos = new ObjectOutputStream(bos);
oos.writeObject(writeGameStateToString());
oos.flush();
}
sendingSocket.close();
}
}
public synchronized void receiveData() throws IOException {
Socket receivingSocket;
InputStream is;
BufferedInputStream bis;
ObjectInputStream ois;
boolean listening = true;
while(listening){
ServerSocket clientListening = new ServerSocket(this.ID);
receivingSocket = clientListening.accept();
is = receivingSocket.getInputStream();
bis = new BufferedInputStream(is);
ois = new ObjectInputStream(bis);
try {
RiskGame temp = (RiskGame) ois.readObject();
if(temp.getActivePlayerID() == ID) {
listening = false;
this.g = temp;
}else{
System.out.println(temp.getActivePlayer() + ": Updated local gamestate");
this.g = temp;
}
} catch (ClassNotFoundException e) {
System.out.println("Received incompatible Object Type from Server");
}
ois.close();
bis.close();
is.close();
clientListening.close();
}
}
}
下面是我正在服务器端生成的输出示例,其中第一个输出块有效,第二个输出块是完全不同的riskgame对象,它没有显示在应用程序上,我不确定它是如何创建的:
问题是“用户2已经加入”到目前为止应该只有一个用户。我知道这是一个巨大的代码转储,但我想确保任何相关的代码将包括在内。我希望我能弄清楚哪些部分是最相关的,重新迭代最相关的代码将在第一位代码的oncreate方法中,在第二位代码的底部是riskgame构造函数,在客户端类的senddata方法中。
如果您需要任何信息或有任何问题/建议/批评;)请随便把它们放在下面,我需要任何我能得到的帮助。也很抱歉gui看起来像垃圾我不是艺术家。哈哈
1条答案
按热度按时间ryhaxcpt1#
我认为问题可能就在这里
为什么在提交中使用while循环?如果使用启动方法?它可以使你更多的线程。在没有线程的情况下尝试它。如果中断,它将只与客户端生成一个线程。