1003 lines
28 KiB
Plaintext
1003 lines
28 KiB
Plaintext
work in progress
|
|
|
|
libraries are "client" and "server"
|
|
stuff in both is also put in "common"
|
|
note, behaviour might be subtly different
|
|
|
|
all files must use "/" as a path separator (Unix Master Race)
|
|
valid characters are listed here (ignoring the path separator):
|
|
-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ[]_abcdefghijklmnopqrstuvwxyz
|
|
anything else is invalid and will just be outright rejected
|
|
|
|
furthermore, no paths will be accepted where a . follows a /
|
|
this means that hidden files on unixlikes will not be accepted!
|
|
|
|
files that are meant to be intercepted should be prefixed with a *
|
|
e.g. "*thismap"
|
|
|
|
maximum path length is 128
|
|
|
|
stuff that's actually implemented will be marked with an @
|
|
|
|
lua base library stuff:
|
|
math.* @
|
|
string.* @
|
|
pcall @
|
|
error @
|
|
print @
|
|
loadstring @
|
|
loadfile @
|
|
dofile @
|
|
_G @
|
|
|
|
nothing else yet
|
|
(TODO: work out what we want to keep,
|
|
as there's stuff being used that isn't listed here!)
|
|
|
|
check http://www.lua.org/manual/5.1/ for more info on these
|
|
|
|
some notes:
|
|
- dofile(x) = loadfile(x)()
|
|
- loadfile(x) = common.fetch_block("lua", x)
|
|
- common.fetch_block(ftype, x) =
|
|
local obj = common.fetch_start(ftype, x)
|
|
if obj ~= true then return obj end
|
|
while true do
|
|
local obj = common.fetch_poll()
|
|
if obj ~= false then return obj end
|
|
yield()
|
|
end
|
|
- just about every "load" function is based on common.fetch_block
|
|
|
|
common.base_dir @
|
|
current base directory (minus the "pkg/" bit)
|
|
|
|
writing to this string will *not* change the base dir,
|
|
so *don't write to it* or else you'll just screw up your own code!
|
|
|
|
server.port @
|
|
port we are bound to
|
|
|
|
there is NO alias to common here! if you want it, do it manually.
|
|
|
|
common.version = {cmp={w,x,y,a,z},num,str="w.x.ya-z"} @
|
|
current engine version
|
|
|
|
cmp is for the individual version components
|
|
num is a comparable number (bit count for each: 5,5,7,5,10)
|
|
str is the version as a string
|
|
|
|
client.renderer @
|
|
current renderer used, "softgm" or "gl"
|
|
there is NO alias to common here! if you want it, do it manually.
|
|
note, as of 0.1.2-11, "softgm" has been removed.
|
|
|
|
client/server.hook_tick = fn(sec_curtime, sec_delta)->sec_wait @
|
|
sets a hook called every "tick"
|
|
returns a number telling it how long it wants to wait for
|
|
|
|
"sec_curtime" is the current time elapsed from the start
|
|
"sec_delta" is the time elapsed from the start of the last
|
|
call to the hooked function
|
|
|
|
setting this to nil will quit your game
|
|
|
|
NOTE: DO NOT USE COMMON FOR THIS HOOK!
|
|
(only client/server is accepted)
|
|
|
|
server.hook_file = fn(neth, ftype, fname)->object
|
|
sets a hook to intercept networked file requests
|
|
|
|
"neth" is the socket handle pertaining to the file request
|
|
|
|
can return nil to cancel the request
|
|
can return true to perform the default request
|
|
can return a string to load a different file instead
|
|
otherwise, return an object to be serialised
|
|
|
|
currently supported values for ftype:
|
|
- "icemap"
|
|
|
|
the rest must be either nil, true, or a string.
|
|
|
|
client.hook_render = fn() @
|
|
sets a hook called every frame
|
|
|
|
this is the only place where it is safe to render stuff
|
|
such as models and HUDs and whatnot
|
|
|
|
client.hook_key = fn(key, state, modif, uni) @
|
|
sets a hook called every time a key changes
|
|
|
|
state is either true or false
|
|
uni is an index of a unicode character, which is 0 if there is no unicode decoding
|
|
|
|
client.hook_mouse_button = fn(button, state) @
|
|
sets a hook called every time a mouse button changes
|
|
|
|
state is either true or false
|
|
|
|
client.hook_mouse_motion = fn(x, y, dx, dy) @
|
|
sets a hook called every time the mouse is moved
|
|
|
|
x, y are the current position
|
|
dx, dy are the delta positions
|
|
|
|
you will probably use the latter pair if the mouse is "locked"
|
|
as x, y are not defined the same on all platforms!
|
|
|
|
server.hook_connect = fn(neth, addrinfo) @
|
|
sets a hook called every time a client connects
|
|
|
|
note, neth == true for "local multiplayer" mode
|
|
|
|
"addrinfo" is a table which may or may not contain:
|
|
- "proto" / "addr": protocol / address used, one of the following:
|
|
- "local" / nil
|
|
- "tcp/ip" or "tcp/ip6" / {
|
|
["ip"] = address,
|
|
["host"] = hostname or nil,
|
|
["cport"] = client port,
|
|
["sport"] = server port,
|
|
}
|
|
|
|
note, not all of the features are supported.
|
|
|
|
server.hook_disconnect = fn(neth, server_force, reason) @
|
|
sets a hook called every time a client disconnects
|
|
|
|
"server_force" is true where the server forces the disconnect
|
|
|
|
client.mouse_lock_set(state) @
|
|
locks / unlocks the mouse depending on "state"
|
|
|
|
client.mouse_visible_set(state) @
|
|
shows / hides the mouse depending on "state"
|
|
|
|
client.mouse_warp(x, y) @
|
|
moves the mouse to x,y
|
|
|
|
obj = common.fetch_start(ftype, fname)
|
|
initiates a file fetch
|
|
|
|
"ftype" is one of the following:
|
|
- "lua": lua script
|
|
- "map": map (autodetect)
|
|
- "icemap": map (icemap) - in-memory maps are serialised as THIS.
|
|
- "vxl": map (vxl) - CANNOT SAVE IN THIS FORMAT.
|
|
- "pmf": pmf model
|
|
- "tga": tga image
|
|
- "png": png image - CANNOT SAVE IN THIS FORMAT (yet).
|
|
- "json": json data
|
|
- "wav": wav sound
|
|
- "it": it music module
|
|
- "bin": raw data
|
|
- "ply": Stanford PLY model
|
|
- "log": log data (TODO)
|
|
|
|
for the server, this just loads the file from the disk.
|
|
|
|
for the client, all clsave/* stuff is taken from the disk,
|
|
but all other files are downloaded from the server.
|
|
|
|
returns true if the fetch has started,
|
|
nil if there is an error,
|
|
or the object if this was an immediate load.
|
|
|
|
if there is already a file in the queue,
|
|
this will return "nil".
|
|
|
|
obj, csize, usize, amount = common.fetch_poll()
|
|
polls the
|
|
"obj" is one of the following:
|
|
- "nil" if transfer aborted or nothing is being fetched
|
|
- in this case, all other fields will be nil
|
|
- "false" if still downloading
|
|
- the object you requested
|
|
- in this case, another poll will just return nils
|
|
|
|
"amount" is in the range 0 <= "amount" <= 1,
|
|
and indicates how much is downloaded
|
|
"csize" is the compressed size of the file
|
|
"usize" is the uncompressed size
|
|
|
|
the two sizes will be nil while unknown.
|
|
|
|
note, all vxl maps will be converted to icemap before sending.
|
|
|
|
obj = common.fetch_block(ftype, fname) @
|
|
fetches a file using common.fetch_*
|
|
|
|
simply returns "nil" on error
|
|
|
|
if there is already something being fetched,
|
|
it will return "nil", too
|
|
|
|
client.gfx_alpha_test(enable) @
|
|
(enable ? glEnable : glDisable)(GL_ALPHA_TEST);
|
|
|
|
this is reset to true by the start of each frame
|
|
|
|
client.gfx_clear_color() @
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
|
|
clears the screen (or FBO if we're using that)
|
|
|
|
client.gfx_clear_depth() @
|
|
glClear(GL_DEPTH_BUFFER_BIT);
|
|
|
|
clears the depth buffer AKA z buffer
|
|
|
|
client.gfx_clear_stencil() @
|
|
glClear(GL_STENCIL_BUFFER_BIT);
|
|
|
|
clears the stencil buffer
|
|
|
|
client.gfx_depth_mask(enable) @
|
|
glDepthMask(enable ? GL_TRUE : GL_FALSE);
|
|
|
|
enables (or disables) writing to the depth buffer AKA z buffer
|
|
this is reset to true by the start of each frame
|
|
|
|
client.gfx_depth_test(enable) @
|
|
(enable ? glEnable : glDisable)(GL_DEPTH_TEST);
|
|
|
|
this is reset to true by the start of each frame
|
|
this is ALSO reset to true after calling client.img_blit
|
|
so watch out!
|
|
|
|
client.gfx_stencil_func(func, ref, mask) @
|
|
glStencilFunc(func, ref, mask)
|
|
|
|
func is a string containing one of these:
|
|
- "0" - GL_NEVER
|
|
- "<" - GL_LESS
|
|
- "<=" - GL_LEQUAL
|
|
- ">" - GL_GREATER
|
|
- ">=" - GL_GEQUAL
|
|
- "==" - GL_EQUAL
|
|
- "~=" - GL_NOTEQUAL
|
|
- "1" - GL_ALWAYS
|
|
|
|
ref and mask are as per the OpenGL spec
|
|
|
|
client.gfx_stencil_op(mask) @
|
|
glStencilOp(fail, zfail, zpass);
|
|
|
|
mask is a 3-char string,
|
|
each char representing fail, zfail and zpass respectively
|
|
|
|
meaning of each char:
|
|
- ; - GL_KEEP
|
|
- 0 - GL_ZERO
|
|
- = - GL_REPLACE
|
|
- + - GL_INCR
|
|
- - - GL_DECR
|
|
- ~ = GL_INVERT
|
|
|
|
client.gfx_stencil_test(enable) @
|
|
(enable ? glEnable : glDisable)(GL_STENCIL_TEST);
|
|
|
|
this is reset to FALSE by the start of each frame
|
|
|
|
client.gfx_viewport(x, y, width, height) @
|
|
glViewport(x, y, width, height);
|
|
|
|
sets up the drawing region
|
|
note, this also corrects the projection matrix
|
|
|
|
WARNING: (x,y) denotes the *bottom*-left of the "screen"!
|
|
|
|
this is reset to full screen by the start of each frame
|
|
|
|
tex_units = client.gfx_tex_available() @
|
|
returns the number of texture units available
|
|
|
|
note, this will be clamped by the engine limit,
|
|
which is currently 8
|
|
|
|
uses GL_MAX_TEXTURE_UNITS_ARB
|
|
will eventually use GL_MAX_TEXTURE_IMAGE_UNITS_ARB
|
|
once shader support is in place
|
|
|
|
success = client.gfx_fbo_available() @
|
|
returns if FBOs are available
|
|
|
|
fbo = client.fbo_create(w, h, use_stencil) @
|
|
creates a new FBO with color/depth/stencil information
|
|
returns nil on failure
|
|
|
|
if use_stencil is set, create a GL_DEPTH_STENCIL_ATTACHMENT,
|
|
otherwise just a GL_DEPTH_ATTACHMENT.
|
|
|
|
WARNING: this also clears the current FBO status
|
|
so if you're using an FBO, you'll have to call fbo_use again!
|
|
(it does the equivalent of client.fbo_use(nil))
|
|
(why are you creating FBOs once you get around to actually using them anyway?)
|
|
|
|
FBO note: the width and height you get will NOT be expanded;
|
|
that is, a 320x200 FBO really IS 320x200,
|
|
rather than 512x256.
|
|
|
|
client.fbo_use(fbo) @
|
|
uses the given FBO
|
|
|
|
"fbo" can be nil, in which case it uses the screen
|
|
|
|
glver = client.gfx_glsl_available() @
|
|
returns if GLSL shaders are available
|
|
|
|
return values:
|
|
- "2.1" if at least GL 2.1
|
|
- "2.0" if GL 2.0
|
|
- nil otherwise
|
|
|
|
shader, result = client.glsl_create(vertex_src_string, fragment_src_string, attr_array = {}) @
|
|
creates a shader
|
|
|
|
attr_array is a list of strings to bind to attributes
|
|
|
|
returns a shader handle in "shader" on success
|
|
returns nil on failure
|
|
|
|
shaders are garbage collected, so watch out!
|
|
|
|
"result" is just the shader compiler output
|
|
|
|
client.glsl_use(shader) @
|
|
uses a shader
|
|
|
|
pass nil if you want to switch to default GL 1 behaviour
|
|
|
|
note, fog is disabled while using a shader
|
|
this does NOT mean that the far plane is extended for you!
|
|
|
|
idx = client.glsl_get_uniform_loc(shader, name) @
|
|
gets the location of a uniform value given a shader
|
|
|
|
returns nil on failure
|
|
|
|
client.glsl_set_uniform_f(uniform_idx, val1[, val2[, val3[, val4]]]) @
|
|
sets a float uniform
|
|
|
|
client.glsl_set_uniform_i(uniform_idx, val1[, val2[, val3[, val4]]]) @
|
|
sets an int uniform
|
|
|
|
client.glsl_set_uniform_ui(uniform_idx, val1[, val2[, val3[, val4]]]) @
|
|
sets an unsigned int uniform
|
|
|
|
client.map_enable_autorender(enabled) @
|
|
enable drawing of the map at the start of each frame
|
|
enabled by default
|
|
|
|
client.map_enable_ao(enabled) @
|
|
enable built-in ambient occlusion (unless smoothlighting is off)
|
|
enabled by default
|
|
|
|
client.map_enable_side_shading(enabled) @
|
|
enable built-in voxlap-style side shading
|
|
enabled by default
|
|
|
|
map = common.map_load(fname, fmt = "auto") @
|
|
loads a map, either "vxl" or "icemap"
|
|
use "auto" to autodetect
|
|
|
|
this will return nil if it fails.
|
|
|
|
map = common.map_new(lx, ly, lz) @
|
|
creates a new map
|
|
|
|
note, ly is the *height* of the new map
|
|
|
|
also all dimensions must be powers of two,
|
|
otherwise this will fail horribly!
|
|
|
|
this will throw a lua error if it fails.
|
|
|
|
common.map_free(map) @
|
|
free the given map
|
|
if you don't do this then it's memoryleaktopia
|
|
(plus i'm allowed to kill you)
|
|
|
|
ALSO DON'T DO THIS TO THE CURRENT "SET" MAP
|
|
|
|
TODO: clean up all models on game kill
|
|
|
|
client.map_set_render_format(map, format) @
|
|
defines what render info a map will have
|
|
|
|
format is as per common.va_make,
|
|
except these are the formats supported:
|
|
- 3v - vertex - must have exactly one
|
|
- 3c - colour - must have at most one
|
|
- 3n - normal - must have at most one
|
|
- 2t - texcoord - must have at most one
|
|
|
|
default formats:
|
|
- 3v,3c,3n - gl_shaders enabled
|
|
- 3v,3c - gl_shaders disabled
|
|
|
|
you should probably just leave it at the defaults
|
|
when shaders are disabled
|
|
|
|
client.map_render(map, px, py, pz, tex = nil, afmt = nil, alpha = 1.0) @
|
|
render a map at global position px, py, pz
|
|
|
|
see client.va_render_global for the meanings of the other arguments
|
|
|
|
NOTE: at the moment it's probably not safe to render two of the same map
|
|
unless the px,py,pz offset is small
|
|
|
|
common.map_set(map) @
|
|
sets the "current" map to "map"
|
|
note, "map" can be nil to disable rendering anything
|
|
|
|
map = common.map_get() @
|
|
sets the "current" map
|
|
may return "nil"
|
|
|
|
common.map_save(map, fname, fmt = "icemap") @
|
|
saves a map to a file, either "vxl" or "icemap"
|
|
|
|
this will throw a lua error if it fails.
|
|
|
|
r, g, b, dist = client.map_fog_get() @
|
|
gets the current fog colour / distance
|
|
|
|
client.map_fog_set(r, g, b, dist) @
|
|
sets the current fog colour / distance
|
|
|
|
lx, ly, lz = common.map_get_dims() @
|
|
gets the map's dimensions
|
|
|
|
these will be nil if there is no map loaded
|
|
|
|
table = common.map_pillar_get(px, pz) @
|
|
returns a full pillar of data, skipping the total size header
|
|
|
|
this will be nil if there is no map loaded
|
|
|
|
note, the data wraps around here (wrt px,pz)!
|
|
|
|
common.map_pillar_set(px, pz, table) @
|
|
sets a full pillar of data
|
|
|
|
this will be nil if there is no map loaded
|
|
|
|
the data is checked before setting,
|
|
and will throw a lua error if it fails.
|
|
|
|
note, the data wraps around here (wrt px,pz)!
|
|
|
|
WARNING: You MUST update the side pillars in your code!
|
|
You MUST ALSO fill in the newly-exposed blocks,
|
|
as well as remove the newly-unexposed blocks!
|
|
Otherwise, there WILL be gaps!
|
|
|
|
WE'RE NOT DOING IT FOR YOU!
|
|
|
|
common.map_mapents_get() @
|
|
gets the MapEnts chunk data (as a hopefully-json string)
|
|
|
|
this will be nil if there is no map loaded or no MapEnts chunk
|
|
|
|
common.map_mapents_set(mapents) @
|
|
sets the MapEnts chunk data
|
|
|
|
mapents should be a json string
|
|
|
|
client.camera_point(dx, dy, dz, zoom = 1.0, roll = 0.0) @
|
|
points the camera in a direction with zoom factor "zoom"
|
|
and roll "roll" (in radians, sorry)
|
|
|
|
client.camera_point_sky(dx, dy, dz, zoom = 1.0, sx = 0.0, sy = -1.0, sz = 0.0) @
|
|
points the camera in a direction with zoom factor "zoom"
|
|
and sky arrow sx,sy,sz
|
|
|
|
client.camera_move_local(dx, dy, dz) @
|
|
moves the camera in the camera-local direction (dx,dy,dz)
|
|
|
|
client.camera_move_global(dx, dy, dz) @
|
|
moves the camera in the world direction (dx,dy,dz)
|
|
|
|
client.camera_move_to(px, py, pz) @
|
|
moves the camera to the world position (px,py,pz)
|
|
|
|
px, py, pz = client.camera_get_pos() @
|
|
gets the camera's position
|
|
|
|
dx, dy, dz = client.camera_get_forward() @
|
|
gets the camera's forward vector
|
|
|
|
client.camera_shade_set(xn, yn, zn, xp, yp, zp)
|
|
sets the face shading levels per face (0 <= v <= 1)
|
|
sides relative to map are: east, bottom, south, west, top, north
|
|
|
|
pmf = common.model_new(bonemax = 5) @
|
|
creates a new model
|
|
|
|
[ paragraph does not apply to 0.1.2-10 and higher ]
|
|
remember to free it when you're done
|
|
as this is only a light userdata
|
|
|
|
"bonemax" is the initial maximum number of "bones"
|
|
this model can have initially
|
|
useful if you know exactly how many you'll need
|
|
|
|
currently the number of bones is limited to 256
|
|
|
|
pmf = common.model_load_pmf(fname) @
|
|
loads a pmf from a file
|
|
|
|
[ paragraph does not apply to 0.1.2-10 and higher ]
|
|
remember to free it when you're done
|
|
as this is only a light userdata
|
|
|
|
limits of bones / points still applies here;
|
|
files which exceed these WILL be rejected!
|
|
|
|
returns nil on failure.
|
|
|
|
success = common.model_save_pmf(pmf, fname) @
|
|
saves a pmf to a file
|
|
|
|
common.model_free(pmf) @
|
|
free the given model
|
|
|
|
[ paragraph does not apply to 0.1.2-10 and higher ]
|
|
if you don't do this then it's memoryleaktopia
|
|
(plus i'm allowed to kill you)
|
|
|
|
TODO: clean up all models on game kill
|
|
|
|
len = common.model_len(pmf) @
|
|
get the number of bones in this model
|
|
|
|
pmf, boneidx = common.model_bone_new(pmf, ptmax = 20) @
|
|
creates a new bone
|
|
|
|
"ptmax" is the initial maximum number of "points"
|
|
this bone can have initially
|
|
useful if you know exactly how many you'll need
|
|
|
|
currently the number of points is limited to 4096
|
|
|
|
WARNING: YOU *MUST* TAKE THE pmf VALUE RETURNED
|
|
AND *NOT* USE THE OLD ONE ANY LONGER!
|
|
|
|
this is because realloc() is called on this dynamic list
|
|
and it can seriously crash badly
|
|
|
|
JUST SAYING
|
|
|
|
common.model_bone_free(pmf, boneidx) @
|
|
removes a bone from the model
|
|
|
|
name, table = common.model_bone_get(pmf, boneidx) @
|
|
gets a table with every point in the given bone
|
|
|
|
each entry in the table has the following keys:
|
|
uint16_t radius; // fixed point 8.8
|
|
int16_t x,y,z; // fixed point 8.8
|
|
uint8_t r,g,b;
|
|
|
|
the reserved field of each point is inaccessible from this API
|
|
|
|
common.model_bone_set(pmf, boneidx, name, table) @
|
|
replaces the bone's contents with that in the table
|
|
|
|
note, bones will be rejected if:
|
|
- name is > 15 chars long
|
|
- radius,x,y,z,r,g,b are missing
|
|
- 0 <= radius < 65536 fails
|
|
- -32768 <= x,y,z < 32768 fails
|
|
- 0 <= r,g,b < 256 fails
|
|
|
|
these exceptions will throw a lua error
|
|
|
|
boneidx = common.model_bone_find(pmf, name) @
|
|
finds the first bone with the given name
|
|
|
|
note, this is case sensitive
|
|
|
|
if it cannot be found, this returns nil
|
|
|
|
client.model_render_bone_global(pmf, boneidx, px, py, pz, ry, rx, ry2, scale) @
|
|
renders a bone at world position (px,py,pz),
|
|
|
|
rotated around Y by "ry" radians,
|
|
then around X by "rx" radians,
|
|
then around Y by "ry2" radians,
|
|
and scaled "scale" times
|
|
|
|
client.model_render_bone_local(pmf, boneidx, px, py, pz, ry, rx, ry2, scale) @
|
|
renders a bone at camera-local position (px,py,pz),
|
|
|
|
rotated around Y by "ry" radians,
|
|
then around X by "rx" radians,
|
|
then around Y by "ry2" radians,
|
|
and scaled "scale" times
|
|
|
|
width, height = client.screen_get_dims() @
|
|
gets the dimensions of the screen
|
|
|
|
screen_width, screen_height
|
|
global vars set in main_client.lua and thus available in any client mod
|
|
|
|
img, width, height = common.img_load(fname, fmt = "tga") @
|
|
loads an image with filename "fname"
|
|
|
|
[ paragraph does not apply to 0.1.2-10 and higher ]
|
|
remember to free it when you're done
|
|
as this is only a light userdata
|
|
|
|
format is either "tga" or "png"
|
|
use "auto" to autodetect [ NOT SUPPORTED YET ]
|
|
|
|
if this fails, img, width, height will all be nil
|
|
|
|
img = common.img_new(width, height) @
|
|
creates a new 32bpp RGBA image
|
|
|
|
[ paragraph does not apply to 0.1.2-10 and higher ]
|
|
remember to free it when you're done
|
|
as this is only a light userdata
|
|
|
|
if this fails, img will be nil
|
|
|
|
common.img_pixel_set(img, x, y, color) @
|
|
sets the pixel x,y on image "img" to "color"
|
|
|
|
drawing is clipped
|
|
|
|
common.img_pixel_get(img, x, y) @
|
|
gets the pixel x,y colour on image "img"
|
|
|
|
x, y clipped
|
|
|
|
common.img_fill(img, color) @
|
|
fills an image with a solid colour
|
|
|
|
common.img_free(img) @
|
|
free the given image
|
|
|
|
[ paragraph does not apply to 0.1.2-10 and higher ]
|
|
if you don't do this then it's memoryleaktopia
|
|
(plus i'm allowed to kill you)
|
|
|
|
width, height = common.img_get_dims(img) @
|
|
gets the image's dimensions
|
|
|
|
common.img_rect_fill(img, x, y, width, height, color) @
|
|
fills a sub-rect of an image with a solid colour
|
|
|
|
client.img_blit(img, dx, dy, width = iwidth, height = iheight, sx = 0, sy = 0, color = 0xFFFFFFFF, scalex = 1.0, scaley = 1.0) @
|
|
blits an image onto screen position dx, dy
|
|
(img can also be an FBO here)
|
|
|
|
"color" indicates a base 0xAARRGGBB colour to use
|
|
|
|
this is clipped to fit!
|
|
|
|
use scalex/scaley to rescale source width/height to your likeness
|
|
(sw = dw/scalex; sh = dh/scaley)
|
|
|
|
client.img_blit_to(target, img, dx, dy, width = iwidth, height = iheight, sx = 0, sy = 0, color = 0xFFFFFFFF, scalex = 1.0, scaley = 1.0) @
|
|
blits an image onto another image at position dx, dy
|
|
|
|
"color" indicates a base 0xAARRGGBB colour to use
|
|
|
|
this is clipped to fit!
|
|
|
|
use scalex/scaley to rescale source width/height to your likeness
|
|
(sw = dw/scalex; sh = dh/scaley)
|
|
|
|
tab = common.json_parse(str) @
|
|
parses the JSON string "str"
|
|
|
|
yes, this parser is anal-retentive,
|
|
and only allows one specific case where it breaks the rules!
|
|
it also errors if it encounters a char 0 in the middle of a string.
|
|
|
|
returns nil on error
|
|
|
|
tab = common.json_load(fname) @
|
|
loads a JSON file and parses it
|
|
|
|
returns nil on error
|
|
|
|
common.json_write(fname, value) @
|
|
takes a value and saves it to a file as JSON
|
|
makes the most sense if value is a table
|
|
|
|
the following types are supported:
|
|
- numbers
|
|
- strings
|
|
- true, false, nil
|
|
- tables
|
|
- arrays
|
|
|
|
and therefore the following types are NOT supported:
|
|
- functions/closures of ANY sort (C or Lua)
|
|
- userdata (light or otherwise)
|
|
- tables with a metatable (the metatable will be stripped)
|
|
|
|
and i think that's about it for types really
|
|
|
|
to tell the difference between tables and arrays it checks for the existence of the key "1"
|
|
|
|
str = common.net_pack(fmt, ...) @
|
|
packs data into a string
|
|
|
|
WARNING: signed/unsigned kinda does matter.
|
|
if you give any values out of range, behaviour is undefined!
|
|
(in other words, ARM behaves differently from x86)
|
|
|
|
format is as such:
|
|
b/B = signed/unsigned 8-bit [def 0]
|
|
h/H = signed/unsigned 16-bit [def 0]
|
|
i/I = signed/unsigned 32-bit [def 0]
|
|
f = single-precision 32-bit float [def 0.0]
|
|
d = double-precision 64-bit float [def 0.0]
|
|
z = zero-terminated string [def ""]
|
|
#s = fixed-length string (replace # with a decimal number) [def ""]
|
|
|
|
throws a lua error if the fmt syntax is invalid.
|
|
|
|
spews defaults if not enough arguments are provided.
|
|
|
|
..., remain = common.net_unpack(fmt, str) @
|
|
unpacks data from a string
|
|
|
|
will attempt to decode from start to end
|
|
|
|
"remain" is the remainder of the string which was not decoded
|
|
|
|
returns nil for fields that could not be decoded
|
|
|
|
success = common.net_send(neth, str, unreliable = false) @
|
|
sends a packet
|
|
|
|
"neth" is ignored C->S and should be nil
|
|
S->C local multiplayer should set "neth" to "true"
|
|
|
|
"unreliable" only makes sense on ENet connections;
|
|
it is ignored for the TCP protocol.
|
|
if "unreliable" is set to "true", we don't care if it arrives or not.
|
|
|
|
COMPATIBILITY WARNING:
|
|
before 0.0-50, neth pertains to an ACTUAL socket.
|
|
from 0.0-50 onwards, neth pertains to a CLIENT HANDLE.
|
|
|
|
this should not be of any concern to you unless you do Really Stupid Stuff.
|
|
having said that, DO NOT EXPECT THIS TO BE A ONE-TO-ONE MAPPING TO CLIENT INDICES.
|
|
|
|
oh, and as expected, same applies to common.net_recv and every other case you see a neth.
|
|
|
|
str, neth = common.net_recv() @
|
|
receives a packet
|
|
|
|
for C->S "neth" is nil
|
|
S->C local multiplayer will result in "neth" being "true"
|
|
|
|
returns nil, nil if nothing is there
|
|
returns false, nil on the client if the connection was terminated
|
|
|
|
server.net_kick(neth, reason) @
|
|
kicks a client from the server
|
|
|
|
fails silently if neth is invalid (but still a plausible parameter),
|
|
or the socket is closed / errors out
|
|
|
|
throws a lua error for other weird errors.
|
|
|
|
client.hook_kick = fn(reason) @
|
|
sets a hook called when the player gets kicked
|
|
|
|
tcpsockfd = common.tcp_connect(hostname, port) @
|
|
creates a raw TCP socket
|
|
returns nil on connection failure
|
|
throws an error in other weird situations
|
|
|
|
data = common.tcp_recv(tcpsockfd) @
|
|
receives from a TCP socket
|
|
returns an empty string if nothing arrived
|
|
returns false on error, such as disconnection
|
|
|
|
WARNING: DO NOT MAKE UP YOUR OWN SOCKFDS!
|
|
DOING THAT CAN REALLY SCREW UP YOUR CONNECTIONS AND WHATNOT.
|
|
SAME WARNING APPLIES TO SENDING.
|
|
|
|
DO NOT MIX TCPSOCKFDS UP WITH UDPSOCKFDS. IT DOESN'T WORK.
|
|
|
|
THAT MEANS DON'T BE A SMARTARSE - LEAVE STDIN/OUT/ERR ALONE.
|
|
NOT EVERYONE DOESN'T USE WINDOWS.
|
|
|
|
remain = common.tcp_send(tcpsockfd, data) @
|
|
sends data to a TCP socket
|
|
|
|
returns the data that wasn't quite sent just yet, which can be an empty string
|
|
returns false on error, such as disconnection
|
|
|
|
common.tcp_close(tcpsockfd) @
|
|
closes a TCP socket
|
|
|
|
udpsockfd = common.udp_open() @
|
|
creates a raw UDP socket
|
|
returns nil on socket creation failure
|
|
throws an error in other weird situations
|
|
|
|
data, host, port = common.udp_recvfrom(udpsockfd) @
|
|
receives from a UDP socket
|
|
returns an empty string + nils if nothing arrived
|
|
returns false on error, such as disconnection
|
|
|
|
remain = common.udp_sendto(udpsockfd, data, host, port) @
|
|
sends data to a host using a UDP socket
|
|
|
|
returns the data that wasn't quite sent just yet, which can be an empty string
|
|
returns false on error, such as disconnection
|
|
|
|
common.udp_close(udpsockfd) @
|
|
closes a UDP socket
|
|
|
|
va = common.va_make(vlist, oldva = nil, format = "3v,3c") @
|
|
makes a vertex array of triangles
|
|
vlist is a list of lists of floats in a format like this:
|
|
- {{x,y,z,r,g,b}, ...} ("3v,3c" since 0.2a-1, format arg unsupported there!)
|
|
- {{x,y,z,r,g,b,u,v}, ...} ("3v,3c,2t" since 0.2a-2)
|
|
- {{x,y,z,r,g,b,a}, ...} ("3v,4c" since 0.2.1-1)
|
|
- {{x,y,z,r,g,b,a,u,v}, ...} ("3v,4c,2t" since 0.2.1-1)
|
|
- {{x,y,z,u,v}, ...} ("3v,2t" since 0.2a-2)
|
|
|
|
anything else requires 0.2.1-7
|
|
|
|
here are the formats you can use:
|
|
- 2v,3v,4v - vertex - must have exactly one
|
|
- 3c,4c - colour - must have at most one
|
|
- 3n - normal - must have at most one
|
|
- 1t,2t,3t,4t - texcoord - must have at most 8
|
|
- 1a,2a,3a,4a - vertex attribute - must have at most 32
|
|
|
|
(note for 0.1.2-7: attributes aren't actually supported,
|
|
but they *are* parsed. wait for shader support before using them!)
|
|
|
|
if you don't follow your given format this can throw an error,
|
|
so WATCH OUT.
|
|
|
|
oldva, when set, replaces an existing VA with new data,
|
|
and then this function will (theoretically) return oldva.
|
|
|
|
client.va_render_global(va, px, py, pz, ry, rx, ry2, scale, tex = nil, afmt = nil, alpha = 1.0) @
|
|
draws a vertex array relative to the world
|
|
|
|
tex must be a power-of-two texture, or nil,
|
|
or a list of either of those two
|
|
|
|
current max for a texture list is 8
|
|
|
|
afmt consists of two characters, or is nil
|
|
note, if afmt is nil, blending is disabled!
|
|
(i.e. it behaves like you used "10")
|
|
|
|
first character defines source from one of these: 01CRahAHs
|
|
second character defines dest from one of these: 01crahAH
|
|
|
|
meanings of each char:
|
|
- sd 0 = GL_ZERO
|
|
- sd 1 = GL_ONE
|
|
- .d c = GL_SRC_COLOR
|
|
- s. C = GL_DST_COLOR
|
|
- .d r = GL_ONE_MINUS_SRC_COLOR
|
|
- s. R = GL_ONE_MINUS_DST_COLOR
|
|
- sd a = GL_SRC_ALPHA
|
|
- sd A = GL_DST_ALPHA
|
|
- sd h = GL_ONE_MINUS_SRC_ALPHA
|
|
- sd H = GL_ONE_MINUS_DST_ALPHA
|
|
- s. s = GL_SRC_ALPHA_SATURATE
|
|
|
|
useful blend modes:
|
|
- additive: "11"
|
|
- multiplicative: "C0" (?)
|
|
- translucent: "ah"
|
|
|
|
note, "alpha" only works with VAs that don't provide a colour
|
|
|
|
client.va_render_local(va, px, py, pz, ry, rx, ry2, scale, tex = nil, afmt = nil, alpha = 1.0) @
|
|
draws a vertex array relative to the camera
|
|
|
|
see above for notes
|
|
|
|
wav = common.wav_load(fname) @
|
|
loads a sound with filename "fname"
|
|
|
|
supported codecs are PCM (0x0001)
|
|
and (as of 0.2.1-26) IMA ADPCM (0x0011)
|
|
|
|
[ paragraph does not apply to 0.1.2-10 and higher ]
|
|
remember to free it when you're done
|
|
as this is only a light userdata
|
|
|
|
common.wav_free(wav) @
|
|
free the given sound
|
|
|
|
[ paragraph does not apply to 0.1.2-10 and higher ]
|
|
if you don't do this then it's memoryleaktopia
|
|
(plus i'm allowed to kill you)
|
|
|
|
client.wav_cube_size(size) @
|
|
sets the size of a block in metres for sound calculations
|
|
|
|
chn = client.wav_play_global(wav, x, y, z, vol = 1.0, freq_mod = 1.0, vol_spread = ?) @
|
|
play the given sound at the given world position
|
|
|
|
TODO: define vol_spread properly
|
|
|
|
returns an index of a channel
|
|
returns nil on error
|
|
|
|
chn = client.wav_play_local(wav, x = 0.0, y = 0.0, z = 0.0, vol = 1.0, freq_mod = 1.0, vol_spread = ?) @
|
|
play the given sound at the given camera-local position
|
|
|
|
returns an index of a channel
|
|
returns nil on error
|
|
|
|
exists = client.wav_chn_exists(chn) @
|
|
checks if an allocated channel still exists
|
|
|
|
if a channel stops, it is garbage collected
|
|
|
|
success = client.wav_chn_update(chn, x = nil, y = nil, z = nil, vol = nil, freq_mod = nil, vol_spread = nil) @
|
|
updates information pertaining to a channel
|
|
|
|
any field which is nil is not affected
|
|
|
|
returns false if the channel no longer exists
|
|
|
|
client.wav_kill(chn) @
|
|
stops and removes a channel
|
|
|
|
if chn == "true", kills all channels
|
|
|
|
mus = common.mus_load_it(fname) @
|
|
loads an ImpulseTracker module with filename "fname"
|
|
remember to free it when you're done
|
|
as this is only a light userdata
|
|
|
|
common.mus_free(mus) @
|
|
free the given atrosity of demofunk
|
|
if you don't do this then it's memoryleaktopia
|
|
(plus i'm allowed to kill you)
|
|
|
|
MAKE SURE YOU HAVE CALLED client.mus_stop IF YOU WERE PLAYING THIS FILE
|
|
otherwise expect a crash.
|
|
|
|
success = client.mus_play(mus, order=0, row=0) @
|
|
starts playing module "mus" at given order/row
|
|
|
|
client.mus_stop() @
|
|
stops playing music
|
|
|
|
client.mus_vol_set(vol) @
|
|
sets the mixing volume for the music
|
|
|
|
str = common.bin_load(fname) @
|
|
loads a raw file into a string
|
|
|
|
common.bin_save(fname, str) @
|
|
saves a raw string to a file
|
|
|
|
common.mk_compat_disable() @
|
|
disables backwards compat with 0.1.2-10 and earlier
|
|
what this currently does is it just removes the border wrap.
|
|
|
|
client.mk_set_title(title) @
|
|
changes the window title
|
|
default is "iceball"
|
|
|
|
client.mk_sys_execv(arglist) @
|
|
relaunches Iceball with the given argv[] list
|
|
THIS CAN ONLY BE DONE IN -s 0 MODE!
|
|
see pkg/iceball/launch/main_client.lua.
|
|
|