Script.java 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821
  1. package me.km.snuviscript;
  2. import me.km.KajetansMod;
  3. import me.km.api.GlobalText;
  4. import me.km.api.Module;
  5. import me.km.exception.CodeTooLongException;
  6. import me.km.exception.GoHigherAtRootTreeException;
  7. import me.km.exception.GotoLabelNotFoundException;
  8. import me.km.exception.HoldCodeException;
  9. import me.km.exception.IllegalStringException;
  10. import me.km.exception.NoChildTreeException;
  11. import me.km.exception.PrescriptException;
  12. import java.io.File;
  13. import java.io.IOException;
  14. import java.nio.file.Files;
  15. import java.util.ArrayList;
  16. import java.util.Arrays;
  17. import java.util.EmptyStackException;
  18. import java.util.List;
  19. import java.util.HashMap;
  20. import java.util.HashSet;
  21. import java.util.Stack;
  22. import java.util.stream.Collectors;
  23. import me.km.api.Location;
  24. import net.minecraft.command.ICommandSender;
  25. import net.minecraft.entity.player.EntityPlayer;
  26. import net.minecraft.util.text.TextComponentString;
  27. public class Script
  28. {
  29. private final SnuviParser parser;
  30. private final BenchmarkClock treeClock;
  31. public final BenchmarkClock exeClock;
  32. private boolean benchmark;
  33. private final Tree<CodeFunction> code;
  34. private CodeFunction currentCode;
  35. private final HashMap<String, Object> variables;
  36. private final HashMap<String, Integer[]> gotos;
  37. private final Stack<Integer[]> stack;
  38. private final HashSet<String> events;
  39. private final HashSet<Location> loadedLocations;
  40. private boolean isWaiting;
  41. private boolean isTrying;
  42. private boolean tryFailed;
  43. private boolean shouldDoElse;
  44. private boolean isValid;
  45. private final Stack<Boolean> whileStack;
  46. private int overflowProtection;
  47. private String info;
  48. private String name;
  49. private final int id;
  50. private final boolean script;
  51. private int idCounter;
  52. private final ArrayList<EntityPlayer> quester;
  53. public Script(int id, EntityPlayer p, String filename, ICommandSender cs)
  54. {
  55. parser = KajetansMod.scripts.getQuestParser();
  56. treeClock = new BenchmarkClock();
  57. exeClock = new BenchmarkClock();
  58. benchmark = false;
  59. this.id = id;
  60. code = new Tree<>();
  61. idCounter = 0;
  62. variables = new HashMap<>();
  63. gotos = new HashMap<>();
  64. stack = new Stack<>();
  65. events = new HashSet<>();
  66. loadedLocations = new HashSet<>();
  67. whileStack = new Stack<>();
  68. name = filename;
  69. isWaiting = false;
  70. isValid = true;
  71. overflowProtection = 0;
  72. isTrying = false;
  73. quester = new ArrayList<>();
  74. info = "Keine Info wurde gesetzt.";
  75. if(p != null)
  76. {
  77. quester.add(p);
  78. script = false;
  79. }
  80. else
  81. {
  82. script = true;
  83. }
  84. readCode(filename, cs);
  85. setVar("quote", "\"");
  86. }
  87. // -------------------------------------------------------------------------
  88. // Überladen
  89. // -------------------------------------------------------------------------
  90. public void loadNewCode(String filename, ICommandSender cs)
  91. {
  92. code.clear();
  93. gotos.clear();
  94. stack.clear();
  95. whileStack.clear();
  96. name = filename;
  97. readCode(filename, cs);
  98. isWaiting = false;
  99. isValid = true;
  100. }
  101. // -------------------------------------------------------------------------
  102. // Flow-Control
  103. // -------------------------------------------------------------------------
  104. public void goDeeper(boolean b) throws NoChildTreeException
  105. {
  106. code.goDeeper();
  107. addWhileMode(b);
  108. }
  109. public void goHigher()
  110. {
  111. try
  112. {
  113. code.goHigher();
  114. }
  115. catch(GoHigherAtRootTreeException ex)
  116. {
  117. }
  118. }
  119. public String getTreePath()
  120. {
  121. return Arrays.asList(code.getCurrentPosition()).stream().map(i -> i.toString()).collect(Collectors.joining(", "));
  122. }
  123. private void readCode(String filename, ICommandSender cs)
  124. {
  125. int line = 0;
  126. StringBuilder codeLine = new StringBuilder();
  127. try
  128. {
  129. File file = new File("./quests/" + filename + ".txt");
  130. List<String> lines;
  131. if(file.exists())
  132. {
  133. try
  134. {
  135. lines = Files.readAllLines(file.toPath());
  136. }
  137. catch (IOException ex)
  138. {
  139. throw new PrescriptException("Die Datei '" + filename + "' kann nicht gelesen werden.");
  140. }
  141. }
  142. else
  143. {
  144. //System.out.println("Die Datei '" + filename + "' wurde nicht gefunden.");
  145. throw new PrescriptException("Die Datei '" + filename + "' wurde nicht gefunden.");
  146. }
  147. int i = 0;
  148. int old = 0;
  149. int bracket = 0;
  150. boolean text = false;
  151. boolean bracketEnd = false;
  152. int bracketEndPos = 0;
  153. boolean oneLineChild = false;
  154. boolean comment = false;
  155. int commentPos = 0;
  156. while(line < lines.size())
  157. {
  158. if(old >= codeLine.length() && !comment)
  159. {
  160. codeLine = new StringBuilder(lines.get(line).trim());
  161. i = 0;
  162. old = 0;
  163. //System.out.println("NEU");
  164. }
  165. else
  166. {
  167. codeLine.append(lines.get(line).trim());
  168. //System.out.println("APPEND");
  169. }
  170. //System.out.println(codeLine);
  171. while(i < codeLine.length())
  172. {
  173. switch(codeLine.charAt(i))
  174. {
  175. case '"':
  176. if(!comment)
  177. {
  178. text = !text;
  179. }
  180. break;
  181. case '@':
  182. if(comment || text)
  183. {
  184. break;
  185. }
  186. old = i;
  187. while(i < codeLine.length() && codeLine.charAt(i) != ' ')
  188. {
  189. i++;
  190. }
  191. try
  192. {
  193. code.selectLastChild(); // Nur für richtige Position
  194. }
  195. catch(NoChildTreeException ex)
  196. {
  197. }
  198. String s = codeLine.substring(old + 1, i);
  199. if(gotos.put(s, code.getCurrentPosition()) != null)
  200. {
  201. throw new PrescriptException("Doppeltes Goto: " + s + " - " + (line + 1));
  202. }
  203. codeLine.delete(old, i + 1);
  204. i = old - 1;
  205. break;
  206. case '/':
  207. if(text)
  208. {
  209. break;
  210. }
  211. if(!comment && i + 1 < codeLine.length() && codeLine.charAt(i + 1) == '*')
  212. {
  213. comment = true;
  214. commentPos = i;
  215. }
  216. if(!comment && i + 1 < codeLine.length() && codeLine.charAt(i + 1) == '/')
  217. {
  218. codeLine.delete(i, codeLine.length());
  219. old = codeLine.length() + 1;
  220. }
  221. break;
  222. case '*':
  223. if(text)
  224. {
  225. break;
  226. }
  227. if(comment && i + 1 < codeLine.length() && codeLine.charAt(i + 1) == '/')
  228. {
  229. comment = false;
  230. codeLine.delete(commentPos, i + 2);
  231. i = commentPos;
  232. }
  233. break;
  234. case ';':
  235. if(comment || text)
  236. {
  237. break;
  238. }
  239. if(bracket != 0)
  240. {
  241. //System.out.println(bracket);
  242. throw new PrescriptException("; aber Klammern ungültig - " + (line + 1));
  243. }
  244. bracketEnd = false;
  245. code.addChild(new CodeFunction(codeLine.substring(old, i)), line);
  246. old = i + 1;
  247. if(oneLineChild)
  248. {
  249. oneLineChild = false;
  250. try
  251. {
  252. code.goHigher();
  253. }
  254. catch(GoHigherAtRootTreeException ex)
  255. {
  256. throw new PrescriptException("Das sollte nicht passieren - " + (line + 1));
  257. }
  258. }
  259. bracket = 0;
  260. break;
  261. case '(':
  262. if(!comment && !text)
  263. {
  264. bracket++;
  265. if(bracketEnd && !oneLineChild)
  266. {
  267. bracketEnd = false;
  268. oneLineChild = true;
  269. code.addChild(new CodeFunction(codeLine.substring(old, bracketEndPos)), line);
  270. old = bracketEndPos;
  271. try
  272. {
  273. code.selectLastChild();
  274. code.goDeeper();
  275. }
  276. catch(NoChildTreeException ex)
  277. {
  278. throw new PrescriptException("Das sollte nicht passieren - " + (line + 1));
  279. }
  280. }
  281. }
  282. break;
  283. case ')':
  284. if(comment || text)
  285. {
  286. break;
  287. }
  288. bracket--;
  289. if(oneLineChild)
  290. {
  291. break;
  292. }
  293. if(bracket < 0)
  294. {
  295. throw new PrescriptException(") ohne ( - " + (line + 1));
  296. }
  297. if(bracket == 0)
  298. {
  299. bracketEnd = true;
  300. bracketEndPos = i + 1;
  301. }
  302. break;
  303. case '{':
  304. if(comment || text)
  305. {
  306. break;
  307. }
  308. bracketEnd = false;
  309. code.addChild(new CodeFunction(codeLine.substring(old, i)), line);
  310. old = i + 1;
  311. try
  312. {
  313. code.selectLastChild();
  314. code.goDeeper();
  315. }
  316. catch(NoChildTreeException ex)
  317. {
  318. throw new PrescriptException("Das sollte nicht passieren - " + (line + 1));
  319. }
  320. break;
  321. case '}':
  322. if(comment || text)
  323. {
  324. break;
  325. }
  326. old = i + 1;
  327. try
  328. {
  329. code.goHigher();
  330. }
  331. catch(GoHigherAtRootTreeException ex)
  332. {
  333. throw new PrescriptException("} ohne { - " + (line + 1));
  334. }
  335. break;
  336. }
  337. i++;
  338. }
  339. line++;
  340. }
  341. code.goToRoot();
  342. }
  343. catch(Exception ex)
  344. {
  345. if(parser.printStack)
  346. {
  347. ex.printStackTrace();
  348. }
  349. resetOverflowProtection();
  350. Module m = KajetansMod.scripts;
  351. int id = script ? 1 : 0;
  352. m.send(cs, "§cError in", id);
  353. m.sendHelpListElement(cs, "§cScript", getName(), id);
  354. m.sendHelpListElement(cs, "§cZeile", codeLine.toString(), id);
  355. m.sendHelpListElement(cs, "§cZeilennummer", String.valueOf(line + 1), id);
  356. if(ex.getLocalizedMessage() == null)
  357. {
  358. m.sendHelpListElement(cs, "§cException", ex.getClass().getSimpleName(), id);
  359. }
  360. else
  361. {
  362. m.sendHelpListElement(cs, "§cException", ex.getClass().getSimpleName() + " - " + ex.getLocalizedMessage(), id);
  363. }
  364. if(ex instanceof IllegalStringException)
  365. {
  366. m.sendHelpListElement(cs, "§cUngültiger Wert", ((IllegalStringException) ex).getBadString(), id);
  367. }
  368. KajetansMod.scripts.term(this);
  369. }
  370. }
  371. public void runCode()
  372. {
  373. try
  374. {
  375. while(!isWaiting)
  376. {
  377. treeClock.pushTime(benchmark);
  378. try
  379. {
  380. //System.out.println("NEXT");
  381. code.selectNextChild();
  382. }
  383. catch(NoChildTreeException ex)
  384. {
  385. //System.out.println("NACH OBEN!");
  386. code.goHigher();
  387. setTryMode(false);
  388. setElseMode(false);
  389. if(removeWhileMode())
  390. {
  391. increaseOverflowProtection();
  392. code.selectPreviousChild();
  393. }
  394. treeClock.pushTime(benchmark);
  395. continue;
  396. }
  397. currentCode = code.getCurrentChildData();
  398. treeClock.pushTime(benchmark);
  399. //System.out.println(currentCode.getFunction());
  400. //System.out.println(currentCode);
  401. //System.out.println("AUFRUF");
  402. //findFunction(currentCode, 0, currentCode.length() - 1);
  403. doFunction(currentCode);
  404. }
  405. }
  406. catch(NoChildTreeException | GoHigherAtRootTreeException ex)
  407. {
  408. KajetansMod.scripts.term(this);
  409. }
  410. catch(HoldCodeException ex2)
  411. {
  412. }
  413. catch(CodeTooLongException | PrescriptException ex3)
  414. {
  415. parser.printQuestException(this, ex3, currentCode.toString());
  416. }
  417. }
  418. public void runCodeLine(String c, ICommandSender cs)
  419. {
  420. try
  421. {
  422. doFunction(new CodeFunction(c));
  423. }
  424. catch(HoldCodeException ex)
  425. {
  426. }
  427. catch(Exception ex)
  428. {
  429. if(ex instanceof IllegalStringException)
  430. {
  431. KajetansMod.scripts.send(cs, ((IllegalStringException) ex).getBadString(), script ? 1 : 0);
  432. }
  433. else
  434. {
  435. KajetansMod.scripts.send(cs, ex.getClass().getSimpleName(), script ? 1 : 0);
  436. }
  437. }
  438. }
  439. private Object doFunction(CodeFunction cf)
  440. {
  441. Object[] old = cf.getParameters();
  442. Object[] newPars = new Object[old.length];
  443. for(int i = 0; i < old.length; i++)
  444. {
  445. if(old[i] != null && old[i].getClass() == CodeFunction.class)
  446. {
  447. newPars[i] = readParameter(doFunction((CodeFunction) old[i]));
  448. continue;
  449. }
  450. newPars[i] = readParameter(old[i]);
  451. }
  452. return parser.parseFunction(this, cf.getFunction(), newPars);
  453. }
  454. private Object readParameter(Object o)
  455. {
  456. if(o != null && o.getClass() == Variable.class)
  457. {
  458. return getVar(o.toString());
  459. }
  460. return o;
  461. }
  462. public int getActualCodeLine()
  463. {
  464. return code.getActualCodeLine();
  465. }
  466. // -------------------------------------------------------------------------
  467. // Quest / Scriptdaten
  468. // -------------------------------------------------------------------------
  469. public boolean isScript()
  470. {
  471. return script;
  472. }
  473. public int getId()
  474. {
  475. return id;
  476. }
  477. public String getInfo()
  478. {
  479. return info;
  480. }
  481. public void setInfo(String t)
  482. {
  483. info = t;
  484. }
  485. public String getName()
  486. {
  487. return name;
  488. }
  489. // -------------------------------------------------------------------------
  490. // Wait für Zeitutils
  491. // -------------------------------------------------------------------------
  492. public boolean isWaiting()
  493. {
  494. return isWaiting;
  495. }
  496. public void setIsWaiting(boolean b)
  497. {
  498. isWaiting = b;
  499. }
  500. // -------------------------------------------------------------------------
  501. // Valid
  502. // -------------------------------------------------------------------------
  503. public boolean isValid()
  504. {
  505. return isValid;
  506. }
  507. public void setIsValid(Boolean bool)
  508. {
  509. isValid = bool;
  510. }
  511. // -------------------------------------------------------------------------
  512. // Variablen
  513. // -------------------------------------------------------------------------
  514. public void setVar(String var, Object value)
  515. {
  516. variables.put(var, value);
  517. }
  518. public void setVar(Object var, Object value)
  519. {
  520. setVar(var.toString(), value);
  521. }
  522. public HashMap<String, Object> getVars()
  523. {
  524. return variables;
  525. }
  526. public Object getVar(String var)
  527. {
  528. return variables.get(var);
  529. }
  530. public Object getVar(Object var)
  531. {
  532. return getVar(var.toString());
  533. }
  534. public boolean getBoolean(String var)
  535. {
  536. try
  537. {
  538. return (boolean) getVar(var);
  539. }
  540. catch(ClassCastException | NullPointerException ex)
  541. {
  542. return false;
  543. }
  544. }
  545. public void removeVar(String var)
  546. {
  547. variables.remove(var);
  548. }
  549. // -------------------------------------------------------------------------
  550. // Goto
  551. // -------------------------------------------------------------------------
  552. public void gotoLabel(String label) throws NoChildTreeException, CodeTooLongException
  553. {
  554. Integer[] i = gotos.get(label);
  555. if(i == null)
  556. {
  557. throw new GotoLabelNotFoundException(label);
  558. }
  559. increaseOverflowProtection();
  560. resetWhileMode();
  561. setTryMode(false);
  562. code.goToPosition(i);
  563. }
  564. public void gotoLabelWithReturn(String label) throws NoChildTreeException, CodeTooLongException
  565. {
  566. stack.push(code.getCurrentPosition());
  567. gotoLabel(label);
  568. }
  569. public void doReturn() throws NoChildTreeException
  570. {
  571. code.goToPosition(stack.pop());
  572. /*try
  573. {
  574. code.selectNextChild();
  575. }
  576. catch(NoChildTreeException ex)
  577. {
  578. code.goHigher();
  579. }*/
  580. }
  581. public void resetOverflowProtection()
  582. {
  583. overflowProtection = 0;
  584. }
  585. public void increaseOverflowProtection() throws CodeTooLongException
  586. {
  587. overflowProtection++;
  588. if(overflowProtection > 1000)
  589. {
  590. overflowProtection = 0;
  591. throw new CodeTooLongException();
  592. }
  593. }
  594. // -------------------------------------------------------------------------
  595. // Event Handling
  596. // -------------------------------------------------------------------------
  597. public void loadEvent(String event)
  598. {
  599. events.add(event);
  600. }
  601. public void unloadEvent(String event)
  602. {
  603. events.remove(event);
  604. }
  605. public boolean isEventLoaded(String event)
  606. {
  607. return events.contains(event);
  608. }
  609. // -------------------------------------------------------------------------
  610. // Player Handling
  611. // -------------------------------------------------------------------------
  612. public void addPlayer(EntityPlayer p)
  613. {
  614. quester.add(p);
  615. }
  616. public boolean removePlayer(EntityPlayer p)
  617. {
  618. quester.remove(p);
  619. return quester.isEmpty();
  620. }
  621. public List<EntityPlayer> getPlayers()
  622. {
  623. return quester;
  624. }
  625. public List<String> getPlayerNames()
  626. {
  627. return getPlayers().stream().map(p -> p.getName()).collect(Collectors.toList());
  628. }
  629. // -------------------------------------------------------------------------
  630. // Location Handling für Wait-For-Location
  631. // -------------------------------------------------------------------------
  632. public void addLocation(Location l)
  633. {
  634. loadedLocations.add(l);
  635. }
  636. public boolean removeLocation(Location l)
  637. {
  638. return loadedLocations.remove(l);
  639. }
  640. public void clearLocations()
  641. {
  642. loadedLocations.clear();
  643. }
  644. // -------------------------------------------------------------------------
  645. // Try-Catch Handler
  646. // -------------------------------------------------------------------------
  647. public void setTryMode(boolean b)
  648. {
  649. isTrying = b;
  650. }
  651. public boolean getTryMode()
  652. {
  653. return isTrying;
  654. }
  655. public void setTryFail(boolean b)
  656. {
  657. tryFailed = b;
  658. }
  659. public boolean getTryFail()
  660. {
  661. return tryFailed;
  662. }
  663. // -------------------------------------------------------------------------
  664. // While Handler
  665. // -------------------------------------------------------------------------
  666. private void addWhileMode(boolean b)
  667. {
  668. whileStack.push(b);
  669. }
  670. public boolean removeWhileMode()
  671. {
  672. try
  673. {
  674. return whileStack.pop();
  675. }
  676. catch(EmptyStackException ex)
  677. {
  678. return false;
  679. }
  680. }
  681. public void resetWhileMode()
  682. {
  683. whileStack.clear();
  684. }
  685. // -------------------------------------------------------------------------
  686. // Else Handler
  687. // -------------------------------------------------------------------------
  688. public void setElseMode(boolean b)
  689. {
  690. shouldDoElse = b;
  691. }
  692. public boolean getElseMode()
  693. {
  694. return shouldDoElse;
  695. }
  696. // -------------------------------------------------------------------------
  697. // Für Inventar-IDs
  698. // -------------------------------------------------------------------------
  699. public int getNewId()
  700. {
  701. idCounter++;
  702. return idCounter;
  703. }
  704. // -------------------------------------------------------------------------
  705. // Für Benchmarks
  706. // -------------------------------------------------------------------------
  707. public void clearBenchmark()
  708. {
  709. exeClock.clear();
  710. treeClock.clear();
  711. }
  712. public boolean isBenchmarking()
  713. {
  714. return benchmark;
  715. }
  716. public void toggleBenchmark()
  717. {
  718. benchmark = !benchmark;
  719. }
  720. public void printBenchmarks(ICommandSender cs)
  721. {
  722. cs.sendMessage(new TextComponentString(GlobalText.Spacer()));
  723. cs.sendMessage(new TextComponentString("Ausführung: " + exeClock.getSummedTime() + " ms"));
  724. cs.sendMessage(new TextComponentString("Baum: " + treeClock.getSummedTime() + " ms"));
  725. }
  726. }