PageRenderTime 94ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/src/simpleserver/Server.java

https://github.com/Simoyd/SimpleServer
Java | 676 lines | 540 code | 112 blank | 24 comment | 60 complexity | 7a483f115075d81ba7681422caef89fd MD5 | raw file
  1. /*
  2. * Copyright (c) 2010 SimpleServer authors (see CONTRIBUTORS)
  3. *
  4. * Permission is hereby granted, free of charge, to any person obtaining a copy
  5. * of this software and associated documentation files (the "Software"), to deal
  6. * in the Software without restriction, including without limitation the rights
  7. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. * copies of the Software, and to permit persons to whom the Software is
  9. * furnished to do so, subject to the following conditions:
  10. * The above copyright notice and this permission notice shall be included in
  11. * all copies or substantial portions of the Software.
  12. *
  13. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  14. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  15. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  16. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  17. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  18. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  19. * THE SOFTWARE.
  20. */
  21. package simpleserver;
  22. import static simpleserver.lang.Translations.t;
  23. import java.io.IOException;
  24. import java.net.BindException;
  25. import java.net.InetAddress;
  26. import java.net.ServerSocket;
  27. import java.net.Socket;
  28. import java.net.UnknownHostException;
  29. import java.security.SecureRandom;
  30. import java.util.LinkedList;
  31. import java.util.List;
  32. import java.util.concurrent.Semaphore;
  33. import simpleserver.bot.BotController;
  34. import simpleserver.config.GiveAliasList;
  35. import simpleserver.config.HelpText;
  36. import simpleserver.config.IPBanList;
  37. import simpleserver.config.KitList;
  38. import simpleserver.config.MOTD;
  39. import simpleserver.config.MuteList;
  40. import simpleserver.config.ReadFiles;
  41. import simpleserver.config.RobotList;
  42. import simpleserver.config.Rules;
  43. import simpleserver.config.WhiteList;
  44. import simpleserver.config.data.GlobalData;
  45. import simpleserver.config.xml.Config;
  46. import simpleserver.config.xml.GlobalConfig;
  47. import simpleserver.export.CustAuthExport;
  48. import simpleserver.lang.Translations;
  49. import simpleserver.log.AdminLog;
  50. import simpleserver.log.ConnectionLog;
  51. import simpleserver.log.ErrorLog;
  52. import simpleserver.log.MessageLog;
  53. import simpleserver.message.Chat;
  54. import simpleserver.message.Messager;
  55. import simpleserver.minecraft.MinecraftWrapper;
  56. import simpleserver.nbt.WorldFile;
  57. import simpleserver.options.Options;
  58. import simpleserver.rcon.RconServer;
  59. import simpleserver.telnet.TelnetServer;
  60. import simpleserver.thread.AutoBackup;
  61. import simpleserver.thread.AutoFreeSpaceChecker;
  62. import simpleserver.thread.AutoRestart;
  63. import simpleserver.thread.AutoRun;
  64. import simpleserver.thread.AutoSave;
  65. import simpleserver.thread.RequestTracker;
  66. import simpleserver.thread.Statistics;
  67. import simpleserver.thread.SystemInputQueue;
  68. public class Server {
  69. private final Listener listener;
  70. private ServerSocket socket;
  71. private List<String> outputLog = new LinkedList<String>();
  72. public static final LocalAddressFactory addressFactory = new LocalAddressFactory();
  73. public Options options;
  74. public MOTD motd;
  75. public KitList kits;
  76. public Rules rules;
  77. public HelpText helptext;
  78. public IPBanList ipBans;
  79. public WhiteList whitelist;
  80. public MuteList mutelist;
  81. public GiveAliasList giveAliasList;
  82. public GlobalData data;
  83. private RobotList robots;
  84. public ReadFiles docs;
  85. public Config config;
  86. private GlobalConfig globalConfig;
  87. private SecureRandom random = new SecureRandom();
  88. public PlayerList playerList;
  89. public Authenticator authenticator;
  90. private List<Resource> resources;
  91. private CommandParser commandParser;
  92. private Messager messager;
  93. private AdminLog adminLog;
  94. private ErrorLog errorLog;
  95. private ConnectionLog connectionLog;
  96. private MessageLog messageLog;
  97. private SystemInputQueue systemInput;
  98. public CustAuthExport custAuthExport;
  99. private MinecraftWrapper minecraft;
  100. private RconServer rconServer;
  101. private TelnetServer telnetServer;
  102. private AutoRun c10t;
  103. public AutoFreeSpaceChecker autoSpaceCheck;
  104. private AutoBackup autoBackup;
  105. private AutoSave autosave;
  106. private AutoRestart autoRestart;
  107. public RequestTracker requestTracker;
  108. private Statistics statistics;
  109. public long mapSeed;
  110. private boolean run = true;
  111. private boolean restart = false;
  112. private boolean save = false;
  113. public Semaphore saveLock = new Semaphore(1);
  114. public Time time;
  115. public BotController bots;
  116. public WorldFile world;
  117. public Server() {
  118. listener = new Listener();
  119. listener.start();
  120. listener.setName("SimpleServerListener");
  121. }
  122. public void restart() {
  123. restart = true;
  124. stop();
  125. }
  126. public void stop() {
  127. run = restart;
  128. try {
  129. socket.close();
  130. } catch (Exception e) {
  131. }
  132. listener.interrupt();
  133. }
  134. public void addRobot(Player p) {
  135. robots.addRobot(p.getIPAddress());
  136. }
  137. public boolean isRobot(String ipAddress) {
  138. return robots.isRobot(ipAddress);
  139. }
  140. public void addRobotPort(int port) {
  141. robots.addRobotPort(port);
  142. }
  143. public void removeRobotPort(int port) {
  144. robots.removeRobotPort(port);
  145. }
  146. public List<Resource> getResources() {
  147. return resources;
  148. }
  149. public Integer[] getRobotPorts() {
  150. if (robots != null) {
  151. return robots.getRobotPorts();
  152. }
  153. return null;
  154. }
  155. public String nextHash() {
  156. return Long.toHexString(random.nextLong());
  157. }
  158. public int numPlayers() {
  159. return playerList.size();
  160. }
  161. public boolean isIPBanned(String ipAddress) {
  162. return ipBans.isBanned(ipAddress);
  163. }
  164. public void banKickIP(String ipAddress, String reason) {
  165. if (!isIPBanned(ipAddress)) {
  166. ipBans.addBan(ipAddress);
  167. }
  168. adminLog("IP Address " + ipAddress + " was banned:\t " + reason);
  169. for (Player player : playerList.getArray()) {
  170. if (player.getIPAddress().equals(ipAddress)) {
  171. player.kick(reason);
  172. adminLog("Player " + player.getName() + " was ip-banned:\t " + reason);
  173. }
  174. }
  175. }
  176. public void banKickIP(String ipAddress) {
  177. banKickIP(ipAddress, t("Banned!"));
  178. }
  179. public void banKick(String name, String msg) {
  180. if (name != null) {
  181. runCommand("ban", name);
  182. Player p = playerList.findPlayer(name);
  183. if (p != null) {
  184. adminLog("Player " + p.getName() + " was banned:\t " + msg);
  185. p.kick(msg);
  186. }
  187. }
  188. }
  189. public void banKick(String name) {
  190. banKick(name, t("Banned!"));
  191. }
  192. public void kick(String name, String reason) {
  193. Player player = playerList.findPlayer(name);
  194. if (player != null) {
  195. player.kick(reason);
  196. }
  197. }
  198. public boolean loadResources() {
  199. for (Resource resource : resources) {
  200. resource.load();
  201. }
  202. if (playerList != null) {
  203. playerList.updatePlayerGroups();
  204. }
  205. if (globalConfig.loadsuccess) {
  206. config = globalConfig.config;
  207. } else {
  208. System.out.println("[SimpleServer] Syntax error in comfig.xml! Config was not reloaded.");
  209. return false;
  210. }
  211. if (!Translations.getInstance().setLanguage(config.properties.get("serverLanguage"))) {
  212. options.set("serverLanguage", "en");
  213. options.save();
  214. }
  215. addressFactory.toggle(!config.properties.getBoolean("disableAddressFactory"));
  216. saveResources();
  217. return globalConfig.loadsuccess;
  218. }
  219. public void saveResources() {
  220. for (Resource resource : resources) {
  221. resource.save();
  222. }
  223. }
  224. public void saveConfig() {
  225. globalConfig.save();
  226. }
  227. public String findName(String prefix) {
  228. Player i = playerList.findPlayer(prefix);
  229. if (i != null) {
  230. return i.getName();
  231. }
  232. return null;
  233. }
  234. public Player findPlayer(String prefix) {
  235. return playerList.findPlayer(prefix);
  236. }
  237. public Player findPlayerExact(String exact) {
  238. return playerList.findPlayerExact(exact);
  239. }
  240. public void updateGroup(String name) {
  241. Player p = playerList.findPlayer(name);
  242. if (p != null) {
  243. p.updateGroup();
  244. }
  245. }
  246. public void updateGroups() {
  247. playerList.updatePlayerGroups();
  248. }
  249. public int localChat(Player player, String msg) {
  250. int localPlayers = 0;
  251. int radius = config.properties.getInt("localChatRadius");
  252. for (Player friend : playerList.getArray()) {
  253. if (friend.distanceTo(player) < radius) {
  254. friend.addCaptionedMessage(t("%s says", player.getName()), msg);
  255. if (player != friend) {
  256. localPlayers++;
  257. }
  258. }
  259. }
  260. return localPlayers;
  261. }
  262. public void addOutputLine(String s) {
  263. synchronized (outputLog) {
  264. int size = outputLog.size();
  265. for (int c = 0; c <= size - 30; ++c) {
  266. outputLog.remove(0);
  267. }
  268. outputLog.add(s);
  269. }
  270. }
  271. public String[] getOutputLog() {
  272. synchronized (outputLog) {
  273. return outputLog.toArray(new String[outputLog.size()]);
  274. }
  275. }
  276. public CommandParser getCommandParser() {
  277. return commandParser;
  278. }
  279. public void runCommand(String command, String arguments) {
  280. minecraft.execute(command, arguments);
  281. }
  282. public Messager getMessager() {
  283. return messager;
  284. }
  285. public void adminLog(String message) {
  286. adminLog.addMessage(message);
  287. }
  288. public void errorLog(Exception exception, String message) {
  289. errorLog.addMessage(exception, message);
  290. }
  291. public void connectionLog(String type, Socket socket, String comments) {
  292. connectionLog.addMessage(type, socket, comments);
  293. }
  294. public void messageLog(Chat chat, String message) {
  295. messageLog.addMessage(chat, message);
  296. }
  297. public boolean isRestarting() {
  298. return restart;
  299. }
  300. public boolean isStopping() {
  301. return !run;
  302. }
  303. public boolean isSaving() {
  304. return save;
  305. }
  306. public void setSaving(boolean save) {
  307. this.save = save;
  308. }
  309. public void forceBackup() {
  310. autoBackup.forceBackup();
  311. }
  312. private void kickAllPlayers() {
  313. String message = t("Server shutting down!");
  314. if (restart) {
  315. message = t("Server restarting!");
  316. }
  317. for (Player player : playerList.getArray()) {
  318. player.kick(message);
  319. }
  320. }
  321. private void initialize() {
  322. resources = new LinkedList<Resource>();
  323. resources.add(options = new Options());
  324. resources.add(globalConfig = new GlobalConfig(options));
  325. resources.add(robots = new RobotList());
  326. resources.add(motd = new MOTD());
  327. resources.add(rules = new Rules());
  328. resources.add(helptext = new HelpText());
  329. resources.add(kits = new KitList());
  330. resources.add(ipBans = new IPBanList());
  331. resources.add(whitelist = new WhiteList());
  332. resources.add(mutelist = new MuteList());
  333. resources.add(giveAliasList = new GiveAliasList());
  334. resources.add(data = new GlobalData());
  335. resources.add(docs = new ReadFiles());
  336. time = new Time(this);
  337. bots = new BotController(this);
  338. systemInput = new SystemInputQueue();
  339. adminLog = new AdminLog();
  340. errorLog = new ErrorLog();
  341. connectionLog = new ConnectionLog();
  342. commandParser = new CommandParser(options);
  343. }
  344. private void cleanup() {
  345. systemInput.stop();
  346. adminLog.stop();
  347. errorLog.stop();
  348. connectionLog.stop();
  349. messageLog.stop();
  350. statistics.halt();
  351. time.unfreeze();
  352. bots.cleanup();
  353. }
  354. private void startup() {
  355. restart = false;
  356. loadResources();
  357. if (!globalConfig.loadsuccess) {
  358. System.out.println("[SimpleServer] Syntax error in comfig.xml! Emergency shutdown...");
  359. System.exit(1);
  360. }
  361. authenticator = new Authenticator(this);
  362. playerList = new PlayerList(this);
  363. requestTracker = new RequestTracker(this);
  364. messager = new Messager(this);
  365. if (options.getBoolean("enableCustAuthExport")) {
  366. resources.add(custAuthExport = new CustAuthExport(this));
  367. custAuthExport.load();
  368. }
  369. messageLog = new MessageLog(config.properties.get("logMessageFormat"), config.properties.getBoolean("logMessages"));
  370. minecraft = new MinecraftWrapper(this, options, systemInput);
  371. if (!minecraft.prepareServerJar()) {
  372. System.out.println("[SimpleServer] Please download minecraft_server.jar to the folder with SimpleServer.jar.");
  373. System.exit(1);
  374. }
  375. try {
  376. minecraft.start();
  377. } catch (InterruptedException e) {
  378. // Severe error happened while starting up.
  379. // Already on track to stop/restart.
  380. }
  381. if (options.getBoolean("enableTelnet")) {
  382. telnetServer = new TelnetServer(this);
  383. }
  384. if (options.getBoolean("enableRcon")) {
  385. rconServer = new RconServer(this);
  386. }
  387. world = new WorldFile(options.get("levelName"));
  388. autoSpaceCheck = new AutoFreeSpaceChecker(this);
  389. autoBackup = new AutoBackup(this);
  390. autosave = new AutoSave(this);
  391. autoRestart = new AutoRestart(this);
  392. statistics = new Statistics(this);
  393. c10t = new AutoRun(this, options.get("c10tArgs"));
  394. if (data.freezeTime() >= 0) {
  395. time.freeze(data.freezeTime());
  396. }
  397. bots.ready();
  398. statistics.start();
  399. }
  400. private void shutdown() {
  401. System.out.println("[SimpleServer] Stopping Server...");
  402. save = false;
  403. bots.stop();
  404. if (!saveLock.tryAcquire()) {
  405. System.out.println("[SimpleServer] Server is currently Backing Up/Saving...");
  406. while (true) {
  407. try {
  408. saveLock.acquire();
  409. break;
  410. } catch (InterruptedException e) {
  411. e.printStackTrace();
  412. }
  413. }
  414. }
  415. kickAllPlayers();
  416. authenticator.finalize();
  417. if (telnetServer != null) {
  418. telnetServer.stop();
  419. }
  420. if (rconServer != null) {
  421. rconServer.stop();
  422. }
  423. autoSpaceCheck.cleanup();
  424. autoBackup.stop();
  425. autosave.stop();
  426. autoRestart.stop();
  427. requestTracker.stop();
  428. c10t.stop();
  429. saveResources();
  430. playerList.waitUntilEmpty();
  431. minecraft.stop();
  432. System.out.println("[SimpleServer] Server stopped successfully!");
  433. saveLock.release();
  434. }
  435. private final class Listener extends Thread {
  436. @Override
  437. public void run() {
  438. initialize();
  439. while (run) {
  440. startup();
  441. String ip = options.get("ipAddress");
  442. int port = options.getInt("port");
  443. InetAddress address;
  444. if (ip.equals("0.0.0.0")) {
  445. address = null;
  446. } else {
  447. try {
  448. address = InetAddress.getByName(ip);
  449. } catch (UnknownHostException e) {
  450. System.out.println("[SimpleServer] " + e);
  451. System.out.println("[SimpleServer] Invalid listening address " + ip);
  452. break;
  453. }
  454. }
  455. try {
  456. socket = new ServerSocket(port, 0, address);
  457. } catch (IOException e) {
  458. System.out.println("[SimpleServer] " + e);
  459. System.out.println("[SimpleServer] Could not listen on port " + port
  460. + "!\nIs it already in use? Exiting application...");
  461. break;
  462. }
  463. System.out.println("[SimpleServer] Wrapper listening on "
  464. + socket.getInetAddress().getHostAddress() + ":"
  465. + socket.getLocalPort() + " (connect here)");
  466. if (socket.getInetAddress().getHostAddress().equals("0.0.0.0")) {
  467. System.out.println("[SimpleServer] Note: 0.0.0.0 means all"
  468. + " IP addresses; you want this.");
  469. }
  470. try {
  471. while (run) {
  472. Socket client;
  473. try {
  474. client = socket.accept();
  475. } catch (IOException e) {
  476. if (run && !restart) {
  477. System.out.println("[SimpleServer] " + e);
  478. System.out.println("[SimpleServer] Accept failed on port "
  479. + port + "!");
  480. }
  481. break;
  482. }
  483. new Player(client, Server.this);
  484. }
  485. } finally {
  486. try {
  487. socket.close();
  488. } catch (IOException e) {
  489. }
  490. }
  491. shutdown();
  492. }
  493. cleanup();
  494. }
  495. }
  496. public void setTime(long time) {
  497. this.time.is(time);
  498. }
  499. public long time() {
  500. return time.get();
  501. }
  502. public void setMapSeed(long seed) {
  503. if (mapSeed != seed) {
  504. mapSeed = seed;
  505. // System.out.println("[MAP SEED] " + mapSeed);
  506. }
  507. }
  508. public long getMapSeed() {
  509. return mapSeed;
  510. }
  511. public static final class LocalAddressFactory {
  512. private static final int[] octets = { 0, 0, 1 };
  513. private static Boolean canCycle = null;
  514. private static boolean enabled = true;
  515. private void toggle(boolean enabled) {
  516. LocalAddressFactory.enabled = enabled;
  517. }
  518. public synchronized String getNextAddress() {
  519. if (!enabled || !canCycle()) {
  520. return "127.0.0.1";
  521. }
  522. if (octets[2] >= 255) {
  523. if (octets[1] >= 255) {
  524. if (octets[0] >= 255) {
  525. octets[0] = 0;
  526. } else {
  527. ++octets[0];
  528. }
  529. octets[1] = 0;
  530. } else {
  531. ++octets[1];
  532. }
  533. octets[2] = 2;
  534. } else {
  535. ++octets[2];
  536. }
  537. return "127." + octets[0] + "." + octets[1] + "." + octets[2];
  538. }
  539. private boolean canCycle() {
  540. if (canCycle == null) {
  541. InetAddress testDestination;
  542. InetAddress testSource;
  543. try {
  544. testDestination = InetAddress.getByName(null);
  545. testSource = InetAddress.getByName("127.0.1.2");
  546. } catch (UnknownHostException e) {
  547. canCycle = false;
  548. return false;
  549. }
  550. try {
  551. Socket testSocket = new Socket(testDestination, 80, testSource, 0);
  552. testSocket.close();
  553. } catch (BindException e) {
  554. canCycle = false;
  555. return false;
  556. } catch (IOException e) {
  557. // Probably nothing listening on port 80
  558. }
  559. canCycle = true;
  560. }
  561. return canCycle;
  562. }
  563. }
  564. }