Fix hang with PC stopping with LuaPlugin waiting for frame
[jpcrr.git] / org / jpc / plugins / LuaPlugin.java
blob01f60178fb1f091cdf5bd1b542c5f837aeb7cf68
1 /*
2 JPC-RR: A x86 PC Hardware Emulator
3 Release 1
5 Copyright (C) 2007-2009 Isis Innovation Limited
6 Copyright (C) 2009-2010 H. Ilari Liusvaara
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License version 2 as published by
10 the Free Software Foundation.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License along
18 with this program; if not, write to the Free Software Foundation, Inc.,
19 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 Based on JPC x86 PC Hardware emulator,
22 A project from the Physics Dept, The University of Oxford
24 Details about original JPC can be found at:
26 www-jpc.physics.ox.ac.uk
30 package org.jpc.plugins;
32 import mnj.lua.*;
34 import java.io.*;
35 import java.util.*;
36 import java.awt.*;
37 import java.awt.event.*;
38 import javax.swing.*;
39 import java.lang.reflect.*;
41 import org.jpc.emulator.PC;
42 import org.jpc.emulator.HardwareComponent;
43 import org.jpc.emulator.VGADigitalOut;
44 import org.jpc.pluginsbase.Plugins;
45 import org.jpc.pluginsbase.Plugin;
46 import static org.jpc.Misc.parseStringToComponents;
47 import static org.jpc.Misc.errorDialog;
48 import static org.jpc.Misc.moveWindow;
50 //Locking this class is used for preventing termination and when terminating.
51 public class LuaPlugin implements ActionListener, Plugin
53 private JFrame window;
54 private JPanel panel;
55 private Plugins vPluginManager;
56 private String kernelName;
57 private Map<String, String> kernelArguments;
58 private Map<String, String> userArguments;
59 private int nativeWidth;
60 private int nativeHeight;
61 private JLabel execLabel;
62 private JTextField execName;
63 private JButton execButton;
64 private JButton termButton;
65 private JButton clearButton;
66 private JTextArea console;
68 private int nextHandle;
70 //luaThread is null if Lua isn't running.
71 private Thread luaThread;
72 private Lua luaState;
73 private volatile boolean pcRunning;
74 private volatile String luaInvokeReq;
75 private volatile boolean luaTerminateReq;
76 private volatile boolean luaTerminateReqAsync;
77 private VGADigitalOut screenOut;
78 private PC pc;
79 private volatile boolean ownsVGALock;
80 private volatile boolean ownsVGALine;
81 private volatile boolean signalComplete;
82 private volatile boolean luaStarted;
83 private volatile boolean mainThreadWait;
84 private volatile boolean reconnectInProgress;
86 private boolean consoleMode;
88 private Map<String, LuaResource> resources;
89 private IdentityHashMap<LuaResource, Integer> liveObjects;
90 private LinkedList<String> messageQueue;
92 public static abstract class LuaResource
94 String handle;
95 LuaPlugin plugin;
97 public LuaResource(LuaPlugin _plugin)
99 handle = "h" + (_plugin.nextHandle++);
100 plugin = _plugin;
101 plugin.resources.put(handle, this);
104 public final String getHandle()
106 return handle;
109 public final void release(boolean noExceptions) throws IOException
111 try {
112 destroy();
113 } catch(IOException e) {
114 if(!noExceptions)
115 throw e;
117 plugin.liveObjects.remove(this);
118 plugin.resources.remove(handle);
121 public abstract void destroy() throws IOException;
124 private String getMethodHandle(Lua l)
126 if(l.type(1) == Lua.TNONE) {
127 l.error("Handle required for method call");
128 return null;
130 l.checkType(1, Lua.TUSERDATA);
131 Object _u = l.toUserdata(l.value(1)).getUserdata();
132 if(!(_u instanceof String)) {
133 if(_u != null) {
134 l.error("Invalid handle to resource: " + _u.getClass().getName());
135 } else {
136 l.error("Invalid handle to resource: Null");
138 return null;
140 return (String)_u;
143 public void destroyLuaObject(Lua l) throws IOException
145 String u = getMethodHandle(l);
146 LuaResource r1 = resources.get(u);
147 if(r1 == null) {
148 l.error("Bad or closed handle passed to method");
150 r1.release(false);
153 public boolean systemShutdown()
155 //Just terminate the emulator.
156 return true;
159 public void reconnect(PC _pc)
161 //Gat the thread out of VGA wait if its there.
162 reconnectInProgress = true;
163 if(luaThread != null) {
164 luaThread.interrupt();
166 synchronized(this) {
167 reconnectInProgress = false;
168 if(ownsVGALock && screenOut != null) {
169 screenOut.releaseOutput(this);
170 ownsVGALock = false;
172 if(ownsVGALine && screenOut != null) {
173 screenOut.unsubscribeOutput(this);
174 ownsVGALine = false;
176 if(_pc != null) {
177 screenOut = _pc.getVideoOutput();
178 pc = _pc;
179 } else {
180 screenOut = null;
181 pc = null;
183 if(screenOut != null && luaThread != null) {
184 screenOut.subscribeOutput(this);
185 ownsVGALine = true;
187 notifyAll();
191 public void pcStarting()
193 pcRunning = true;
196 public void pcStopping()
198 pcRunning = false;
199 //Gat the thread out of VGA wait if its there.
200 reconnectInProgress = true;
201 if(luaThread != null) {
202 luaThread.interrupt();
204 synchronized(this) {
205 reconnectInProgress = false;
206 notifyAll();
210 class LuaCallback extends LuaJavaCallback
212 Method callbackMethod;
213 Object onObject;
215 LuaCallback(Object target, Method callback)
217 onObject = target;
218 callbackMethod = callback;
221 public int luaFunction(Lua l) {
222 synchronized(LuaPlugin.this) {
223 try {
224 if(!liveObjects.containsKey(onObject)) {
225 l.error("Attempted to call method on dead object");
226 return 0;
227 } else
228 return ((Integer)callbackMethod.invoke(onObject, luaState, LuaPlugin.this)).intValue();
229 } catch(InvocationTargetException e) {
230 if(e.getCause() instanceof LuaError)
231 throw (LuaError)e.getCause(); //Pass runtime exceptions through.
232 errorDialog(e.getCause(), "Error in callback", null, "Terminate Lua VM");
233 terminateLuaVMAsync();
234 } catch(Exception e) {
235 if(e instanceof LuaError)
236 throw (LuaError)e; //Pass runtime exceptions through.
237 errorDialog(e, "Error invoking callback", null, "Terminate Lua VM");
238 terminateLuaVMAsync();
241 while(true);
245 public void tableAddFunctions(Lua l, LuaTable table, Object obj, Class<?> clazz)
247 if(obj != null)
248 clazz = obj.getClass();
249 //Add all exported callbacks.
250 Method[] candidateMethods = clazz.getMethods();
251 for(Method candidate: candidateMethods) {
252 if(obj != null && Modifier.isStatic(candidate.getModifiers()))
253 continue; //Want non-static.
254 if(obj == null && !Modifier.isStatic(candidate.getModifiers()))
255 continue; //Want static.
256 if(!Modifier.isPublic(candidate.getModifiers()))
257 continue; //Want public.
258 if(!candidate.getName().startsWith("luaCB_"))
259 continue; //Not this...
260 String luaName = candidate.getName().substring(6);
261 Class<?>[] paramTypes = candidate.getParameterTypes();
262 Class<?> retType = candidate.getReturnType();
263 if(retType != int.class) {
264 System.err.println("Warning: Incorrect return type for " + candidate.getName() +
265 ": " + retType.getName() + ".");
266 continue;
268 if(paramTypes == null || paramTypes.length != 2) {
269 System.err.println("Warning: Incorrect parameter type for " + candidate.getName() + ".");
270 continue;
272 if(paramTypes[0] != Lua.class || paramTypes[1] != LuaPlugin.class) {
273 System.err.println("Warning: Incorrect parameter type for " + candidate.getName() + ".");
274 continue;
277 l.setTable(table, luaName, new LuaCallback(obj, candidate));
281 public LuaUserdata generateLuaClass(Lua l, LuaResource towrap)
283 LuaUserdata user = new LuaUserdata(towrap.getHandle());
284 LuaTable t = l.newTable();
285 tableAddFunctions(l, t, towrap, null);
286 liveObjects.put(towrap, null);
287 l.setTable(t, "__index" , t);
288 l.setMetatable(user, t);
289 l.push(user);
290 return user;
293 class LuaThread implements Runnable
295 Lua lua;
296 String script;
298 LuaThread(Lua _lua, String _script)
300 BaseLib.open(_lua);
301 StringLib.open(_lua);
302 MathLib.open(_lua);
303 TableLib.open(_lua);
304 lua = _lua;
305 script = _script;
308 private String describeFault(int r)
310 if(r == 0) return null;
311 else if(r == Lua.YIELD) return "Main thread yielded.";
312 else if(r == Lua.ERRRUN) return "Unprotected runtime error";
313 else if(r == Lua.ERRSYNTAX) return "syntax error";
314 //else if(r == Lua.ERRMEM) return "Out of memory");
315 else if(r == Lua.ERRFILE) return "I/O error loading";
316 else if(r == Lua.ERRERR) return "Double fault";
317 else return "Unknown fault #" + r;
320 public void run()
322 LuaTable sTable;
324 lua.setGlobal("script", script);
326 lua.setGlobal("args", sTable = lua.newTable());
327 for(Map.Entry<String, String> x : kernelArguments.entrySet())
328 lua.setTable(sTable, x.getKey(), x.getValue());
329 for(Map.Entry<String, String> x : userArguments.entrySet())
330 lua.setTable(sTable, "x-" + x.getKey(), x.getValue());
332 tableAddFunctions(lua, lua.getGlobals(), null, LuaPlugin.class);
334 //Wait for lua startup to be signaled in order to avoid deadlocks.
335 while(!luaStarted)
336 try {
337 wait();
338 } catch(Exception e) {
341 InputStream kernel = null;
342 try {
343 kernel = new BufferedInputStream(new FileInputStream(kernelName));
344 int r = lua.load(kernel, "Kernel");
345 String fault = describeFault(r);
346 if(fault != null)
347 throw new Exception("Kernel loading error: " + fault);
348 r = lua.pcall(0, 0, null);
349 fault = describeFault(r);
350 if(fault != null)
351 throw new Exception("Kernel error: " + fault);
352 } catch(Exception e) {
353 printConsoleMsg("\n\nLua Error: " + e.getMessage() + "\n" +
354 lua.value(-1).toString() + "\n\n");
355 //e.printStackTrace();
356 errorDialog(e, "Lua error", null, "Dismiss");
358 //Lua script quit. Terminate the VM.
359 synchronized(LuaPlugin.this) {
360 cleanupLuaResources();
361 luaThread = null;
362 luaState = null;
363 LuaPlugin.this.notifyAll();
365 printConsoleMsg("Lua VM: Lua script finished.\n");
369 private void cleanupLuaResources()
371 if(ownsVGALock) {
372 screenOut.releaseOutput(LuaPlugin.this);
373 ownsVGALock = false;
375 if(ownsVGALine) {
376 screenOut.unsubscribeOutput(LuaPlugin.this);
377 ownsVGALine = false;
380 while(resources.size() > 0) {
381 Map.Entry<String, LuaResource> entry = resources.entrySet().iterator().next();
382 String key = entry.getKey();
383 LuaResource obj = entry.getValue();
384 try {
385 obj.release(true);
386 } catch(Exception e) {
388 resources.remove(key);
389 liveObjects.remove(obj);
391 resources.clear();
394 public void main()
396 while(true) {
397 try {
398 synchronized(this) {
399 mainThreadWait = true;
400 notifyAll();
401 if(luaInvokeReq == null && !luaTerminateReq)
402 wait();
403 mainThreadWait = false;
405 } catch(Exception e) {
406 continue;
408 if(luaInvokeReq != null && luaThread == null) {
409 //Run the Lua VM.
410 if(screenOut != null && !ownsVGALine) {
411 screenOut.subscribeOutput(this);
412 ownsVGALine = true;
414 luaStarted = false;
415 luaState = new Lua();
416 luaThread = new Thread(new LuaThread(luaState, luaInvokeReq));
417 luaThread.start();
418 synchronized(this) {
419 luaInvokeReq = null;
420 signalComplete = true;
421 luaStarted = true;
422 notifyAll();
424 } else if(luaInvokeReq != null) {
425 //Invoke request with Lua running? Shouldn't happen.
426 System.err.println("Error: Lua invoke request with Lua running!");
427 luaInvokeReq = null;
428 } else if(luaTerminateReq && luaThread != null) {
429 //This is fun... Terminate Lua VM. Sychronize in order to avoid terminating VM in
430 //inapporiate place. And yes, that thread gets killed! The interrupt is to prevent
431 //or kick the object from sleeping on VGA wait.
432 luaThread.interrupt();
433 synchronized(this) {
434 luaThread.stop();
435 cleanupLuaResources();
436 luaState = null;
437 luaThread = null;
438 luaTerminateReq = false;
439 signalComplete = true;
440 notifyAll();
441 if(luaTerminateReqAsync)
442 setLuaButtons();
444 printConsoleMsg("Lua VM: Lua VM terminated.\n");
445 cleanupLuaResources();
446 } else if(luaTerminateReq) {
447 //Invoke request with Lua running? Shouldn't happen.
448 System.err.println("Error: Lua terminate request with Lua not running!");
449 luaTerminateReq = false;
450 setLuaButtons();
451 } else {
452 setLuaButtons();
457 public void printConsoleMsg(String msg)
459 final String _msg = msg;
461 if(consoleMode) {
462 System.out.print(msg);
463 return;
466 if(!SwingUtilities.isEventDispatchThread())
467 try {
468 //Do this async to avoid deadlocks with PCRunner stop.
469 SwingUtilities.invokeLater(new Thread() { public void run() {
470 console.setText(console.getText() + _msg);
471 }});
472 } catch(Exception e) {
474 else
475 console.setText(console.getText() + msg);
478 private synchronized void invokeLuaVM(String script) throws Exception
480 if(luaThread != null) {
481 return;
484 int split = script.indexOf('(');
485 if(split < 0) {
486 userArguments = new HashMap<String, String>();
487 } else {
488 String arguments = script.substring(split + 1);
489 int split2 = arguments.lastIndexOf(')');
490 if(split2 != arguments.length() - 1)
491 throw new Exception("Bad argument syntax");
492 arguments = arguments.substring(0, split2);
493 userArguments = parseStringToComponents(arguments);
496 //Starting from Lua itself is not possible.
497 signalComplete = false;
498 if(split < 0)
499 luaInvokeReq = script;
500 else
501 luaInvokeReq = script.substring(0, split);
502 luaTerminateReq = false;
503 notifyAll();
504 while(!signalComplete)
505 try {
506 wait();
507 } catch(Exception e) {
510 setLuaButtons();
513 private synchronized void terminateLuaVM()
515 notifyAll();
516 if(luaThread == null)
517 return;
519 //This request won't go to lua execution thread.
520 clearMessages();
521 signalComplete = false;
522 luaTerminateReq = true;
523 luaTerminateReqAsync = false;
524 notifyAll();
525 synchronized(messageQueue) {
526 messageQueue.notifyAll();
528 while(luaThread != null)
529 try {
530 wait();
531 } catch(Exception e) {
534 setLuaButtons();
537 private synchronized void terminateLuaVMAsync()
539 if(luaThread == null)
540 return;
542 //This request won't go to lua execution thread.
543 clearMessages();
544 signalComplete = false;
545 luaTerminateReq = true;
546 luaTerminateReqAsync = true;
547 notifyAll();
548 synchronized(messageQueue) {
549 messageQueue.notifyAll();
553 private void setLuaButtons()
555 if(consoleMode)
556 return;
558 if(!SwingUtilities.isEventDispatchThread())
559 try {
560 SwingUtilities.invokeAndWait(new Thread() { public void run() {
561 LuaPlugin.this.execButton.setText((luaThread == null) ? "Run" : "Send");
562 LuaPlugin.this.termButton.setEnabled(luaThread != null);
563 }});
564 } catch(Exception e) {
566 else {
567 LuaPlugin.this.execButton.setText((luaThread == null) ? "Run" : "Send");
568 LuaPlugin.this.termButton.setEnabled(luaThread != null);
572 private void clearConsole()
574 if(consoleMode)
575 return;
577 if(!SwingUtilities.isEventDispatchThread())
578 try {
579 SwingUtilities.invokeAndWait(new Thread() { public void run() {
580 console.setText("");
581 }});
582 } catch(Exception e) {
584 else {
585 console.setText("");
589 public void actionPerformed(ActionEvent evt)
591 if(evt.getSource() == execButton) {
592 try {
593 if(luaThread == null)
594 invokeLuaVM(execName.getText());
595 else
596 postMessage(execName.getText());
597 } catch(Exception e) {
598 printConsoleMsg("Lua script starting / message send error: " + e.getMessage());
600 } else if(evt.getSource() == termButton) {
601 luaTerminateReq = true;
602 if(luaThread != null)
603 luaThread.interrupt();
604 terminateLuaVM();
605 } else if(evt.getSource() == clearButton) {
606 clearConsole();
610 public void eci_luaplugin_setwinpos(Integer x, Integer y)
612 moveWindow(window, x.intValue(), y.intValue(), nativeWidth, nativeHeight);
615 public void eci_luaplugin_run(String script)
617 if(luaThread == null)
618 try {
619 invokeLuaVM(script);
620 } catch(Exception e) {
621 printConsoleMsg("Lua script starting error: " + e.getMessage());
625 public void eci_luaplugin_terminate()
627 luaTerminateReq = true;
628 if(luaThread != null)
629 luaThread.interrupt();
630 terminateLuaVMAsync();
633 public void eci_luaplugin_clearconsole()
635 clearConsole();
638 private void invokeCommand(String cmd, String[] args)
640 if("luaplugin-terminate".equals(cmd) && args == null && luaThread != null) {
641 luaTerminateReq = true;
642 if(luaThread != null)
643 luaThread.interrupt();
644 terminateLuaVMAsync();
648 public void callInvokeCommand(String cmd, String[] args, boolean sync)
650 if(consoleMode)
651 invokeCommand(cmd, args);
652 else if(sync)
653 vPluginManager.invokeExternalCommandSynchronous(cmd, args);
654 else
655 vPluginManager.invokeExternalCommand(cmd, args);
658 public Object[] callCommand(String cmd, String[] args)
660 if(consoleMode) {
661 invokeCommand(cmd, args);
662 return null;
664 return vPluginManager.invokeExternalCommandReturn(cmd, args);
667 public HardwareComponent getComponent(Class<? extends HardwareComponent> clazz)
669 if(pc == null)
670 return null;
671 return pc.getComponent(clazz);
674 public synchronized void waitPCAttach()
676 while(screenOut == null && !luaTerminateReq) {
677 try {
678 wait();
679 } catch(InterruptedException e) {
684 public synchronized boolean waitPCStop()
686 //Temporarily release VGA output line to avoid deadlocking.
687 if(ownsVGALock && screenOut != null) {
688 screenOut.releaseOutput(this);
689 ownsVGALock = false;
691 if(ownsVGALine && screenOut != null) {
692 screenOut.unsubscribeOutput(this);
693 ownsVGALine = false;
696 while(pcRunning && screenOut != null && !luaTerminateReq) {
697 try {
698 wait();
699 } catch(InterruptedException e) {
703 if(screenOut != null && !luaTerminateReq) {
704 screenOut.subscribeOutput(this);
705 ownsVGALine = true;
707 notifyAll();
709 return !pcRunning;
712 public synchronized String waitMessage()
714 String msg = null;
715 synchronized(messageQueue) {
716 while((msg = messageQueue.poll()) == null && !luaTerminateReq) {
717 try {
718 messageQueue.wait();
719 } catch(InterruptedException e) {
723 return msg;
726 public synchronized String pollMessage()
728 String msg = null;
729 synchronized(messageQueue) {
730 msg = messageQueue.poll();
732 return msg;
735 public void postMessage(String msg)
737 synchronized(messageQueue) {
738 messageQueue.add(msg);
739 messageQueue.notifyAll();
743 public void clearMessages()
745 synchronized(messageQueue) {
746 messageQueue.clear();
750 public void doLockVGA()
752 if(screenOut != null && ownsVGALine && !ownsVGALock && !luaTerminateReq && !reconnectInProgress)
753 if(screenOut.waitOutput(this))
754 ownsVGALock = true;
757 public void doReleaseVGA()
759 if(screenOut != null && ownsVGALine && ownsVGALock)
760 screenOut.releaseOutput(this);
761 ownsVGALock = false;
764 public boolean getOwnsVGALock()
766 return ownsVGALock;
769 public int getXResolution()
771 if(screenOut != null && ownsVGALine)
772 return screenOut.getWidth();
773 return -1;
776 public int getYResolution()
778 if(screenOut != null && ownsVGALine)
779 return screenOut.getHeight();
780 return -1;
783 public boolean getPCConnected()
785 return (screenOut != null);
788 public boolean getPCRunning()
790 return pcRunning;
793 public LuaPlugin(String args) throws Exception
795 kernelArguments = parseStringToComponents(args);
796 userArguments = new HashMap<String, String>();
797 kernelName = kernelArguments.get("kernel");
798 kernelArguments.remove("kernel");
799 if(kernelName == null)
800 throw new IOException("Kernel name (kernel) required for LuaPlugin");
802 this.pcRunning = false;
803 this.luaThread = null;
804 this.luaInvokeReq = null;
805 this.luaTerminateReq = false;
806 this.consoleMode = true;
808 this.resources = new HashMap<String, LuaResource>();
809 this.liveObjects = new IdentityHashMap<LuaResource, Integer>();
810 this.messageQueue = new LinkedList<String>();
811 liveObjects.put(null, null); //NULL is always considered live.
814 public LuaPlugin(Plugins manager, String args) throws Exception
816 this(args);
818 this.consoleMode = false;
819 this.vPluginManager = manager;
821 window = new JFrame("Lua window");
822 GridBagLayout layout = new GridBagLayout();
823 GridBagConstraints c = new GridBagConstraints();
824 panel = new JPanel(layout);
825 window.add(panel);
827 console = new JTextArea(25, 80);
828 JScrollPane consoleScroller = new JScrollPane(console);
829 console.setEditable(false);
830 c.fill = GridBagConstraints.HORIZONTAL;
831 c.gridwidth = 5;
832 c.gridx = 0;
833 c.gridy = 0;
834 panel.add(consoleScroller, c);
836 execLabel = new JLabel("Lua script");
837 c.fill = GridBagConstraints.NONE;
838 c.weightx = 0;
839 c.gridwidth = 1;
840 c.gridx = 0;
841 c.gridy = 1;
842 panel.add(execLabel, c);
844 execName = new JTextField("", 40);
845 c.fill = GridBagConstraints.HORIZONTAL;
846 c.weightx = 1;
847 c.gridwidth = 1;
848 c.gridx = 1;
849 c.gridy = 1;
850 panel.add(execName, c);
852 execButton = new JButton("Run");
853 c.fill = GridBagConstraints.NONE;
854 c.weightx = 0;
855 c.gridwidth = 1;
856 c.gridx = 2;
857 c.gridy = 1;
858 panel.add(execButton, c);
859 execButton.addActionListener(this);
861 termButton = new JButton("Terminate Lua VM");
862 c.fill = GridBagConstraints.NONE;
863 c.weightx = 0;
864 c.gridwidth = 1;
865 c.gridx = 3;
866 c.gridy = 1;
867 panel.add(termButton, c);
868 termButton.addActionListener(this);
869 termButton.setEnabled(false);
871 clearButton = new JButton("Clear Console");
872 c.fill = GridBagConstraints.NONE;
873 c.weightx = 0;
874 c.gridwidth = 1;
875 c.gridx = 4;
876 c.gridy = 1;
877 panel.add(clearButton, c);
878 clearButton.addActionListener(this);
880 window.pack();
881 window.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
882 Dimension d = window.getSize();
883 nativeWidth = d.width;
884 nativeHeight = d.height;
885 window.setVisible(true);
888 class DedicatedShutdownHandler extends Thread
890 public void run()
892 terminateLuaVM();
896 class RunMainThread implements Runnable
898 public void run()
900 main();
904 public static void main(String[] args)
906 if(args.length != 2) {
907 System.err.println("Syntax: LuaPlugin <script> <args>");
908 return;
912 LuaPlugin p;
913 try {
914 p = new LuaPlugin(args[1]);
915 } catch(Exception e) {
916 System.err.println("Can't initialize LuaPlugin: " + e.getMessage());
917 e.printStackTrace();
918 return;
921 Runtime.getRuntime().addShutdownHook(p.new DedicatedShutdownHandler());
922 Thread mThread = new Thread(p.new RunMainThread());
923 mThread.start();
925 synchronized(p) {
926 //Wait for main thread to become ready and send invoke request.
927 while(!p.mainThreadWait)
928 try {
929 p.wait();
930 } catch(Exception e) {
932 try {
933 p.invokeLuaVM(args[0]);
934 } catch(Exception e) {
935 System.err.println("Error: Can't start Lua VM: " + e.getMessage());
938 //Wait for lua VM to finish.
939 while(p.luaThread != null || p.luaInvokeReq != null)
940 try {
941 p.wait();
942 } catch(Exception e) {
945 mThread.stop();
948 //Some extremely important callbacks.
949 public static int luaCB_print_console_msg(Lua l, LuaPlugin plugin)
951 if(l.type(1) != Lua.TSTRING) {
952 l.error("Unexpected types to print_console_msg");
953 return 0;
955 plugin.printConsoleMsg(l.value(1).toString() + "\n");
956 return 0;
959 public static int luaCB_loadmodule(Lua l, LuaPlugin plugin)
961 if(l.type(1) != Lua.TSTRING) {
962 l.error("Unexpected types to loadmodule");
963 return 0;
965 try {
966 Class<?> clazz = Class.forName(l.checkString(1));
967 LuaTable tab = l.newTable();
968 plugin.tableAddFunctions(l, tab, null, clazz);
969 l.push(tab);
970 } catch(Exception e) {
971 l.error("No such extension module: " + l.checkString(1));
972 return 0;
974 return 1;