703 lines
18 KiB
Java
703 lines
18 KiB
Java
package net.minecraft.server;
|
|
|
|
import java.io.IOException;
|
|
import java.util.ArrayList;
|
|
import java.util.Iterator;
|
|
import java.util.LinkedList;
|
|
import java.util.List;
|
|
|
|
import net.lax1dude.eaglercraft.EaglerAdapter;
|
|
import net.lax1dude.eaglercraft.sp.ipc.IPCPacket0DProgressUpdate;
|
|
import net.minecraft.src.AxisAlignedBB;
|
|
import net.minecraft.src.ChunkCoordinates;
|
|
import net.minecraft.src.CommandBase;
|
|
import net.minecraft.src.DispenserBehaviors;
|
|
import net.minecraft.src.EntityPlayer;
|
|
import net.minecraft.src.EntityPlayerMP;
|
|
import net.minecraft.src.EnumGameType;
|
|
import net.minecraft.src.ICommandManager;
|
|
import net.minecraft.src.ICommandSender;
|
|
import net.minecraft.src.ILogAgent;
|
|
import net.minecraft.src.IProgressUpdate;
|
|
import net.minecraft.src.ISaveHandler;
|
|
import net.minecraft.src.IUpdatePlayerListBox;
|
|
import net.minecraft.src.MinecraftException;
|
|
import net.minecraft.src.Packet;
|
|
import net.minecraft.src.Packet4UpdateTime;
|
|
import net.minecraft.src.ServerCommandManager;
|
|
import net.minecraft.src.ServerConfigurationManager;
|
|
import net.minecraft.src.StringTranslate;
|
|
import net.minecraft.src.StringUtils;
|
|
import net.minecraft.src.World;
|
|
import net.minecraft.src.WorldInfo;
|
|
import net.minecraft.src.WorldManager;
|
|
import net.minecraft.src.WorldServer;
|
|
import net.minecraft.src.WorldServerMulti;
|
|
import net.minecraft.src.WorldSettings;
|
|
|
|
public abstract class MinecraftServer implements ICommandSender, Runnable {
|
|
/** Instance of Minecraft Server. */
|
|
protected static MinecraftServer mcServer = null;
|
|
|
|
/** List of names of players who are online. */
|
|
protected final List playersOnline = new ArrayList();
|
|
protected final ICommandManager commandManager;
|
|
|
|
/** The server world instances. */
|
|
public WorldServer[] worldServers;
|
|
|
|
/** The ServerConfigurationManager instance. */
|
|
protected ServerConfigurationManager serverConfigManager;
|
|
|
|
/**
|
|
* Indicates whether the server is running or not. Set to false to initiate a
|
|
* shutdown.
|
|
*/
|
|
protected boolean serverRunning = true;
|
|
|
|
/** Indicates to other classes that the server is safely stopped. */
|
|
protected boolean serverStopped = false;
|
|
|
|
/** Incremented every tick. */
|
|
protected int tickCounter = 0;
|
|
|
|
/**
|
|
* The task the server is currently working on(and will output on
|
|
* outputPercentRemaining).
|
|
*/
|
|
protected String currentTask;
|
|
|
|
/** The percentage of the current task finished so far. */
|
|
protected int percentDone;
|
|
|
|
/** True if the server has animals turned on. */
|
|
protected boolean canSpawnAnimals;
|
|
protected boolean canSpawnNPCs;
|
|
|
|
/** Indicates whether PvP is active on the server or not. */
|
|
protected boolean pvpEnabled;
|
|
|
|
/** Determines if flight is allowed or not. */
|
|
protected boolean allowFlight;
|
|
|
|
/** The server MOTD string. */
|
|
protected String motd;
|
|
|
|
/** Maximum build height. */
|
|
protected int buildLimit;
|
|
protected long lastSentPacketID;
|
|
protected long lastSentPacketSize;
|
|
protected long lastReceivedID;
|
|
protected long lastReceivedSize;
|
|
public final long[] sentPacketCountArray = new long[100];
|
|
public final long[] sentPacketSizeArray = new long[100];
|
|
public final long[] receivedPacketCountArray = new long[100];
|
|
public final long[] receivedPacketSizeArray = new long[100];
|
|
public final long[] tickTimeArray = new long[100];
|
|
|
|
/** Stats are [dimension][tick%100] system.nanoTime is stored. */
|
|
public long[][] timeOfLastDimensionTick;
|
|
|
|
/** Username of the server owner (for integrated servers) */
|
|
protected String serverOwner;
|
|
protected String folderName;
|
|
|
|
/**
|
|
* If true, there is no need to save chunks or stop the server, because that is
|
|
* already being done.
|
|
*/
|
|
protected boolean worldIsBeingDeleted;
|
|
protected String texturePack = "";
|
|
protected boolean serverIsRunning = false;
|
|
|
|
/**
|
|
* Set when warned for "Can't keep up", which triggers again after 15 seconds.
|
|
*/
|
|
protected long timeOfLastWarning;
|
|
protected String userMessage;
|
|
protected boolean field_104057_T = false;
|
|
|
|
public MinecraftServer(String folder) {
|
|
mcServer = this;
|
|
this.folderName = folder;
|
|
this.commandManager = new ServerCommandManager();
|
|
this.registerDispenseBehaviors();
|
|
}
|
|
|
|
/**
|
|
* Register all dispense behaviors.
|
|
*/
|
|
private void registerDispenseBehaviors() {
|
|
DispenserBehaviors.registerDispenserBehaviours();
|
|
}
|
|
|
|
/**
|
|
* Initialises the server and starts it.
|
|
*/
|
|
protected abstract boolean startServer() throws IOException;
|
|
|
|
/**
|
|
* Typically "menu.convertingLevel", "menu.loadingLevel" or others.
|
|
*/
|
|
protected void setUserMessage(String par1Str) {
|
|
this.logInfo(par1Str);
|
|
this.userMessage = par1Str;
|
|
}
|
|
|
|
protected void setUserMessage(String par1Str, float prog) {
|
|
this.logInfo(par1Str + ": " + (prog > 1.0f ? "" + (int)prog : "" + (int)(prog * 100.0f) + "%"));
|
|
this.userMessage = par1Str;
|
|
}
|
|
public abstract boolean canStructuresSpawn();
|
|
|
|
public abstract EnumGameType getGameType();
|
|
|
|
/**
|
|
* Defaults to "1" (Easy) for the dedicated server, defaults to "2" (Normal) on
|
|
* the client.
|
|
*/
|
|
public abstract int getDifficulty();
|
|
|
|
/**
|
|
* Defaults to false.
|
|
*/
|
|
public abstract boolean isHardcore();
|
|
|
|
/**
|
|
* Used to display a percent remaining given text and the percentage.
|
|
*/
|
|
protected void outputPercentRemaining(String par1Str, int par2) {
|
|
this.currentTask = par1Str;
|
|
this.percentDone = par2;
|
|
setUserMessage(par1Str, (par2 / 100.0f));
|
|
}
|
|
|
|
/**
|
|
* Set current task to null and set its percentage to 0.
|
|
*/
|
|
protected void clearCurrentTask() {
|
|
this.currentTask = null;
|
|
this.percentDone = 0;
|
|
}
|
|
|
|
/**
|
|
* par1 indicates if a log message should be output.
|
|
*/
|
|
public void saveAllWorlds(boolean par1) {
|
|
if (!this.worldIsBeingDeleted) {
|
|
WorldServer[] var2 = this.worldServers;
|
|
int var3 = var2.length;
|
|
|
|
for (int var4 = 0; var4 < var3; ++var4) {
|
|
WorldServer var5 = var2[var4];
|
|
|
|
if (var5 != null) {
|
|
setUserMessage("Saving chunks for level \'" + var5.getWorldInfo().getWorldName() + "\'/" + var5.provider.getDimensionName());
|
|
|
|
try {
|
|
var5.saveAllChunks(true, (IProgressUpdate) null);
|
|
} catch (MinecraftException var7) {
|
|
this.getLogAgent().logWarning(var7.getMessage());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Saves all necessary data as preparation for stopping the server.
|
|
*/
|
|
public void stopServer() {
|
|
if (!this.worldIsBeingDeleted) {
|
|
setUserMessage("Stopping server");
|
|
|
|
if (this.serverConfigManager != null) {
|
|
this.getLogAgent().logInfo("Saving players");
|
|
this.serverConfigManager.saveAllPlayerData();
|
|
this.serverConfigManager.removeAllPlayers();
|
|
}
|
|
|
|
setUserMessage("Saving worlds");
|
|
this.saveAllWorlds(false);
|
|
|
|
for (int var1 = 0; var1 < this.worldServers.length; ++var1) {
|
|
WorldServer var2 = this.worldServers[var1];
|
|
var2.flush();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* "getHostname" is already taken, but both return the hostname.
|
|
*/
|
|
public String getServerHostname() {
|
|
return "127.1.1.1";
|
|
}
|
|
|
|
public void setHostname(String par1Str) {
|
|
throw new IllegalArgumentException("variable removed");
|
|
}
|
|
|
|
public boolean isServerRunning() {
|
|
return this.serverRunning;
|
|
}
|
|
|
|
/**
|
|
* Sets the serverRunning variable to false, in order to get the server to shut
|
|
* down.
|
|
*/
|
|
public void initiateShutdown() {
|
|
this.serverRunning = false;
|
|
}
|
|
|
|
public void run() {
|
|
try {
|
|
if (this.startServer()) {
|
|
long var1 = EaglerAdapter.steadyTimeMillis();
|
|
|
|
for (long var50 = 0L; this.serverRunning; this.serverIsRunning = true) {
|
|
long var5 = EaglerAdapter.steadyTimeMillis();
|
|
long var7 = var5 - var1;
|
|
|
|
if (var7 > 2000L && var1 - this.timeOfLastWarning >= 15000L) {
|
|
this.getLogAgent().logWarning(
|
|
"Can\'t keep up! Did the system time change, or is the server overloaded?");
|
|
var7 = 2000L;
|
|
this.timeOfLastWarning = var1;
|
|
}
|
|
|
|
if (var7 < 0L) {
|
|
this.getLogAgent().logWarning("Time ran backwards! Did the system time change?");
|
|
var7 = 0L;
|
|
}
|
|
|
|
var50 += var7;
|
|
var1 = var5;
|
|
|
|
if (this.worldServers[0].areAllPlayersAsleep()) {
|
|
this.tick();
|
|
var50 = 0L;
|
|
} else {
|
|
while (var50 > 50L) {
|
|
var50 -= 50L;
|
|
this.tick();
|
|
}
|
|
}
|
|
|
|
EaglerAdapter.sleep(1);
|
|
}
|
|
} else {
|
|
throw new RuntimeException("Server did not init correctly");
|
|
}
|
|
} catch (Throwable var48) {
|
|
this.getLogAgent().logSevereException(
|
|
"Encountered an unexpected exception " + var48.getClass().getSimpleName(), var48);
|
|
var48.printStackTrace();
|
|
//IntegratedServer.throwExceptionToClient("Encountered an unexpected exception", var48);
|
|
} finally {
|
|
try {
|
|
this.stopServer();
|
|
this.serverStopped = true;
|
|
} catch (Throwable var46) {
|
|
var46.printStackTrace();
|
|
} finally {
|
|
this.systemExitNow();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Directly calls System.exit(0), instantly killing the program.
|
|
*/
|
|
protected void systemExitNow() {
|
|
}
|
|
|
|
/**
|
|
* Main function called by run() every loop.
|
|
*/
|
|
protected void tick() {
|
|
long var1 = System.nanoTime();
|
|
AxisAlignedBB.getAABBPool().cleanPool();
|
|
++this.tickCounter;
|
|
|
|
this.updateTimeLightAndEntities();
|
|
|
|
if (this.tickCounter % 900 == 0) {
|
|
this.serverConfigManager.saveAllPlayerData();
|
|
this.saveAllWorlds(true);
|
|
}
|
|
|
|
this.tickTimeArray[this.tickCounter % 100] = System.nanoTime() - var1;
|
|
this.sentPacketCountArray[this.tickCounter % 100] = Packet.sentID - this.lastSentPacketID;
|
|
this.lastSentPacketID = Packet.sentID;
|
|
this.sentPacketSizeArray[this.tickCounter % 100] = Packet.sentSize - this.lastSentPacketSize;
|
|
this.lastSentPacketSize = Packet.sentSize;
|
|
this.receivedPacketCountArray[this.tickCounter % 100] = Packet.receivedID - this.lastReceivedID;
|
|
this.lastReceivedID = Packet.receivedID;
|
|
this.receivedPacketSizeArray[this.tickCounter % 100] = Packet.receivedSize - this.lastReceivedSize;
|
|
this.lastReceivedSize = Packet.receivedSize;
|
|
}
|
|
|
|
public List<String> getTPSAndChunkBuffer(int tpsCounter) {
|
|
ArrayList<String> strs = new ArrayList();
|
|
strs.add("Ticks/Second: " + tpsCounter + "/20");
|
|
|
|
int c = 0;
|
|
int oc = 0;
|
|
int e = 0;
|
|
int te = 0;
|
|
int r = 0;
|
|
int w = 0;
|
|
int g = 0;
|
|
int tu = 0;
|
|
int lu = 0;
|
|
for(int i = 0; i < worldServers.length; ++i) {
|
|
c += worldServers[i].getChunkProvider().getLoadedChunkCount();
|
|
e += worldServers[i].loadedEntityList.size();
|
|
te += worldServers[i].loadedTileEntityList.size();
|
|
r += worldServers[i].getR();
|
|
w += worldServers[i].getW();
|
|
g += worldServers[i].getG();
|
|
lu += worldServers[i].getLU();
|
|
tu += worldServers[i].getTU();
|
|
}
|
|
for(EntityPlayerMP p : (List<EntityPlayerMP>)this.playersOnline) {
|
|
oc += p.loadedChunks.size();
|
|
}
|
|
|
|
strs.add("Chunks: " + c + "/" + (c + oc));
|
|
strs.add("Entities: " + e + "+" + te);
|
|
strs.add("R: " + r + ", G: " + g + ", W: " + w);
|
|
strs.add("TU: " + tu + " LU: " + lu);
|
|
int pp = this.playersOnline.size();
|
|
if(pp > 1) {
|
|
strs.add("Players: " + pp);
|
|
}
|
|
return strs;
|
|
}
|
|
|
|
public void updateTimeLightAndEntities() {
|
|
int var1;
|
|
|
|
for (var1 = 0; var1 < this.worldServers.length; ++var1) {
|
|
long var2 = System.nanoTime();
|
|
|
|
if (var1 == 0 || this.getAllowNether()) {
|
|
WorldServer var4 = this.worldServers[var1];
|
|
var4.getWorldVec3Pool().clear();
|
|
|
|
if (this.tickCounter % 20 == 0) {
|
|
this.serverConfigManager.sendPacketToAllPlayersInDimension(
|
|
new Packet4UpdateTime(var4.getTotalWorldTime(), var4.getWorldTime(), this.worldServers[var1].getGameRules().getGameRuleBooleanValue("doDaylightCycle")),
|
|
var4.provider.dimensionId);
|
|
}
|
|
|
|
var4.tick();
|
|
var4.updateEntities();
|
|
|
|
var4.getEntityTracker().updateTrackedEntities();
|
|
}
|
|
|
|
this.timeOfLastDimensionTick[var1][this.tickCounter % 100] = System.nanoTime() - var2;
|
|
}
|
|
|
|
this.serverConfigManager.sendPlayerInfoToAllPlayers();
|
|
|
|
for (var1 = 0; var1 < this.playersOnline.size(); ++var1) {
|
|
((IUpdatePlayerListBox) this.playersOnline.get(var1)).update();
|
|
}
|
|
}
|
|
|
|
public boolean getAllowNether() {
|
|
return true;
|
|
}
|
|
|
|
public void func_82010_a(IUpdatePlayerListBox par1IUpdatePlayerListBox) {
|
|
this.playersOnline.add(par1IUpdatePlayerListBox);
|
|
}
|
|
|
|
/**
|
|
* Logs the message with a level of INFO.
|
|
*/
|
|
public void logInfo(String par1Str) {
|
|
this.getLogAgent().logInfo(par1Str);
|
|
}
|
|
|
|
/**
|
|
* Logs the message with a level of WARN.
|
|
*/
|
|
public void logWarning(String par1Str) {
|
|
this.getLogAgent().logWarning(par1Str);
|
|
}
|
|
|
|
/**
|
|
* Gets the worldServer by the given dimension.
|
|
*/
|
|
public WorldServer worldServerForDimension(int par1) {
|
|
return par1 == -1 ? this.worldServers[1] : (par1 == 1 ? this.worldServers[2] : this.worldServers[0]);
|
|
}
|
|
|
|
/**
|
|
* Returns the number of players currently on the server.
|
|
*/
|
|
public int getCurrentPlayerCount() {
|
|
return this.serverConfigManager.getCurrentPlayerCount();
|
|
}
|
|
|
|
/**
|
|
* Returns the maximum number of players allowed on the server.
|
|
*/
|
|
public int getMaxPlayers() {
|
|
return this.serverConfigManager.getMaxPlayers();
|
|
}
|
|
|
|
/**
|
|
* Returns an array of the usernames of all the connected players.
|
|
*/
|
|
public String[] getAllUsernames() {
|
|
return this.serverConfigManager.getAllUsernames();
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* If par2Str begins with /, then it searches for commands, otherwise it returns
|
|
* players.
|
|
*/
|
|
public List getPossibleCompletions(ICommandSender par1ICommandSender, String par2Str) {
|
|
ArrayList var3 = new ArrayList();
|
|
|
|
if (par2Str.startsWith("/")) {
|
|
par2Str = par2Str.substring(1);
|
|
boolean var10 = !par2Str.contains(" ");
|
|
List var11 = this.commandManager.getPossibleCommands(par1ICommandSender, par2Str);
|
|
|
|
if (var11 != null) {
|
|
Iterator var12 = var11.iterator();
|
|
|
|
while (var12.hasNext()) {
|
|
String var13 = (String) var12.next();
|
|
|
|
if (var10) {
|
|
var3.add("/" + var13);
|
|
} else {
|
|
var3.add(var13);
|
|
}
|
|
}
|
|
}
|
|
|
|
return var3;
|
|
} else {
|
|
String[] var4 = par2Str.split(" ", -1);
|
|
String var5 = var4[var4.length - 1];
|
|
String[] var6 = this.serverConfigManager.getAllUsernames();
|
|
int var7 = var6.length;
|
|
|
|
for (int var8 = 0; var8 < var7; ++var8) {
|
|
String var9 = var6[var8];
|
|
|
|
if (CommandBase.doesStringStartWith(var5, var9)) {
|
|
var3.add(var9);
|
|
}
|
|
}
|
|
|
|
return var3;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gets mcServer.
|
|
*/
|
|
public static MinecraftServer getServer() {
|
|
return mcServer;
|
|
}
|
|
|
|
/**
|
|
* Gets the name of this command sender (usually username, but possibly "Rcon")
|
|
*/
|
|
public String getCommandSenderName() {
|
|
return "Host";
|
|
}
|
|
|
|
public void sendChatToPlayer(String par1Str) {
|
|
this.getLogAgent().logInfo(StringUtils.stripControlCodes(par1Str));
|
|
}
|
|
|
|
/**
|
|
* Returns true if the command sender is allowed to use the given command.
|
|
*/
|
|
public boolean canCommandSenderUseCommand(int par1, String par2Str) {
|
|
return par2Str.equals(this.getServerOwner());
|
|
}
|
|
|
|
public ICommandManager getCommandManager() {
|
|
return this.commandManager;
|
|
}
|
|
|
|
/**
|
|
* Gets serverPort.
|
|
*/
|
|
public int getServerPort() {
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* Returns the username of the server owner (for integrated servers)
|
|
*/
|
|
public String getServerOwner() {
|
|
return this.serverOwner;
|
|
}
|
|
|
|
|
|
public boolean isSinglePlayer() {
|
|
return this.serverOwner != null;
|
|
}
|
|
|
|
|
|
public void setDifficultyForAllWorlds(int par1) {
|
|
for (int var2 = 0; var2 < this.worldServers.length; ++var2) {
|
|
WorldServer var3 = this.worldServers[var2];
|
|
|
|
if (var3 != null) {
|
|
if (var3.getWorldInfo().isHardcoreModeEnabled()) {
|
|
var3.difficultySetting = 3;
|
|
var3.setAllowedSpawnTypes(true, true);
|
|
} else if (this.isSinglePlayer()) {
|
|
var3.difficultySetting = par1;
|
|
var3.setAllowedSpawnTypes(var3.difficultySetting > 0, true);
|
|
} else {
|
|
var3.difficultySetting = par1;
|
|
var3.setAllowedSpawnTypes(this.allowSpawnMonsters(), this.canSpawnAnimals);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
protected boolean allowSpawnMonsters() {
|
|
return true;
|
|
}
|
|
|
|
|
|
/**
|
|
* WARNING : directly calls
|
|
* getActiveAnvilConverter().deleteWorldDirectory(theWorldServer[0].getSaveHandler().getWorldDirectoryName());
|
|
*/
|
|
public void deleteWorldAndStopServer() {
|
|
this.worldIsBeingDeleted = true;
|
|
|
|
for (int var1 = 0; var1 < this.worldServers.length; ++var1) {
|
|
WorldServer var2 = this.worldServers[var1];
|
|
|
|
if (var2 != null) {
|
|
var2.flush();
|
|
}
|
|
}
|
|
|
|
String dir = this.worldServers[0].getSaveHandler().getWorldDirectoryName();
|
|
|
|
this.initiateShutdown();
|
|
}
|
|
|
|
public String getTexturePack() {
|
|
return null;
|
|
}
|
|
|
|
|
|
/**
|
|
* This is checked to be 16 upon receiving the packet, otherwise the packet is
|
|
* ignored.
|
|
*/
|
|
public int textureSize() {
|
|
return 16;
|
|
}
|
|
|
|
public abstract boolean isDedicatedServer();
|
|
|
|
|
|
|
|
public boolean getCanSpawnAnimals() {
|
|
return this.canSpawnAnimals;
|
|
}
|
|
|
|
|
|
|
|
public boolean getCanSpawnNPCs() {
|
|
return this.canSpawnNPCs;
|
|
}
|
|
|
|
|
|
|
|
public boolean isPVPEnabled() {
|
|
return this.pvpEnabled;
|
|
}
|
|
|
|
|
|
|
|
public boolean isFlightAllowed() {
|
|
return this.allowFlight;
|
|
}
|
|
|
|
|
|
/**
|
|
* Return whether command blocks are enabled.
|
|
*/
|
|
public abstract boolean isCommandBlockEnabled();
|
|
|
|
|
|
public int getBuildLimit() {
|
|
return 256;
|
|
}
|
|
|
|
public ServerConfigurationManager getConfigurationManager() {
|
|
return this.serverConfigManager;
|
|
}
|
|
|
|
public void setConfigurationManager(ServerConfigurationManager par1ServerConfigurationManager) {
|
|
this.serverConfigManager = par1ServerConfigurationManager;
|
|
}
|
|
|
|
/**
|
|
* Sets the game type for all worlds.
|
|
*/
|
|
public void setGameType(EnumGameType par1EnumGameType) {
|
|
for (int var2 = 0; var2 < this.worldServers.length; ++var2) {
|
|
getServer().worldServers[var2].getWorldInfo().setGameType(par1EnumGameType);
|
|
}
|
|
}
|
|
|
|
public boolean getGuiEnabled() {
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* On dedicated does nothing. On integrated, sets commandsAllowedForAll,
|
|
* gameType and allows external connections.
|
|
*/
|
|
public abstract String shareToLAN(EnumGameType var1, boolean var2);
|
|
|
|
public int getTickCounter() {
|
|
return this.tickCounter;
|
|
}
|
|
|
|
/**
|
|
* Return the position for this command sender.
|
|
*/
|
|
public ChunkCoordinates getPlayerCoordinates() {
|
|
return new ChunkCoordinates(0, 0, 0);
|
|
}
|
|
|
|
/**
|
|
* Return the spawn protection area's size.
|
|
*/
|
|
public int getSpawnProtectionSize() {
|
|
return 0;
|
|
}
|
|
|
|
public boolean isBlockProtected(World par1World, int par2, int par3, int par4, EntityPlayer par5EntityPlayer) {
|
|
return false;
|
|
}
|
|
|
|
public abstract ILogAgent getLogAgent();
|
|
|
|
}
|