2020-04-15 18:39:17 +02:00
// main.cpp: initialisation & main loop
2020-04-18 04:06:25 +02:00
# include "blend.hh"
2020-05-13 00:40:14 +02:00
# include "movie.hh"
2020-05-11 22:50:44 +02:00
# include "rendergl.hh"
2020-05-27 01:00:45 +02:00
# include "rendermodel.hh" // cleanupmodels
2020-05-17 18:51:15 +02:00
# include "renderparticles.hh"
2020-05-13 00:48:09 +02:00
# include "rendersky.hh"
2020-05-03 03:37:25 +02:00
# include "rendertext.hh"
2020-05-01 22:06:23 +02:00
# include "renderva.hh"
2020-04-30 22:50:35 +02:00
# include "shader.hh"
2020-05-13 00:52:56 +02:00
# include "stain.hh"
2020-04-30 22:50:35 +02:00
# include "texture.hh"
2020-04-28 04:02:15 +02:00
# include "world.hh"
2020-04-18 04:06:25 +02:00
2020-04-16 20:28:40 +02:00
# include "engine.hh"
2020-04-15 18:39:17 +02:00
2020-05-13 00:30:58 +02:00
VAR ( mainmenu , 1 , 1 , 0 ) ;
2020-04-26 00:20:00 +02:00
void clientkeepalive ( ) { }
bool multiplayer ( bool msg )
{
return false ;
}
bool isconnected ( bool attempt , bool local )
{
return haslocalclients ( ) ;
}
2020-05-13 00:30:58 +02:00
void clearmainmenu ( )
{
if ( mainmenu & & isconnected ( ) )
{
mainmenu = 0 ;
//UI::hideui(NULL);
}
}
2020-04-26 00:20:00 +02:00
void localdisconnect ( bool cleanup )
{
game : : gamedisconnect ( cleanup ) ;
mainmenu = 1 ;
}
void trydisconnect ( )
{
if ( haslocalclients ( ) ) localdisconnect ( ) ;
else conoutf ( " not connected " ) ;
}
ICOMMAND ( disconnect , " " , ( ) , trydisconnect ( ) ) ;
2020-04-15 18:39:17 +02:00
extern void cleargamma ( ) ;
void cleanup ( )
{
recorder : : stop ( ) ;
SDL_ShowCursor ( SDL_TRUE ) ;
SDL_SetRelativeMouseMode ( SDL_FALSE ) ;
if ( screen ) SDL_SetWindowGrab ( screen , SDL_FALSE ) ;
cleargamma ( ) ;
freeocta ( worldroot ) ;
2020-04-23 17:12:16 +02:00
//UI::cleanup();
2020-04-15 18:39:17 +02:00
extern void clear_command ( ) ; clear_command ( ) ;
extern void clear_console ( ) ; clear_console ( ) ;
extern void clear_models ( ) ; clear_models ( ) ;
2020-04-16 20:13:59 +02:00
//extern void clear_sound(); clear_sound();
2020-04-15 18:39:17 +02:00
# ifdef __APPLE__
if ( screen ) SDL_SetWindowFullscreen ( screen , 0 ) ;
# endif
SDL_Quit ( ) ;
}
extern void writeinitcfg ( ) ;
void quit ( ) // normal exit
{
writeinitcfg ( ) ;
localdisconnect ( ) ;
writecfg ( ) ;
cleanup ( ) ;
exit ( EXIT_SUCCESS ) ;
}
void fatal ( const char * s , . . . ) // failure exit
{
static int errors = 0 ;
errors + + ;
if ( errors < = 2 ) // print up to one extra recursive error
{
defvformatstring ( msg , s , s ) ;
2020-04-26 00:20:00 +02:00
printf ( " %s \n " , msg ) ;
2020-04-15 18:39:17 +02:00
if ( errors < = 1 ) // avoid recursion
{
if ( SDL_WasInit ( SDL_INIT_VIDEO ) )
{
SDL_ShowCursor ( SDL_TRUE ) ;
SDL_SetRelativeMouseMode ( SDL_FALSE ) ;
if ( screen ) SDL_SetWindowGrab ( screen , SDL_FALSE ) ;
cleargamma ( ) ;
# ifdef __APPLE__
if ( screen ) SDL_SetWindowFullscreen ( screen , 0 ) ;
# endif
}
SDL_Quit ( ) ;
SDL_ShowSimpleMessageBox ( SDL_MESSAGEBOX_ERROR , " Tesseract fatal error " , msg , NULL ) ;
}
}
exit ( EXIT_FAILURE ) ;
}
VAR ( desktopw , 1 , 0 , 0 ) ;
VAR ( desktoph , 1 , 0 , 0 ) ;
int screenw = 0 , screenh = 0 ;
SDL_Window * screen = NULL ;
SDL_GLContext glcontext = NULL ;
int curtime = 0 , lastmillis = 1 , elapsedtime = 0 , totalmillis = 1 ;
dynent * player = NULL ;
int initing = NOT_INITING ;
bool initwarning ( const char * desc , int level , int type )
{
if ( initing < level )
{
2020-05-13 00:30:58 +02:00
//addchange(desc, type);
2020-04-15 18:39:17 +02:00
return true ;
}
return false ;
}
# define SCR_MINW 320
# define SCR_MINH 200
# define SCR_MAXW 10000
# define SCR_MAXH 10000
# define SCR_DEFAULTW 1024
# define SCR_DEFAULTH 768
VARFN ( screenw , scr_w , SCR_MINW , - 1 , SCR_MAXW , initwarning ( " screen resolution " ) ) ;
VARFN ( screenh , scr_h , SCR_MINH , - 1 , SCR_MAXH , initwarning ( " screen resolution " ) ) ;
void writeinitcfg ( )
{
stream * f = openutf8file ( " config/init.cfg " , " 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 ( " screenw %d \n " , scr_w ) ;
f - > printf ( " screenh %d \n " , scr_h ) ;
delete f ;
}
COMMAND ( quit , " " ) ;
static void getbackgroundres ( int & w , int & h )
{
float wk = 1 , hk = 1 ;
if ( w < 1024 ) wk = 1024.0f / w ;
if ( h < 768 ) hk = 768.0f / h ;
wk = hk = max ( wk , hk ) ;
w = int ( ceil ( w * wk ) ) ;
h = int ( ceil ( h * hk ) ) ;
}
string backgroundcaption = " " ;
Texture * backgroundmapshot = NULL ;
string backgroundmapname = " " ;
char * backgroundmapinfo = NULL ;
void bgquad ( float x , float y , float w , float h , float tx = 0 , float ty = 0 , float tw = 1 , float th = 1 )
{
gle : : begin ( GL_TRIANGLE_STRIP ) ;
gle : : attribf ( x , y ) ; gle : : attribf ( tx , ty ) ;
gle : : attribf ( x + w , y ) ; gle : : attribf ( tx + tw , ty ) ;
gle : : attribf ( x , y + h ) ; gle : : attribf ( tx , ty + th ) ;
gle : : attribf ( x + w , y + h ) ; gle : : attribf ( tx + tw , ty + th ) ;
gle : : end ( ) ;
}
void renderbackgroundview ( int w , int h , const char * caption , Texture * mapshot , const char * mapname , const char * mapinfo )
{
static int lastupdate = - 1 , lastw = - 1 , lasth = - 1 ;
static float backgroundu = 0 , backgroundv = 0 ;
if ( ( renderedframe & & ! mainmenu & & lastupdate ! = lastmillis ) | | lastw ! = w | | lasth ! = h )
{
lastupdate = lastmillis ;
lastw = w ;
lasth = h ;
backgroundu = rndscale ( 1 ) ;
backgroundv = rndscale ( 1 ) ;
}
else if ( lastupdate ! = lastmillis ) lastupdate = lastmillis ;
hudmatrix . ortho ( 0 , w , h , 0 , - 1 , 1 ) ;
resethudmatrix ( ) ;
resethudshader ( ) ;
gle : : defvertex ( 2 ) ;
gle : : deftexcoord0 ( ) ;
settexture ( " media/interface/background.png " , 0 ) ;
float bu = w * 0.67f / 256.0f , bv = h * 0.67f / 256.0f ;
bgquad ( 0 , 0 , w , h , backgroundu , backgroundv , bu , bv ) ;
glEnable ( GL_BLEND ) ;
glBlendFunc ( GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA ) ;
settexture ( " media/interface/shadow.png " , 3 ) ;
bgquad ( 0 , 0 , w , h ) ;
glBlendFunc ( GL_ONE , GL_ONE_MINUS_SRC_ALPHA ) ;
float lh = 0.5f * min ( w , h ) , lw = lh * 2 ,
lx = 0.5f * ( w - lw ) , ly = 0.5f * ( h * 0.5f - lh ) ;
settexture ( ( maxtexsize ? min ( maxtexsize , hwtexsize ) : hwtexsize ) > = 1024 & & ( hudw > 1280 | | hudh > 800 ) ? " <premul>media/interface/logo_1024.png " : " <premul>media/interface/logo.png " , 3 ) ;
bgquad ( lx , ly , lw , lh ) ;
glBlendFunc ( GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA ) ;
if ( caption )
{
int tw = text_width ( caption ) ;
float tsz = 0.04f * min ( w , h ) / FONTH ,
tx = 0.5f * ( w - tw * tsz ) , ty = h - 0.075f * 1.5f * min ( w , h ) - FONTH * tsz ;
pushhudtranslate ( tx , ty , tsz ) ;
draw_text ( caption , 0 , 0 ) ;
pophudmatrix ( ) ;
}
if ( mapshot | | mapname )
{
float infowidth = 14 * FONTH , sz = 0.35f * min ( w , h ) , msz = ( 0.85f * min ( w , h ) - sz ) / ( infowidth + FONTH ) , x = 0.5f * w , y = ly + lh - sz / 15 , mx = 0 , my = 0 , mw = 0 , mh = 0 ;
if ( mapinfo )
{
text_boundsf ( mapinfo , mw , mh , infowidth ) ;
x - = 0.5f * mw * msz ;
if ( mapshot & & mapshot ! = notexture )
{
x - = 0.5f * FONTH * msz ;
mx = sz + FONTH * msz ;
}
}
if ( mapshot & & mapshot ! = notexture )
{
x - = 0.5f * sz ;
resethudshader ( ) ;
glBindTexture ( GL_TEXTURE_2D , mapshot - > id ) ;
bgquad ( x , y , sz , sz ) ;
}
if ( mapname )
{
float tw = text_widthf ( mapname ) , tsz = sz / ( 8 * FONTH ) , tx = max ( 0.5f * ( mw * msz - tw * tsz ) , 0.0f ) ;
pushhudtranslate ( x + mx + tx , y , tsz ) ;
draw_text ( mapname , 0 , 0 ) ;
pophudmatrix ( ) ;
my = 1.5f * FONTH * tsz ;
}
if ( mapinfo )
{
pushhudtranslate ( x + mx , y + my , msz ) ;
draw_text ( mapinfo , 0 , 0 , 0xFF , 0xFF , 0xFF , 0xFF , - 1 , infowidth ) ;
pophudmatrix ( ) ;
}
}
glDisable ( GL_BLEND ) ;
}
VAR ( menumute , 0 , 1 , 1 ) ;
void setbackgroundinfo ( const char * caption = NULL , Texture * mapshot = NULL , const char * mapname = NULL , const char * mapinfo = NULL )
{
renderedframe = false ;
copystring ( backgroundcaption , caption ? caption : " " ) ;
backgroundmapshot = mapshot ;
copystring ( backgroundmapname , mapname ? mapname : " " ) ;
if ( mapinfo ! = backgroundmapinfo )
{
DELETEA ( backgroundmapinfo ) ;
if ( mapinfo ) backgroundmapinfo = newstring ( mapinfo ) ;
}
}
void renderbackground ( const char * caption , Texture * mapshot , const char * mapname , const char * mapinfo , bool force )
{
if ( ! inbetweenframes & & ! force ) return ;
2020-04-16 20:13:59 +02:00
//if(menumute) stopsounds(); // stop sounds while loading
2020-04-15 18:39:17 +02:00
int w = hudw , h = hudh ;
if ( forceaspect ) w = int ( ceil ( h * forceaspect ) ) ;
getbackgroundres ( w , h ) ;
gettextres ( w , h ) ;
if ( force )
{
renderbackgroundview ( w , h , caption , mapshot , mapname , mapinfo ) ;
return ;
}
loopi ( 3 )
{
renderbackgroundview ( w , h , caption , mapshot , mapname , mapinfo ) ;
swapbuffers ( false ) ;
}
setbackgroundinfo ( caption , mapshot , mapname , mapinfo ) ;
}
void restorebackground ( int w , int h , bool force = false )
{
if ( renderedframe )
{
if ( ! force ) return ;
setbackgroundinfo ( ) ;
}
renderbackgroundview ( w , h , backgroundcaption [ 0 ] ? backgroundcaption : NULL , backgroundmapshot , backgroundmapname [ 0 ] ? backgroundmapname : NULL , backgroundmapinfo ) ;
}
float loadprogress = 0 ;
void renderprogressview ( int w , int h , float bar , const char * text ) // also used during loading
{
hudmatrix . ortho ( 0 , w , h , 0 , - 1 , 1 ) ;
resethudmatrix ( ) ;
resethudshader ( ) ;
gle : : defvertex ( 2 ) ;
gle : : deftexcoord0 ( ) ;
float fh = 0.060f * min ( w , h ) , fw = fh * 15 ,
fx = renderedframe ? w - fw - fh / 4 : 0.5f * ( w - fw ) ,
fy = renderedframe ? fh / 4 : h - fh * 1.5f ;
settexture ( " media/interface/loading_frame.png " , 3 ) ;
bgquad ( fx , fy , fw , fh ) ;
glEnable ( GL_BLEND ) ;
glBlendFunc ( GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA ) ;
float bw = fw * ( 512 - 2 * 8 ) / 512.0f , bh = fh * 20 / 32.0f ,
bx = fx + fw * 8 / 512.0f , by = fy + fh * 6 / 32.0f ,
su1 = 0 / 32.0f , su2 = 8 / 32.0f , sw = fw * 8 / 512.0f ,
eu1 = 24 / 32.0f , eu2 = 32 / 32.0f , ew = fw * 8 / 512.0f ,
mw = bw - sw - ew ,
ex = bx + sw + max ( mw * bar , fw * 8 / 512.0f ) ;
if ( bar > 0 )
{
settexture ( " media/interface/loading_bar.png " , 3 ) ;
bgquad ( bx , by , sw , bh , su1 , 0 , su2 - su1 , 1 ) ;
bgquad ( bx + sw , by , ex - ( bx + sw ) , bh , su2 , 0 , eu1 - su2 , 1 ) ;
bgquad ( ex , by , ew , bh , eu1 , 0 , eu2 - eu1 , 1 ) ;
}
if ( text )
{
int tw = text_width ( text ) ;
float tsz = bh * 0.6f / FONTH ;
if ( tw * tsz > mw ) tsz = mw / tw ;
pushhudtranslate ( bx + sw , by + ( bh - FONTH * tsz ) / 2 , tsz ) ;
draw_text ( text , 0 , 0 ) ;
pophudmatrix ( ) ;
}
glDisable ( GL_BLEND ) ;
}
VAR ( progressbackground , 0 , 0 , 1 ) ;
void renderprogress ( float bar , const char * text , bool background ) // also used during loading
{
if ( ! inbetweenframes | | drawtex ) return ;
extern int menufps , maxfps ;
int fps = menufps ? ( maxfps ? min ( maxfps , menufps ) : menufps ) : maxfps ;
if ( fps )
{
static int lastprogress = 0 ;
int ticks = SDL_GetTicks ( ) , diff = ticks - lastprogress ;
if ( bar > 0 & & diff > = 0 & & diff < ( 1000 + fps - 1 ) / fps ) return ;
lastprogress = ticks ;
}
clientkeepalive ( ) ; // make sure our connection doesn't time out while loading maps etc.
# ifdef __APPLE__
interceptkey ( SDLK_UNKNOWN ) ; // keep the event queue awake to avoid 'beachball' cursor
# endif
int w = hudw , h = hudh ;
if ( forceaspect ) w = int ( ceil ( h * forceaspect ) ) ;
getbackgroundres ( w , h ) ;
gettextres ( w , h ) ;
extern int mesa_swap_bug , curvsync ;
bool forcebackground = progressbackground | | ( mesa_swap_bug & & ( curvsync | | totalmillis = = 1 ) ) ;
if ( background | | forcebackground ) restorebackground ( w , h , forcebackground ) ;
renderprogressview ( w , h , bar , text ) ;
swapbuffers ( false ) ;
}
VARNP ( relativemouse , userelativemouse , 0 , 1 , 1 ) ;
bool shouldgrab = false , grabinput = false , minimized = false , canrelativemouse = true , relativemouse = false ;
int keyrepeatmask = 0 , textinputmask = 0 ;
Uint32 textinputtime = 0 ;
VAR ( textinputfilter , 0 , 5 , 1000 ) ;
void keyrepeat ( bool on , int mask )
{
if ( on ) keyrepeatmask | = mask ;
else keyrepeatmask & = ~ mask ;
}
void textinput ( bool on , int mask )
{
if ( on )
{
if ( ! textinputmask )
{
SDL_StartTextInput ( ) ;
textinputtime = SDL_GetTicks ( ) ;
}
textinputmask | = mask ;
}
else
{
textinputmask & = ~ mask ;
if ( ! textinputmask ) SDL_StopTextInput ( ) ;
}
}
void inputgrab ( bool on )
{
if ( on )
{
SDL_ShowCursor ( SDL_FALSE ) ;
if ( canrelativemouse & & userelativemouse )
{
if ( SDL_SetRelativeMouseMode ( SDL_TRUE ) > = 0 )
{
SDL_SetWindowGrab ( screen , SDL_TRUE ) ;
relativemouse = true ;
}
else
{
SDL_SetWindowGrab ( screen , SDL_FALSE ) ;
canrelativemouse = false ;
relativemouse = false ;
}
}
}
else
{
SDL_ShowCursor ( SDL_TRUE ) ;
if ( relativemouse )
{
SDL_SetRelativeMouseMode ( SDL_FALSE ) ;
SDL_SetWindowGrab ( screen , SDL_FALSE ) ;
relativemouse = false ;
}
}
shouldgrab = false ;
}
bool initwindowpos = false ;
void setfullscreen ( bool enable )
{
if ( ! screen ) return ;
//initwarning(enable ? "fullscreen" : "windowed");
SDL_SetWindowFullscreen ( screen , enable ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0 ) ;
if ( ! enable )
{
SDL_SetWindowSize ( screen , scr_w , scr_h ) ;
if ( initwindowpos )
{
int winx = SDL_WINDOWPOS_CENTERED , winy = SDL_WINDOWPOS_CENTERED ;
SDL_SetWindowPosition ( screen , winx , winy ) ;
initwindowpos = false ;
}
}
}
# ifdef _DEBUG
VARF ( fullscreen , 0 , 0 , 1 , setfullscreen ( fullscreen ! = 0 ) ) ;
# else
VARF ( fullscreen , 0 , 1 , 1 , setfullscreen ( fullscreen ! = 0 ) ) ;
# endif
void screenres ( int w , int h )
{
scr_w = clamp ( w , SCR_MINW , SCR_MAXW ) ;
scr_h = clamp ( h , SCR_MINH , SCR_MAXH ) ;
if ( screen )
{
scr_w = min ( scr_w , desktopw ) ;
scr_h = min ( scr_h , desktoph ) ;
if ( SDL_GetWindowFlags ( screen ) & SDL_WINDOW_FULLSCREEN ) gl_resize ( ) ;
else SDL_SetWindowSize ( screen , scr_w , scr_h ) ;
}
else
{
initwarning ( " screen resolution " ) ;
}
}
ICOMMAND ( screenres , " ii " , ( int * w , int * h ) , screenres ( * w , * h ) ) ;
static void setgamma ( int val )
{
if ( screen & & SDL_SetWindowBrightness ( screen , val / 100.0f ) < 0 ) conoutf ( CON_ERROR , " Could not set gamma: %s " , SDL_GetError ( ) ) ;
}
static int curgamma = 100 ;
VARFNP ( gamma , reqgamma , 30 , 100 , 300 ,
{
if ( initing | | reqgamma = = curgamma ) return ;
curgamma = reqgamma ;
setgamma ( curgamma ) ;
} ) ;
void restoregamma ( )
{
if ( initing | | reqgamma = = 100 ) return ;
curgamma = reqgamma ;
setgamma ( curgamma ) ;
}
void cleargamma ( )
{
if ( curgamma ! = 100 & & screen ) SDL_SetWindowBrightness ( screen , 1.0f ) ;
}
int curvsync = - 1 ;
void restorevsync ( )
{
if ( initing | | ! glcontext ) return ;
extern int vsync , vsynctear ;
if ( ! SDL_GL_SetSwapInterval ( vsync ? ( vsynctear ? - 1 : 1 ) : 0 ) )
curvsync = vsync ;
}
VARFP ( vsync , 0 , 0 , 1 , restorevsync ( ) ) ;
VARFP ( vsynctear , 0 , 0 , 1 , { if ( vsync ) restorevsync ( ) ; } ) ;
VAR ( dbgmodes , 0 , 0 , 1 ) ;
void setupscreen ( )
{
if ( glcontext )
{
SDL_GL_DeleteContext ( glcontext ) ;
glcontext = NULL ;
}
if ( screen )
{
SDL_DestroyWindow ( screen ) ;
screen = NULL ;
}
curvsync = - 1 ;
SDL_Rect desktop ;
if ( SDL_GetDisplayBounds ( 0 , & desktop ) < 0 ) fatal ( " failed querying desktop bounds: %s " , SDL_GetError ( ) ) ;
desktopw = desktop . w ;
desktoph = desktop . h ;
if ( scr_h < 0 ) scr_h = SCR_DEFAULTH ;
if ( scr_w < 0 ) scr_w = ( scr_h * desktopw ) / desktoph ;
scr_w = min ( scr_w , desktopw ) ;
scr_h = min ( scr_h , desktoph ) ;
int winx = SDL_WINDOWPOS_UNDEFINED , winy = SDL_WINDOWPOS_UNDEFINED , winw = scr_w , winh = scr_h , flags = SDL_WINDOW_RESIZABLE ;
if ( fullscreen )
{
winw = desktopw ;
winh = desktoph ;
flags | = SDL_WINDOW_FULLSCREEN_DESKTOP ;
initwindowpos = true ;
}
SDL_GL_ResetAttributes ( ) ;
SDL_GL_SetAttribute ( SDL_GL_DOUBLEBUFFER , 1 ) ;
SDL_GL_SetAttribute ( SDL_GL_STENCIL_SIZE , 0 ) ;
SDL_GL_SetAttribute ( SDL_GL_DEPTH_SIZE , 0 ) ;
screen = SDL_CreateWindow ( " Tesseract " , winx , winy , winw , winh , SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_INPUT_FOCUS | SDL_WINDOW_MOUSE_FOCUS | flags ) ;
if ( ! screen ) fatal ( " failed to create OpenGL window: %s " , SDL_GetError ( ) ) ;
SDL_SetWindowMinimumSize ( screen , SCR_MINW , SCR_MINH ) ;
SDL_SetWindowMaximumSize ( screen , SCR_MAXW , SCR_MAXH ) ;
# ifdef __APPLE__
static const int glversions [ ] = { 32 , 20 } ;
# else
static const int glversions [ ] = { 40 , 33 , 32 , 31 , 30 , 20 } ;
# endif
loopi ( sizeof ( glversions ) / sizeof ( glversions [ 0 ] ) )
{
glcompat = glversions [ i ] < = 30 ? 1 : 0 ;
SDL_GL_SetAttribute ( SDL_GL_CONTEXT_MAJOR_VERSION , glversions [ i ] / 10 ) ;
SDL_GL_SetAttribute ( SDL_GL_CONTEXT_MINOR_VERSION , glversions [ i ] % 10 ) ;
SDL_GL_SetAttribute ( SDL_GL_CONTEXT_PROFILE_MASK , glversions [ i ] > = 32 ? SDL_GL_CONTEXT_PROFILE_CORE : 0 ) ;
glcontext = SDL_GL_CreateContext ( screen ) ;
if ( glcontext ) break ;
}
if ( ! glcontext ) fatal ( " failed to create OpenGL context: %s " , SDL_GetError ( ) ) ;
SDL_GetWindowSize ( screen , & screenw , & screenh ) ;
renderw = min ( scr_w , screenw ) ;
renderh = min ( scr_h , screenh ) ;
hudw = screenw ;
hudh = screenh ;
}
void resetgl ( )
{
2020-05-13 00:30:58 +02:00
//clearchanges(CHANGE_GFX|CHANGE_SHADERS);
2020-04-15 18:39:17 +02:00
renderbackground ( " resetting OpenGL " ) ;
recorder : : cleanup ( ) ;
cleanupva ( ) ;
cleanupparticles ( ) ;
cleanupstains ( ) ;
cleanupsky ( ) ;
cleanupmodels ( ) ;
cleanupprefabs ( ) ;
cleanuptextures ( ) ;
cleanupblendmap ( ) ;
cleanuplights ( ) ;
cleanupshaders ( ) ;
cleanupgl ( ) ;
setupscreen ( ) ;
inputgrab ( grabinput ) ;
gl_init ( ) ;
inbetweenframes = false ;
if ( ! reloadtexture ( * notexture ) | |
! reloadtexture ( " <premul>media/interface/logo.png " ) | |
! reloadtexture ( " <premul>media/interface/logo_1024.png " ) | |
! reloadtexture ( " media/interface/background.png " ) | |
! reloadtexture ( " media/interface/shadow.png " ) | |
! reloadtexture ( " media/interface/mapshot_frame.png " ) | |
! reloadtexture ( " media/interface/loading_frame.png " ) | |
! reloadtexture ( " media/interface/loading_bar.png " ) )
fatal ( " failed to reload core texture " ) ;
reloadfonts ( ) ;
inbetweenframes = true ;
renderbackground ( " initializing... " ) ;
restoregamma ( ) ;
restorevsync ( ) ;
initgbuffer ( ) ;
reloadshaders ( ) ;
reloadtextures ( ) ;
allchanged ( true ) ;
}
COMMAND ( resetgl , " " ) ;
vector < SDL_Event > events ;
void pushevent ( const SDL_Event & e )
{
events . add ( e ) ;
}
static bool filterevent ( const SDL_Event & event )
{
switch ( event . type )
{
case SDL_MOUSEMOTION :
if ( grabinput & & ! relativemouse & & ! ( SDL_GetWindowFlags ( screen ) & SDL_WINDOW_FULLSCREEN ) )
{
if ( event . motion . x = = screenw / 2 & & event . motion . y = = screenh / 2 )
return false ; // ignore any motion events generated by SDL_WarpMouse
# ifdef __APPLE__
if ( event . motion . y = = 0 )
return false ; // let mac users drag windows via the title bar
# endif
}
break ;
}
return true ;
}
static inline bool pollevent ( SDL_Event & event )
{
while ( SDL_PollEvent ( & event ) )
{
if ( filterevent ( event ) ) return true ;
}
return false ;
}
bool interceptkey ( int sym )
{
static int lastintercept = SDLK_UNKNOWN ;
int len = lastintercept = = sym ? events . length ( ) : 0 ;
SDL_Event event ;
while ( 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 ;
}
static void ignoremousemotion ( )
{
SDL_Event e ;
SDL_PumpEvents ( ) ;
while ( SDL_PeepEvents ( & e , 1 , SDL_GETEVENT , SDL_MOUSEMOTION , SDL_MOUSEMOTION ) ) ;
}
static void resetmousemotion ( )
{
if ( grabinput & & ! relativemouse & & ! ( SDL_GetWindowFlags ( screen ) & SDL_WINDOW_FULLSCREEN ) )
{
SDL_WarpMouseInWindow ( screen , screenw / 2 , screenh / 2 ) ;
}
}
static void checkmousemotion ( int & dx , int & dy )
{
loopv ( events )
{
SDL_Event & event = events [ i ] ;
if ( event . type ! = SDL_MOUSEMOTION )
{
if ( i > 0 ) events . remove ( 0 , i ) ;
return ;
}
dx + = event . motion . xrel ;
dy + = event . motion . yrel ;
}
events . setsize ( 0 ) ;
SDL_Event event ;
while ( pollevent ( event ) )
{
if ( event . type ! = SDL_MOUSEMOTION )
{
events . add ( event ) ;
return ;
}
dx + = event . motion . xrel ;
dy + = event . motion . yrel ;
}
}
void checkinput ( )
{
SDL_Event event ;
//int lasttype = 0, lastbut = 0;
bool mousemoved = false ;
while ( events . length ( ) | | pollevent ( event ) )
{
if ( events . length ( ) ) event = events . remove ( 0 ) ;
switch ( event . type )
{
case SDL_QUIT :
quit ( ) ;
return ;
case SDL_TEXTINPUT :
if ( textinputmask & & int ( event . text . timestamp - textinputtime ) > = textinputfilter )
{
uchar buf [ SDL_TEXTINPUTEVENT_TEXT_SIZE + 1 ] ;
size_t len = decodeutf8 ( buf , sizeof ( buf ) - 1 , ( const uchar * ) event . text . text , strlen ( event . text . text ) ) ;
if ( len > 0 ) { buf [ len ] = ' \0 ' ; processtextinput ( ( const char * ) buf , len ) ; }
}
break ;
case SDL_KEYDOWN :
case SDL_KEYUP :
if ( keyrepeatmask | | ! event . key . repeat )
processkey ( event . key . keysym . sym , event . key . state = = SDL_PRESSED , event . key . keysym . mod | SDL_GetModState ( ) ) ;
break ;
case SDL_WINDOWEVENT :
switch ( event . window . event )
{
case SDL_WINDOWEVENT_CLOSE :
quit ( ) ;
break ;
case SDL_WINDOWEVENT_FOCUS_GAINED :
shouldgrab = true ;
break ;
case SDL_WINDOWEVENT_ENTER :
inputgrab ( grabinput = true ) ;
break ;
case SDL_WINDOWEVENT_LEAVE :
case SDL_WINDOWEVENT_FOCUS_LOST :
inputgrab ( grabinput = false ) ;
break ;
case SDL_WINDOWEVENT_MINIMIZED :
minimized = true ;
break ;
case SDL_WINDOWEVENT_MAXIMIZED :
case SDL_WINDOWEVENT_RESTORED :
minimized = false ;
break ;
case SDL_WINDOWEVENT_RESIZED :
break ;
case SDL_WINDOWEVENT_SIZE_CHANGED :
SDL_GetWindowSize ( screen , & screenw , & screenh ) ;
if ( ! ( SDL_GetWindowFlags ( screen ) & SDL_WINDOW_FULLSCREEN ) )
{
scr_w = clamp ( screenw , SCR_MINW , SCR_MAXW ) ;
scr_h = clamp ( screenh , SCR_MINH , SCR_MAXH ) ;
}
gl_resize ( ) ;
break ;
}
break ;
case SDL_MOUSEMOTION :
if ( grabinput )
{
int dx = event . motion . xrel , dy = event . motion . yrel ;
checkmousemotion ( dx , dy ) ;
2020-04-23 17:12:16 +02:00
/*if(!UI::movecursor(dx, dy))*/ mousemove ( dx , dy ) ;
2020-04-15 18:39:17 +02:00
mousemoved = true ;
}
else if ( shouldgrab ) inputgrab ( grabinput = true ) ;
break ;
case SDL_MOUSEBUTTONDOWN :
case SDL_MOUSEBUTTONUP :
//if(lasttype==event.type && lastbut==event.button.button) break; // why?? get event twice without it
switch ( event . button . button )
{
case SDL_BUTTON_LEFT : processkey ( - 1 , event . button . state = = SDL_PRESSED ) ; break ;
case SDL_BUTTON_MIDDLE : processkey ( - 2 , event . button . state = = SDL_PRESSED ) ; break ;
case SDL_BUTTON_RIGHT : processkey ( - 3 , event . button . state = = SDL_PRESSED ) ; break ;
case SDL_BUTTON_X1 : processkey ( - 6 , event . button . state = = SDL_PRESSED ) ; break ;
case SDL_BUTTON_X2 : processkey ( - 7 , event . button . state = = SDL_PRESSED ) ; break ;
}
//lasttype = event.type;
//lastbut = event.button.button;
break ;
case SDL_MOUSEWHEEL :
if ( event . wheel . y > 0 ) { processkey ( - 4 , true ) ; processkey ( - 4 , false ) ; }
else if ( event . wheel . y < 0 ) { processkey ( - 5 , true ) ; processkey ( - 5 , false ) ; }
break ;
}
}
if ( mousemoved ) resetmousemotion ( ) ;
}
void swapbuffers ( bool overlay )
{
recorder : : capture ( overlay ) ;
gle : : disable ( ) ;
SDL_GL_SwapWindow ( screen ) ;
}
VAR ( menufps , 0 , 60 , 1000 ) ;
VARP ( maxfps , 0 , 125 , 1000 ) ;
void limitfps ( int & millis , int curmillis )
{
int limit = ( mainmenu | | minimized ) & & menufps ? ( maxfps ? min ( maxfps , menufps ) : menufps ) : maxfps ;
if ( ! limit ) return ;
static int fpserror = 0 ;
int delay = 1000 / limit - ( millis - curmillis ) ;
if ( delay < 0 ) fpserror = 0 ;
else
{
fpserror + = 1000 % limit ;
if ( fpserror > = limit )
{
+ + delay ;
fpserror - = limit ;
}
if ( delay > 0 )
{
SDL_Delay ( delay ) ;
millis + = delay ;
}
}
}
# ifdef WIN32
// Force Optimus setups to use the NVIDIA GPU
extern " C "
{
# ifdef __GNUC__
__attribute__ ( ( dllexport ) )
# else
__declspec ( dllexport )
# endif
DWORD NvOptimusEnablement = 1 ;
# ifdef __GNUC__
__attribute__ ( ( dllexport ) )
# else
__declspec ( dllexport )
# endif
DWORD AmdPowerXpressRequestHighPerformance = 1 ;
}
# endif
# if defined(WIN32) && !defined(_DEBUG) && !defined(__GNUC__)
void stackdumper ( unsigned int type , EXCEPTION_POINTERS * ep )
{
if ( ! ep ) fatal ( " unknown type " ) ;
EXCEPTION_RECORD * er = ep - > ExceptionRecord ;
CONTEXT * context = ep - > ContextRecord ;
char out [ 512 ] ;
formatstring ( out , " Tesseract Win32 Exception: 0x%x [0x%x] \n \n " , er - > ExceptionCode , er - > ExceptionCode = = EXCEPTION_ACCESS_VIOLATION ? er - > ExceptionInformation [ 1 ] : - 1 ) ;
SymInitialize ( GetCurrentProcess ( ) , NULL , TRUE ) ;
# ifdef _AMD64_
STACKFRAME64 sf = { { context - > Rip , 0 , AddrModeFlat } , { } , { context - > Rbp , 0 , AddrModeFlat } , { context - > Rsp , 0 , AddrModeFlat } , 0 } ;
while ( : : StackWalk64 ( IMAGE_FILE_MACHINE_AMD64 , GetCurrentProcess ( ) , GetCurrentThread ( ) , & sf , context , NULL , : : SymFunctionTableAccess , : : SymGetModuleBase , NULL ) )
{
union { IMAGEHLP_SYMBOL64 sym ; char symext [ sizeof ( IMAGEHLP_SYMBOL64 ) + sizeof ( string ) ] ; } ;
sym . SizeOfStruct = sizeof ( sym ) ;
sym . MaxNameLength = sizeof ( symext ) - sizeof ( sym ) ;
IMAGEHLP_LINE64 line ;
line . SizeOfStruct = sizeof ( line ) ;
DWORD64 symoff ;
DWORD lineoff ;
if ( SymGetSymFromAddr64 ( GetCurrentProcess ( ) , sf . AddrPC . Offset , & symoff , & sym ) & & SymGetLineFromAddr64 ( GetCurrentProcess ( ) , sf . AddrPC . Offset , & lineoff , & line ) )
# else
STACKFRAME sf = { { context - > Eip , 0 , AddrModeFlat } , { } , { context - > Ebp , 0 , AddrModeFlat } , { context - > Esp , 0 , AddrModeFlat } , 0 } ;
while ( : : StackWalk ( IMAGE_FILE_MACHINE_I386 , GetCurrentProcess ( ) , GetCurrentThread ( ) , & sf , context , NULL , : : SymFunctionTableAccess , : : SymGetModuleBase , NULL ) )
{
union { IMAGEHLP_SYMBOL sym ; char symext [ sizeof ( IMAGEHLP_SYMBOL ) + sizeof ( string ) ] ; } ;
sym . SizeOfStruct = sizeof ( sym ) ;
sym . MaxNameLength = sizeof ( symext ) - sizeof ( sym ) ;
IMAGEHLP_LINE line ;
line . SizeOfStruct = sizeof ( line ) ;
DWORD symoff , lineoff ;
if ( SymGetSymFromAddr ( GetCurrentProcess ( ) , sf . AddrPC . Offset , & symoff , & sym ) & & SymGetLineFromAddr ( GetCurrentProcess ( ) , sf . AddrPC . Offset , & lineoff , & line ) )
# endif
{
char * del = strrchr ( line . FileName , ' \\ ' ) ;
concformatstring ( out , " %s - %s [%d] \n " , sym . Name , del ? del + 1 : line . FileName , line . LineNumber ) ;
}
}
fatal ( out ) ;
}
# endif
# define MAXFPSHISTORY 60
int fpspos = 0 , fpshistory [ MAXFPSHISTORY ] ;
void resetfpshistory ( )
{
loopi ( MAXFPSHISTORY ) fpshistory [ i ] = 1 ;
fpspos = 0 ;
}
void updatefpshistory ( int millis )
{
fpshistory [ fpspos + + ] = max ( 1 , min ( 1000 , millis ) ) ;
if ( fpspos > = MAXFPSHISTORY ) fpspos = 0 ;
}
void getframemillis ( float & avg , float & bestdiff , float & worstdiff )
{
int total = fpshistory [ MAXFPSHISTORY - 1 ] , best = total , worst = total ;
loopi ( MAXFPSHISTORY - 1 )
{
int millis = fpshistory [ i ] ;
total + = millis ;
if ( millis < best ) best = millis ;
if ( millis > worst ) worst = millis ;
}
avg = total / float ( MAXFPSHISTORY ) ;
best = best - avg ;
worstdiff = avg - worst ;
}
void getfps ( int & fps , int & bestdiff , int & worstdiff )
{
int total = fpshistory [ MAXFPSHISTORY - 1 ] , best = total , worst = total ;
loopi ( MAXFPSHISTORY - 1 )
{
int millis = fpshistory [ i ] ;
total + = millis ;
if ( millis < best ) best = millis ;
if ( millis > worst ) worst = millis ;
}
fps = ( 1000 * MAXFPSHISTORY ) / total ;
bestdiff = 1000 / best - fps ;
worstdiff = fps - 1000 / worst ;
}
void getfps_ ( int * raw )
{
if ( * raw ) floatret ( 1000.0f / fpshistory [ ( fpspos + MAXFPSHISTORY - 1 ) % MAXFPSHISTORY ] ) ;
else
{
int fps , bestdiff , worstdiff ;
getfps ( fps , bestdiff , worstdiff ) ;
intret ( fps ) ;
}
}
COMMANDN ( getfps , getfps_ , " i " ) ;
bool inbetweenframes = false , renderedframe = true ;
static bool findarg ( int argc , char * * argv , const char * str )
{
for ( int i = 1 ; i < argc ; i + + ) if ( strstr ( argv [ i ] , str ) = = argv [ i ] ) return true ;
return 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 ( ) ) ;
int getclockmillis ( )
{
int millis = SDL_GetTicks ( ) - clockrealbase ;
if ( clockfix ) millis = int ( millis * ( double ( clockerror ) / 1000000 ) ) ;
millis + = clockvirtbase ;
return max ( millis , totalmillis ) ;
}
VAR ( numcpus , 1 , 1 , 16 ) ;
int main ( int argc , char * * argv )
{
# ifdef WIN32
//atexit((void (__cdecl *)(void))_CrtDumpMemoryLeaks);
# ifndef _DEBUG
# ifndef __GNUC__
__try {
# endif
# endif
# endif
2020-04-26 00:20:00 +02:00
char * initscript = NULL ;
2020-04-15 18:39:17 +02:00
initing = INIT_RESET ;
// set home dir first
2020-04-16 20:20:26 +02:00
sethomedir ( " $HOME/.octacore " ) ;
2020-04-15 18:39:17 +02:00
execfile ( " config/init.cfg " , false ) ;
for ( int i = 1 ; i < argc ; i + + )
{
if ( argv [ i ] [ 0 ] = = ' - ' ) switch ( argv [ i ] [ 1 ] )
{
case ' k ' :
{
const char * dir = addpackagedir ( & argv [ i ] [ 2 ] ) ;
2020-04-26 00:20:00 +02:00
if ( dir ) printf ( " Adding package directory: %s \n " , dir ) ;
2020-04-15 18:39:17 +02:00
break ;
}
case ' g ' : break ;
case ' w ' : scr_w = clamp ( atoi ( & argv [ i ] [ 2 ] ) , SCR_MINW , SCR_MAXW ) ; if ( ! findarg ( argc , argv , " -h " ) ) scr_h = - 1 ; break ;
case ' h ' : scr_h = clamp ( atoi ( & argv [ i ] [ 2 ] ) , SCR_MINH , SCR_MAXH ) ; if ( ! findarg ( argc , argv , " -w " ) ) scr_w = - 1 ; break ;
case ' f ' : fullscreen = atoi ( & argv [ i ] [ 2 ] ) ; break ;
case ' x ' : initscript = & argv [ i ] [ 2 ] ; break ;
2020-04-25 05:05:29 +02:00
default : break ;
2020-04-15 18:39:17 +02:00
}
}
numcpus = clamp ( SDL_GetCPUCount ( ) , 1 , 16 ) ;
2020-04-26 00:20:00 +02:00
printf ( " init: sdl \n " ) ;
2020-04-15 18:39:17 +02:00
2020-04-25 05:05:29 +02:00
if ( SDL_Init ( SDL_INIT_TIMER | SDL_INIT_VIDEO | SDL_INIT_AUDIO ) < 0 ) fatal ( " Unable to initialize SDL: %s " , SDL_GetError ( ) ) ;
2020-04-15 18:39:17 +02:00
2020-04-26 00:20:00 +02:00
printf ( " init: game \n " ) ;
2020-04-15 18:39:17 +02:00
game : : initclient ( ) ;
2020-04-26 00:20:00 +02:00
printf ( " init: video \n " ) ;
2020-04-15 18:39:17 +02:00
SDL_SetHint ( SDL_HINT_GRAB_KEYBOARD , " 0 " ) ;
# if !defined(WIN32) && !defined(__APPLE__)
SDL_SetHint ( SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS , " 0 " ) ;
# endif
setupscreen ( ) ;
SDL_ShowCursor ( SDL_FALSE ) ;
SDL_StopTextInput ( ) ; // workaround for spurious text-input events getting sent on first text input toggle?
2020-04-26 00:20:00 +02:00
printf ( " init: gl \n " ) ;
2020-04-15 18:39:17 +02:00
gl_checkextensions ( ) ;
gl_init ( ) ;
notexture = textureload ( " media/texture/game/notexture.png " ) ;
if ( ! notexture ) fatal ( " could not find core textures " ) ;
2020-04-26 00:20:00 +02:00
printf ( " init: console \n " ) ;
2020-04-15 18:39:17 +02:00
if ( ! execfile ( " config/stdlib.cfg " , false ) ) fatal ( " cannot find data files (you are running from the wrong folder, try .bat file in the main folder) " ) ; // this is the first file we load.
if ( ! execfile ( " config/font.cfg " , false ) ) fatal ( " cannot find font definitions " ) ;
if ( ! setfont ( " default " ) ) fatal ( " no default font specified " ) ;
2020-04-23 17:12:16 +02:00
//UI::setup();
2020-04-15 18:39:17 +02:00
inbetweenframes = true ;
renderbackground ( " initializing... " ) ;
2020-04-26 00:20:00 +02:00
printf ( " init: world \n " ) ;
2020-04-15 18:39:17 +02:00
camera1 = player = game : : iterdynents ( 0 ) ;
emptymap ( 0 , true , NULL , false ) ;
2020-04-26 00:20:00 +02:00
printf ( " init: sound \n " ) ;
2020-04-16 20:13:59 +02:00
//initsound();
2020-04-15 18:39:17 +02:00
2020-04-26 00:20:00 +02:00
printf ( " init: cfg \n " ) ;
2020-04-15 18:39:17 +02:00
initing = INIT_LOAD ;
execfile ( " config/keymap.cfg " ) ;
execfile ( " config/stdedit.cfg " ) ;
execfile ( game : : gameconfig ( ) ) ;
execfile ( " config/heightmap.cfg " ) ;
execfile ( " config/blendbrush.cfg " ) ;
if ( game : : savedservers ( ) ) execfile ( game : : savedservers ( ) , false ) ;
identflags | = IDF_PERSIST ;
if ( ! execfile ( game : : savedconfig ( ) , false ) )
{
execfile ( game : : defaultconfig ( ) ) ;
writecfg ( game : : restoreconfig ( ) ) ;
}
execfile ( game : : autoexec ( ) , false ) ;
identflags & = ~ IDF_PERSIST ;
initing = INIT_GAME ;
game : : loadconfigs ( ) ;
initing = NOT_INITING ;
2020-04-26 00:20:00 +02:00
printf ( " init: render \n " ) ;
2020-04-15 18:39:17 +02:00
restoregamma ( ) ;
restorevsync ( ) ;
initgbuffer ( ) ;
loadshaders ( ) ;
initparticles ( ) ;
initstains ( ) ;
identflags | = IDF_PERSIST ;
2020-04-26 00:20:00 +02:00
printf ( " init: mainloop \n " ) ;
2020-04-15 18:39:17 +02:00
if ( execfile ( " once.cfg " , false ) ) remove ( findfile ( " once.cfg " , " rb " ) ) ;
if ( initscript ) execute ( initscript ) ;
2020-04-16 20:13:59 +02:00
//initmumble();
2020-04-15 18:39:17 +02:00
resetfpshistory ( ) ;
inputgrab ( grabinput = true ) ;
ignoremousemotion ( ) ;
for ( ; ; )
{
static int frames = 0 ;
int millis = getclockmillis ( ) ;
limitfps ( millis , totalmillis ) ;
elapsedtime = millis - totalmillis ;
static int timeerr = 0 ;
int scaledtime = game : : scaletime ( elapsedtime ) + timeerr ;
curtime = scaledtime / 100 ;
timeerr = scaledtime % 100 ;
if ( ! multiplayer ( false ) & & curtime > 200 ) curtime = 200 ;
if ( game : : ispaused ( ) ) curtime = 0 ;
lastmillis + = curtime ;
totalmillis = millis ;
checkinput ( ) ;
2020-04-23 17:12:16 +02:00
//UI::update();
2020-05-13 00:30:58 +02:00
//menuprocess();
2020-04-15 18:39:17 +02:00
tryedit ( ) ;
if ( lastmillis ) game : : updateworld ( ) ;
checksleep ( lastmillis ) ;
if ( frames ) updatefpshistory ( elapsedtime ) ;
frames + + ;
// miscellaneous general game effects
recomputecamera ( ) ;
updateparticles ( ) ;
2020-04-16 20:13:59 +02:00
//updatesounds();
2020-04-15 18:39:17 +02:00
if ( minimized ) continue ;
gl_setupframe ( ! mainmenu ) ;
inbetweenframes = false ;
gl_drawframe ( ) ;
swapbuffers ( ) ;
renderedframe = inbetweenframes = true ;
}
ASSERT ( 0 ) ;
return EXIT_FAILURE ;
# if defined(WIN32) && !defined(_DEBUG) && !defined(__GNUC__)
} __except ( stackdumper ( 0 , GetExceptionInformation ( ) ) , EXCEPTION_CONTINUE_SEARCH ) { return 0 ; }
# endif
}