Page Menu
Home
Phabricator (Chris)
Search
Configure Global Search
Log In
Files
F631833
manager.cpp
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Authored By
Unknown
Size
42 KB
Referenced Files
None
Subscribers
None
manager.cpp
View Options
#include
"main/references.h"
#include
"main/input_handling.h"
#include
"main/logging.h"
#include
"main/tick.h"
#include
"main/console.h"
#include
"scripts/manager.h"
#include
"scripts/binds.h"
#include
"scripts/context_cache.h"
#include
"str_util.h"
#include
"files.h"
#include
"threads.h"
#include
"angelscript.h"
#include
"scriptarray.h"
#include
"scriptstdstring.h"
#include
"scriptmath.h"
#include
"scriptdictionary.h"
#include
"scriptmap.h"
#include
"scriptany.h"
#include
"scripthandle.h"
#include
"compat/misc.h"
#include
"compat/regex.h"
#include
"util/save_file.h"
#include
<stack>
#include
<string>
#include
<iostream>
#include
<fstream>
#include
<queue>
#include
<algorithm>
#include
<set>
#include
"../angelscript/source/as_module.h"
#include
"../angelscript/source/as_objecttype.h"
//Fix angelscript including windows.h in an ugly way
#ifdef _MSC_VER
#undef max
#endif
#ifdef _DEBUG
#ifndef SCRIPT_MAX_TICK_FAILS
#define SCRIPT_MAX_TICK_FAILS 1
#endif
#ifndef SCRIPT_MAX_DRAW_FAILS
#define SCRIPT_MAX_DRAW_FAILS 1
#endif
#ifndef SCRIPT_MAX_RENDER_FAILS
#define SCRIPT_MAX_RENDER_FAILS 1
#endif
#else
#ifndef SCRIPT_MAX_TICK_FAILS
#define SCRIPT_MAX_TICK_FAILS 16
#endif
#ifndef SCRIPT_MAX_DRAW_FAILS
#define SCRIPT_MAX_DRAW_FAILS 16
#endif
#ifndef SCRIPT_MAX_RENDER_FAILS
#define SCRIPT_MAX_RENDER_FAILS 16
#endif
#endif
#define DISABLE_SCRIPT_CACHE
namespace
scripts
{
extern
void
RegisterEarlyNetworkBinds
(
asIScriptEngine
*
engine
);
Threaded
(
asIScriptContext
*
)
clientContext
=
0
;
threads
::
Mutex
sectionLock
;
std
::
string
lastSection
;
#ifndef DISABLE_SCRIPT_CACHE
static
bool
loadCached
(
Module
&
mod
,
const
File
&
fl
,
const
std
::
string
&
cache_file
,
unsigned
cache_version
);
static
void
saveCached
(
Module
&
mod
,
const
std
::
string
&
cache_file
,
unsigned
cache_version
);
#endif
void
printErrors
(
asSMessageInfo
*
msg
,
void
*
ptr
)
{
if
(
msg
->
section
)
{
threads
::
Lock
lock
(
sectionLock
);
if
(
lastSection
!=
msg
->
section
)
{
lastSection
=
msg
->
section
;
error
(
"%s:"
,
msg
->
section
);
}
}
if
(
msg
->
type
==
asMSGTYPE_INFORMATION
)
{
error
(
" %s"
,
msg
->
message
);
}
else
{
const
char
*
type
;
if
(
msg
->
type
==
asMSGTYPE_ERROR
)
type
=
"Error"
;
else
// if(msg->type == asMSGTYPE_WARNING)
type
=
"Warning"
;
if
(
msg
->
col
||
msg
->
row
)
error
(
" %s (%d:%d): %s"
,
type
,
msg
->
row
,
msg
->
col
,
msg
->
message
);
else
error
(
" %s: %s"
,
type
,
msg
->
message
);
}
}
enum
ManagerType
{
MT_Server
,
MT_Shadow
,
MT_Menu
,
MT_GUI
,
MT_Server_Side
,
MT_Client_Side
,
MT_Game
,
};
const
char
*
manager_names
[
MT_Game
]
=
{
"server"
,
"shadow"
,
"menu"
,
"gui"
,
};
Manager
*
getActiveManager
()
{
auto
*
ctx
=
asGetActiveContext
();
if
(
!
ctx
)
return
0
;
return
&
Manager
::
fromEngine
(
ctx
->
GetEngine
());
}
ManagerType
getManagerType
(
Manager
*
man
)
{
if
(
man
==
devices
.
scripts
.
cache_server
)
return
MT_Server
;
if
(
man
==
devices
.
scripts
.
cache_shadow
)
return
MT_Shadow
;
if
(
man
==
devices
.
scripts
.
menu
)
return
MT_Menu
;
return
MT_GUI
;
}
void
throwException
(
const
char
*
msg
)
{
asGetActiveContext
()
->
SetException
(
msg
);
}
const
char
*
callback_decl
[
SC_COUNT
]
=
{
"void preInit()"
,
"void init()"
,
"void postInit()"
,
"void tick(double)"
,
"void deinit()"
,
"void preRender(double)"
,
"void render(double)"
,
"void draw()"
,
"void onSettingsChanged()"
,
"void onGameSettings(Message&)"
,
"void syncInitial(Message&)"
,
"bool sendPeriodic(Message&)"
,
"void recvPeriodic(Message&)"
,
"void save(SaveFile&)"
,
"void load(SaveFile&)"
,
"void saveIdentifiers(SaveFile&)"
,
"void onGameStateChange()"
,
"void preReload(Message&)"
,
"void postReload(Message&)"
,
};
asIScriptFunction
*
Module::getFunction
(
const
char
*
decl
)
{
return
module
->
GetFunctionByDecl
(
decl
);
}
Module
*
Manager::getModule
(
const
char
*
module
)
{
auto
it
=
modules
.
find
(
module
);
if
(
it
!=
modules
.
end
())
return
it
->
second
;
return
0
;
}
asIScriptFunction
*
Manager::getFunction
(
int
fid
)
{
return
engine
->
GetFunctionById
(
fid
);
}
asIScriptFunction
*
Manager::getFunction
(
const
char
*
module
,
const
char
*
decl
)
{
auto
it
=
modules
.
find
(
module
);
if
(
it
!=
modules
.
end
())
return
it
->
second
->
getFunction
(
decl
);
return
0
;
}
asIScriptFunction
*
Manager::getFunction
(
const
std
::
string
&
def
,
const
char
*
fin
,
const
char
*
ret
)
{
std
::
vector
<
std
::
string
>
args
;
split
(
def
,
args
,
"::"
);
//Find the module the function is in
scripts
::
Module
*
module
=
nullptr
;
if
(
args
.
size
()
==
1
)
{
auto
*
ctx
=
asGetActiveContext
();
if
(
ctx
)
{
auto
*
func
=
ctx
->
GetFunction
();
module
=
getModule
(
func
->
GetModuleName
());
}
if
(
!
module
)
{
error
(
"Error: Invalid script module."
);
return
0
;
}
}
else
if
(
args
.
size
()
==
2
)
{
module
=
getModule
(
args
[
0
].
c_str
());
if
(
!
module
)
{
error
(
"Error: Invalid script module '%s'."
,
args
[
0
].
c_str
());
return
0
;
}
}
else
{
error
(
"Error: Invalid script function reference '%s'."
,
def
.
c_str
());
return
0
;
}
//Build declaration for function
std
::
string
decl
=
std
::
string
(
ret
)
+
" "
+
args
[
args
.
size
()
-
1
]
+
fin
;
//Find the function
asIScriptFunction
*
func
=
module
->
getFunction
(
decl
.
c_str
());
if
(
!
func
)
{
error
(
"Error: Unknown script function '%s'."
,
decl
.
c_str
());
return
0
;
}
return
func
;
}
asITypeInfo
*
Module::getClass
(
const
char
*
decl
)
{
int
id
=
module
->
GetTypeIdByDecl
(
decl
);
return
manager
->
engine
->
GetTypeInfoById
(
id
);
}
asITypeInfo
*
Manager::getClass
(
const
char
*
module
,
const
char
*
decl
)
{
auto
it
=
modules
.
find
(
module
);
if
(
it
!=
modules
.
end
())
return
it
->
second
->
getClass
(
decl
);
return
0
;
}
Manager
::
Manager
(
asIJITCompiler
*
Jit
)
:
prevGCSize
(
5000
),
pauseScripts
(
false
),
clearScripts
(
false
)
{
//Create the engine
engine
=
asCreateScriptEngine
(
ANGELSCRIPT_VERSION
);
if
(
Jit
)
{
engine
->
SetEngineProperty
(
asEP_INCLUDE_JIT_INSTRUCTIONS
,
1
);
engine
->
SetJITCompiler
(
Jit
);
}
engine
->
SetEngineProperty
(
asEP_ALLOW_UNSAFE_REFERENCES
,
1
);
engine
->
SetEngineProperty
(
asEP_USE_CHARACTER_LITERALS
,
1
);
engine
->
SetEngineProperty
(
asEP_ALLOW_MULTILINE_STRINGS
,
1
);
engine
->
SetEngineProperty
(
asEP_SCRIPT_SCANNER
,
1
);
#ifndef DO_LINE_CALLBACK
engine
->
SetEngineProperty
(
asEP_BUILD_WITHOUT_LINE_CUES
,
1
);
#endif
engine
->
SetEngineProperty
(
asEP_OPTIMIZE_BYTECODE
,
1
);
engine
->
SetEngineProperty
(
asEP_AUTO_GARBAGE_COLLECT
,
0
);
engine
->
SetEngineProperty
(
asEP_ALTER_SYNTAX_NAMED_ARGS
,
1
);
engine
->
SetMessageCallback
(
asFUNCTION
(
printErrors
),
0
,
asCALL_CDECL
);
engine
->
SetUserData
(
this
,
EDID_Manager
);
//Bind all addon types
RegisterScriptAny
(
engine
);
RegisterScriptHandle
(
engine
);
RegisterScriptArray
(
engine
,
true
);
//HACK: Has to be here so stdstring doesn't instantiate array<string> yet
scripts
::
RegisterEarlyNetworkBinds
(
engine
);
RegisterStdString
(
engine
);
RegisterStdStringUtils
(
engine
);
RegisterScriptDictionary
(
engine
);
RegisterScriptMap
(
engine
);
RegisterScriptMath
(
engine
);
}
Manager
&
Manager
::
fromEngine
(
asIScriptEngine
*
engine
)
{
return
*
(
Manager
*
)
engine
->
GetUserData
(
EDID_Manager
);
}
void
Manager
::
addIncludePath
(
std
::
string
path
)
{
includePaths
.
push_back
(
path
);
}
void
Manager
::
loadDirectory
(
const
std
::
string
&
dirname
)
{
std
::
vector
<
std
::
string
>
list
;
listDirectory
(
dirname
,
list
,
"*.as"
);
foreach
(
it
,
list
)
{
std
::
string
&
file
=
*
it
;
load
(
file
.
substr
(
0
,
file
.
size
()
-
3
),
path_join
(
dirname
,
file
));
}
}
class
CachedScript
{
bool
loaded
;
threads
::
Mutex
lock
;
std
::
string
path
;
std
::
vector
<
std
::
string
>
lines
;
public
:
size_t
size
;
CachedScript
(
const
std
::
string
&
filename
)
:
loaded
(
false
),
path
(
filename
),
size
(
0
)
{
}
const
std
::
vector
<
std
::
string
>&
get
()
{
threads
::
Lock
l
(
lock
);
if
(
!
loaded
)
{
std
::
ifstream
file
(
path
.
c_str
(),
std
::
ios_base
::
in
);
skipBOM
(
file
);
std
::
string
line
;
while
(
file
.
good
())
{
std
::
getline
(
file
,
line
);
size
+=
line
.
size
()
+
1
;
lines
.
push_back
(
line
);
}
loaded
=
true
;
}
return
lines
;
}
};
threads
::
Mutex
loadedScriptsLock
;
std
::
unordered_map
<
std
::
string
,
CachedScript
*>
cachedScripts
;
const
std
::
vector
<
std
::
string
>&
loadScript
(
const
std
::
string
&
filename
,
size_t
&
size
)
{
auto
path
=
getAbsolutePath
(
filename
);
CachedScript
*
script
=
nullptr
;
{
threads
::
Lock
l
(
loadedScriptsLock
);
auto
i
=
cachedScripts
.
find
(
path
);
if
(
i
!=
cachedScripts
.
end
())
{
script
=
i
->
second
;
}
else
{
script
=
new
CachedScript
(
path
);
cachedScripts
[
path
]
=
script
;
}
}
assert
(
script
);
size
=
script
->
size
;
return
script
->
get
();
}
void
clearCachedScripts
()
{
threads
::
Lock
l
(
loadedScriptsLock
);
for
(
auto
i
=
cachedScripts
.
begin
(),
end
=
cachedScripts
.
end
();
i
!=
end
;
++
i
)
delete
i
->
second
;
cachedScripts
.
clear
();
}
void
preloadScript
(
const
std
::
string
&
filename
)
{
size_t
dummy
;
loadScript
(
filename
,
dummy
);
}
threads
::
Mutex
reg_compile_lock
;
void
parseFile
(
Manager
*
man
,
File
&
fl
,
const
std
::
string
&
filename
,
bool
cacheFiles
=
true
)
{
std
::
string
path
=
getAbsolutePath
(
filename
);
size_t
size
;
auto
&
lines
=
loadScript
(
path
,
size
);
fl
.
path
=
path
;
std
::
string
&
output
=
fl
.
contents
;
output
.
reserve
(
size
);
ManagerType
curType
=
getManagerType
(
man
);
ManagerType
selType
=
curType
;
reg_compile_lock
.
lock
();
reg_compile
(
pre_include
,
"^[
\t
]*#include[
\t
]+
\"
(.+)
\"
[
\t
]*"
);
reg_compile
(
pre_priority
,
"^[
\t
]*#priority[
\t
]+(.+)[
\t
]+(.+)[
\t
]*"
);
reg_compile
(
pre_section
,
"^[
\t
]*#section[
\\
t]+(.+)[
\t
]*"
);
reg_compile
(
pre_import_from
,
"^[
\t
]*from[
\t
]+([A-Za-z0-9._-]+)[
\t
]+import[
\t
]+([^;]+);"
);
reg_compile
(
pre_import_all
,
"^[
\t
]*import[
\t
]+(([A-Za-z0-9._-]|, )+);"
);
reg_compile
(
pre_export
,
"^[
\t
]*export[
\t
]+(([A-Za-z0-9._-]|,* )+);"
);
reg_compile_lock
.
release
();
reg_result
match
;
for
(
auto
iLine
=
lines
.
begin
(),
end
=
lines
.
end
();
iLine
!=
end
;
++
iLine
)
{
const
std
::
string
&
line
=
*
iLine
;
auto
lineStart
=
line
.
find_first_not_of
(
"
\t
"
);
if
(
lineStart
==
std
::
string
::
npos
)
{
output
.
append
(
1
,
'\n'
);
continue
;
}
bool
mayBeDirective
=
line
[
lineStart
]
==
'#'
;
if
(
mayBeDirective
&&
reg_match
(
line
,
match
,
pre_section
))
{
std
::
string
type
=
trim
(
reg_str
(
line
,
match
,
1
));
if
(
type
==
"server"
)
selType
=
MT_Server
;
else
if
(
type
==
"server-side"
)
selType
=
MT_Server_Side
;
else
if
(
type
==
"client-side"
)
selType
=
MT_Client_Side
;
else
if
(
type
==
"client"
)
selType
=
MT_Client_Side
;
else
if
(
type
==
"shadow"
)
selType
=
MT_Shadow
;
else
if
(
type
==
"gui"
)
selType
=
MT_GUI
;
else
if
(
type
==
"game"
)
selType
=
MT_Game
;
else
if
(
type
==
"menu"
)
selType
=
MT_Menu
;
else
if
(
type
==
"all"
)
selType
=
curType
;
else
if
(
type
==
"disable menu"
)
{
if
(
curType
==
MT_Menu
)
break
;
}
else
selType
=
MT_Client_Side
;
output
+=
"
\n
"
;
continue
;
}
else
{
switch
(
selType
)
{
case
MT_Server
:
case
MT_Shadow
:
case
MT_GUI
:
case
MT_Menu
:
if
(
selType
!=
curType
)
{
output
+=
"
\n
"
;
continue
;
}
break
;
case
MT_Game
:
if
(
curType
!=
MT_GUI
&&
curType
!=
MT_Server
&&
curType
!=
MT_Shadow
)
{
output
+=
"
\n
"
;
continue
;
}
break
;
case
MT_Client_Side
:
if
(
curType
!=
MT_Menu
&&
curType
!=
MT_GUI
)
{
output
+=
"
\n
"
;
continue
;
}
break
;
case
MT_Server_Side
:
if
(
curType
!=
MT_Shadow
&&
curType
!=
MT_Server
)
{
output
+=
"
\n
"
;
continue
;
}
break
;
}
}
if
(
line
[
lineStart
]
==
'f'
&&
reg_match
(
line
,
match
,
pre_import_from
))
{
std
::
string
mod
=
reg_str
(
line
,
match
,
1
);
std
::
string
def
=
reg_str
(
line
,
match
,
2
);
fl
.
imports
.
insert
(
ImportSpec
(
mod
,
def
));
output
+=
"
\n
"
;
continue
;
}
if
(
line
[
lineStart
]
==
'i'
&&
reg_match
(
line
,
match
,
pre_import_all
))
{
std
::
string
mod
=
reg_str
(
line
,
match
,
1
);
if
(
mod
.
find
(
","
)
!=
std
::
string
::
npos
)
{
std
::
vector
<
std
::
string
>
modules
;
split
(
mod
,
modules
,
","
,
true
);
foreach
(
m
,
modules
)
fl
.
imports
.
insert
(
ImportSpec
(
*
m
,
"*"
));
}
else
{
fl
.
imports
.
insert
(
ImportSpec
(
mod
,
"*"
));
}
output
+=
"
\n
"
;
continue
;
}
if
(
line
[
lineStart
]
==
'e'
&&
reg_match
(
line
,
match
,
pre_export
))
{
std
::
string
sym
=
reg_str
(
line
,
match
,
1
);
auto
*
lst
=
&
fl
.
exports
;
if
(
sym
.
compare
(
0
,
5
,
"from "
)
==
0
)
{
sym
=
sym
.
substr
(
5
);
lst
=
&
fl
.
exportsFrom
;
}
if
(
sym
.
find
(
","
)
!=
std
::
string
::
npos
)
{
std
::
vector
<
std
::
string
>
symbols
;
split
(
sym
,
symbols
,
","
,
true
);
foreach
(
s
,
symbols
)
lst
->
insert
(
*
s
);
}
else
{
lst
->
insert
(
sym
);
}
output
+=
"
\n
"
;
continue
;
}
if
(
mayBeDirective
&&
reg_match
(
line
,
match
,
pre_priority
))
{
std
::
string
type
=
reg_str
(
line
,
match
,
1
);
std
::
string
prior
=
reg_str
(
line
,
match
,
2
);
int
priority
=
toNumber
<
int
>
(
prior
);
if
(
type
==
"init"
)
{
fl
.
priority_init
=
priority
;
}
else
if
(
type
==
"render"
)
{
fl
.
priority_render
=
priority
;
}
else
if
(
type
==
"draw"
)
{
fl
.
priority_draw
=
priority
;
}
else
if
(
type
==
"sync"
)
{
fl
.
priority_sync
=
priority
;
}
else
{
error
(
"ERROR: In File %s:
\n
Invalid priority directive '%s'."
,
filename
.
c_str
(),
type
.
c_str
());
}
output
+=
"
\n
"
;
}
else
if
(
mayBeDirective
&&
reg_match
(
line
,
match
,
pre_include
))
{
std
::
string
filePart
=
reg_str
(
line
,
match
,
1
);
//Check include relative to current file first
std
::
string
includeFile
=
getAbsolutePath
(
devices
.
mods
.
resolve
(
filePart
,
getDirname
(
filename
)));
//Load the included file contents
if
(
!
fileExists
(
includeFile
))
{
//Then check include paths
bool
exists
=
false
;
foreach
(
it
,
man
->
includePaths
)
{
includeFile
=
getAbsolutePath
(
devices
.
mods
.
resolve
(
filePart
,
*
it
));
if
(
fileExists
(
includeFile
))
{
exists
=
true
;
break
;
}
}
if
(
!
exists
)
{
error
(
"ERROR: In File %s:
\n
Could not find file '%s' to include."
,
filename
.
c_str
(),
filePart
.
c_str
());
foreach
(
it
,
man
->
includePaths
)
{
includeFile
=
getAbsolutePath
(
devices
.
mods
.
resolve
(
filePart
,
*
it
));
error
(
" Tried %s"
,
includeFile
.
c_str
());
}
output
+=
"
\n
"
;
continue
;
}
}
//Don't include the same file multiple times
if
(
fl
.
includes
.
find
(
includeFile
)
!=
fl
.
includes
.
end
())
{
output
+=
"
\n
"
;
continue
;
}
if
(
!
cacheFiles
)
{
std
::
string
path
=
getAbsolutePath
(
includeFile
);
File
&
incl
=
*
new
File
();
parseFile
(
man
,
incl
,
includeFile
,
false
);
fl
.
includes
[
path
]
=
&
incl
;
}
else
{
man
->
load
(
""
,
includeFile
);
//Update and flatten include file tree
auto
it
=
man
->
files
.
find
(
includeFile
);
if
(
it
!=
man
->
files
.
end
())
{
fl
.
includes
[
it
->
first
]
=
it
->
second
;
foreach
(
inc
,
it
->
second
->
includes
)
fl
.
includes
[
inc
->
first
]
=
inc
->
second
;
}
}
output
.
append
(
1
,
'\n'
);
}
else
{
output
+=
line
;
output
.
append
(
1
,
'\n'
);
}
}
}
void
Manager::load
(
const
std
::
string
&
modulename
,
const
std
::
string
&
filename
)
{
std
::
string
path
=
getAbsolutePath
(
filename
);
auto
ex
=
files
.
find
(
path
);
if
(
ex
!=
files
.
end
())
{
//If a file was included before, give it its
//proper module name afterwards
if
(
ex
->
second
->
module
.
empty
())
ex
->
second
->
module
=
modulename
;
return
;
}
File
&
fl
=
*
new
File
();
fl
.
module
=
modulename
;
parseFile
(
this
,
fl
,
filename
,
true
);
files
[
path
]
=
&
fl
;
}
void
importSymbol
(
Module
&
mod
,
Module
&
otherMod
,
const
std
::
string
&
symbol
)
{
auto
*
self
=
(
asCModule
*
)
mod
.
module
;
auto
*
other
=
(
asCModule
*
)
otherMod
.
module
;
//Check if it's a type
auto
*
type
=
other
->
GetType
(
symbol
.
c_str
(),
other
->
defaultNamespace
);
if
(
type
)
{
self
->
ImportType
(
type
);
return
;
}
//Check if it's a global
int
glob
=
other
->
GetGlobalVarIndexByName
(
symbol
.
c_str
());
if
(
glob
!=
asNO_GLOBAL_VAR
)
{
self
->
ImportGlobalVariable
(
other
,
(
asUINT
)
glob
);
return
;
}
glob
=
other
->
importedGlobals
.
GetFirstIndex
(
self
->
defaultNamespace
,
symbol
.
c_str
());
if
(
glob
!=
-1
)
{
mod
.
globalExports
.
push_back
(
other
->
scriptGlobals
.
GetSize
()
+
(
unsigned
)
glob
);
return
;
}
//Check if it's a function
bool
foundFunction
=
false
;
{
const
asCArray
<
unsigned
int
>&
idxs
=
other
->
globalFunctions
.
GetIndexes
(
other
->
defaultNamespace
,
symbol
.
c_str
());
for
(
unsigned
i
=
0
;
i
<
idxs
.
GetLength
();
++
i
)
{
asCScriptFunction
*
func
=
other
->
globalFunctions
.
Get
(
idxs
[
i
]);
if
(
func
)
{
self
->
ImportGlobalFunction
(
func
);
foundFunction
=
true
;
}
}
}
{
const
asCArray
<
unsigned
int
>&
idxs
=
other
->
importedGlobalFunctions
.
GetIndexes
(
other
->
defaultNamespace
,
symbol
.
c_str
());
for
(
unsigned
i
=
0
;
i
<
idxs
.
GetLength
();
++
i
)
{
asCScriptFunction
*
func
=
other
->
importedGlobalFunctions
.
Get
(
idxs
[
i
]);
if
(
func
)
{
self
->
ImportGlobalFunction
(
func
);
foundFunction
=
true
;
}
}
}
if
(
!
foundFunction
)
{
//Check if it's a global accessor
auto
*
getAcc
=
(
asCScriptFunction
*
)
other
->
GetFunctionByName
((
std
::
string
(
"get_"
)
+
symbol
).
c_str
());
auto
*
setAcc
=
(
asCScriptFunction
*
)
other
->
GetFunctionByName
((
std
::
string
(
"set_"
)
+
symbol
).
c_str
());
if
(
getAcc
||
setAcc
)
{
if
(
getAcc
)
self
->
ImportGlobalFunction
(
getAcc
);
if
(
setAcc
)
self
->
ImportGlobalFunction
(
setAcc
);
}
else
{
error
(
"ERROR: %s: Cannot find '%s' to import from '%s'."
,
mod
.
name
.
c_str
(),
symbol
.
c_str
(),
otherMod
.
name
.
c_str
());
}
}
}
void
buildModule
(
Manager
*
man
,
File
&
fl
,
Module
&
mod
)
{
//Create the module in the script engine
mod
.
manager
=
man
;
mod
.
file
=
&
fl
;
mod
.
module
=
man
->
engine
->
GetModule
(
mod
.
name
.
c_str
(),
asGM_ALWAYS_CREATE
);
//Add the script sections and imports
mod
.
module
->
AddScriptSection
(
fl
.
path
.
c_str
(),
fl
.
contents
.
c_str
(),
fl
.
contents
.
size
());
mod
.
imports
=
fl
.
imports
;
mod
.
exports
=
fl
.
exports
;
mod
.
exportsFrom
=
fl
.
exportsFrom
;
foreach
(
inc
,
fl
.
includes
)
{
mod
.
module
->
AddScriptSection
(
inc
->
second
->
path
.
c_str
(),
inc
->
second
->
contents
.
c_str
(),
inc
->
second
->
contents
.
size
());
foreach
(
s
,
inc
->
second
->
imports
)
mod
.
imports
.
insert
(
*
s
);
foreach
(
s
,
inc
->
second
->
exports
)
mod
.
exports
.
insert
(
*
s
);
foreach
(
s
,
inc
->
second
->
exportsFrom
)
mod
.
exportsFrom
.
insert
(
*
s
);
fl
.
priority_init
=
std
::
max
(
fl
.
priority_init
,
inc
->
second
->
priority_init
);
fl
.
priority_render
=
std
::
max
(
fl
.
priority_render
,
inc
->
second
->
priority_render
);
fl
.
priority_draw
=
std
::
max
(
fl
.
priority_draw
,
inc
->
second
->
priority_draw
);
fl
.
priority_tick
=
std
::
max
(
fl
.
priority_init
,
inc
->
second
->
priority_tick
);
fl
.
priority_sync
=
std
::
max
(
fl
.
priority_init
,
inc
->
second
->
priority_sync
);
}
}
bool
compileModule
(
Manager
*
man
,
Module
&
mod
)
{
if
(
mod
.
compiled
)
return
true
;
mod
.
compiled
=
true
;
mod
.
compiling
=
true
;
//Prepare all dependencies
bool
foundImports
=
true
;
foreach
(
it
,
mod
.
imports
)
{
auto
f
=
man
->
modules
.
find
(
it
->
first
);
if
(
f
==
man
->
modules
.
end
())
{
error
(
"ERROR: %s: Cannot find module '%s' to import from."
,
mod
.
name
.
c_str
(),
it
->
first
.
c_str
());
continue
;
}
Module
&
impMod
=
*
f
->
second
;
mod
.
dependencies
.
insert
(
&
impMod
);
//Build dependent module
if
(
!
impMod
.
compiled
)
{
impMod
.
compiledBy
=
mod
.
name
;
compileModule
(
man
,
impMod
);
}
if
(
impMod
.
module
==
nullptr
)
{
//Silently fail here, we already reported errors before
foundImports
=
false
;
continue
;
}
//Inject imports
auto
*
self
=
(
asCModule
*
)
mod
.
module
;
auto
*
other
=
(
asCModule
*
)
impMod
.
module
;
//Check cyclic imports
if
(
impMod
.
compiling
)
{
error
(
"ERROR: %s: Cannot import from module '%s', circular import."
,
mod
.
name
.
c_str
(),
it
->
first
.
c_str
());
Module
*
cur
=
&
mod
;
std
::
string
prefix
;
while
(
cur
)
{
error
(
"%s^-- %s"
,
prefix
.
c_str
(),
cur
->
name
.
c_str
());
prefix
+=
" "
;
if
(
!
cur
->
compiledBy
.
empty
())
{
auto
it
=
man
->
modules
.
find
(
cur
->
compiledBy
);
if
(
it
!=
man
->
modules
.
end
())
{
cur
=
it
->
second
;
}
else
{
cur
=
nullptr
;
error
(
"??? %s
\n
"
,
cur
->
compiledBy
.
c_str
());
}
}
else
cur
=
nullptr
;
}
continue
;
}
if
(
it
->
second
==
"*"
)
{
//Check if the module defined any exports
if
(
!
impMod
.
exports
.
empty
())
{
foreach
(
tp
,
impMod
.
typeExports
)
self
->
ImportType
(
*
tp
);
foreach
(
fun
,
impMod
.
funcExports
)
self
->
ImportGlobalFunction
(
*
fun
);
foreach
(
var
,
impMod
.
globalExports
)
self
->
ImportGlobalVariable
(
impMod
.
module
,
*
var
);
}
else
{
//Import types
for
(
unsigned
i
=
0
,
cnt
=
other
->
classTypes
.
GetLength
();
i
<
cnt
;
++
i
)
self
->
ImportType
(
other
->
classTypes
[
i
]);
for
(
unsigned
i
=
0
,
cnt
=
other
->
enumTypes
.
GetLength
();
i
<
cnt
;
++
i
)
self
->
ImportType
(
other
->
enumTypes
[
i
]);
for
(
unsigned
i
=
0
,
cnt
=
other
->
typeDefs
.
GetLength
();
i
<
cnt
;
++
i
)
self
->
ImportType
(
other
->
typeDefs
[
i
]);
for
(
unsigned
i
=
0
,
cnt
=
other
->
funcDefs
.
GetLength
();
i
<
cnt
;
++
i
)
self
->
ImportType
(
other
->
funcDefs
[
i
]);
//This is recursive: also import things that were imported
for
(
unsigned
i
=
0
,
cnt
=
other
->
importedClassTypes
.
GetLength
();
i
<
cnt
;
++
i
)
self
->
ImportType
(
other
->
importedClassTypes
[
i
]);
for
(
unsigned
i
=
0
,
cnt
=
other
->
importedEnumTypes
.
GetLength
();
i
<
cnt
;
++
i
)
self
->
ImportType
(
other
->
importedEnumTypes
[
i
]);
for
(
unsigned
i
=
0
,
cnt
=
other
->
importedTypeDefs
.
GetLength
();
i
<
cnt
;
++
i
)
self
->
ImportType
(
other
->
importedTypeDefs
[
i
]);
for
(
unsigned
i
=
0
,
cnt
=
other
->
importedFuncDefs
.
GetLength
();
i
<
cnt
;
++
i
)
self
->
ImportType
(
other
->
importedFuncDefs
[
i
]);
//Import global functions
for
(
unsigned
i
=
0
,
cnt
=
other
->
GetFunctionCount
();
i
<
cnt
;
++
i
)
{
auto
*
func
=
(
asCScriptFunction
*
)
other
->
GetFunctionByIndex
(
i
);
//Don't star-import any callback global functions
bool
isCallback
=
false
;
for
(
int
i
=
0
;
i
<
SC_COUNT
;
++
i
)
{
if
(
func
==
impMod
.
callbacks
[
i
])
{
isCallback
=
true
;
break
;
}
}
if
(
isCallback
)
continue
;
//Inject into the new module
self
->
ImportGlobalFunction
(
func
);
}
for
(
unsigned
i
=
0
,
cnt
=
other
->
importedGlobalFunctions
.
GetSize
();
i
<
cnt
;
++
i
)
{
auto
*
func
=
(
asCScriptFunction
*
)
other
->
importedGlobalFunctions
.
Get
(
i
);
self
->
ImportGlobalFunction
(
func
);
}
//Import global values
for
(
unsigned
i
=
0
,
cnt
=
other
->
scriptGlobals
.
GetSize
();
i
<
cnt
;
++
i
)
self
->
ImportGlobalVariable
(
other
,
i
);
for
(
unsigned
i
=
0
,
cnt
=
other
->
importedGlobals
.
GetSize
();
i
<
cnt
;
++
i
)
self
->
ImportGlobalVariable
(
other
,
i
+
other
->
scriptGlobals
.
GetSize
());
}
}
else
{
std
::
vector
<
std
::
string
>
declarations
;
split
(
it
->
second
,
declarations
,
','
,
true
);
foreach
(
imp
,
declarations
)
importSymbol
(
mod
,
impMod
,
*
imp
);
}
}
//Build the module
mod
.
compiling
=
false
;
int
code
;
if
(
foundImports
)
{
code
=
mod
.
module
->
Build
();
if
(
code
!=
asSUCCESS
)
{
error
(
"ERROR: Failed to build module '%s' in manager '%s' (code %d)."
,
mod
.
name
.
c_str
(),
manager_names
[
getManagerType
(
man
)],
code
);
mod
.
module
=
nullptr
;
}
}
else
{
mod
.
module
=
nullptr
;
}
//Cache exports
if
(
mod
.
module
)
{
for
(
int
i
=
0
;
i
<
SC_COUNT
;
++
i
)
mod
.
callbacks
[
i
]
=
mod
.
getFunction
(
callback_decl
[
i
]);
foreach
(
exp
,
mod
.
exports
)
{
auto
*
self
=
(
asCModule
*
)
mod
.
module
;
const
std
::
string
&
symbol
=
*
exp
;
//Check if it's a type
auto
*
type
=
self
->
GetType
(
symbol
.
c_str
(),
self
->
defaultNamespace
);
if
(
type
)
{
mod
.
typeExports
.
push_back
(
type
);
continue
;
}
//Check if it's a global
int
glob
=
self
->
GetGlobalVarIndexByName
(
symbol
.
c_str
());
if
(
glob
!=
asNO_GLOBAL_VAR
)
{
mod
.
globalExports
.
push_back
((
unsigned
)
glob
);
continue
;
}
glob
=
self
->
importedGlobals
.
GetFirstIndex
(
self
->
defaultNamespace
,
symbol
.
c_str
());
if
(
glob
!=
-1
)
{
mod
.
globalExports
.
push_back
(
self
->
scriptGlobals
.
GetSize
()
+
(
unsigned
)
glob
);
continue
;
}
//Check if it's a function
bool
foundExports
=
false
;
{
const
asCArray
<
unsigned
int
>&
idxs
=
self
->
globalFunctions
.
GetIndexes
(
self
->
defaultNamespace
,
symbol
.
c_str
());
for
(
unsigned
i
=
0
;
i
<
idxs
.
GetLength
();
++
i
)
{
asCScriptFunction
*
func
=
self
->
globalFunctions
.
Get
(
idxs
[
i
]);
if
(
func
)
{
mod
.
funcExports
.
push_back
(
func
);
foundExports
=
true
;
}
}
}
{
const
asCArray
<
unsigned
int
>&
idxs
=
self
->
globalFunctions
.
GetIndexes
(
self
->
defaultNamespace
,
symbol
.
c_str
());
for
(
unsigned
i
=
0
;
i
<
idxs
.
GetLength
();
++
i
)
{
asCScriptFunction
*
func
=
self
->
globalFunctions
.
Get
(
idxs
[
i
]);
if
(
func
)
{
mod
.
funcExports
.
push_back
(
func
);
foundExports
=
true
;
}
}
}
if
(
!
foundExports
)
{
//Check if it's a global accessor
auto
*
getAcc
=
(
asCScriptFunction
*
)
self
->
GetFunctionByName
((
std
::
string
(
"get_"
)
+
symbol
).
c_str
());
auto
*
setAcc
=
(
asCScriptFunction
*
)
self
->
GetFunctionByName
((
std
::
string
(
"set_"
)
+
symbol
).
c_str
());
if
(
getAcc
||
setAcc
)
{
if
(
getAcc
)
mod
.
funcExports
.
push_back
(
getAcc
);
if
(
setAcc
)
mod
.
funcExports
.
push_back
(
setAcc
);
}
else
{
error
(
"ERROR: %s: Cannot find '%s' to export."
,
mod
.
name
.
c_str
(),
symbol
.
c_str
());
}
}
}
foreach
(
exp
,
mod
.
exportsFrom
)
{
auto
f
=
man
->
modules
.
find
(
*
exp
);
if
(
f
==
man
->
modules
.
end
())
{
error
(
"ERROR: %s: Cannot find module '%s' to export from."
,
mod
.
name
.
c_str
(),
exp
->
c_str
());
}
foreach
(
it
,
f
->
second
->
typeExports
)
mod
.
typeExports
.
push_back
(
*
it
);
foreach
(
it
,
f
->
second
->
funcExports
)
mod
.
funcExports
.
push_back
(
*
it
);
foreach
(
it
,
f
->
second
->
globalExports
)
mod
.
globalExports
.
push_back
(
*
it
);
}
}
else
{
for
(
int
i
=
0
;
i
<
SC_COUNT
;
++
i
)
mod
.
callbacks
[
i
]
=
nullptr
;
}
return
mod
.
module
!=
nullptr
;
}
void
bindImports
(
Manager
*
man
,
Module
&
mod
)
{
auto
*
tMod
=
mod
.
module
;
if
(
tMod
==
nullptr
)
return
;
unsigned
fcnt
=
tMod
->
GetImportedFunctionCount
();
for
(
unsigned
i
=
0
;
i
<
fcnt
;
++
i
)
{
const
char
*
decl
=
tMod
->
GetImportedFunctionDeclaration
(
i
);
const
char
*
sourcemod
=
tMod
->
GetImportedFunctionSourceModule
(
i
);
auto
it
=
man
->
modules
.
find
(
sourcemod
);
if
(
it
==
man
->
modules
.
end
())
{
error
(
"ERROR: '%s': Cannot import function '%s': module '%s' does not exist."
,
tMod
->
GetName
(),
decl
,
sourcemod
);
continue
;
}
auto
*
sMod
=
it
->
second
->
module
;
if
(
sMod
==
nullptr
)
continue
;
asIScriptFunction
*
func
=
sMod
->
GetFunctionByDecl
(
decl
);
if
(
func
==
0
)
{
error
(
"ERROR: '%s': Cannot import function '%s' from module '%s': function does not exist."
,
tMod
->
GetName
(),
decl
,
sourcemod
);
continue
;
}
tMod
->
BindImportedFunction
(
i
,
func
);
}
}
void
sortPriorities
(
Manager
*
man
)
{
man
->
priority_init
.
clear
();
man
->
priority_render
.
clear
();
man
->
priority_draw
.
clear
();
man
->
priority_tick
.
clear
();
man
->
priority_sync
.
clear
();
for
(
auto
it
=
man
->
modules
.
begin
();
it
!=
man
->
modules
.
end
();
++
it
)
{
Module
&
mod
=
*
it
->
second
;
File
&
fl
=
*
mod
.
file
;
if
(
mod
.
callbacks
[
SC_init
]
||
mod
.
callbacks
[
SC_preInit
]
||
mod
.
callbacks
[
SC_postInit
])
man
->
priority_init
.
insert
(
std
::
pair
<
int
,
Module
*>
(
-
fl
.
priority_init
,
&
mod
));
if
(
mod
.
callbacks
[
SC_render
])
man
->
priority_render
.
insert
(
std
::
pair
<
int
,
Module
*>
(
-
fl
.
priority_render
,
&
mod
));
if
(
mod
.
callbacks
[
SC_draw
])
man
->
priority_draw
.
insert
(
std
::
pair
<
int
,
Module
*>
(
-
fl
.
priority_draw
,
&
mod
));
if
(
mod
.
callbacks
[
SC_tick
])
man
->
priority_tick
.
insert
(
std
::
pair
<
int
,
Module
*>
(
-
fl
.
priority_tick
,
&
mod
));
if
(
mod
.
callbacks
[
SC_sync_initial
])
man
->
priority_sync
.
insert
(
std
::
pair
<
int
,
Module
*>
(
-
fl
.
priority_sync
,
&
mod
));
}
}
void
Manager::compile
(
const
std
::
string
&
cache_root
,
unsigned
cache_version
)
{
//Generate all the modules
for
(
auto
it
=
files
.
begin
();
it
!=
files
.
end
();
++
it
)
{
File
&
fl
=
*
it
->
second
;
//Skip over empty module names, they're included files
if
(
fl
.
path
.
empty
()
||
fl
.
module
.
empty
())
continue
;
//Find unused module name
std
::
string
modname
=
fl
.
module
;
int
i
=
2
;
while
(
modules
.
find
(
modname
)
!=
modules
.
end
())
{
modname
=
fl
.
module
+
toString
(
i
);
++
i
;
}
//Display a warning when we have duplicate module names
if
(
i
!=
2
)
{
warn
(
"WARNING: File '%s' has duplicate module name '%s'."
" Using temporary module name '%s' instead."
,
fl
.
path
.
c_str
(),
fl
.
module
.
c_str
(),
modname
.
c_str
());
}
//Create the module
Module
&
mod
=
*
new
Module
();
mod
.
name
=
modname
;
buildModule
(
this
,
fl
,
mod
);
modules
[
modname
]
=
&
mod
;
}
//Compile the modules
for
(
auto
it
=
modules
.
begin
();
it
!=
modules
.
end
();
++
it
)
{
Module
&
mod
=
*
it
->
second
;
//Build the module dependency tree
compileModule
(
this
,
mod
);
}
//Bind all imported functions
for
(
auto
mod
=
modules
.
begin
();
mod
!=
modules
.
end
();
++
mod
)
{
bindImports
(
this
,
*
mod
->
second
);
}
//Erase modules that didn't compile succesfully
for
(
auto
it
=
modules
.
begin
();
it
!=
modules
.
end
();)
{
if
(
it
->
second
->
module
==
nullptr
)
it
=
modules
.
erase
(
it
);
else
++
it
;
}
//Sort by priority
sortPriorities
(
this
);
}
void
reloadModule
(
Manager
*
man
,
Module
&
curMod
)
{
//Load the file
std
::
string
filename
=
curMod
.
file
->
path
;
std
::
string
module
=
curMod
.
name
;
File
&
fl
=
*
new
File
();
fl
.
module
=
module
+
"__reload"
;
parseFile
(
man
,
fl
,
filename
,
false
);
//Build the module
Module
&
mod
=
*
new
Module
();
mod
.
name
=
module
+
"__reload"
;
buildModule
(
man
,
fl
,
mod
);
print
(
"Reloading module %s"
,
module
.
c_str
());
if
(
!
compileModule
(
man
,
mod
))
{
man
->
engine
->
DiscardModule
(
mod
.
name
.
c_str
());
delete
&
fl
;
delete
&
mod
;
console
.
show
();
return
;
}
net
::
Message
msg
;
//Call pre-reload
if
(
curMod
.
callbacks
[
SC_preReload
])
{
Call
cl
=
curMod
.
call
(
SC_preReload
);
cl
.
push
(
&
msg
);
cl
.
call
();
}
//Replace successful
Module
&
oldMod
=
*
man
->
modules
[
module
];
((
asCModule
*
)
oldMod
.
module
)
->
name
=
(
module
+
"__old"
).
c_str
();
((
asCModule
*
)
mod
.
module
)
->
name
=
module
.
c_str
();
fl
.
module
=
module
;
mod
.
name
=
module
;
//Overwrite in manager for ticks
man
->
files
[
fl
.
path
]
=
&
fl
;
man
->
modules
[
module
]
=
&
mod
;
//Bind imports for this module
bindImports
(
man
,
mod
);
//Call post-reload
if
(
mod
.
callbacks
[
SC_postReload
])
{
Call
cl
=
mod
.
call
(
SC_postReload
);
cl
.
push
(
&
msg
);
cl
.
call
();
}
else
if
(
mod
.
callbacks
[
SC_init
])
{
Call
cl
=
mod
.
call
(
SC_init
);
cl
.
call
();
}
//Special stuff
if
(
module
==
"input"
)
bindInputScripts
(
GS_Game
,
devices
.
scripts
.
client
);
}
void
renderRebuildSet
(
Manager
*
man
,
Module
&
curMod
,
std
::
unordered_set
<
Module
*>&
totalSet
)
{
//Queue up dependencies
for
(
auto
it
=
man
->
modules
.
begin
();
it
!=
man
->
modules
.
end
();
++
it
)
{
Module
*
mod
=
it
->
second
;
if
(
mod
->
dependencies
.
find
(
&
curMod
)
==
mod
->
dependencies
.
end
())
continue
;
totalSet
.
insert
(
mod
);
renderRebuildSet
(
man
,
*
mod
,
totalSet
);
}
}
void
visitRebuildSet
(
Manager
*
man
,
Module
&
mod
,
std
::
unordered_set
<
Module
*>&
totalSet
,
std
::
vector
<
Module
*>&
queue
)
{
if
(
totalSet
.
find
(
&
mod
)
==
totalSet
.
end
())
return
;
totalSet
.
erase
(
&
mod
);
foreach
(
it
,
mod
.
dependencies
)
visitRebuildSet
(
man
,
**
it
,
totalSet
,
queue
);
queue
.
push_back
(
&
mod
);
}
void
Manager::reload
(
const
std
::
string
&
module
)
{
//Clear cached stuff
clearCachedScripts
();
auto
it
=
modules
.
find
(
module
);
if
(
it
==
modules
.
end
())
return
;
Module
&
curMod
=
*
it
->
second
;
std
::
unordered_set
<
Module
*>
totalSet
;
totalSet
.
insert
(
&
curMod
);
renderRebuildSet
(
this
,
curMod
,
totalSet
);
std
::
vector
<
Module
*>
queue
;
while
(
!
totalSet
.
empty
())
{
Module
*
mod
=
*
totalSet
.
begin
();
visitRebuildSet
(
this
,
*
mod
,
totalSet
,
queue
);
}
foreach
(
it
,
queue
)
{
reloadModule
(
this
,
**
it
);
}
//Rebind all explicit imports
for
(
auto
mod
=
modules
.
begin
();
mod
!=
modules
.
end
();
++
mod
)
bindImports
(
this
,
*
mod
->
second
);
//Resort hook priorities
sortPriorities
(
this
);
}
void
Manager::init
()
{
foreach
(
it
,
priority_init
)
{
Module
&
mod
=
*
it
->
second
;
Call
cl
=
mod
.
call
(
SC_preInit
);
cl
.
call
();
}
foreach
(
it
,
priority_init
)
{
Module
&
mod
=
*
it
->
second
;
Call
cl
=
mod
.
call
(
SC_init
);
cl
.
call
();
}
foreach
(
it
,
priority_init
)
{
Module
&
mod
=
*
it
->
second
;
Call
cl
=
mod
.
call
(
SC_postInit
);
cl
.
call
();
}
}
void
Manager::deinit
()
{
foreach
(
it
,
modules
)
{
Module
&
mod
=
*
it
->
second
;
Call
cl
=
mod
.
call
(
SC_deinit
);
cl
.
call
();
}
}
void
Manager::tick
(
double
time
)
{
foreach
(
it
,
priority_tick
)
{
Module
&
mod
=
*
it
->
second
;
if
(
mod
.
tickFails
<
SCRIPT_MAX_TICK_FAILS
)
{
Call
cl
=
mod
.
call
(
SC_tick
);
cl
.
push
(
time
);
#ifdef PROFILE_SCRIPT_CALLBACKS
double
startTime
=
devices
.
driver
->
getAccurateTime
();
#endif
if
(
!
cl
.
call
())
{
mod
.
tickFails
++
;
if
(
mod
.
tickFails
==
SCRIPT_MAX_TICK_FAILS
)
error
(
"Module '%s' failed its tick too many times, disabling."
,
mod
.
module
->
GetName
());
}
#ifdef PROFILE_SCRIPT_CALLBACKS
double
endTime
=
devices
.
driver
->
getAccurateTime
();
mod
.
tickTime
=
endTime
-
startTime
;
#endif
}
}
}
void
Manager::draw
()
{
foreach
(
it
,
priority_draw
)
{
Module
&
mod
=
*
it
->
second
;
if
(
mod
.
drawFails
<
SCRIPT_MAX_DRAW_FAILS
)
{
Call
cl
=
mod
.
call
(
SC_draw
);
#ifdef PROFILE_SCRIPT_CALLBACKS
double
startTime
=
devices
.
driver
->
getAccurateTime
();
#endif
if
(
!
cl
.
call
())
{
mod
.
drawFails
++
;
if
(
mod
.
drawFails
==
SCRIPT_MAX_DRAW_FAILS
)
error
(
"Module '%s' failed its draw too many times, disabling."
,
mod
.
module
->
GetName
());
}
#ifdef PROFILE_SCRIPT_CALLBACKS
double
endTime
=
devices
.
driver
->
getAccurateTime
();
mod
.
drawTime
=
endTime
-
startTime
;
#endif
}
}
}
void
Manager::render
(
double
frameTime
)
{
foreach
(
it
,
priority_render
)
{
Module
&
mod
=
*
it
->
second
;
if
(
mod
.
renderFails
<
SCRIPT_MAX_RENDER_FAILS
)
{
Call
cl
=
mod
.
call
(
SC_render
);
cl
.
push
(
frameTime
);
#ifdef PROFILE_SCRIPT_CALLBACKS
double
startTime
=
devices
.
driver
->
getAccurateTime
();
#endif
if
(
!
cl
.
call
())
{
mod
.
renderFails
++
;
if
(
mod
.
renderFails
==
SCRIPT_MAX_RENDER_FAILS
)
error
(
"Module '%s' failed its render too many times, disabling."
,
mod
.
module
->
GetName
());
}
#ifdef PROFILE_SCRIPT_CALLBACKS
double
endTime
=
devices
.
driver
->
getAccurateTime
();
mod
.
renderTime
=
endTime
-
startTime
;
#endif
}
}
}
#ifdef PROFILE_SCRIPT_CALLBACKS
void
Manager::printProfile
()
{
foreach
(
it
,
modules
)
{
Module
&
mod
=
*
it
->
second
;
if
(
mod
.
tickTime
>
0
||
mod
.
drawTime
>
0
||
mod
.
renderTime
>
0
)
{
print
(
" %s -- tick: %.3gms -- draw: %.3gms -- render: %.3gms"
,
mod
.
name
.
c_str
(),
mod
.
tickTime
*
1000.0
,
mod
.
drawTime
*
1000.0
,
mod
.
renderTime
*
1000.0
);
}
}
}
#endif
void
Manager::preRender
(
double
frameTime
)
{
foreach
(
it
,
modules
)
{
Module
&
mod
=
*
it
->
second
;
if
(
mod
.
callbacks
[
SC_preRender
])
{
Call
cl
=
mod
.
call
(
SC_preRender
);
cl
.
push
(
frameTime
);
cl
.
call
();
}
}
}
void
Manager::save
(
SaveFile
&
file
)
{
foreach
(
it
,
modules
)
{
Module
&
mod
=
*
it
->
second
;
SaveMessage
msg
(
file
);
Call
cl
=
mod
.
call
(
SC_save
);
cl
.
push
(
&
msg
);
if
(
cl
.
call
()
&&
msg
.
size
()
>
0
)
{
file
<<
mod
.
name
;
char
*
pData
;
net
::
msize_t
size
;
msg
.
getAsPacket
(
pData
,
size
);
file
<<
size
;
file
.
write
(
pData
,
size
);
}
}
file
<<
""
;
}
void
Manager::load
(
SaveFile
&
file
)
{
while
(
true
)
{
std
::
string
moduleName
;
file
>>
moduleName
;
if
(
moduleName
.
empty
())
return
;
SaveMessage
msg
(
file
);
net
::
msize_t
size
=
file
;
if
(
size
>
0
)
{
char
*
buffer
=
(
char
*
)
malloc
(
size
);
file
.
read
(
buffer
,
size
);
msg
.
setPacket
(
buffer
,
size
);
free
(
buffer
);
}
auto
iter
=
modules
.
find
(
moduleName
);
if
(
iter
!=
modules
.
end
())
{
Module
&
mod
=
*
iter
->
second
;
if
(
mod
.
callbacks
[
SC_load
])
{
Call
cl
=
mod
.
call
(
SC_load
);
cl
.
push
(
&
msg
);
cl
.
call
();
}
}
}
}
void
Manager::saveIdentifiers
(
SaveFile
&
file
)
{
SaveMessage
msg
(
file
);
foreach
(
it
,
modules
)
{
Module
&
mod
=
*
it
->
second
;
Call
cl
=
mod
.
call
(
SC_saveIdentifiers
);
cl
.
push
(
&
msg
);
cl
.
call
();
}
}
void
Manager::stateChange
()
{
foreach
(
it
,
modules
)
{
Module
&
mod
=
*
it
->
second
;
Call
cl
=
mod
.
call
(
SC_stateChange
);
if
(
cl
.
valid
())
cl
.
call
();
}
}
#ifdef TRACE_GC_LOCK
void
Manager::markGCImpossible
()
{
if
(
gcPossible
==
nullptr
)
gcPossible
=
(
bool
*
)
malloc
(
sizeof
(
bool
));
*
gcPossible
=
false
;
}
void
Manager::markGCPossible
()
{
if
(
gcPossible
==
nullptr
)
gcPossible
=
(
bool
*
)
malloc
(
sizeof
(
bool
));
*
gcPossible
=
true
;
}
#endif
int
Manager::garbageCollect
(
bool
full
)
{
asUINT
size
,
newObjects
,
detected
;
engine
->
GetGCStatistics
(
&
size
,
0
,
&
detected
,
&
newObjects
,
0
);
if
(
full
/* || size > prevGCSize + (prevGCSize / 2)*/
)
{
engine
->
GarbageCollect
(
asGC_FULL_CYCLE
);
engine
->
GetGCStatistics
(
&
prevGCSize
,
0
,
0
,
0
,
0
);
return
2
;
}
else
if
(
newObjects
>
500
)
{
asUINT
runs
=
(
asUINT
)(
log
((
double
)(
newObjects
+
detected
+
2
))
/
log
(
2.0
))
*
10
;
for
(
asUINT
i
=
0
;
i
<
runs
;
++
i
)
if
(
engine
->
GarbageCollect
()
==
0
)
break
;
return
1
;
}
else
{
asUINT
runs
=
(
asUINT
)(
log
((
double
)(
newObjects
+
detected
+
2
))
/
log
(
2.0
))
*
50
;
for
(
asUINT
i
=
0
;
i
<
runs
;
++
i
)
if
(
engine
->
GarbageCollect
(
asGC_ONE_STEP
)
==
0
)
break
;
return
0
;
}
}
Call
Manager::call
(
int
funcID
)
{
if
(
funcID
<=
0
)
return
Call
();
return
call
(
engine
->
GetFunctionById
(
funcID
));
}
Call
Manager::call
(
asIScriptFunction
*
func
)
{
if
(
func
==
0
)
return
Call
();
asIScriptContext
*
ctx
=
asGetActiveContext
();
bool
nested
;
if
(
ctx
&&
ctx
->
GetEngine
()
==
engine
)
{
nested
=
true
;
}
else
{
ctx
=
fetchContext
(
engine
);
nested
=
ctx
->
GetState
()
==
asEXECUTION_ACTIVE
;
}
if
(
nested
)
{
ctx
->
PushState
();
int
status
=
ctx
->
Prepare
(
func
);
if
(
status
!=
asSUCCESS
)
{
ctx
->
PopState
();
ctx
=
0
;
}
}
else
{
ctx
->
Prepare
(
func
);
}
return
Call
(
this
,
ctx
,
nested
);
}
Call
Manager::call
(
const
char
*
module
,
const
char
*
decl
)
{
return
call
(
getFunction
(
module
,
decl
));
}
MultiCall
::
MultiCall
()
{}
MultiCall
Manager
::
call
(
ScriptCallback
cb
)
{
MultiCall
calls
;
for
(
auto
it
=
modules
.
begin
(),
end
=
modules
.
end
();
it
!=
end
;
++
it
)
{
Module
&
mod
=
*
it
->
second
;
if
(
mod
.
callbacks
[
cb
]
!=
nullptr
)
calls
.
calls
.
push_back
(
mod
.
call
(
cb
));
}
return
calls
;
}
Call
Module
::
call
(
ScriptCallback
cb
)
{
asIScriptFunction
*
func
=
callbacks
[
cb
];
if
(
func
==
nullptr
)
return
Call
();
asIScriptEngine
*
engine
=
module
->
GetEngine
();
auto
*
ctx
=
fetchContext
(
engine
);
ctx
->
Prepare
(
func
);
return
Call
(
manager
,
ctx
);
}
void
Manager
::
clear
()
{
for
(
auto
it
=
modules
.
begin
(),
end
=
modules
.
end
();
it
!=
end
;
++
it
)
{
if
(
engine
->
DiscardModule
(
it
->
first
.
c_str
())
!=
asSUCCESS
)
error
(
"Failed to discard module '%s'"
,
it
->
first
.
c_str
());
}
foreach
(
it
,
modules
)
delete
it
->
second
;
modules
.
clear
();
foreach
(
it
,
files
)
delete
it
->
second
;
files
.
clear
();
}
Manager
::~
Manager
()
{
clear
();
delete
engine
->
GetJITCompiler
();
}
void
Manager
::
clearScriptThreads
()
{
clearScripts
=
true
;
pauseScripts
=
false
;
while
(
scriptThreadsExistent
!=
0
)
threads
::
idle
();
clearScripts
=
false
;
}
void
Manager
::
scriptThreadCreate
()
{
++
scriptThreadsExistent
;
}
void
Manager
::
scriptThreadDestroy
()
{
--
scriptThreadsExistent
;
}
void
Manager
::
pauseScriptThreads
()
{
pauseScripts
=
true
;
while
(
scriptThreadsActive
!=
0
)
threads
::
idle
();
}
void
Manager
::
resumeScriptThreads
()
{
pauseScripts
=
false
;
}
bool
Manager
::
scriptThreadStart
()
{
retry
:
while
(
pauseScripts
&&
!
clearScripts
)
threads
::
idle
();
if
(
clearScripts
)
return
false
;
++
scriptThreadsActive
;
if
(
pauseScripts
)
{
--
scriptThreadsActive
;
goto
retry
;
}
return
true
;
}
void
Manager
::
scriptThreadEnd
()
{
--
scriptThreadsActive
;
}
Call
::
Call
()
:
manager
(
0
),
ctx
(
0
),
arg
(
0
),
nested
(
false
)
{
}
Call
::
Call
(
Manager
*
man
,
asIScriptContext
*
Ctx
,
bool
Nested
)
:
manager
(
man
),
ctx
(
Ctx
),
arg
(
0
),
nested
(
Nested
)
{
}
Call
::~
Call
()
{
if
(
ctx
)
{
if
(
nested
)
ctx
->
PopState
();
}
}
void
Call
::
setObject
(
void
*
obj
)
{
if
(
ctx
)
ctx
->
SetObject
((
void
*
)
obj
);
}
void
*
Call
::
getReturnObject
()
{
if
(
!
ctx
)
return
0
;
return
ctx
->
GetReturnObject
();
}
void
Call
::
push
(
int
value
)
{
if
(
ctx
)
ctx
->
SetArgDWord
(
arg
++
,
value
);
}
void
Call
::
push
(
long
long
value
)
{
if
(
ctx
)
ctx
->
SetArgQWord
(
arg
++
,
value
);
}
void
Call
::
push
(
unsigned
value
)
{
if
(
ctx
)
ctx
->
SetArgDWord
(
arg
++
,
value
);
}
void
Call
::
push
(
float
value
)
{
if
(
ctx
)
ctx
->
SetArgFloat
(
arg
++
,
value
);
}
void
Call
::
push
(
double
value
)
{
if
(
ctx
)
ctx
->
SetArgDouble
(
arg
++
,
value
);
}
void
Call
::
push
(
bool
value
)
{
if
(
ctx
)
ctx
->
SetArgByte
(
arg
++
,
value
);
}
bool
Call
::
call
()
{
if
(
!
ctx
)
{
return
false
;
}
status
=
ctx
->
Execute
();
return
status
==
asSUCCESS
;
}
bool
Call
::
call
(
unsigned
&
value
)
{
if
(
!
call
())
{
value
=
0
;
return
false
;
}
value
=
ctx
->
GetReturnDWord
();
return
true
;
}
bool
Call
::
call
(
int
&
value
)
{
if
(
!
call
())
{
value
=
0
;
return
false
;
}
value
=
ctx
->
GetReturnDWord
();
return
true
;
}
bool
Call
::
call
(
long
long
&
value
)
{
if
(
!
call
())
{
value
=
0
;
return
false
;
}
value
=
ctx
->
GetReturnQWord
();
return
true
;
}
bool
Call
::
call
(
float
&
value
)
{
if
(
!
call
())
{
value
=
0
;
return
false
;
}
value
=
ctx
->
GetReturnFloat
();
return
true
;
}
bool
Call
::
call
(
double
&
value
)
{
if
(
!
call
())
{
value
=
0
;
return
false
;
}
value
=
ctx
->
GetReturnDouble
();
return
true
;
}
bool
Call
::
call
(
bool
&
value
)
{
if
(
!
call
())
{
value
=
0
;
return
false
;
}
value
=
ctx
->
GetReturnByte
()
!=
0
;
return
true
;
}
void
MultiCall
::
setObject
(
void
*
obj
)
{
foreach
(
call
,
calls
)
call
->
setObject
(
obj
);
}
void
MultiCall
::
push
(
int
value
)
{
foreach
(
call
,
calls
)
call
->
push
(
value
);
}
void
MultiCall
::
push
(
float
value
)
{
foreach
(
call
,
calls
)
call
->
push
(
value
);
}
void
MultiCall
::
push
(
double
value
)
{
foreach
(
call
,
calls
)
call
->
push
(
value
);
}
void
MultiCall
::
push
(
bool
value
)
{
foreach
(
call
,
calls
)
call
->
push
(
value
);
}
void
MultiCall
::
push
(
void
*
value
)
{
foreach
(
call
,
calls
)
call
->
push
(
value
);
}
void
MultiCall
::
call
()
{
foreach
(
call
,
calls
)
call
->
call
();
}
struct
ReadBytecode
:
public
asIBinaryStream
{
std
::
ifstream
&
stream
;
ReadBytecode
(
std
::
ifstream
&
is
)
:
stream
(
is
)
{
}
void
Read
(
void
*
ptr
,
asUINT
size
)
{
if
(
stream
.
eof
())
memset
(
ptr
,
0
,
size
);
else
stream
.
read
((
char
*
)
ptr
,
size
);
}
void
Write
(
const
void
*
ptr
,
asUINT
size
)
{
}
};
#ifndef DISABLE_SCRIPT_CACHE
static
bool
loadCached
(
Module
&
mod
,
const
File
&
fl
,
const
std
::
string
&
cache_file
,
unsigned
cache_version
)
{
//Make sure the file exists
if
(
!
fileExists
(
cache_file
))
return
false
;
//Make sure the cache is new enough
time_t
cache_time
=
getModifiedTime
(
cache_file
);
if
(
getModifiedTime
(
fl
.
path
)
>
cache_time
)
return
false
;
foreach
(
inc
,
fl
.
includes
)
if
(
getModifiedTime
(
inc
->
second
->
path
)
>
cache_time
)
return
false
;
//Open the file
std
::
ifstream
stream
(
cache_file
,
std
::
ios
::
in
|
std
::
ios
::
binary
);
//Read build version from cache
unsigned
file_version
;
stream
.
read
((
char
*
)
&
file_version
,
sizeof
(
unsigned
));
//Needs to be the same version to load the cache
if
(
cache_version
!=
file_version
)
{
stream
.
close
();
return
false
;
}
//Write the bytecode
printf
(
"Load %s from cache
\n
"
,
cache_file
.
c_str
());
ReadBytecode
read
(
stream
);
int
code
=
mod
.
module
->
LoadByteCode
(
&
read
);
if
(
code
!=
asSUCCESS
)
{
//Discard the module and recreate it, we're in
//an inconsistent state otherwise
asIScriptEngine
*
eng
=
mod
.
module
->
GetEngine
();
eng
->
DiscardModule
(
mod
.
module
->
GetName
());
mod
.
module
=
eng
->
GetModule
(
mod
.
name
.
c_str
(),
asGM_ALWAYS_CREATE
);
//Warn the user
warn
(
"WARNING: Failed to load bytecode cache for"
" module '%s' (code %d)."
,
mod
.
name
.
c_str
(),
code
);
stream
.
close
();
return
false
;
}
//Close the file
stream
.
close
();
return
true
;
}
struct
WriteBytecode
:
public
asIBinaryStream
{
std
::
ofstream
&
stream
;
WriteBytecode
(
std
::
ofstream
&
os
)
:
stream
(
os
)
{
}
void
Read
(
void
*
ptr
,
asUINT
size
)
{
}
void
Write
(
const
void
*
ptr
,
asUINT
size
)
{
stream
.
write
((
const
char
*
)
ptr
,
size
);
}
};
static
void
saveCached
(
Module
&
mod
,
const
std
::
string
&
cache_file
,
unsigned
cache_version
)
{
//Open the file
std
::
ofstream
stream
(
cache_file
,
std
::
ios
::
out
|
std
::
ios
::
binary
);
//Write build version to cache
stream
.
write
((
const
char
*
)
&
cache_version
,
sizeof
(
unsigned
));
//Write the bytecode
printf
(
"Save %s to cache
\n
"
,
cache_file
.
c_str
());
WriteBytecode
write
(
stream
);
int
code
=
mod
.
module
->
SaveByteCode
(
&
write
);
if
(
code
<
0
)
error
(
"Failed to save bytecode for '%s'"
,
mod
.
name
.
c_str
());
//Close the file
stream
.
close
();
}
#endif
};
File Metadata
Details
Attached
Mime Type
text/x-c++
Expires
Sun, Nov 15, 12:54 PM (1 d, 20 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
129835
Default Alt Text
manager.cpp (42 KB)
Attached To
Mode
R80 StarRuler2-Source
Attached
Detach File
Event Timeline