mdx-m3-viewer
Version:
A browser WebGL model viewer. Mainly focused on models of the games Warcraft 3 and Starcraft 2.
220 lines (173 loc) • 5.79 kB
text/typescript
import { EventEmitter } from 'events';
// @ts-ignore
import { to_luastring, to_jsstring } from 'fengari/src/fengaricore';
// @ts-ignore
import { lua_State, 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';
// @ts-ignore
import { luaL_newstate, luaL_loadstring, luaL_tolstring, luaL_unref, luaL_checknumber } from 'fengari/src/lauxlib';
//import { luaL_openlibs } from 'fengari/src/lualib';
import jass2lua from './jass2lua';
import bindNatives from './natives';
import JassPlayer from './types/player';
import constantHandles from './constanthandles';
import Thread from './thread';
import War3Map from '../../parsers/w3x/map';
import JassHandle from './types/handle';
import JassLocation from './types/location';
import JassTimer from './types/timer';
import { JassTrigger } from './types/index';
/**
* A Jass2 context.
*/
export default class Context extends EventEmitter {
L: lua_State;
map: War3Map | null = null;
handle: number = 0;
freeHandles: number[] = [];
handles: (JassHandle | null)[] = [];
name: string = '';
description: string = '';
players: JassPlayer[] = [];
actualPlayers: number = 0;
startLocations: JassLocation[] = [];
constantHandles = constantHandles();
timers: Set<JassTimer> = new Set();
triggers: Set<JassTrigger> = new Set();
threads: Set<Thread> = new Set();
currentThread: Thread | null = null;
enumUnit: JassHandle | null = null;
filterUnit: JassHandle | null = null;
enumPlayer: JassHandle | null = null;
t: number = 0;
constructor() {
super();
this.L = luaL_newstate();
//luaL_openlibs(this.L);
bindNatives(this);
lua_atnativeerror(this.L, (L: lua_State) => {
let e = lua_touserdata(L, -1);
lua_pushstring(L, e.stack);
return 1;
});
for (let i = 0; i < 28; i++) {
this.players[i] = <JassPlayer>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();
}
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;
}
addHandle(handle: JassHandle) {
if (handle.handleId === -1) {
let handleId;
if (this.freeHandles.length) {
handleId = <number>this.freeHandles.pop();
} else {
handleId = this.handle++;
}
this.handles[handleId] = handle;
handle.handleId = handleId;
}
return handle;
}
freeHandle(handle: JassHandle) {
if (handle.handleId !== -1) {
this.freeHandles.push(handle.handleId);
this.handles[handle.handleId] = null;
handle.handleId = -1;
}
}
call(name?: string | number) {
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);
}
}
run(code: string, isJass: boolean) {
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);
}
}
open(map: War3Map) {
this.map = map;
let file = map.getScriptFile();
if (file) {
let buffer = file.text();
if (buffer) {
let isJass = file.name.endsWith('.j');
this.run(buffer, isJass);
}
}
}
}