PageRenderTime 67ms CodeModel.GetById 33ms RepoModel.GetById 1ms app.codeStats 0ms

/src/simpleserver/bot/Bot.java

https://github.com/Simoyd/SimpleServer
Java | 639 lines | 582 code | 37 blank | 20 comment | 34 complexity | 520f8d3f2ea605b7d036e179ad27a3cd 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.bot;
  22. import java.io.BufferedInputStream;
  23. import java.io.BufferedOutputStream;
  24. import java.io.DataInputStream;
  25. import java.io.DataOutputStream;
  26. import java.io.File;
  27. import java.io.IOException;
  28. import java.net.InetAddress;
  29. import java.net.Socket;
  30. import java.net.UnknownHostException;
  31. import java.util.concurrent.locks.ReentrantLock;
  32. import simpleserver.Coordinate.Dimension;
  33. import simpleserver.Position;
  34. import simpleserver.Server;
  35. public class Bot {
  36. private static final int VERSION = 17;
  37. protected String name;
  38. protected Server server;
  39. private boolean connected;
  40. private boolean expectDisconnect;
  41. protected boolean ready;
  42. protected boolean dead;
  43. private Socket socket;
  44. protected DataInputStream in;
  45. protected DataOutputStream out;
  46. ReentrantLock writeLock;
  47. protected Position position;
  48. protected BotController controller;
  49. private byte lastPacket;
  50. private short health;
  51. public Bot(Server server, String name) {
  52. this.name = name;
  53. this.server = server;
  54. position = new Position();
  55. }
  56. void connect() throws UnknownHostException, IOException {
  57. try {
  58. InetAddress localAddress = InetAddress.getByName(Server.addressFactory.getNextAddress());
  59. socket = new Socket(InetAddress.getByName(null), server.options.getInt("internalPort"), localAddress, 0);
  60. } catch (Exception e) {
  61. socket = new Socket(InetAddress.getByName(null), server.options.getInt("internalPort"));
  62. }
  63. in = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
  64. out = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));
  65. writeLock = new ReentrantLock();
  66. connected = true;
  67. new Tunneler().start();
  68. handshake();
  69. }
  70. boolean ninja() {
  71. return false;
  72. }
  73. protected void positionUpdate() throws IOException {
  74. }
  75. private void keepAlive(int keepAliveId) throws IOException {
  76. writeLock.lock();
  77. out.writeByte(0x0);
  78. out.writeInt(keepAliveId);
  79. writeLock.unlock();
  80. }
  81. private void handshake() throws IOException {
  82. writeLock.lock();
  83. out.writeByte(2);
  84. write(name);
  85. out.flush();
  86. writeLock.unlock();
  87. }
  88. public void logout() throws IOException {
  89. die();
  90. expectDisconnect = true;
  91. out.writeByte(0xff);
  92. write("quitting");
  93. out.flush();
  94. }
  95. protected void login() throws IOException {
  96. writeLock.lock();
  97. out.writeByte(1);
  98. out.writeInt(VERSION);
  99. write(name);
  100. out.writeLong(0);
  101. out.writeInt(0);
  102. out.writeByte(0);
  103. out.writeByte(0);
  104. out.writeByte(0);
  105. out.writeByte(0);
  106. writeLock.unlock();
  107. }
  108. private void respawn() throws IOException {
  109. writeLock.lock();
  110. out.writeByte(9);
  111. out.writeByte(position.dimension.index());
  112. out.writeByte(0);
  113. out.writeShort(128);
  114. out.writeLong(0);
  115. writeLock.unlock();
  116. }
  117. protected void ready() throws IOException {
  118. ready = true;
  119. }
  120. protected void walk(double d) {
  121. double heading = position.yaw * Math.PI / 180;
  122. position.x -= Math.sin(heading) * d;
  123. position.z += Math.cos(heading) * d;
  124. }
  125. protected void ascend(double d) {
  126. position.y += d;
  127. position.stance += d;
  128. if (position.stance - position.y > 1.6 || position.stance - position.y < 0.15) {
  129. position.stance = position.y + 0.5;
  130. }
  131. }
  132. protected void sendPosition() throws IOException {
  133. writeLock.lock();
  134. position.send(out);
  135. writeLock.unlock();
  136. }
  137. protected boolean trashdat() {
  138. return true;
  139. }
  140. protected void handlePacket(byte packetId) throws IOException {
  141. switch (packetId) {
  142. case 0x2:
  143. readUTF16();
  144. login();
  145. break;
  146. case 0x1:
  147. in.readInt();
  148. readUTF16();
  149. in.readLong();
  150. in.readInt();
  151. position.dimension = Dimension.get(in.readByte());
  152. in.readByte();
  153. in.readByte();
  154. in.readByte();
  155. break;
  156. case 0x0d: // Player Position & Look
  157. double x = in.readDouble();
  158. double stance = in.readDouble();
  159. double y = in.readDouble();
  160. double z = in.readDouble();
  161. float yaw = in.readFloat();
  162. float pitch = in.readFloat();
  163. boolean onGround = in.readBoolean();
  164. position.updatePosition(x, y, z, stance);
  165. position.updateLook(yaw, pitch);
  166. position.updateGround(onGround);
  167. if (!ready) {
  168. sendPosition();
  169. ready();
  170. } else if (dead) {
  171. sendPosition();
  172. dead = false;
  173. }
  174. positionUpdate();
  175. break;
  176. case 0x0b: // Player Position
  177. double x2 = in.readDouble();
  178. double stance2 = in.readDouble();
  179. double y2 = in.readDouble();
  180. double z2 = in.readDouble();
  181. boolean onGround2 = in.readBoolean();
  182. position.updatePosition(x2, y2, z2, stance2);
  183. position.updateGround(onGround2);
  184. positionUpdate();
  185. break;
  186. case (byte) 0xff: // Disconnect/Kick
  187. String reason = readUTF16();
  188. error(reason);
  189. break;
  190. case 0x00: // Keep Alive
  191. keepAlive(in.readInt());
  192. break;
  193. case 0x03: // Chat Message
  194. readUTF16();
  195. break;
  196. case 0x04: // Time Update
  197. in.readLong();
  198. break;
  199. case 0x05: // Player Inventory
  200. in.readInt();
  201. in.readShort();
  202. in.readShort();
  203. in.readShort();
  204. break;
  205. case 0x06: // Spawn Position
  206. readNBytes(12);
  207. break;
  208. case 0x07: // Use Entity?
  209. in.readInt();
  210. in.readInt();
  211. in.readBoolean();
  212. in.readBoolean();
  213. break;
  214. case 0x08: // Update Health
  215. health = in.readShort();
  216. in.readShort();
  217. in.readFloat();
  218. if (health <= 0) {
  219. dead = true;
  220. respawn();
  221. }
  222. break;
  223. case 0x09: // Respawn
  224. position.dimension = Dimension.get(in.readByte());
  225. in.readByte();
  226. in.readByte();
  227. in.readShort();
  228. in.readLong();
  229. break;
  230. case 0x0a: // Player
  231. in.readByte();
  232. break;
  233. case 0x0c: // Player Look
  234. readNBytes(9);
  235. break;
  236. case 0x0e: // Player Digging
  237. in.readByte();
  238. in.readInt();
  239. in.readByte();
  240. in.readInt();
  241. in.readByte();
  242. break;
  243. case 0x0f: // Player Block Placement
  244. in.readInt();
  245. in.readByte();
  246. in.readInt();
  247. in.readByte();
  248. final short dropItem = in.readShort();
  249. if (dropItem != -1) {
  250. in.readByte();
  251. in.readShort();
  252. }
  253. break;
  254. case 0x10: // Holding Change
  255. readNBytes(2);
  256. break;
  257. case 0x11: // Use Bed
  258. readNBytes(14);
  259. break;
  260. case 0x12: // Animation
  261. readNBytes(5);
  262. break;
  263. case 0x13: // ???
  264. in.readInt();
  265. in.readByte();
  266. break;
  267. case 0x14: // Named Entity Spawn
  268. in.readInt();
  269. readUTF16();
  270. readNBytes(16);
  271. break;
  272. case 0x15: // Pickup spawn
  273. readNBytes(24);
  274. break;
  275. case 0x16: // Collect Item
  276. readNBytes(8);
  277. break;
  278. case 0x17: // Add Object/Vehicle
  279. in.readInt();
  280. in.readByte();
  281. in.readInt();
  282. in.readInt();
  283. in.readInt();
  284. int flag = in.readInt();
  285. if (flag > 0) {
  286. in.readShort();
  287. in.readShort();
  288. in.readShort();
  289. }
  290. break;
  291. case 0x18: // Mob Spawn
  292. in.readInt();
  293. in.readByte();
  294. in.readInt();
  295. in.readInt();
  296. in.readInt();
  297. in.readByte();
  298. in.readByte();
  299. readUnknownBlob();
  300. break;
  301. case 0x19: // Painting
  302. in.readInt();
  303. readUTF16();
  304. in.readInt();
  305. in.readInt();
  306. in.readInt();
  307. in.readInt();
  308. break;
  309. case 0x1a:
  310. in.readInt();
  311. in.readInt();
  312. in.readInt();
  313. in.readInt();
  314. in.readShort();
  315. break;
  316. case 0x1b: // ???
  317. readNBytes(18);
  318. break;
  319. case 0x1c: // Entity Velocity?
  320. readNBytes(10);
  321. break;
  322. case 0x1d: // Destroy Entity
  323. readNBytes(4);
  324. break;
  325. case 0x1e: // Entity
  326. readNBytes(4);
  327. break;
  328. case 0x1f: // Entity Relative Move
  329. readNBytes(7);
  330. break;
  331. case 0x20: // Entity Look
  332. readNBytes(6);
  333. break;
  334. case 0x21: // Entity Look and Relative Move
  335. readNBytes(9);
  336. break;
  337. case 0x22: // Entity Teleport
  338. readNBytes(18);
  339. break;
  340. case 0x26: // Entity status?
  341. readNBytes(5);
  342. break;
  343. case 0x27: // Attach Entity?
  344. readNBytes(8);
  345. break;
  346. case 0x28: // Entity Metadata
  347. in.readInt();
  348. readUnknownBlob();
  349. break;
  350. case 0x29: // new in 1.8, add status effect (41)
  351. in.readInt();
  352. in.readByte();
  353. in.readByte();
  354. in.readShort();
  355. break;
  356. case 0x2a: // new in 1.8, remove status effect (42)
  357. in.readInt();
  358. in.readByte();
  359. break;
  360. case 0x2b: // experience
  361. in.readByte();
  362. in.readByte();
  363. in.readShort();
  364. break;
  365. case 0x32: // Pre-Chunk
  366. readNBytes(9);
  367. break;
  368. case 0x33: // Map Chunk
  369. readNBytes(13);
  370. int chunkSize = in.readInt();
  371. readNBytes(chunkSize);
  372. break;
  373. case 0x34: // Multi Block Change
  374. readNBytes(8);
  375. short arraySize = in.readShort();
  376. readNBytes(arraySize * 4);
  377. break;
  378. case 0x35: // Block Change
  379. in.readInt();
  380. in.readByte();
  381. in.readInt();
  382. in.readByte();
  383. in.readByte();
  384. break;
  385. case 0x36: // ???
  386. readNBytes(12);
  387. break;
  388. case 0x3c: // Explosion
  389. readNBytes(28);
  390. int recordCount = in.readInt();
  391. readNBytes(recordCount * 3);
  392. break;
  393. case 0x3d: // Unknown
  394. in.readInt();
  395. in.readInt();
  396. in.readByte();
  397. in.readInt();
  398. in.readInt();
  399. break;
  400. case 0x46: // Invalid Bed
  401. readNBytes(2);
  402. break;
  403. case 0x47: // Thunder
  404. readNBytes(17);
  405. break;
  406. case 0x64: // Open window
  407. in.readByte();
  408. in.readByte();
  409. readUTF16();
  410. in.readByte();
  411. break;
  412. case 0x65:
  413. in.readByte();
  414. break;
  415. case 0x66: // Inventory Item Move
  416. in.readByte();
  417. in.readShort();
  418. in.readByte();
  419. in.readShort();
  420. in.readBoolean();
  421. short moveItem = in.readShort();
  422. if (moveItem != -1) {
  423. in.readByte();
  424. in.readShort();
  425. }
  426. break;
  427. case 0x67: // Inventory Item Update
  428. in.readByte();
  429. in.readShort();
  430. short setItem = in.readShort();
  431. if (setItem != -1) {
  432. in.readByte();
  433. in.readShort();
  434. }
  435. break;
  436. case 0x68: // Inventory
  437. in.readByte();
  438. short count = in.readShort();
  439. for (int c = 0; c < count; ++c) {
  440. short item = in.readShort();
  441. if (item != -1) {
  442. in.readByte();
  443. in.readShort();
  444. }
  445. }
  446. break;
  447. case 0x69:
  448. in.readByte();
  449. in.readShort();
  450. in.readShort();
  451. break;
  452. case 0x6a: // item transaction
  453. in.readByte();
  454. in.readShort();
  455. in.readByte();
  456. break;
  457. case 0x6b: // creative item get
  458. in.readShort();
  459. in.readShort();
  460. in.readShort();
  461. in.readShort();
  462. break;
  463. case (byte) 0x82: // Update Sign
  464. in.readInt();
  465. in.readShort();
  466. in.readInt();
  467. readUTF16();
  468. readUTF16();
  469. readUTF16();
  470. readUTF16();
  471. break;
  472. case (byte) 0x83: // Map data
  473. in.readShort();
  474. in.readShort();
  475. byte length = in.readByte();
  476. readNBytes(0xff & length);
  477. break;
  478. case (byte) 0xc8: // Statistic
  479. readNBytes(5);
  480. break;
  481. case (byte) 0xc9: // User list
  482. readUTF16();
  483. in.readBoolean();
  484. in.readShort();
  485. break;
  486. case (byte) 0xe6: // ModLoaderMP by SDK
  487. in.readInt(); // mod
  488. in.readInt(); // packet id
  489. readNBytes(in.readInt() * 4); // ints
  490. readNBytes(in.readInt() * 4); // floats
  491. int sizeString = in.readInt(); // strings
  492. for (int i = 0; i < sizeString; i++) {
  493. readNBytes(in.readInt());
  494. }
  495. break;
  496. case (byte) 0xfe:
  497. break;
  498. default:
  499. error("Unable to handle packet 0x" + Integer.toHexString(packetId)
  500. + " after 0x" + Integer.toHexString(lastPacket));
  501. }
  502. lastPacket = packetId;
  503. }
  504. private void readUnknownBlob() throws IOException {
  505. byte unknown = in.readByte();
  506. while (unknown != 0x7f) {
  507. int type = (unknown & 0xE0) >> 5;
  508. switch (type) {
  509. case 0:
  510. in.readByte();
  511. break;
  512. case 1:
  513. in.readShort();
  514. break;
  515. case 2:
  516. in.readInt();
  517. break;
  518. case 3:
  519. in.readFloat();
  520. break;
  521. case 4:
  522. readUTF16();
  523. break;
  524. case 5:
  525. in.readShort();
  526. in.readByte();
  527. in.readShort();
  528. break;
  529. case 6:
  530. in.readInt();
  531. in.readInt();
  532. in.readInt();
  533. }
  534. unknown = in.readByte();
  535. }
  536. }
  537. protected String write(String s) throws IOException {
  538. byte[] bytes = s.getBytes("UTF-16");
  539. if (s.length() == 0) {
  540. out.write((byte) 0x00);
  541. out.write((byte) 0x00);
  542. return s;
  543. }
  544. bytes[0] = (byte) ((s.length() >> 8) & 0xFF);
  545. bytes[1] = (byte) ((s.length() & 0xFF));
  546. for (byte b : bytes) {
  547. out.write(b);
  548. }
  549. return s;
  550. }
  551. protected String readUTF16() throws IOException {
  552. short length = in.readShort();
  553. byte[] bytes = new byte[length * 2 + 2];
  554. for (short i = 0; i < length * 2; i++) {
  555. bytes[i + 2] = in.readByte();
  556. }
  557. bytes[0] = (byte) 0xfffffffe;
  558. bytes[1] = (byte) 0xffffffff;
  559. return new String(bytes, "UTF-16");
  560. }
  561. private void readNBytes(int bytes) throws IOException {
  562. for (int c = 0; c < bytes; ++c) {
  563. in.readByte();
  564. }
  565. }
  566. protected void die() {
  567. connected = false;
  568. if (controller != null) {
  569. controller.remove(this);
  570. }
  571. if (trashdat()) {
  572. File dat = new File(server.options.get("levelName") + File.separator + "players" + File.separator + name + ".dat");
  573. if (controller != null) {
  574. controller.trash(dat);
  575. } else {
  576. dat.delete();
  577. }
  578. }
  579. }
  580. protected void error(String reason) {
  581. die();
  582. if (!expectDisconnect) {
  583. System.out.print("[SimpleServer] Bot " + name + " died (" + reason + ")");
  584. }
  585. }
  586. public void setController(BotController controller) {
  587. this.controller = controller;
  588. }
  589. private final class Tunneler extends Thread {
  590. @Override
  591. public void run() {
  592. while (connected) {
  593. try {
  594. handlePacket(in.readByte());
  595. out.flush();
  596. } catch (IOException e) {
  597. error("Soket closed");
  598. }
  599. }
  600. }
  601. }
  602. }