--[[ This file is part of Ice Lua Components. Ice Lua Components is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Ice Lua Components is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with Ice Lua Components. If not, see . ]] local function copytab(t) local nt = {} local k,v for k,v in pairs(t) do if type(v) == type({}) then nt[k] = copytab(v) else nt[k] = v end end return nt end local function wrapenv(f, state) print("wrap", f, state) return function(...) local oldenv = getfenv(f) setfenv(f, state.e) return (function (...) setfenv(f, oldenv) return ... end)(f(...)) end end function fastload_getfile(fname, state) -- Get our file if we don't have it already if state.f[fname] == nil then if fname == "*GAMEMODE" then state.f[fname] = wrapenv(loadfile(GAME_MODE), state) else state.cache["lua:" .. fname] = common.bin_load(fname) state.f[fname] = wrapenv(loadfile(fname), state) end end return state.f[fname] end function fastload_analyse_client() local k,v -- Create our state state = { cache = {}, e = { client = {}, --server = {}, common = { version = copytab(common.version) }, coroutine = {}, math = {}, string = {}, table = {}, }, f = {}, } -- _G refers to the environment state.e._G = state.e -- Stash the current map local svmap = common.map_get() local svmap_copy = common.map_new(common.map_get_dims()) print("Copying server map") do local x,y local w,h,_ w, _, h = common.map_get_dims() for y=0,h-1 do for x=0,w-1 do common.map_set(svmap) local l = common.map_pillar_get(x, y) common.map_set(svmap_copy) common.map_pillar_set(x, y, l) end end end common.map_set(nil) print("Server map copied!") -- Functions we need to dummy out for k,v in pairs({ "print", }) do state.e[v] = function(...) end end -- Function tables we need to copy for k,v in pairs(coroutine) do state.e.coroutine[k] = v end for k,v in pairs(math) do state.e.math[k] = v end for k,v in pairs(string) do state.e.string[k] = v end for k,v in pairs(table) do state.e.table[k] = v end -- Special wrappers -- loadfile: we need to feed through fastload_analyse local function loadfile_wrap(fn) return fastload_getfile(fn, state) end state.e.loadfile = loadfile_wrap -- loadstring: we need to wrap what we've got function state.e.loadstring(...) return wrapenv(loadstring, state)(...) end -- dofile: depends on loadfile function state.e.dofile(fn) local f = state.e.loadfile(fn) return f() end -- Some things we need to just pass through state.e.getfenv = getfenv state.e.setfenv = setfenv state.e.getmetatable = getmetatable state.e.setmetatable = setmetatable state.e.rawget = rawget state.e.rawset = rawset state.e.pcall = pcall state.e.pairs = pairs state.e.ipairs = ipairs state.e.unpack = unpack -- Tell it there's a sandbox state.e.sandbox = {} -- client.wav_play_local/global function state.e.client.wav_play_local(...) return 1 end function state.e.client.wav_play_global(...) return 1 end -- common.map_set/get: Pass straight through state.e.common.map_set = common.map_set state.e.common.map_get = common.map_get -- client.map_fog_set/get: Stash some defaults do local fog = {192, 238, 255, 60} function state.e.client.map_fog_set(r, g, b, dist) fog[1] = r fog[2] = g fog[3] = b fog[4] = dist end function state.e.client.map_fog_get() return fog[1], fog[2], fog[3], fog[4] end end -- common.json_load: refers to stuff in clsave as well as other stuff function state.e.common.json_load(fname) print("json_load", fname) if fname == "clsave/pub/user.json" then return { name = "FastLoadTest", kick_on_join = false, sensitivity = 1.0, hold_to_zoom = false, fog = 127.5, skins = {}, } elseif fname == "clsave/pub/controls.json" then return { } elseif fname == "*MODCFG" then return common.json_load(mod_conf_file) else state.cache["json:" .. fname] = common.bin_load(fname) return common.json_load(fname) end end -- common.map_load: *MAP anyone? function state.e.common.map_load(fname, fmt) print("map_load", fname, fmt) if fname == "*MAP" then return svmap_copy else state.cache[fmt .. ":" .. fname] = common.bin_load(fname) return common.map_load(fname, fmt) end end -- common.img_load function state.e.common.img_load(fname, fmt) if fname:find("clsave/pub/skin/", 1, true) == 1 then return nil end print("fetch_img", fmt, fname) if fname == "*MAPIMG" then return common.img_new(800, 600) else fmt = fmt or "tga" state.cache[fmt .. ":" .. fname] = common.bin_load(fname) return common.img_load(fname, fmt) end end -- Some direct functions state.e.common.time = common.time state.e.common.img_get_dims = common.img_get_dims state.e.common.img_free = common.img_free state.e.common.model_bone_find = common.model_bone_find state.e.common.model_bone_get = common.model_bone_get state.e.common.model_new = common.model_new state.e.common.model_bone_new = common.model_bone_new state.e.common.model_bone_set = common.model_bone_set state.e.common.map_get_dims = common.map_get_dims state.e.common.img_new = common.img_new state.e.common.map_pillar_get = common.map_pillar_get state.e.common.img_pixel_set = common.img_pixel_set state.e.common.net_unpack = common.net_unpack --state.e.common.@ = common.@ --state.e.common.@ = common.@ -- client.screen_get_dims: 800, 600 function state.e.client.screen_get_dims() return 800, 600 end -- client.mk_set_title: dummy function state.e.client.mk_set_title() end -- client.wav_cube_size: dummy function state.e.client.wav_cube_size() end -- client.va_make: dummy function state.e.client.va_make() end function state.e.common.va_make() end -- client.img_*: dummy function state.e.client.img_fill() end function state.e.client.img_rect_fill() end function state.e.common.img_rect_fill() end function state.e.client.img_blit_to() end -- common.fetch_block: Need to cache all the things, except for the things that we can't local function fetch_block(typ, fname) print("fetch", typ, fname) if fname:find("clsave/pub/skin/", 1, true) == 1 then return nil end if typ == "lua" then return loadfile_wrap(fname) elseif typ == "png" and fname == "*MAPIMG" then return common.img_new(800, 600) else state.cache[typ .. ":" .. fname] = common.bin_load(fname) return common.fetch_block(typ, fname) end end state.e.common.fetch_block = fetch_block -- common.bin_load: Call fetch_block instead function state.e.common.bin_load(fname) return fetch_block("bin", fname) end -- common.mus_load_it: Call fetch_block instead function state.e.common.mus_load_it(fname) return fetch_block("it", fname) end -- common.fetch_start: Call fetch_block instead function state.e.common.fetch_start(ftype, fname) print(ftype, fname) return fetch_block(ftype, fname) end -- Make copies of the common functions we're using for k,v in pairs(state.e.common) do state.e.client[k] = v --state.e.server[k] = v end -- Load and run. state.e.dofile("pkg/base/main_client.lua") state.e.dofile("pkg/base/client_start.lua") -- Restore the current map common.map_set(svmap) print("Destroying map copy!") common.map_free(svmap_copy) print("Cache analysed.") local cstr = "" local clist = {} for k,v in pairs(state.cache) do cstr = cstr .. " " .. k clist[1+#clist] = k end print("Cached files:"..cstr) common.json_write("svsave/vol/fastload.json", { files = clist, }) local clcmp = common.json_load("svsave/vol/fastload.json") print(clcmp.files[1]) print("Filenames dumped to svsave/vol/fastload.json") end function fastload_pack_client() local r1, r2 r1, r2 = pcall(function () return common.json_load("svsave/vol/fastload.json") end) if not r1 then print("Error loading fastload names: "..r2) print("Run the server with the -flcache flag to generate svsave/vol/fastload.json") return string.char(0) elseif not r2 then print("Error loading fastload names - file not found") print("Run the server with the -flcache flag to generate svsave/vol/fastload.json") return string.char(0) end print("Generating fastload pack") local dat = "" local k,v for k,v in pairs(r2.files) do local r1, r2 r1, r2 = pcall(function () local pivot = v:find(":", 1, true) local fmt = v:sub(1, pivot-1) local fname = v:sub(pivot+1) print("load:", fmt, fname) local body = common.fetch_block("bin", fname) if body == nil then error("fetch_block returned nil") end dat = dat .. string.char(#v) .. v dat = dat .. string.char(math.floor(((#body) / (2^0)) % 256)) dat = dat .. string.char(math.floor(((#body) / (2^8)) % 256)) dat = dat .. string.char(math.floor(((#body) / (2^16)) % 256)) dat = dat .. string.char(math.floor(((#body) / (2^24)) % 256)) dat = dat .. body end) if not r1 then print("ERROR: Fastload failed to pack file \"" .. v .. "\"! (Is the cache outdated?)") end end dat = dat .. string.char(0) print(string.format("fastload data size: %i bytes", #dat)) common.bin_save("svsave/vol/fldata.bin", dat) end local fldata_int = nil function fastload_fetch() local body = common.bin_load("*FASTLOAD") if body == nil then print("ERROR: Server does not provide fastload. Loading more slowly now!") return end fldata_int = {} local i = 1 while body:byte(i) ~= 0 do local len = body:byte(i) i = i + 1 local fnp = body:sub(i, i+len-1) i = i + len local dlen = (body:byte(i+0) * (2^0) + body:byte(i+1) * (2^8) + body:byte(i+2) * (2^16) + body:byte(i+3) * (2^24)) i = i + 4 local dat = body:sub(i, i+dlen-1) i = i + dlen print("fastload data: fnpair", len, fnp, #fnp, dlen, #dat) common.bin_save("clsave/vol/fastload.tmp", dat) local pivot = fnp:find(":", 1, true) local fmt = fnp:sub(1, pivot-1) local r1,r2 r1,r2 = pcall(function () fldata_int[fnp] = common.fetch_block(fmt, "clsave/vol/fastload.tmp") end) if not r1 then print("ERROR: failed to preload \"" ..fnp.. "\": ".. r2) end end end function fastload_check(fmt, fname) if fldata_int == nil then return nil end local pname = fmt..":"..fname return fldata_int[pname] end