mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-18 19:58:00 +00:00
Merge branch 'master' of git.cardforge.org:core-developers/forge into agetian-master
This commit is contained in:
@@ -186,6 +186,11 @@ public class DestroyAi extends SpellAbilityAi {
|
|||||||
if (hasXCost) {
|
if (hasXCost) {
|
||||||
// TODO: currently the AI will maximize mana spent on X, trying to maximize damage. This may need improvement.
|
// TODO: currently the AI will maximize mana spent on X, trying to maximize damage. This may need improvement.
|
||||||
maxTargets = Math.min(ComputerUtilMana.determineMaxAffordableX(ai, sa), abTgt.getMaxTargets(sa.getHostCard(), sa));
|
maxTargets = Math.min(ComputerUtilMana.determineMaxAffordableX(ai, sa), abTgt.getMaxTargets(sa.getHostCard(), sa));
|
||||||
|
// X can't be more than the lands we have in our hand for "discard X lands"!
|
||||||
|
if ("ScorchedEarth".equals(logic)) {
|
||||||
|
int lands = CardLists.filter(ai.getCardsIn(ZoneType.Hand), CardPredicates.Presets.LANDS).size();
|
||||||
|
maxTargets = Math.min(maxTargets, lands);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (sa.hasParam("AIMaxTgtsCount")) {
|
if (sa.hasParam("AIMaxTgtsCount")) {
|
||||||
// Cards that have confusing costs for the AI (e.g. Eliminate the Competition) can have forced max target constraints specified
|
// Cards that have confusing costs for the AI (e.g. Eliminate the Competition) can have forced max target constraints specified
|
||||||
|
|||||||
@@ -295,9 +295,10 @@ public class CardFactory {
|
|||||||
sa.setRightSplit();
|
sa.setRightSplit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
CardFactoryUtil.setupKeywordedAbilities(card);
|
||||||
final CardState original = card.getState(CardStateName.Original);
|
final CardState original = card.getState(CardStateName.Original);
|
||||||
original.addNonManaAbilities(card.getCurrentState().getNonManaAbilities());
|
original.addNonManaAbilities(card.getCurrentState().getNonManaAbilities());
|
||||||
original.addIntrinsicKeywords(card.getCurrentState().getIntrinsicKeywordStrings(), false); // Copy 'Fuse' to original side
|
original.addIntrinsicKeywords(card.getCurrentState().getIntrinsicKeywords()); // Copy 'Fuse' to original side
|
||||||
original.getSVars().putAll(card.getCurrentState().getSVars()); // Unfortunately need to copy these to (Effect looks for sVars on execute)
|
original.getSVars().putAll(card.getCurrentState().getSVars()); // Unfortunately need to copy these to (Effect looks for sVars on execute)
|
||||||
} else if (state != CardStateName.Original){
|
} else if (state != CardStateName.Original){
|
||||||
CardFactoryUtil.setupKeywordedAbilities(card);
|
CardFactoryUtil.setupKeywordedAbilities(card);
|
||||||
@@ -305,6 +306,10 @@ public class CardFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
card.setState(CardStateName.Original, false);
|
card.setState(CardStateName.Original, false);
|
||||||
|
// need to update keyword cache for original spell
|
||||||
|
if (card.isSplitCard()) {
|
||||||
|
card.updateKeywordsCache(card.getCurrentState());
|
||||||
|
}
|
||||||
|
|
||||||
// ******************************************************************
|
// ******************************************************************
|
||||||
// ************** Link to different CardFactories *******************
|
// ************** Link to different CardFactories *******************
|
||||||
|
|||||||
@@ -2999,7 +2999,7 @@ public class CardFactoryUtil {
|
|||||||
public static void addReplacementEffect(final KeywordInterface inst, final Card card, final boolean intrinsic) {
|
public static void addReplacementEffect(final KeywordInterface inst, final Card card, final boolean intrinsic) {
|
||||||
|
|
||||||
String keyword = inst.getOriginal();
|
String keyword = inst.getOriginal();
|
||||||
if (keyword.equals("Aftermath")) {
|
if (keyword.equals("Aftermath") && card.getCurrentStateName().equals(CardStateName.RightSplit)) {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append("Event$ Moved | ValidCard$ Card.Self | Origin$ Stack | ExcludeDestination$ Exile ");
|
sb.append("Event$ Moved | ValidCard$ Card.Self | Origin$ Stack | ExcludeDestination$ Exile ");
|
||||||
sb.append("| ValidStackSa$ Spell.Aftermath | Description$ Aftermath");
|
sb.append("| ValidStackSa$ Spell.Aftermath | Description$ Aftermath");
|
||||||
@@ -3021,8 +3021,6 @@ public class CardFactoryUtil {
|
|||||||
|
|
||||||
re.setOverridingAbility(saExile);
|
re.setOverridingAbility(saExile);
|
||||||
|
|
||||||
// Aftermath only on Rightsplit
|
|
||||||
// doesn't make a copy with it
|
|
||||||
inst.addReplacement(re);
|
inst.addReplacement(re);
|
||||||
} else if (keyword.startsWith("Amplify")) {
|
} else if (keyword.startsWith("Amplify")) {
|
||||||
final String[] ampString = keyword.split(":");
|
final String[] ampString = keyword.split(":");
|
||||||
@@ -3493,11 +3491,11 @@ public class CardFactoryUtil {
|
|||||||
inst.addSpellAbility(newSA);
|
inst.addSpellAbility(newSA);
|
||||||
|
|
||||||
}
|
}
|
||||||
} else if (keyword.equals("Aftermath")) {
|
} else if (keyword.equals("Aftermath") && card.getCurrentStateName().equals(CardStateName.RightSplit)) {
|
||||||
// Aftermath does modify existing SA, and does not add new one
|
// Aftermath does modify existing SA, and does not add new one
|
||||||
|
|
||||||
// only target RightSplit of it
|
// only target RightSplit of it
|
||||||
final SpellAbility origSA = card.getState(CardStateName.RightSplit).getFirstAbility();
|
final SpellAbility origSA = card.getFirstSpellAbility();
|
||||||
origSA.setAftermath(true);
|
origSA.setAftermath(true);
|
||||||
origSA.getRestrictions().setZone(ZoneType.Graveyard);
|
origSA.getRestrictions().setZone(ZoneType.Graveyard);
|
||||||
// The Exile part is done by the System itself
|
// The Exile part is done by the System itself
|
||||||
@@ -3742,9 +3740,9 @@ public class CardFactoryUtil {
|
|||||||
inst.addSpellAbility(sa);
|
inst.addSpellAbility(sa);
|
||||||
|
|
||||||
|
|
||||||
} else if (keyword.startsWith("Fuse")) {
|
} else if (keyword.startsWith("Fuse") && card.getCurrentStateName().equals(CardStateName.Original)) {
|
||||||
final SpellAbility sa = AbilityFactory.buildFusedAbility(card);
|
final SpellAbility sa = AbilityFactory.buildFusedAbility(card);
|
||||||
card.getState(CardStateName.Original).addNonManaAbility(sa);
|
card.addSpellAbility(sa);
|
||||||
|
|
||||||
sa.setTemporary(!intrinsic);
|
sa.setTemporary(!intrinsic);
|
||||||
inst.addSpellAbility(sa);
|
inst.addSpellAbility(sa);
|
||||||
|
|||||||
@@ -164,9 +164,6 @@ public class CardState extends GameObject {
|
|||||||
public final Collection<KeywordInterface> getIntrinsicKeywords() {
|
public final Collection<KeywordInterface> getIntrinsicKeywords() {
|
||||||
return intrinsicKeywords.getValues();
|
return intrinsicKeywords.getValues();
|
||||||
}
|
}
|
||||||
public final Iterable<String> getIntrinsicKeywordStrings() {
|
|
||||||
return intrinsicKeywords;
|
|
||||||
}
|
|
||||||
public final boolean hasIntrinsicKeyword(String k) {
|
public final boolean hasIntrinsicKeyword(String k) {
|
||||||
return intrinsicKeywords.contains(k);
|
return intrinsicKeywords.contains(k);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,5 +3,6 @@ ManaCost:W
|
|||||||
Types:Enchantment
|
Types:Enchantment
|
||||||
A:AB$ PumpAll | Cost$ 2 W | ValidCards$ Creature.YouCtrl | NumAtt$ +1 | NumDef$ +1 | SpellDescription$ Creatures you control get +1/+1 until end of turn.
|
A:AB$ PumpAll | Cost$ 2 W | ValidCards$ Creature.YouCtrl | NumAtt$ +1 | NumDef$ +1 | SpellDescription$ Creatures you control get +1/+1 until end of turn.
|
||||||
SVar:NonStackingEffect:True
|
SVar:NonStackingEffect:True
|
||||||
|
SVar:PlayMain1:TRUE
|
||||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/gerrards_battle_cry.jpg
|
SVar:Picture:http://www.wizards.com/global/images/magic/general/gerrards_battle_cry.jpg
|
||||||
Oracle:{2}{W}: Creatures you control get +1/+1 until end of turn.
|
Oracle:{2}{W}: Creatures you control get +1/+1 until end of turn.
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
Name:Scorched Earth
|
Name:Scorched Earth
|
||||||
ManaCost:X R
|
ManaCost:X R
|
||||||
Types:Sorcery
|
Types:Sorcery
|
||||||
A:SP$ Destroy | Cost$ X R Discard<X/Land> | TargetMin$ 0 | TargetMax$ MaxTgts | ValidTgts$ Land | TgtPrompt$ Select target land | References$ X | SpellDescription$ Destroy X target lands.
|
A:SP$ Destroy | Cost$ X R Discard<X/Land> | TargetMin$ 0 | TargetMax$ MaxTgts | ValidTgts$ Land | TgtPrompt$ Select target land | References$ X | SpellDescription$ Destroy X target lands. | AILogic$ ScorchedEarth
|
||||||
# It may seem wrong to not use X in the target, but since the Targets are what defines X, it's redundant (and not supported by the code)
|
# It may seem wrong to not use X in the target, but since the Targets are what defines X, it's redundant (and not supported by the code)
|
||||||
SVar:X:Targeted$Amount
|
SVar:X:Targeted$Amount
|
||||||
SVar:MaxTgts:Count$Valid Land
|
SVar:MaxTgts:Count$Valid Land
|
||||||
SVar:RemAIDeck:True
|
SVar:RemRandomDeck:True
|
||||||
|
SVar:PlayBeforeLandDrop:true
|
||||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/scorched_earth.jpg
|
SVar:Picture:http://www.wizards.com/global/images/magic/general/scorched_earth.jpg
|
||||||
Oracle:As an additional cost to cast Scorched Earth, discard X land cards.\nDestroy X target lands.
|
Oracle:As an additional cost to cast Scorched Earth, discard X land cards.\nDestroy X target lands.
|
||||||
|
|||||||
@@ -4,10 +4,12 @@ import java.io.Serializable;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
|
||||||
import forge.util.TextUtil;
|
import forge.util.TextUtil;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
@@ -49,7 +51,7 @@ public abstract class GameLobby implements IHasGameType {
|
|||||||
|
|
||||||
private final boolean allowNetworking;
|
private final boolean allowNetworking;
|
||||||
private HostedMatch hostedMatch;
|
private HostedMatch hostedMatch;
|
||||||
private final Map<LobbySlot, IGameController> gameControllers = Maps.newHashMap();
|
private final HashMap<LobbySlot, IGameController> gameControllers = Maps.newHashMap();
|
||||||
protected GameLobby(final boolean allowNetworking) {
|
protected GameLobby(final boolean allowNetworking) {
|
||||||
this.allowNetworking = allowNetworking;
|
this.allowNetworking = allowNetworking;
|
||||||
}
|
}
|
||||||
@@ -481,6 +483,8 @@ public abstract class GameLobby implements IHasGameType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hostedMatch.gameControllers = gameControllers;
|
||||||
|
|
||||||
onGameStarted();
|
onGameStarted();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,11 +3,14 @@ package forge.match;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import forge.LobbyPlayer;
|
||||||
|
import forge.interfaces.IGameController;
|
||||||
import forge.util.TextUtil;
|
import forge.util.TextUtil;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
@@ -55,6 +58,7 @@ public class HostedMatch {
|
|||||||
private Match match;
|
private Match match;
|
||||||
private Game game;
|
private Game game;
|
||||||
private String title;
|
private String title;
|
||||||
|
public HashMap<LobbySlot, IGameController> gameControllers = null;
|
||||||
private Runnable startGameHook = null;
|
private Runnable startGameHook = null;
|
||||||
private final List<PlayerControllerHuman> humanControllers = Lists.newArrayList();
|
private final List<PlayerControllerHuman> humanControllers = Lists.newArrayList();
|
||||||
private Map<RegisteredPlayer, IGuiGame> guis;
|
private Map<RegisteredPlayer, IGuiGame> guis;
|
||||||
@@ -180,6 +184,12 @@ public class HostedMatch {
|
|||||||
|
|
||||||
game.subscribeToEvents(new FControlGameEventHandler(humanController));
|
game.subscribeToEvents(new FControlGameEventHandler(humanController));
|
||||||
playersPerGui.add(gui, p.getView());
|
playersPerGui.add(gui, p.getView());
|
||||||
|
|
||||||
|
if (gameControllers != null ) {
|
||||||
|
LobbySlot lobbySlot = getLobbySlot(p.getLobbyPlayer());
|
||||||
|
gameControllers.put(lobbySlot, humanController);
|
||||||
|
}
|
||||||
|
|
||||||
humanControllers.add(humanController);
|
humanControllers.add(humanController);
|
||||||
humanCount++;
|
humanCount++;
|
||||||
}
|
}
|
||||||
@@ -238,6 +248,18 @@ public class HostedMatch {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private LobbySlot getLobbySlot(LobbyPlayer lobbyPlayer) {
|
||||||
|
for (LobbySlot key: gameControllers.keySet()) {
|
||||||
|
IGameController value = gameControllers.get(key);
|
||||||
|
if (value instanceof PlayerControllerHuman) {
|
||||||
|
if (lobbyPlayer == ((PlayerControllerHuman) value).getLobbyPlayer()) {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public void registerSpectator(final IGuiGame gui) {
|
public void registerSpectator(final IGuiGame gui) {
|
||||||
final PlayerControllerHuman humanController = new WatchLocalGame(game, null, gui);
|
final PlayerControllerHuman humanController = new WatchLocalGame(game, null, gui);
|
||||||
gui.setSpectator(humanController);
|
gui.setSpectator(humanController);
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ final class GameClientHandler extends GameProtocolHandler<IGuiGame> {
|
|||||||
private final FGameClient client;
|
private final FGameClient client;
|
||||||
private final IGuiGame gui;
|
private final IGuiGame gui;
|
||||||
private Tracker tracker;
|
private Tracker tracker;
|
||||||
|
private Match match;
|
||||||
|
private Game game;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a client-side game handler.
|
* Creates a client-side game handler.
|
||||||
@@ -36,6 +38,8 @@ final class GameClientHandler extends GameProtocolHandler<IGuiGame> {
|
|||||||
this.client = client;
|
this.client = client;
|
||||||
this.gui = client.getGui();
|
this.gui = client.getGui();
|
||||||
this.tracker = null;
|
this.tracker = null;
|
||||||
|
this.match = null;
|
||||||
|
this.game = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -58,32 +62,26 @@ final class GameClientHandler extends GameProtocolHandler<IGuiGame> {
|
|||||||
protected void beforeCall(final ProtocolMethod protocolMethod, final Object[] args) {
|
protected void beforeCall(final ProtocolMethod protocolMethod, final Object[] args) {
|
||||||
switch (protocolMethod) {
|
switch (protocolMethod) {
|
||||||
case openView:
|
case openView:
|
||||||
if (this.tracker == null) {
|
// only need one **match**
|
||||||
int maxAttempts = 5;
|
if (this.match == null) {
|
||||||
for (int numAttempts = 0; numAttempts < maxAttempts; numAttempts++) {
|
this.match = createMatch();
|
||||||
try {
|
}
|
||||||
|
|
||||||
this.tracker = createTracker();
|
// openView is called **once** per game, for now create a new Game instance each time
|
||||||
|
this.game = createGame();
|
||||||
|
|
||||||
for (PlayerView myPlayer : (TrackableCollection<PlayerView>) args[0]) {
|
// get a tracker
|
||||||
if (myPlayer.getTracker() == null) {
|
this.tracker = createTracker();
|
||||||
myPlayer.setTracker(this.tracker);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final TrackableCollection<PlayerView> myPlayers = (TrackableCollection<PlayerView>) args[0];
|
for (PlayerView myPlayer : (TrackableCollection<PlayerView>) args[0]) {
|
||||||
client.setGameControllers(myPlayers);
|
if (myPlayer.getTracker() == null) {
|
||||||
|
myPlayer.setTracker(this.tracker);
|
||||||
} catch (Exception e) {
|
|
||||||
System.err.println("Failed: attempt number: " + numAttempts + " - " + e.toString());
|
|
||||||
try {
|
|
||||||
Thread.sleep(100);
|
|
||||||
} catch (InterruptedException e1) {
|
|
||||||
e1.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final TrackableCollection<PlayerView> myPlayers = (TrackableCollection<PlayerView>) args[0];
|
||||||
|
client.setGameControllers(myPlayers);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@@ -108,19 +106,17 @@ final class GameClientHandler extends GameProtocolHandler<IGuiGame> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method creates the necessary objects and state to retrieve a <b>Tracker</b> object.
|
* This method retrieves enough of the existing (incomplete) game state to
|
||||||
*
|
* recreate a new viable Match object
|
||||||
* Near as I can tell, that means that we need to create a <b>Match</b>.
|
|
||||||
*
|
*
|
||||||
* Creating a <b>Match</b> requires that we have:
|
* Creating a <b>Match</b> requires that we have:
|
||||||
* * <b>GameRules</b>
|
* * <b>GameRules</b>
|
||||||
* * <b>RegisteredPlayers</b>
|
* * <b>RegisteredPlayers</b>
|
||||||
* * Title
|
* * Title
|
||||||
*
|
*
|
||||||
* @return Tracker
|
* @return Match
|
||||||
*/
|
*/
|
||||||
private Tracker createTracker() {
|
private Match createMatch() {
|
||||||
|
|
||||||
// retrieve what we can from the existing (but incomplete) state
|
// retrieve what we can from the existing (but incomplete) state
|
||||||
final IGuiGame gui = client.getGui();
|
final IGuiGame gui = client.getGui();
|
||||||
GameView gameView = gui.getGameView();
|
GameView gameView = gui.getGameView();
|
||||||
@@ -134,12 +130,24 @@ final class GameClientHandler extends GameProtocolHandler<IGuiGame> {
|
|||||||
|
|
||||||
// create a valid match object and game
|
// create a valid match object and game
|
||||||
Match match = new Match(gameRules, registeredPlayers, title);
|
Match match = new Match(gameRules, registeredPlayers, title);
|
||||||
Game game = match.createGame();
|
|
||||||
|
|
||||||
|
return match;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Game createGame() {
|
||||||
|
this.tracker = null;
|
||||||
|
return this.match.createGame();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensure the stored GameView is correct and retrieve a <b>Tracker</b> object.
|
||||||
|
*
|
||||||
|
* @return Tracker
|
||||||
|
*/
|
||||||
|
private Tracker createTracker() {
|
||||||
// replace the existing incomplete GameView with the newly created one
|
// replace the existing incomplete GameView with the newly created one
|
||||||
gui.setGameView(null);
|
gui.setGameView(null);
|
||||||
gui.setGameView(game.getView());
|
gui.setGameView(game.getView());
|
||||||
|
|
||||||
return gui.getGameView().getTracker();
|
return gui.getGameView().getTracker();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ import com.google.common.collect.Maps;
|
|||||||
public final class FServerManager {
|
public final class FServerManager {
|
||||||
private static FServerManager instance = null;
|
private static FServerManager instance = null;
|
||||||
|
|
||||||
|
private byte[] externalAddress = new byte[]{8,8,8,8};
|
||||||
private boolean isHosting = false;
|
private boolean isHosting = false;
|
||||||
private final EventLoopGroup bossGroup = new NioEventLoopGroup(1);
|
private final EventLoopGroup bossGroup = new NioEventLoopGroup(1);
|
||||||
private final EventLoopGroup workerGroup = new NioEventLoopGroup();
|
private final EventLoopGroup workerGroup = new NioEventLoopGroup();
|
||||||
@@ -210,7 +211,7 @@ public final class FServerManager {
|
|||||||
// https://stackoverflow.com/a/901943
|
// https://stackoverflow.com/a/901943
|
||||||
private String getRoutableAddress(boolean preferIpv4, boolean preferIPv6) throws SocketException, UnknownHostException {
|
private String getRoutableAddress(boolean preferIpv4, boolean preferIPv6) throws SocketException, UnknownHostException {
|
||||||
DatagramSocket s = new DatagramSocket();
|
DatagramSocket s = new DatagramSocket();
|
||||||
s.connect(InetAddress.getByAddress(new byte[]{8,8,8,8}), 0);
|
s.connect(InetAddress.getByAddress(this.externalAddress), 0);
|
||||||
NetworkInterface n = NetworkInterface.getByInetAddress(s.getLocalAddress());
|
NetworkInterface n = NetworkInterface.getByInetAddress(s.getLocalAddress());
|
||||||
Enumeration en = n.getInetAddresses();
|
Enumeration en = n.getInetAddresses();
|
||||||
while (en.hasMoreElements()) {
|
while (en.hasMoreElements()) {
|
||||||
|
|||||||
Reference in New Issue
Block a user