PageRenderTime 155ms CodeModel.GetById 0ms RepoModel.GetById 0ms app.codeStats 1ms

/src/etc.java

https://github.com/kpfile/Minecraft-Server-Mod
Java | 824 lines | 531 code | 69 blank | 224 comment | 202 complexity | 7780cd828180fdfc66152958a78fc0dc MD5 | raw file
  1. /* Just a general storage for all my crap */
  2. import java.io.File;
  3. import java.util.ArrayList;
  4. import java.util.LinkedHashMap;
  5. import java.util.logging.Level;
  6. import java.util.logging.Logger;
  7. import net.minecraft.server.*;
  8. /**
  9. * etc.java - My catch-all class for a bunch of shit. If there's something you
  10. * need it's probably in here.
  11. * @author James
  12. */
  13. public class etc {
  14. private static final Logger log = Logger.getLogger("Minecraft");
  15. private static volatile etc instance;
  16. private static MinecraftServer server;
  17. private ArrayList<String> muted = new ArrayList<String>();
  18. /**
  19. *
  20. */
  21. /**
  22. *
  23. */
  24. /**
  25. *
  26. */
  27. /**
  28. *
  29. */
  30. /**
  31. *
  32. */
  33. /**
  34. *
  35. */
  36. /**
  37. *
  38. */
  39. public String usersLoc = "users.txt", kitsLoc = "kits.txt", homeLoc = "homes.txt", warpLoc = "warps.txt", itemLoc = "items.txt", groupLoc = "groups.txt", commandsLoc = "commands.txt";
  40. /**
  41. *
  42. */
  43. /**
  44. *
  45. */
  46. public String whitelistLoc = "whitelist.txt", reservelistLoc = "reservelist.txt";
  47. /**
  48. *
  49. */
  50. public String whitelistMessage = "Not on whitelist.";
  51. /**
  52. *
  53. */
  54. public String[] allowedItems = null;
  55. /**
  56. *
  57. */
  58. public String[] disallowedItems = null;
  59. /**
  60. *
  61. */
  62. public String[] itemSpawnBlacklist = null;
  63. /**
  64. *
  65. */
  66. public String[] motd = null;
  67. /**
  68. *
  69. */
  70. public boolean saveHomes = true;
  71. /**
  72. *
  73. */
  74. public boolean firstLoad = true;
  75. /**
  76. *
  77. */
  78. public boolean whitelistEnabled = false;
  79. /**
  80. *
  81. */
  82. public int playerLimit = 20;
  83. /**
  84. *
  85. */
  86. public int spawnProtectionSize = 16;
  87. /**
  88. *
  89. */
  90. public long sleepTime = 30000;
  91. /**
  92. *
  93. */
  94. public long saveInterval = 1800000;
  95. /**
  96. *
  97. */
  98. public LinkedHashMap<String, String> commands = new LinkedHashMap<String, String>();
  99. private String dataSourceType;
  100. private ReloadThread reloadThread;
  101. private SaveAllThread saveThread;
  102. private DataSource dataSource;
  103. private PropertiesFile properties;
  104. private PluginLoader loader;
  105. private boolean logging = false;
  106. private etc() {
  107. commands.put("/help", "[Page] - Shows a list of commands. 7 per page.");
  108. commands.put("/playerlist", "- Shows a list of players");
  109. commands.put("/reload", "- Reloads config");
  110. commands.put("/listbans", "<IP or bans> - Gives a list of bans");
  111. commands.put("/banip", "[Player] <Reason> - Bans the player's IP");
  112. commands.put("/unbanip", "[IP] - Unbans the IP");
  113. commands.put("/ban", "[Player] <Reason> - Bans the player");
  114. commands.put("/unban", "[Player] - Unbans the player");
  115. commands.put("/mute", "[Player] - Toggles mute on player.");
  116. commands.put("/tp", "[Player] - Teleports to player. Credits to Zet from SA");
  117. commands.put("/tphere", "[Player] - Teleports the player to you");
  118. commands.put("/kick", "[Player] <Reason> - Kicks player");
  119. commands.put("/item", "[ID] [Amount] <Player> - Gives items");
  120. commands.put("/kit", "[Kit] - Gives a kit. To get a list of kits type /kit");
  121. commands.put("/listwarps", "- Gives a list of available warps");
  122. commands.put("/home", "- Teleports you home");
  123. commands.put("/sethome", "- Sets your home");
  124. commands.put("/setspawn", "- Sets the spawn point to your position.");
  125. commands.put("/me", "[Message] - * hey0 says hi!");
  126. commands.put("/msg", "[Player] [Message] - Sends a message to player");
  127. commands.put("/spawn", "- Teleports you to spawn");
  128. commands.put("/warp", "[Warp] - Warps to the specified warp.");
  129. commands.put("/setwarp", "[Warp] - Sets the warp to your current position.");
  130. commands.put("/removewarp", "[Warp] - Removes the specified warp.");
  131. commands.put("/getpos", "- Displays your current position.");
  132. commands.put("/compass", "- Gives you a compass reading.");
  133. commands.put("/time", "[Time|day|night] - Changes time");
  134. commands.put("/lighter", "- Gives you a lighter for lighting furnaces");
  135. commands.put("/motd", "- Displays the MOTD");
  136. commands.put("/modify", "[player] [key] [value] - Type /modify for more info");
  137. commands.put("/whitelist", "[operation (add or remove)] [player]");
  138. commands.put("/reservelist", "[operation (add or remove)] [player]");
  139. load();
  140. }
  141. /**
  142. * Loads or reloads the mod
  143. */
  144. public final void load() {
  145. if (properties == null) {
  146. properties = new PropertiesFile("server.properties");
  147. } else {
  148. properties.load();
  149. }
  150. try {
  151. dataSourceType = properties.getString("data-source", "flatfile");
  152. allowedItems = properties.getString("alloweditems", "").split(",");
  153. disallowedItems = properties.getString("disalloweditems", "").split(",");
  154. itemSpawnBlacklist = properties.getString("itemspawnblacklist", "").split(",");
  155. motd = properties.getString("motd", "Type /help for a list of commands.").split("@");
  156. playerLimit = properties.getInt("max-players", 20);
  157. saveHomes = properties.getBoolean("save-homes", true);
  158. whitelistEnabled = properties.getBoolean("whitelist", false);
  159. whitelistMessage = properties.getString("whitelist-message", "Not on whitelist.");
  160. if (dataSourceType.equalsIgnoreCase("flatfile")) {
  161. usersLoc = properties.getString("admintxtlocation", "users.txt");
  162. kitsLoc = properties.getString("kitstxtlocation", "kits.txt");
  163. homeLoc = properties.getString("homelocation", "homes.txt");
  164. warpLoc = properties.getString("warplocation", "warps.txt");
  165. itemLoc = properties.getString("itemstxtlocation", "items.txt");
  166. groupLoc = properties.getString("group-txt-location", "groups.txt");
  167. whitelistLoc = properties.getString("whitelist-txt-location", "whitelist.txt");
  168. reservelistLoc = properties.getString("reservelist-txt-location", "reservelist.txt");
  169. }
  170. spawnProtectionSize = properties.getInt("spawn-protection-size", 16);
  171. sleepTime = properties.getLong("reload-interval", 30000);
  172. saveInterval = properties.getLong("save-interval", 1800000);
  173. logging = properties.getBoolean("logging", false);
  174. } catch (Exception e) {
  175. log.log(Level.SEVERE, "Exception while reading from server.properties", e);
  176. // Just in case...
  177. disallowedItems = new String[]{""};
  178. allowedItems = new String[]{""};
  179. itemSpawnBlacklist = new String[]{""};
  180. motd = new String[]{"Type /help for a list of commands."};
  181. }
  182. }
  183. /**
  184. * Loads or reloads the data source
  185. */
  186. public void loadData() {
  187. if (dataSourceType.equalsIgnoreCase("flatfile") && dataSource == null) {
  188. dataSource = new FlatFileSource();
  189. } else if (dataSourceType.equalsIgnoreCase("mysql") && dataSource == null) {
  190. dataSource = new MySQLSource();
  191. }
  192. dataSource.initialize();
  193. }
  194. /**
  195. * Returns the instance
  196. * @return
  197. */
  198. public static etc getInstance() {
  199. if (instance == null) {
  200. instance = new etc();
  201. }
  202. return instance;
  203. }
  204. /**
  205. * Sets the server to be used.
  206. * @param s
  207. */
  208. public static void setServer(MinecraftServer s) {
  209. server = s;
  210. }
  211. /**
  212. * Returns the minecraft server
  213. * @return
  214. */
  215. public static MinecraftServer getMCServer() {
  216. return server;
  217. }
  218. /**
  219. * Returns the minecraft server interface
  220. * @return
  221. */
  222. public static Server getServer() {
  223. return getInstance().getLoader().getServer();
  224. }
  225. /**
  226. * Returns the plugin loader
  227. * @return
  228. */
  229. public PluginLoader getLoader() {
  230. if (loader == null) {
  231. loader = new PluginLoader(server);
  232. loader.load();
  233. }
  234. return loader;
  235. }
  236. /**
  237. * Starts the save and reload thread
  238. * @param paramMinecraftServer
  239. */
  240. public void startThreads(MinecraftServer paramMinecraftServer) {
  241. if (saveInterval > 0 && saveThread == null) {
  242. saveThread = new SaveAllThread(paramMinecraftServer, saveInterval);
  243. saveThread.start();
  244. }
  245. if (sleepTime > 0 && reloadThread == null) {
  246. reloadThread = new ReloadThread(sleepTime);
  247. reloadThread.start();
  248. }
  249. }
  250. /**
  251. * Checks to see if specified user is in the specified group
  252. * @param player
  253. * @param group
  254. * @return
  255. */
  256. public boolean isUserInGroup(Player player, String group) {
  257. return isUserInGroup(player.getName(), group);
  258. }
  259. /**
  260. * Checks to see if specified user is in the specified group
  261. * @param e
  262. * @param group
  263. * @return
  264. */
  265. public boolean isUserInGroup(ea e, String group) {
  266. return isUserInGroup(e.aq, group);
  267. }
  268. /**
  269. * Checks to see if specified user is in the specified group
  270. * @param name
  271. * @param group
  272. * @return
  273. */
  274. public boolean isUserInGroup(String name, String group) {
  275. if (group != null) {
  276. if (getDefaultGroup() != null) {
  277. if (group.equalsIgnoreCase(getDefaultGroup().Name)) {
  278. return true;
  279. }
  280. } else {
  281. log.info("There's no default group.");
  282. }
  283. }
  284. User user = getUser(name);
  285. if (user != null) {
  286. for (String str : user.Groups) {
  287. if (recursiveUserInGroup(dataSource.getGroup(str), group)) {
  288. return true;
  289. }
  290. }
  291. }
  292. return false;
  293. }
  294. private boolean recursiveUserInGroup(Group g, String group) {
  295. if (g == null || group == null) {
  296. return false;
  297. }
  298. if (g.Name.equalsIgnoreCase(group)) {
  299. return true;
  300. }
  301. if (g.InheritedGroups != null) {
  302. for (String str : g.InheritedGroups) {
  303. if (g.Name.equalsIgnoreCase(str)) {
  304. return true;
  305. }
  306. Group g2 = dataSource.getGroup(str);
  307. if (g2 != null) {
  308. if (recursiveUserInGroup(g2, group)) {
  309. return true;
  310. }
  311. }
  312. }
  313. }
  314. return false;
  315. }
  316. /**
  317. * Returns user
  318. * @param name
  319. * @return user
  320. */
  321. public User getUser(String name) {
  322. return dataSource.getUser(name);
  323. }
  324. /**
  325. * Returns the default group
  326. * @return default group
  327. */
  328. public Group getDefaultGroup() {
  329. Group group = dataSource.getDefaultGroup();
  330. if (group == null)
  331. log.log(Level.SEVERE, "No default group! Expect lots of errors!");
  332. return group;
  333. }
  334. /**
  335. * The user's color or prefix
  336. * @param name
  337. * @return color/prefix
  338. */
  339. public String getUserColor(String name) {
  340. User user = getUser(name);
  341. if (user != null) {
  342. if (user.Prefix != null) {
  343. if (!user.Prefix.equals("")) {
  344. return "§" + user.Prefix;
  345. }
  346. }
  347. Group group = dataSource.getGroup(user.Groups[0]);
  348. if (group != null) {
  349. return "§" + group.Prefix;
  350. }
  351. }
  352. Group def = getDefaultGroup();
  353. return def != null ? "§" + def.Prefix : "";
  354. }
  355. /**
  356. * Returns true if the player can use the specified command
  357. * @param name
  358. * @param command
  359. * @return
  360. */
  361. public boolean canUseCommand(String name, String command) {
  362. User user = getUser(name);
  363. //holy motherfuck
  364. if (user != null) {
  365. for (String str : user.Commands) {
  366. if (str.equalsIgnoreCase(command)) {
  367. return true;
  368. }
  369. }
  370. for (String str : user.Groups) {
  371. Group g = dataSource.getGroup(str);
  372. if (g != null) {
  373. if (recursiveUseCommand(g, command)) {
  374. return true;
  375. }
  376. }
  377. }
  378. }
  379. Group def = getDefaultGroup();
  380. if (def != null) {
  381. if (recursiveUseCommand(def, command)) {
  382. return true;
  383. }
  384. } else {
  385. log.info("No default group.");
  386. }
  387. return false;
  388. }
  389. private boolean recursiveUseCommand(Group g, String command) {
  390. for (String str : g.Commands) {
  391. if (str.equalsIgnoreCase(command) || str.equals("*")) {
  392. return true;
  393. }
  394. }
  395. if (g.InheritedGroups != null) {
  396. for (String str : g.InheritedGroups) {
  397. Group g2 = dataSource.getGroup(str);
  398. if (g2 != null) {
  399. if (recursiveUseCommand(g2, command)) {
  400. return true;
  401. }
  402. }
  403. }
  404. }
  405. return false;
  406. }
  407. /**
  408. * Returns true if the player's an administrator
  409. * @param player
  410. * @return
  411. */
  412. public boolean isAdmin(ea player) {
  413. return isAdmin(player.aq);
  414. }
  415. /**
  416. * Returns true if the player's an administrator
  417. * @param player
  418. * @return
  419. */
  420. public boolean isAdmin(String player) {
  421. User user = getUser(player);
  422. if (user == null) {
  423. return false;
  424. }
  425. if (user.Administrator) {
  426. return true;
  427. }
  428. for (String str : user.Groups) {
  429. Group group = dataSource.getGroup(str);
  430. if (group != null) {
  431. if (group.Administrator) {
  432. return true;
  433. }
  434. }
  435. }
  436. return false;
  437. }
  438. /**
  439. * Returns true if the player can ignore restrictions
  440. * @param player
  441. * @return
  442. */
  443. public boolean canIgnoreRestrictions(ea player) {
  444. return canIgnoreRestrictions(player.aq);
  445. }
  446. /**
  447. * Returns true if the player can ignore restrictions
  448. * @param player
  449. * @return
  450. */
  451. public boolean canIgnoreRestrictions(String player) {
  452. User user = getUser(player);
  453. if (user == null) {
  454. return false;
  455. }
  456. if (user.Administrator || user.IgnoreRestrictions) {
  457. return true;
  458. }
  459. for (String str : user.Groups) {
  460. Group group = dataSource.getGroup(str);
  461. if (group != null) {
  462. if (group.Administrator || group.IgnoreRestrictions) {
  463. return true;
  464. }
  465. }
  466. }
  467. return false;
  468. }
  469. /**
  470. * Returns false if the player can not build and can not modify chests or
  471. * furnaces
  472. * @param player
  473. * @return
  474. */
  475. public boolean canBuild(ea player) {
  476. User user = getUser(player.aq);
  477. if (user == null) {
  478. return getDefaultGroup().CanModifyWorld;
  479. }
  480. if (!user.CanModifyWorld) {
  481. return false;
  482. }
  483. for (String str : user.Groups) {
  484. Group group = dataSource.getGroup(str);
  485. if (group != null) {
  486. if (!group.CanModifyWorld) {
  487. return false;
  488. }
  489. }
  490. }
  491. return true;
  492. }
  493. /**
  494. * Returns true if the player is muted
  495. * @param e
  496. * @return
  497. */
  498. public boolean isMuted(ea e) {
  499. return muted.contains(e.aq);
  500. }
  501. /**
  502. * Toggles mute for specified player
  503. * @param e
  504. * @return
  505. */
  506. public boolean toggleMute(ea e) {
  507. if (muted.contains(e.aq)) {
  508. muted.remove(e.aq);
  509. } else {
  510. muted.add(e.aq);
  511. }
  512. return muted.contains(e.aq);
  513. }
  514. /**
  515. * Adds or modifies the home.
  516. * @param home
  517. */
  518. public void changeHome(Warp home) {
  519. if (dataSource.getHome(home.Name) == null) {
  520. dataSource.addHome(home);
  521. } else {
  522. dataSource.changeHome(home);
  523. }
  524. }
  525. /**
  526. * Adds or modifies the warp
  527. * @param warp
  528. */
  529. public void setWarp(Warp warp) {
  530. if (dataSource.getWarp(warp.Name) == null) {
  531. dataSource.addWarp(warp);
  532. } else {
  533. dataSource.changeWarp(warp);
  534. }
  535. }
  536. /**
  537. * Returns true if the item is on the blacklist
  538. * @param id
  539. * @return
  540. */
  541. public boolean isOnItemBlacklist(int id) {
  542. for (String str : itemSpawnBlacklist) {
  543. if (Integer.toString(id).equalsIgnoreCase(str)) {
  544. return true;
  545. }
  546. }
  547. return false;
  548. }
  549. /**
  550. * Returns the data source
  551. * @return
  552. */
  553. public DataSource getDataSource() {
  554. return dataSource;
  555. }
  556. /**
  557. * Returns true if we're logging commands and such
  558. * @return
  559. */
  560. public boolean isLogging() {
  561. return logging;
  562. }
  563. /**
  564. * Adds command to the /help list
  565. * @param command
  566. * @param description
  567. */
  568. public void addCommand(String command, String description) {
  569. commands.put(command, description);
  570. }
  571. /**
  572. * Removes command from /help list
  573. * @param command
  574. */
  575. public void removeCommand(String command) {
  576. commands.remove(command);
  577. }
  578. /**
  579. * Toggles the whitelist (doesn't persist)
  580. * @return
  581. */
  582. public boolean toggleWhitelist() {
  583. whitelistEnabled = !whitelistEnabled;
  584. return whitelistEnabled;
  585. }
  586. /**
  587. * Parses a console command
  588. * @param command
  589. * @param server
  590. * @return
  591. */
  592. public boolean parseConsoleCommand(String command, MinecraftServer server) {
  593. String[] split = command.split(" ");
  594. boolean dontParseRegular = true;
  595. if (split[0].equalsIgnoreCase("help") || split[0].equalsIgnoreCase("mod-help")) {
  596. if (split[0].equalsIgnoreCase("help")) {
  597. dontParseRegular = false;
  598. }
  599. log.info("Server mod help:");
  600. log.info("help Displays this mod's and server's help");
  601. log.info("mod-help Displays this mod's help");
  602. log.info("reload Reloads the config");
  603. log.info("modify Type modify for more info");
  604. log.info("whitelist Type whitelist for more info");
  605. log.info("reservelist Type reservelist for more info");
  606. } else if (split[0].equalsIgnoreCase("reload")) {
  607. load();
  608. loadData();
  609. log.info("Reloaded mod");
  610. } else if (split[0].equalsIgnoreCase("modify")) {
  611. if (split.length < 4) {
  612. log.info("Usage is: /modify [player] [key] [value]");
  613. log.info("Keys:");
  614. log.info("prefix: only the letter the color represents");
  615. log.info("commands: list seperated by comma");
  616. log.info("groups: list seperated by comma");
  617. log.info("ignoresrestrictions: true or false");
  618. log.info("admin: true or false");
  619. log.info("modworld: true or false");
  620. return true;
  621. }
  622. ea player = match(split[1], server);
  623. if (player == null) {
  624. log.info("Player does not exist.");
  625. return true;
  626. }
  627. String key = split[2];
  628. String value = split[3];
  629. User user = etc.getInstance().getUser(split[1]);
  630. boolean newUser = false;
  631. if (user == null) {
  632. if (!key.equalsIgnoreCase("groups") && !key.equalsIgnoreCase("g")) {
  633. log.info("When adding a new user, set their group(s) first.");
  634. return true;
  635. }
  636. log.info("Adding new user.");
  637. newUser = true;
  638. user = new User();
  639. user.Name = split[1];
  640. user.Administrator = false;
  641. user.CanModifyWorld = true;
  642. user.IgnoreRestrictions = false;
  643. user.Commands = new String[]{""};
  644. user.Prefix = "";
  645. }
  646. if (key.equalsIgnoreCase("prefix") || key.equalsIgnoreCase("p")) {
  647. user.Prefix = "§" + value;
  648. } else if (key.equalsIgnoreCase("commands") || key.equalsIgnoreCase("c")) {
  649. user.Commands = value.split(",");
  650. } else if (key.equalsIgnoreCase("groups") || key.equalsIgnoreCase("g")) {
  651. user.Groups = value.split(",");
  652. } else if (key.equalsIgnoreCase("ignoresrestrictions") || key.equalsIgnoreCase("ir")) {
  653. user.IgnoreRestrictions = value.equalsIgnoreCase("true") || value.equals("1");
  654. } else if (key.equalsIgnoreCase("admin") || key.equalsIgnoreCase("a")) {
  655. user.Administrator = value.equalsIgnoreCase("true") || value.equals("1");
  656. } else if (key.equalsIgnoreCase("modworld") || key.equalsIgnoreCase("mw")) {
  657. user.CanModifyWorld = value.equalsIgnoreCase("true") || value.equals("1");
  658. }
  659. if (newUser) {
  660. dataSource.addUser(user);
  661. } else {
  662. dataSource.modifyUser(user);
  663. }
  664. log.info("Modified user.");
  665. } else if (split[0].equalsIgnoreCase("whitelist")) {
  666. if (split.length < 2) {
  667. log.info("whitelist [operation (toggle, add or remove)] [player]");
  668. return true;
  669. }
  670. if (split[1].equalsIgnoreCase("toggle")) {
  671. log.info(toggleWhitelist() ? "Whitelist enabled" : "Whitelist disabled");
  672. } else if(split.length == 3) {
  673. if (split[1].equalsIgnoreCase("add")) {
  674. dataSource.addToWhitelist(split[2]);
  675. log.info(split[2] + " added to whitelist");
  676. } else if (split[1].equalsIgnoreCase("remove")) {
  677. dataSource.removeFromWhitelist(split[2]);
  678. log.info(split[2] + " removed from whitelist");
  679. } else {
  680. log.info("Invalid operation.");
  681. }
  682. } else {
  683. log.info("Invalid operation.");
  684. }
  685. } else if (split[0].equalsIgnoreCase("reservelist")) {
  686. if (split.length != 3) {
  687. log.info("reservelist [operation (add or remove)] [player]");
  688. return true;
  689. }
  690. if (split[1].equalsIgnoreCase("add")) {
  691. dataSource.addToReserveList(split[2]);
  692. log.info(split[2] + " added to reservelist");
  693. } else if (split[1].equalsIgnoreCase("remove")) {
  694. dataSource.removeFromReserveList(split[2]);
  695. log.info(split[2] + " removed from reservelist");
  696. } else {
  697. log.info("Invalid operation.");
  698. }
  699. } else {
  700. dontParseRegular = false;
  701. }
  702. return dontParseRegular;
  703. }
  704. private static ea match(String name, MinecraftServer d) {
  705. ea player = null;
  706. boolean found = false;
  707. if (("`" + d.f.c().toUpperCase() + "`").split(name.toUpperCase()).length == 2) {
  708. for (int i = 0; i < d.f.b.size() && !found; ++i) {
  709. ea localea = (ea) d.f.b.get(i);
  710. if (("`" + localea.aq.toUpperCase() + "`").split(name.toUpperCase()).length == 2) {
  711. player = localea;
  712. found = true;
  713. }
  714. }
  715. } else if (("`" + d.f.c() + "`").split(name).length > 2) {
  716. // Too many partial matches.
  717. for (int i = 0; i < d.f.b.size() && !found; ++i) {
  718. ea localea = (ea) d.f.b.get(i);
  719. if (localea.aq.equalsIgnoreCase(name)) {
  720. player = localea;
  721. found = true;
  722. }
  723. }
  724. }
  725. return player;
  726. }
  727. /**
  728. * Returns compass direction according to your rotation
  729. * @param degrees
  730. * @return
  731. */
  732. public static String getCompassPointForDirection(double degrees) {
  733. if (0 <= degrees && degrees < 22.5) {
  734. return "N";
  735. } else if (22.5 <= degrees && degrees < 67.5) {
  736. return "NE";
  737. } else if (67.5 <= degrees && degrees < 112.5) {
  738. return "E";
  739. } else if (112.5 <= degrees && degrees < 157.5) {
  740. return "SE";
  741. } else if (157.5 <= degrees && degrees < 202.5) {
  742. return "S";
  743. } else if (202.5 <= degrees && degrees < 247.5) {
  744. return "SW";
  745. } else if (247.5 <= degrees && degrees < 292.5) {
  746. return "W";
  747. } else if (292.5 <= degrees && degrees < 337.5) {
  748. return "NW";
  749. } else if (337.5 <= degrees && degrees < 360.0) {
  750. return "N";
  751. } else {
  752. return "ERR";
  753. }
  754. }
  755. }