Files
assaultcube-/source/src/main.cpp
T
Lucas GAUTHERONandac-stef 4fd4e09f43 Added AC icon to SDL window (fixes #29)
As zeSKB reported, there was no icon for the game window.
This should now be fixed. We might want to make it transparent in the future,
but we would have to make sure it will be supported by any system.
2014-05-14 14:28:54 +02:00

1333 lines
37 KiB
C++

// main.cpp: initialisation & main loop
#include "cube.h"
void cleanup(char *msg) // single program exit point;
{
if(!msg)
{
cleanupclient();
audiomgr.soundcleanup();
cleanupserver();
extern void cleargamma();
cleargamma();
}
SDL_ShowCursor(1);
if(msg)
{
#ifdef WIN32
MessageBox(NULL, msg, "AssaultCube fatal error", MB_OK|MB_SYSTEMMODAL|MB_ICONERROR);
#else
printf("%s", msg);
#endif
}
SDL_Quit();
}
VAR(resetcfg, 0, 0, 1);
void quit() // normal exit
{
const char *onquit = getalias("onQuit");
if(onquit && onquit[0]) { execute(onquit); alias("onQuit", ""); }
extern void writeinitcfg();
writeinitcfg();
writeservercfg();
writepcksourcecfg();
if(resetcfg) deletecfg();
else writecfg();
savehistory();
cleanup(NULL);
popscontext();
exit(EXIT_SUCCESS);
}
void fatal(const char *s, ...) // failure exit
{
static int errors = 0;
errors++;
if(errors <= 2)
{
defvformatstring(msg,s,s);
if(errors <= 1)
{
defvformatstring(msg,s,s);
defformatstring(msgerr)("%s (%s)\n", msg, SDL_GetError());
cleanup(msgerr);
}
else puts(msg);
}
exit(EXIT_FAILURE);
}
SDL_Surface *screen = NULL;
static int initing = NOT_INITING;
static bool restoredinits = false;
bool initwarning(const char *desc, int level, int type)
{
if(initing < level)
{
addchange(desc, type);
return true;
}
return false;
}
VARF(scr_w, 320, 1024, 10000, initwarning("screen resolution"));
VARF(scr_h, 200, 768, 10000, initwarning("screen resolution"));
VARF(colorbits, 0, 0, 32, initwarning("color depth"));
VARF(depthbits, 0, 0, 32, initwarning("depth-buffer precision"));
VARF(stencilbits, 0, 0, 32, initwarning("stencil-buffer precision"));
VARF(fsaa, -1, -1, 16, initwarning("anti-aliasing"));
VARF(vsync, -1, -1, 1, initwarning("vertical sync"));
static bool grabinput = false, minimized = false;
void inputgrab(bool on)
{
#ifndef WIN32
if(!(screen->flags & SDL_FULLSCREEN)) SDL_WM_GrabInput(SDL_GRAB_OFF);
else
#endif
SDL_WM_GrabInput(on ? SDL_GRAB_ON : SDL_GRAB_OFF);
SDL_ShowCursor(on ? SDL_DISABLE : SDL_ENABLE);
}
void setfullscreen(bool enable)
{
if(!screen) return;
#if defined(WIN32) || defined(__APPLE__)
initwarning(enable ? "fullscreen" : "windowed");
#else
if(enable == !(screen->flags&SDL_FULLSCREEN))
{
SDL_WM_ToggleFullScreen(screen);
inputgrab(grabinput);
}
#endif
}
#ifdef _DEBUG
VARF(fullscreen, 0, 0, 1, setfullscreen(fullscreen!=0));
#else
VARF(fullscreen, 0, 1, 1, setfullscreen(fullscreen!=0));
#endif
void writeinitcfg()
{
if(!restoredinits) return;
stream *f = openfile(path("config/init.cfg", true), "w");
if(!f) return;
f->printf("// automatically written on exit, DO NOT MODIFY\n// modify settings in game\n");
extern int fullscreen;
f->printf("fullscreen %d\n", fullscreen);
f->printf("scr_w %d\n", scr_w);
f->printf("scr_h %d\n", scr_h);
f->printf("colorbits %d\n", colorbits);
f->printf("depthbits %d\n", depthbits);
f->printf("stencilbits %d\n", stencilbits);
f->printf("fsaa %d\n", fsaa);
f->printf("vsync %d\n", vsync);
extern int audio, soundchannels;
f->printf("audio %d\n", audio > 0 ? 1 : 0);
f->printf("soundchannels %d\n", soundchannels);
f->printf("lang %s\n", lang);
delete f;
}
#if 0
VARP(highprocesspriority, 0, 1, 1);
void setprocesspriority(bool high)
{
#if defined(WIN32) && !defined(_DEBUG)
SetPriorityClass(GetCurrentProcess(), high && highprocesspriority && fullscreen ? HIGH_PRIORITY_CLASS : NORMAL_PRIORITY_CLASS);
#endif
}
#endif
const char *screenshotpath(const char *imagepath, const char *suffix)
{
static string buf;
if(imagepath && imagepath[0]) copystring(buf, imagepath);
else
{
if(getclientmap()[0])
formatstring(buf)("screenshots/%s_%s_%s.%s", timestring(), behindpath(getclientmap()), modestr(gamemode, true), suffix);
else
formatstring(buf)("screenshots/%s.%s", timestring(), suffix);
}
path(buf);
return buf;
}
FVARP(screenshotscale, 0.1f, 1.0f, 1.0f);
void bmp_screenshot(const char *imagepath, bool mapshot = false)
{
extern int minimaplastsize;
int iw = mapshot?minimaplastsize:screen->w;
int ih = mapshot?minimaplastsize:screen->h;
int tw = mapshot ? iw : iw*screenshotscale;
int th = mapshot ? ih : ih*screenshotscale;
SDL_Surface *image = creatergbsurface(tw, th);
if(!image) return;
uchar *tmp = new uchar[iw*ih*3];
uchar *dst = (uchar *)image->pixels;
if(mapshot)
{
extern GLuint minimaptex;
if(minimaptex)
{
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glBindTexture(GL_TEXTURE_2D, minimaptex);
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGB, GL_UNSIGNED_BYTE, tmp);
}
else
{
conoutf("no mapshot prepared!");
return;
}
}
else
{
glPixelStorei(GL_PACK_ALIGNMENT, 1);
if(screenshotscale != 1.0f)
{
uchar *buf = new uchar[iw*ih*3];
glReadPixels(0, 0, iw, ih, GL_RGB, GL_UNSIGNED_BYTE, buf); //screen->w//screen->h
scaletexture(buf, iw, ih, 3, tmp, tw, th);
delete[] buf;
}
else glReadPixels(0, 0, tw, th, GL_RGB, GL_UNSIGNED_BYTE, tmp);
}
loopi(th)
{
memcpy(dst, &tmp[3*tw*(th-i-1)], 3*tw);
dst += image->pitch;
}
delete[] tmp;
const char *filename = screenshotpath(imagepath, "bmp");
stream *file = openfile(filename, "wb");
if(!file) conoutf("failed to create: %s", filename);
else
{
SDL_SaveBMP_RW(image, file->rwops(), 1);
delete file;
}
SDL_FreeSurface(image);
}
// best: 100 - good: 85 [default] - bad: 70 - terrible: 50
VARP(jpegquality, 10, 85, 100);
#include "jpegenc.h"
void jpeg_screenshot(const char *imagepath, bool mapshot = false)
{
extern int minimaplastsize;
int iw = mapshot?minimaplastsize:screen->w;
int ih = mapshot?minimaplastsize:screen->h;
int tw = mapshot ? iw : iw*screenshotscale;
int th = mapshot ? ih : ih*screenshotscale;
uchar *pixels = new uchar[3*tw*th];
if(mapshot)
{
extern GLuint minimaptex;
if(minimaptex)
{
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glBindTexture(GL_TEXTURE_2D, minimaptex);
glGetTexImage(GL_TEXTURE_2D, 0, GL_BGR, GL_UNSIGNED_BYTE, pixels);
}
else
{
conoutf("no mapshot prepared!");
delete[] pixels;
return;
}
}
else
{
glPixelStorei(GL_PACK_ALIGNMENT, 1);
if(screenshotscale != 1.0f)
{
uchar *buf = new uchar[iw*ih*3];
glReadPixels(0, 0, iw, ih, GL_BGR, GL_UNSIGNED_BYTE, buf);
scaletexture(buf, iw, ih, 3, pixels, tw, th);
delete[] buf;
}
else glReadPixels(0, 0, tw, th, GL_BGR, GL_UNSIGNED_BYTE, pixels);
int stride = 3*tw;
GLubyte *swapline = (GLubyte *) malloc(stride);
for(int row = 0; row < th/2; row++)
{
memcpy(swapline, pixels + row * stride, stride);
memcpy(pixels + row * stride, pixels + (th - row - 1) * stride, stride);
memcpy(pixels + (th - row -1) * stride, swapline, stride);
}
free(swapline);
}
const char *filename = findfile(screenshotpath(imagepath, "jpg"), "wb");
conoutf("writing to file: %s", filename);
jpegenc *jpegencoder = new jpegenc;
jpegencoder->encode(filename, (colorRGB *)pixels, tw, th, jpegquality);
delete jpegencoder;
delete[] pixels;
}
VARP(pngcompress, 0, 9, 9);
void writepngchunk(stream *f, const char *type, uchar *data = NULL, uint len = 0)
{
f->putbig<uint>(len);
f->write(type, 4);
f->write(data, len);
uint crc = crc32(0, Z_NULL, 0);
crc = crc32(crc, (const Bytef *)type, 4);
if(data) crc = crc32(crc, data, len);
f->putbig<uint>(crc);
}
int save_png(const char *filename, SDL_Surface *image)
{
uchar *data = (uchar *)image->pixels;
int iw = image->w, ih = image->h, pitch = image->pitch;
stream *f = openfile(filename, "wb");
if(!f) { conoutf("could not write to %s", filename); return -1; }
uchar signature[] = { 137, 80, 78, 71, 13, 10, 26, 10 };
f->write(signature, sizeof(signature));
struct pngihdr
{
uint width, height;
uchar bitdepth, colortype, compress, filter, interlace;
} ihdr = { bigswap<uint>(iw), bigswap<uint>(ih), 8, 2, 0, 0, 0 };
writepngchunk(f, "IHDR", (uchar *)&ihdr, 13);
int idat = f->tell();
uint len = 0;
f->write("\0\0\0\0IDAT", 8);
uint crc = crc32(0, Z_NULL, 0);
crc = crc32(crc, (const Bytef *)"IDAT", 4);
z_stream z;
z.zalloc = NULL;
z.zfree = NULL;
z.opaque = NULL;
if(deflateInit(&z, pngcompress) != Z_OK)
goto error;
uchar buf[1<<12];
z.next_out = (Bytef *)buf;
z.avail_out = sizeof(buf);
loopi(ih)
{
uchar filter = 0;
loopj(2)
{
z.next_in = j ? (Bytef *)data + i*pitch : (Bytef *)&filter;
z.avail_in = j ? iw*3 : 1;
while(z.avail_in > 0)
{
if(deflate(&z, Z_NO_FLUSH) != Z_OK) goto cleanuperror;
#define FLUSHZ do { \
int flush = sizeof(buf) - z.avail_out; \
crc = crc32(crc, buf, flush); \
len += flush; \
f->write(buf, flush); \
z.next_out = (Bytef *)buf; \
z.avail_out = sizeof(buf); \
} while(0)
FLUSHZ;
}
}
}
for(;;)
{
int err = deflate(&z, Z_FINISH);
if(err != Z_OK && err != Z_STREAM_END) goto cleanuperror;
FLUSHZ;
if(err == Z_STREAM_END) break;
}
deflateEnd(&z);
f->seek(idat, SEEK_SET);
f->putbig<uint>(len);
f->seek(0, SEEK_END);
f->putbig<uint>(crc);
writepngchunk(f, "IEND");
delete f;
return 0;
cleanuperror:
deflateEnd(&z);
error:
delete f;
return -1;
}
void png_screenshot(const char *imagepath, bool mapshot = false)
{
extern int minimaplastsize;
int iw = mapshot?minimaplastsize:screen->w;
int ih = mapshot?minimaplastsize:screen->h;
int tw = mapshot ? iw : iw*screenshotscale;
int th = mapshot ? ih : ih*screenshotscale;
SDL_Surface *image = creatergbsurface(tw, th);
if(!image) return;
uchar *tmp = new uchar[tw*th*3];
uchar *dst = (uchar *)image->pixels;
if(mapshot)
{
extern GLuint minimaptex;
if(minimaptex)
{
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glBindTexture(GL_TEXTURE_2D, minimaptex);
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGB, GL_UNSIGNED_BYTE, tmp);
}
else
{
conoutf("no mapshot prepared!");
return;
}
}
else
{
glPixelStorei(GL_PACK_ALIGNMENT, 1);
if(screenshotscale != 1.0f)
{
uchar *buf = new uchar[iw*ih*3];
glReadPixels(0, 0, iw, ih, GL_RGB, GL_UNSIGNED_BYTE, buf);
scaletexture(buf, iw, ih, 3, tmp, tw, th);
delete[] buf;
}
else glReadPixels(0, 0, tw, th, GL_RGB, GL_UNSIGNED_BYTE, tmp);
}
loopi(th)
{
memcpy(dst, &tmp[3*tw*(th-i-1)], 3*tw);
dst += image->pitch;
}
delete[] tmp;
const char *filename = screenshotpath(imagepath, "png");
if(save_png(filename, image) < 0) conoutf("\f3Error saving png file");
SDL_FreeSurface(image);
}
VARP(screenshottype, 0, 1, 2);
void screenshot(const char *imagepath)
{
switch(screenshottype)
{
case 2: png_screenshot(imagepath,false); break;
case 1: jpeg_screenshot(imagepath,false); break;
case 0:
default: bmp_screenshot(imagepath,false); break;
}
}
void mapshot()
{
string suffix;
switch(screenshottype)
{
case 2: copystring(suffix, "png"); break;
case 1: copystring(suffix, "jpg"); break;
case 0:
default: copystring(suffix, "bmp"); break;
}
defformatstring(buf)("screenshots/mapshot_%s_%s.%s", behindpath(getclientmap()), timestring(), suffix);
switch(screenshottype)
{
case 2: png_screenshot(buf,true); break;
case 1: jpeg_screenshot(buf,true); break;
case 0:
default: bmp_screenshot(buf,true); break;
}
}
bool needsautoscreenshot = false;
COMMAND(screenshot, "s");
COMMAND(mapshot, "");
COMMAND(quit, "");
void screenres(int w, int h)
{
#if !defined(WIN32) && !defined(__APPLE__)
if(initing >= INIT_RESET)
{
#endif
scr_w = w;
scr_h = h;
#if defined(WIN32) || defined(__APPLE__)
initwarning("screen resolution");
#else
return;
}
SDL_Surface *surf = SDL_SetVideoMode(w, h, 0, SDL_OPENGL|SDL_RESIZABLE|(screen->flags&SDL_FULLSCREEN));
if(!surf) return;
screen = surf;
scr_w = screen->w;
scr_h = screen->h;
glViewport(0, 0, scr_w, scr_h);
VIRTW = scr_w*VIRTH/scr_h;
#endif
}
#if defined(WIN32) || defined(__APPLE__) || !defined(WIN32)
void setresdata(char *s, enet_uint32 c)
{
extern hashtable<char *, enet_uint32> &resdata;
resdata[newstring(s)] = c;
}
#endif
COMMANDF(screenres, "ii", (int *w, int *h) { screenres(*w, *h); });
static int curgamma = 100;
VARFP(gamma, 30, 100, 300,
{
if(gamma == curgamma) return;
curgamma = gamma;
float f = gamma/100.0f;
if(SDL_SetGamma(f,f,f)==-1) conoutf("Could not set gamma: %s", SDL_GetError());
});
void cleargamma()
{
if(curgamma != 100) SDL_SetGamma(1, 1, 1);
}
void restoregamma()
{
if(curgamma == 100) return;
float f = curgamma/100.0f;
SDL_SetGamma(1, 1, 1);
SDL_SetGamma(f, f, f);
}
void setupscreen(int &usedcolorbits, int &useddepthbits, int &usedfsaa)
{
int flags = SDL_RESIZABLE;
#if defined(WIN32) || defined(__APPLE__)
flags = 0;
putenv("SDL_VIDEO_CENTERED=1"); //Center window
#endif
if(fullscreen) flags |= SDL_FULLSCREEN;
SDL_Rect **modes = SDL_ListModes(NULL, SDL_OPENGL|flags);
if(modes && modes!=(SDL_Rect **)-1)
{
bool hasmode = false;
for(int i = 0; modes[i]; i++)
{
if(scr_w <= modes[i]->w && scr_h <= modes[i]->h) { hasmode = true; break; }
}
if(!hasmode) { scr_w = modes[0]->w; scr_h = modes[0]->h; }
}
bool hasbpp = true;
if(colorbits && modes)
hasbpp = SDL_VideoModeOK(modes!=(SDL_Rect **)-1 ? modes[0]->w : scr_w, modes!=(SDL_Rect **)-1 ? modes[0]->h : scr_h, colorbits, SDL_OPENGL|flags)==colorbits;
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
#if SDL_VERSION_ATLEAST(1, 2, 11)
if(vsync>=0) SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, vsync);
#endif
static int configs[] =
{
0x7, /* try everything */
0x6, 0x5, 0x3, /* try disabling one at a time */
0x4, 0x2, 0x1, /* try disabling two at a time */
0 /* try disabling everything */
};
int config = 0;
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 0);
if(!depthbits) SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
if(!fsaa)
{
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0);
}
loopi(sizeof(configs)/sizeof(configs[0]))
{
config = configs[i];
if(!depthbits && config&1) continue;
if(!stencilbits && config&2) continue;
if(fsaa<=0 && config&4) continue;
if(depthbits) SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, config&1 ? depthbits : 16);
if(stencilbits)
{
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, config&2 ? stencilbits : 0);
hasstencil = (config&2)!=0;
}
else hasstencil = false;
if(fsaa>0)
{
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, config&4 ? 1 : 0);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, config&4 ? fsaa : 0);
}
screen = SDL_SetVideoMode(scr_w, scr_h, hasbpp ? colorbits : 0, SDL_OPENGL|flags);
if(screen) break;
}
if(!screen) fatal("Unable to create OpenGL screen");
else
{
if(!hasbpp) conoutf("%d bit color buffer not supported - disabling", colorbits);
if(depthbits && (config&1)==0) conoutf("%d bit z-buffer not supported - disabling", depthbits);
if(stencilbits && (config&2)==0) conoutf("%d bit stencil buffer not supported - disabling", stencilbits);
if(fsaa>0 && (config&4)==0) conoutf("%dx anti-aliasing not supported - disabling", fsaa);
}
scr_w = screen->w;
scr_h = screen->h;
VIRTW = scr_w*VIRTH/scr_h;
#ifdef WIN32
SDL_WM_GrabInput(SDL_GRAB_ON);
#else
SDL_WM_GrabInput(fullscreen ? SDL_GRAB_ON : SDL_GRAB_OFF);
#endif
usedcolorbits = hasbpp ? colorbits : 0;
useddepthbits = config&1 ? depthbits : 0;
usedfsaa = config&2 ? fsaa : 0;
}
extern int hirestextures;
void resetgl()
{
clearchanges(CHANGE_GFX);
loadingscreen();
extern void cleanupparticles();
extern void cleanupmodels();
extern void cleanuptextures();
extern void cleanuptmus();
extern void cleanupgl();
cleanupparticles();
cleanupmodels();
cleanuptextures();
cleanuptmus();
cleanupgl();
uniformtexres = !hirestextures;
c2skeepalive();
SDL_SetVideoMode(0, 0, 0, 0);
int usedcolorbits = 0, useddepthbits = 0, usedfsaa = 0;
setupscreen(usedcolorbits, useddepthbits, usedfsaa);
gl_init(scr_w, scr_h, usedcolorbits, useddepthbits, usedfsaa);
extern void reloadfonts();
extern void reloadtextures();
c2skeepalive();
if(!reloadtexture(*notexture) ||
!reloadtexture("packages/misc/startscreen.png"))
fatal("failed to reload core texture");
loadingscreen();
c2skeepalive();
restoregamma();
c2skeepalive();
reloadfonts();
reloadtextures();
c2skeepalive();
drawscope(true); // 2011feb05:ft: preload scope.png
preload_playermodels();
c2skeepalive();
preload_hudguns();
c2skeepalive();
preload_entmodels();
c2skeepalive();
preload_mapmodels();
c2skeepalive();
}
COMMAND(resetgl, "");
VARFP(maxfps, 0, 200, 1000, if(maxfps && maxfps < 25) maxfps = 25);
void limitfps(int &millis, int curmillis)
{
if(!maxfps) return;
static int fpserror = 0;
int delay = 1000/maxfps - (millis-curmillis);
if(delay < 0) fpserror = 0;
else
{
fpserror += 1000%maxfps;
if(fpserror >= maxfps)
{
++delay;
fpserror -= maxfps;
}
if(delay > 0)
{
SDL_Delay(delay);
millis += delay;
}
}
}
int lowfps = 30, highfps = 40;
void fpsrange(int *low, int *high)
{
if(*low>*high || *low<1) return;
lowfps = *low;
highfps = *high;
}
COMMAND(fpsrange, "ii");
void keyrepeat(bool on)
{
SDL_EnableKeyRepeat(on ? SDL_DEFAULT_REPEAT_DELAY : 0,
SDL_DEFAULT_REPEAT_INTERVAL);
}
vector<SDL_Event> events;
void pushevent(const SDL_Event &e)
{
events.add(e);
}
bool interceptkey(int sym)
{
static int lastintercept = SDLK_UNKNOWN;
int len = lastintercept == sym ? events.length() : 0;
SDL_Event event;
while(SDL_PollEvent(&event)) switch(event.type)
{
case SDL_MOUSEMOTION: break;
default: pushevent(event); break;
}
lastintercept = sym;
if(sym != SDLK_UNKNOWN) for(int i = len; i < events.length(); i++)
{
if(events[i].type == SDL_KEYDOWN && events[i].key.keysym.sym == sym) { events.remove(i); return true; }
}
return false;
}
void togglegrab()
{
if (SDL_WM_GrabInput(SDL_GRAB_QUERY) == SDL_GrabMode(0))
{
SDL_WM_GrabInput(SDL_GRAB_ON);
conoutf("mouse input locked");
}
else
{
SDL_WM_GrabInput(SDL_GrabMode(0));
conoutf("mouse input released");
}
}
COMMAND(togglegrab, "");
static void resetmousemotion()
{
#ifndef WIN32
if(!(screen->flags&SDL_FULLSCREEN))
{
SDL_WarpMouse(screen->w / 2, screen->h / 2);
}
#endif
}
static inline bool skipmousemotion(SDL_Event &event)
{
if(event.type != SDL_MOUSEMOTION) return true;
#ifndef WIN32
if(!(screen->flags&SDL_FULLSCREEN))
{
#ifdef __APPLE__
if(event.motion.y == 0) return true; // let mac users drag windows via the title bar
#endif
if(event.motion.x == screen->w / 2 && event.motion.y == screen->h / 2) return true; // ignore any motion events generated SDL_WarpMouse
}
#endif
return false;
}
static void checkmousemotion(int &dx, int &dy)
{
loopv(events)
{
SDL_Event &event = events[i];
if(skipmousemotion(event))
{
if(i > 0) events.remove(0, i);
return;
}
dx += event.motion.xrel;
dy += event.motion.yrel;
}
events.setsize(0);
SDL_Event event;
while(SDL_PollEvent(&event))
{
if(skipmousemotion(event))
{
events.add(event);
return;
}
dx += event.motion.xrel;
dy += event.motion.yrel;
}
}
static int ignoremouse = 5;
void checkinput()
{
SDL_Event event;
int lasttype = 0, lastbut = 0;
int tdx=0,tdy=0;
while(events.length() || SDL_PollEvent(&event))
{
if(events.length()) event = events.remove(0);
switch(event.type)
{
case SDL_QUIT:
quit();
break;
#if !defined(WIN32) && !defined(__APPLE__)
case SDL_VIDEORESIZE:
screenres(event.resize.w, event.resize.h);
break;
#endif
case SDL_KEYDOWN:
case SDL_KEYUP:
extern bool senst;
if (event.key.keysym.sym <= SDLK_5 && event.key.keysym.sym >= SDLK_1 && senst)
{
if (event.key.state==SDL_PRESSED)
{
extern int tsens(int x);
tsens(event.key.keysym.sym);
}
}
else
{
keypress(event.key.keysym.sym, event.key.state==SDL_PRESSED, event.key.keysym.unicode, event.key.keysym.mod);
}
break;
case SDL_ACTIVEEVENT:
if(event.active.state & SDL_APPINPUTFOCUS)
inputgrab(grabinput = event.active.gain!=0);
if(event.active.state & SDL_APPACTIVE)
minimized = !event.active.gain;
#if 0
if(event.active.state==SDL_APPMOUSEFOCUS) setprocesspriority(event.active.gain > 0); // switch priority on focus change
#endif
break;
case SDL_MOUSEMOTION:
if(ignoremouse) { ignoremouse--; break; }
if(grabinput && !skipmousemotion(event))
{
int dx = event.motion.xrel, dy = event.motion.yrel;
checkmousemotion(dx, dy);
resetmousemotion();
tdx+=dx;tdy+=dy;
}
break;
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
if(lasttype==event.type && lastbut==event.button.button) break;
keypress(-event.button.button, event.button.state!=0, 0);
lasttype = event.type;
lastbut = event.button.button;
break;
}
}
mousemove(tdx, tdy);
}
VARF(gamespeed, 10, 100, 1000, if(multiplayer()) gamespeed = 100);
VARF(paused, 0, 0, 1, if(multiplayer()) paused = 0);
bool firstrun = false, inmainloop = false;
static int clockrealbase = 0, clockvirtbase = 0;
static void clockreset() { clockrealbase = SDL_GetTicks(); clockvirtbase = totalmillis; }
VARFP(clockerror, 990000, 1000000, 1010000, clockreset());
VARFP(clockfix, 0, 0, 1, clockreset());
VARP(gamestarts, 0, 0, INT_MAX);
const char *rndmapname()
{
vector<char *> maps;
listfiles("packages/maps/official", "cgz", maps);
if (maps.length() > 0)
{
char *map = newstring(maps[rnd(maps.length())]);
maps.deletearrays();
return map;
}
else return "";
}
extern void connectserv(char *, int *, char *);
void connectprotocol(char *protocolstring, string &servername, int &serverport, string &password, bool &direct_connect)
{
const char *c = &protocolstring[14], *p = c;
int len = 0;
direct_connect = false;
string sp;
servername[0] = password[0] = '\0';
serverport = 0;
while(*c && *c!='/' && *c!='?' && *c!=':') { len++; c++; }
if(!len) { conoutf("\f3bad commandline syntax", protocolstring); return; }
copystring(servername, p, min(len+1, MAXSTRLEN));
direct_connect = true;
if(*c && *c==':')
{
c++; p = c; len = 0;
while(*c && *c!='/' && *c!='?') { len++; c++; }
if(len)
{
copystring(sp, p, min(len+1, MAXSTRLEN));
serverport = atoi(sp);
}
}
if(*c && *c=='/') c++;
if(!*c || *c!='?') return;
do
{
if(*c) c++;
if(!strncmp(c, "port=", 5))
{
c += 5; p = c; len = 0;
while(*c && *c!='&' && *c!='/') { len++; c++; }
if(len)
{
copystring(sp, p, min(len+1, MAXSTRLEN));
serverport = atoi(sp);
}
}
else if(!strncmp(c, "password=", 9))
{
c += 9; p = c; len = 0;
while(*c && *c!='&' && *c!='/') { len++, c++; }
if(len) copystring(password, p, min(len+1, MAXSTRLEN));
}
else break;
} while(*c && *c=='&' && *c!='/');
}
#ifdef WIN32
static char *parsecommandline(const char *src, vector<char *> &args)
{
char *buf = new char[strlen(src) + 1], *dst = buf;
for(;;)
{
while(isspace(*src)) src++;
if(!*src) break;
args.add(dst);
for(bool quoted = false; *src && (quoted || !isspace(*src)); src++)
{
if(*src != '"') *dst++ = *src;
else if(dst > buf && src[-1] == '\\') dst[-1] = '"';
else quoted = !quoted;
}
*dst++ = '\0';
}
args.add(NULL);
return buf;
}
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int sw)
{
vector<char *> args;
char *buf = parsecommandline(GetCommandLine(), args);
SDL_SetModuleHandle(hInst);
int status = SDL_main(args.length()-1, args.getbuf());
delete[] buf;
exit(status);
return 0;
}
#endif
VARP(compatibilitymode, 0, 1, 1); // FIXME : find a better place to put this ?
int main(int argc, char **argv)
{
extern struct servercommandline scl;
#ifdef WIN32
//atexit((void (__cdecl *)(void))_CrtDumpMemoryLeaks);
#ifndef _DEBUG
#ifndef __GNUC__
__try {
#endif
#endif
#endif
bool dedicated = false;
bool quitdirectly = false;
char *initscript = NULL;
char *initdemo = NULL;
bool direct_connect = false; // to connect via assaultcube:// browser switch
string servername, password;
int serverport;
const char *initmap = rndmapname();
pushscontext(IEXC_CFG);
#define initlog(s) clientlogf("init: " s)
initing = INIT_RESET;
for(int i = 1; i<argc; i++)
{
// server: ufimNFTLAckyxpDWrXBKIoOnPMVC
if(!scl.checkarg(argv[i]))
{
char *a = &argv[i][2];
if(argv[i][0]=='-') switch(argv[i][1])
{
case '-':
if(!strncmp(argv[i], "--home=", 7))
{
sethomedir(&argv[i][7]);
}
else if(!strncmp(argv[i], "--mod=", 6))
{
addpackagedir(&argv[i][6]);
}
else if(!strcmp(argv[i], "--init"))
{
execfile((char *)"config/init.cfg");
restoredinits = true;
}
else if(!strncmp(argv[i], "--init=", 7))
{
execfile(&argv[i][7]);
restoredinits = true;
}
else if(!strcmp(argv[i], "--version"))
{
printf("%.3f\n", AC_VERSION/1000.0f);
quitdirectly = true;
}
else if(!strcmp(argv[i], "--protocol"))
{
printf("%d\n", PROTOCOL_VERSION);
quitdirectly = true;
}
else if(!strncmp(argv[i], "--loadmap=", 10))
{
initmap = &argv[i][10];
}
else if(!strncmp(argv[i], "--loaddemo=", 11))
{
initdemo = &argv[i][11];
}
else conoutf("\f3unknown commandline switch: %s", argv[i]);
break;
case 'd': dedicated = true; break;
case 't': fullscreen = atoi(a); break;
case 'w': scr_w = atoi(a); break;
case 'h': scr_h = atoi(a); break;
case 'z': depthbits = atoi(a); break;
case 'b': colorbits = atoi(a); break;
case 's': stencilbits = atoi(&argv[i][2]); break;
case 'a': fsaa = atoi(a); break;
case 'v': vsync = atoi(a); break;
case 'e': initscript = &argv[i][2]; break;
default: conoutf("\f3unknown commandline option: -%c", argv[i][1]);
}
else if(!strncmp(argv[i], "assaultcube://", 14)) // browser direct connection
{
connectprotocol(argv[i], servername, serverport, password, direct_connect);
}
else conoutf("\f3unknown commandline argument: %c", argv[i][0]);
}
}
if(quitdirectly) return EXIT_SUCCESS;
i18nmanager i18n("AC", path("packages/locale", true));
initing = NOT_INITING;
initlog("sdl");
int par = 0;
#ifdef _DEBUG
par = SDL_INIT_NOPARACHUTE;
#endif
if(SDL_Init(SDL_INIT_TIMER|SDL_INIT_VIDEO|par)<0) fatal("Unable to initialize SDL");
#if 0
if(highprocesspriority) setprocesspriority(true);
#endif
if (!dedicated) initlog("net");
if(enet_initialize()<0) fatal("Unable to initialise network module");
if (!dedicated) initclient();
//FIXME the server executed in this way does not catch the SIGTERM or ^C
initserver(dedicated,argc,argv); // never returns if dedicated
initlog("world");
empty_world(7, true);
initlog("video: sdl");
if(SDL_InitSubSystem(SDL_INIT_VIDEO)<0) fatal("Unable to initialize SDL Video");
initlog("video: mode");
int usedcolorbits = 0, useddepthbits = 0, usedfsaa = 0;
setupscreen(usedcolorbits, useddepthbits, usedfsaa);
// no more TTF ATM.
//initlog("font");
//initfont();
initlog("video: misc");
SDL_WM_SetCaption("AssaultCube", NULL);
SDL_Surface *icon = IMG_Load("packages/misc/icon.bmp");
SDL_WM_SetIcon(icon, NULL);
keyrepeat(false);
SDL_ShowCursor(0);
initlog("gl");
gl_checkextensions();
gl_init(scr_w, scr_h, usedcolorbits, useddepthbits, usedfsaa);
notexture = noworldtexture = textureload("packages/misc/notexture.jpg");
if(!notexture) fatal("could not find core textures (hint: run AssaultCube from the parent of the bin directory)");
nomodel = loadmodel("misc/gib01", -1); //FIXME: need actual placeholder model
if(!notexture) fatal("could not find core models");
initlog("console");
per_idents = false;
// Main font file, all other font files execute from here.
if(!execfile("config/font.cfg")) fatal("cannot find default font definitions");
// Check these 2 standard fonts have been executed.
if(!setfont("mono")) fatal("no mono font specified");
if(!setfont("default")) fatal("no default font specified");
loadingscreen();
particleinit();
initlog("sound");
audiomgr.initsound();
initlog("cfg");
extern void *scoremenu, *servmenu, *searchmenu, *serverinfomenu, *kickmenu, *banmenu, *forceteammenu, *giveadminmenu, *docmenu, *applymenu;
scoremenu = addmenu("score", "columns", false, renderscores, NULL, false, true);
servmenu = addmenu("server", NULL, true, refreshservers, serverskey);
searchmenu = addmenu("search", NULL, true, refreshservers, serverskey);
serverinfomenu = addmenu("serverinfo", NULL, true, refreshservers, serverinfokey);
kickmenu = addmenu("kick player", NULL, true, refreshsopmenu);
banmenu = addmenu("ban player", NULL, true, refreshsopmenu);
forceteammenu = addmenu("force team", NULL, true, refreshsopmenu);
giveadminmenu = addmenu("give admin", NULL, true, refreshsopmenu);
docmenu = addmenu("reference", NULL, true, renderdocmenu);
applymenu = addmenu("apply", "apply changes now?", true, refreshapplymenu);
exec("config/scontext.cfg");
exec("config/locale.cfg");
exec("config/keymap.cfg");
exec("config/menus.cfg");
exec("config/scripts.cfg");
exec("config/prefabs.cfg");
exec("config/sounds.cfg");
exec("config/securemaps.cfg");
exec("config/admin.cfg");
execfile("config/servers.cfg");
loadhistory();
per_idents = true;
static char resdata[] = { 112, 97, 99, 107, 97, 103, 101, 115, 47, 116, 101, 120, 116, 117, 114, 101, 115, 47, 107, 117, 114, 116, 47, 107, 108, 105, 116, 101, 50, 46, 106, 112, 103, 0 };
stream *f = opengzfile(resdata, "rb");
if(f)
{
int n = f->getlil<int>();
loopi(n)
{
string s;
f->read(s, sizeof(string));
enet_uint32 c = f->getlil<enet_uint32>();
setresdata(s, c);
}
delete f;
}
initing = INIT_LOAD;
if(!execfile("config/saved.cfg"))
{
exec("config/defaults.cfg");
firstrun = true;
}
if(identexists("afterinit")) execute("afterinit");
if(compatibilitymode)
{
per_idents = false;
exec("config/compatibility.cfg"); // exec after saved.cfg to get "compatibilitymode", but before user scripts..
per_idents = true;
}
execfile("config/autoexec.cfg");
execfile("config/auth.cfg");
execute("addallfavcatmenus"); // exec here, to add all categories (including those defined in autoexec.cfg)
initing = NOT_INITING;
uniformtexres = !hirestextures;
initlog("models");
preload_playermodels();
preload_hudguns();
initlog("curl");
setupcurl();
preload_entmodels();
initlog("docs");
per_idents = false;
execfile("config/docs.cfg");
per_idents = true;
initlog("localconnect");
extern string clientmap;
if(initdemo)
{
extern int gamemode;
gamemode = -1;
copystring(clientmap, initdemo);
}
else
copystring(clientmap, initmap); // ac_complex for 1.0, ac_shine for 1.1, ..
localconnect();
if(initscript) execute(initscript);
gamestarts = max(1, gamestarts+1);
if(autodownload && (gamestarts % 100 == 1)) sortpckservers();
initlog("mainloop");
inputgrab(grabinput = true);
inmainloop = true;
#ifdef _DEBUG
int lastflush = 0;
#endif
for(;;)
{
static int frames = 0;
static float fps = 10.0f;
int millis = SDL_GetTicks() - clockrealbase;
if(clockfix) millis = int(millis*(double(clockerror)/1000000));
millis += clockvirtbase;
if(millis<totalmillis) millis = totalmillis;
limitfps(millis, totalmillis);
int elapsed = millis-totalmillis;
if(multiplayer(false)) curtime = elapsed;
else
{
static int timeerr = 0;
int scaledtime = elapsed*gamespeed + timeerr;
curtime = scaledtime/100;
timeerr = scaledtime%100;
if(nextmillis && watchingdemo)
{
curtime += nextmillis;
nextmillis = 0;
}
if(paused) curtime = 0;
}
lastmillis += curtime;
totalmillis = millis;
checkinput();
if(lastmillis) updateworld(curtime, lastmillis);
if(needsautoscreenshot) showscores(true);
serverslice(0);
if(elapsed) fps = (1000.0f/elapsed+fps*10)/11; // avoid DIV-by-0
frames++;
audiomgr.updateaudio();
computeraytable(camera1->o.x, camera1->o.y, dynfov());
if(frames>3 && !minimized)
{
gl_drawframe(screen->w, screen->h, fps<lowfps ? fps/lowfps : (fps>highfps ? fps/highfps : 1.0f), fps);
if(frames>4) SDL_GL_SwapBuffers();
}
if(needsautoscreenshot)
{
showscores(true);
// draw again after swapping buffers, to make sure menu is captured
// in the screenshot regardless of which frame buffer is current
if (!minimized)
{
gl_drawframe(screen->w, screen->h, fps<lowfps ? fps/lowfps : (fps>highfps ? fps/highfps : 1.0f), fps);
}
addsleep(0, "screenshot");
needsautoscreenshot = false;
}
#ifdef _DEBUG
if(millis>lastflush+60000) { fflush(stdout); lastflush = millis; }
#endif
if (direct_connect)
{
direct_connect = false;
connectserv(servername, &serverport, password);
}
}
quit();
return EXIT_SUCCESS;
#if defined(WIN32) && !defined(_DEBUG) && !defined(__GNUC__)
} __except(stackdumper(0, GetExceptionInformation()), EXCEPTION_CONTINUE_SEARCH) { return 0; }
#endif
}
VAR(version, 1, AC_VERSION, 0);
VAR(protocol, 1, PROTOCOL_VERSION, 0);