UNPKG

mdx-m3-viewer

Version:

A browser WebGL model viewer. Mainly focused on models of the games Warcraft 3 and Starcraft 2.

253 lines (198 loc) 5.82 kB
import EventEmitter from 'events'; import {to_luastring, to_jsstring} from 'fengari/src/fengaricore'; import {lua_pop, lua_getglobal, lua_pcall, lua_atnativeerror, lua_pushstring, lua_touserdata, lua_rawgeti, LUA_REGISTRYINDEX, lua_resume, LUA_OK, LUA_YIELD} from 'fengari/src/lua'; import {luaL_newstate, luaL_loadstring, luaL_tolstring, luaL_unref, luaL_checknumber} from 'fengari/src/lauxlib'; import {luaL_openlibs} from 'fengari/src/lualib'; import MappedData from '../mappeddata'; import jass2lua from './jass2lua'; import bindNatives from './natives'; import JassPlayer from './types/player'; import constantHandles from './constanthandles'; import Thread from './thread'; /** * A Jass2 context. */ export default class Context extends EventEmitter { /** * */ constructor() { super(); /** @member {lua_State} */ this.L = luaL_newstate(); luaL_openlibs(this.L); bindNatives(this); lua_atnativeerror(this.L, (L) => { let e = lua_touserdata(L, -1); lua_pushstring(L, e.stack); return 1; }); /** @member {?War3Map} */ this.map = null; /** @member {number} */ this.handle = 0; /** @member {Array<number>} */ this.freeHandles = []; /** @member {Array<?JassHandle} */ this.handles = []; this.name = ''; this.description = ''; this.players = []; this.actualPlayers = 0; this.startLocations = []; this.constantHandles = constantHandles(this); for (let i = 0; i < 28; i++) { this.players[i] = this.addHandle(new JassPlayer(i, 28)); } // this.mappedData = new MappedData(); // this.mapName = ''; // this.mapDescription = ''; // this.gamePlacement = null; // this.gameSpeed = null; // this.gameDifficulty = null; // this.playerCount = 0; // this.teamCount = 0; // this.startLocations = []; // this.players = []; // this.teams = []; // this.stringTable = map.readStringTable(); /** @member {Set<JassTimer>} */ this.timers = new Set(); /** @member {Set<Trigger>} */ this.triggers = new Set(); /** @member {Set<Thread>} */ this.threads = new Set(); /** @member {?Thread} */ this.currentThread = null; /** @member {?Handle} */ this.enumUnit = null; /** @member {?Handle} */ this.filterUnit = null; /** @member {?Handle} */ this.enumPlayer = null; this.t = 0; } /** * */ start() { this.t = performance.now(); } /** * */ step() { let t = performance.now(); let dt = (t - this.t) * 0.001; let timers = this.timers; let threads = this.threads; for (let timer of timers) { timer.elapsed += dt; if (timer.elapsed >= timer.timeout) { let thread = new Thread(this.L, {expiredTimer: timer}); let L = thread.L; // Push the entry point onto the thread's stack, so when the thread is resumed it will immediately be called. lua_rawgeti(L, LUA_REGISTRYINDEX, timer.handlerFunc); this.threads.add(thread); if (timer.periodic) { timer.elapsed = 0; } else { timers.delete(timer); /// TODO: better way to clean references. // If the timer isn't periodic, the callback reference can be collected. ///luaL_unref(timer.handlerFunc); } } } for (let thread of threads) { thread.sleep -= dt; if (thread.sleep <= 0) { this.currentThread = thread; let L = thread.L; let status = lua_resume(L, this.L, 0); if (status === LUA_OK) { threads.delete(thread); } else if (status === LUA_YIELD) { thread.sleep = luaL_checknumber(L, 1); } else { console.log('[JS] Something went wrong during execution'); console.log(to_jsstring(luaL_tolstring(L, -1))); lua_pop(L, 2); } } } this.t = t; } /** * @param {JassHandle} handle * @return {JassHandle} */ addHandle(handle) { if (handle.handleId === -1) { let handleId; if (this.freeHandles.length) { handleId = this.freeHandles.pop(); } else { handleId = this.handle++; } this.handles[handleId] = handle; handle.handleId = handleId; } return handle; } /** * @param {JassHandle} handle */ freeHandle(handle) { if (handle.handleId !== -1) { this.freeHandles.push(handle.handleId); this.handles[handle.handleId] = null; handle.handleId = -1; } } /** * @param {string|number|null} name */ call(name) { let L = this.L; if (typeof name === 'string') { lua_getglobal(L, name); } else if (typeof name === 'number') { lua_rawgeti(L, LUA_REGISTRYINDEX, name); } if (lua_pcall(L, 0, 0, 0)) { console.log('Something went wrong during execution'); console.log(to_jsstring(luaL_tolstring(L, -1))); lua_pop(L, 2); } } /** * @param {string} code * @param {boolean} isJass */ run(code, isJass) { let L = this.L; if (isJass) { code = jass2lua(code); } if (luaL_loadstring(L, to_luastring(code))) { console.log('Something went wrong during execution'); console.log(to_jsstring(luaL_tolstring(L, -1))); lua_pop(L, 2); } if (lua_pcall(L, 0, 0, 0)) { console.log('Something went wrong during execution'); console.log(to_jsstring(luaL_tolstring(L, -1))); lua_pop(L, 2); } } /** * @param {War3Map} map */ open(map) { this.map = map; let file = map.get('war3map.j') || map.get('war3map.lua') || map.get('scripts\\war3map.j') || map.get('scripts\\war3map.lua'); let isJass = file.name.endsWith('.j'); this.run(file.text(), isJass); } }