diff --git a/data/gui/ghost_replay_info_dialog.stkgui b/data/gui/ghost_replay_info_dialog.stkgui new file mode 100644 index 000000000..b4d2de9b5 --- /dev/null +++ b/data/gui/ghost_replay_info_dialog.stkgui @@ -0,0 +1,30 @@ + + +
+
+
+ + +
+ +
+
+
+ +
+ + + + + +
+
+
diff --git a/data/gui/ghost_replay_selection.stkgui b/data/gui/ghost_replay_selection.stkgui new file mode 100644 index 000000000..771910e3e --- /dev/null +++ b/data/gui/ghost_replay_selection.stkgui @@ -0,0 +1,15 @@ + + +
+ +
+ +
+ +
+ + + + +
+
diff --git a/data/gui/help3.stkgui b/data/gui/help3.stkgui index dfe2f9dfd..469b2bf99 100644 Binary files a/data/gui/help3.stkgui and b/data/gui/help3.stkgui differ diff --git a/data/gui/mode_ghost.png b/data/gui/mode_ghost.png new file mode 100644 index 000000000..d41d6ccd3 Binary files /dev/null and b/data/gui/mode_ghost.png differ diff --git a/data/gui/online/online_screen.stkgui b/data/gui/online/online_screen.stkgui deleted file mode 100644 index b8ee57373..000000000 --- a/data/gui/online/online_screen.stkgui +++ /dev/null @@ -1,63 +0,0 @@ - - -
-
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - -
diff --git a/data/gui/online/profile_achievements_tab.stkgui b/data/gui/online/profile_achievements_tab.stkgui index 7d519d661..dbf3256b5 100644 --- a/data/gui/online/profile_achievements_tab.stkgui +++ b/data/gui/online/profile_achievements_tab.stkgui @@ -9,6 +9,7 @@ + diff --git a/data/gui/online/profile_friends.stkgui b/data/gui/online/profile_friends.stkgui index e0e49473b..0e1213c61 100644 --- a/data/gui/online/profile_friends.stkgui +++ b/data/gui/online/profile_friends.stkgui @@ -9,6 +9,7 @@ + diff --git a/data/gui/online/profile_servers.stkgui b/data/gui/online/profile_servers.stkgui new file mode 100644 index 000000000..25ca99ffa --- /dev/null +++ b/data/gui/online/profile_servers.stkgui @@ -0,0 +1,61 @@ + + + + +
+
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
diff --git a/data/gui/online/profile_settings.stkgui b/data/gui/online/profile_settings.stkgui index d89d52c23..126519d9f 100644 --- a/data/gui/online/profile_settings.stkgui +++ b/data/gui/online/profile_settings.stkgui @@ -8,6 +8,7 @@ + + +
+ +
+ + + + Waiting... +
+
diff --git a/data/gui/track_info.stkgui b/data/gui/track_info.stkgui index 957bcf7f8..8e37a4cbf 100644 --- a/data/gui/track_info.stkgui +++ b/data/gui/track_info.stkgui @@ -3,9 +3,9 @@
-
+
- + @@ -49,8 +49,8 @@
- - + +
- +
+
+
- + diff --git a/data/skins/Forest.stkskin b/data/skins/Forest.stkskin index e21df8788..cd7514f44 100644 --- a/data/skins/Forest.stkskin +++ b/data/skins/Forest.stkskin @@ -81,6 +81,10 @@ when the border that intersect at this corner are enabled. left_border="128" right_border="13" top_border="13" bottom_border="13" preserve_h_aspect_ratios="true" hborder_out_portion="0" vborder_out_portion="0"/> + + diff --git a/data/skins/Ocean.stkskin b/data/skins/Ocean.stkskin index e8b75b13a..b251e5b37 100644 --- a/data/skins/Ocean.stkskin +++ b/data/skins/Ocean.stkskin @@ -80,6 +80,10 @@ when the border that intersect at this corner are enabled. left_border="128" right_border="13" top_border="13" bottom_border="13" preserve_h_aspect_ratios="true" hborder_out_portion="0" vborder_out_portion="0"/> + + diff --git a/data/skins/Peach.stkskin b/data/skins/Peach.stkskin index 0bed84a66..4b67f0f5b 100644 --- a/data/skins/Peach.stkskin +++ b/data/skins/Peach.stkskin @@ -80,6 +80,10 @@ when the border that intersect at this corner are enabled. left_border="128" right_border="13" top_border="13" bottom_border="13" preserve_h_aspect_ratios="true" hborder_out_portion="0" vborder_out_portion="0"/> + + diff --git a/data/skins/Ruby.stkskin b/data/skins/Ruby.stkskin index be8e6d327..76407f632 100644 --- a/data/skins/Ruby.stkskin +++ b/data/skins/Ruby.stkskin @@ -81,6 +81,10 @@ when the border that intersect at this corner are enabled. left_border="128" right_border="13" top_border="13" bottom_border="13" preserve_h_aspect_ratios="true" hborder_out_portion="0" vborder_out_portion="0"/> + + diff --git a/data/skins/forest/generic.png b/data/skins/forest/generic.png new file mode 100644 index 000000000..5352e53f0 Binary files /dev/null and b/data/skins/forest/generic.png differ diff --git a/data/skins/ocean/generic.png b/data/skins/ocean/generic.png new file mode 100644 index 000000000..34c791161 Binary files /dev/null and b/data/skins/ocean/generic.png differ diff --git a/data/skins/peach/generic.png b/data/skins/peach/generic.png new file mode 100644 index 000000000..57c34ec29 Binary files /dev/null and b/data/skins/peach/generic.png differ diff --git a/data/skins/ruby/generic.png b/data/skins/ruby/generic.png new file mode 100644 index 000000000..a4bcee9c6 Binary files /dev/null and b/data/skins/ruby/generic.png differ diff --git a/lib/angelscript/include/angelscript.h b/lib/angelscript/include/angelscript.h index 25a0d7402..98d33c516 100644 --- a/lib/angelscript/include/angelscript.h +++ b/lib/angelscript/include/angelscript.h @@ -58,8 +58,8 @@ BEGIN_AS_NAMESPACE // AngelScript version -#define ANGELSCRIPT_VERSION 23000 -#define ANGELSCRIPT_VERSION_STRING "2.30.0 WIP" +#define ANGELSCRIPT_VERSION 23002 +#define ANGELSCRIPT_VERSION_STRING "2.30.2" // Data types @@ -543,7 +543,7 @@ struct asSMessageInfo extern "C" { // Engine - AS_API asIScriptEngine *asCreateScriptEngine(asDWORD version); + AS_API asIScriptEngine *asCreateScriptEngine(asDWORD version = ANGELSCRIPT_VERSION); AS_API const char *asGetLibraryVersion(); AS_API const char *asGetLibraryOptions(); @@ -585,20 +585,20 @@ template asUINT asGetTypeTraits() { #if defined(_MSC_VER) || defined(_LIBCPP_TYPE_TRAITS) || (__GNUC__ >= 5) - // MSVC & XCode/Clang, and gnuc 5+ + // MSVC, XCode/Clang, and gnuc 5+ // C++11 compliant code bool hasConstructor = std::is_default_constructible::value && !std::is_trivially_default_constructible::value; bool hasDestructor = std::is_destructible::value && !std::is_trivially_destructible::value; bool hasAssignmentOperator = std::is_copy_assignable::value && !std::is_trivially_copy_assignable::value; bool hasCopyConstructor = std::is_copy_constructible::value && !std::is_trivially_copy_constructible::value; #elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) - // gnuc 4.8+ - // gnuc is using a mix of C++11 standard and pre-standard templates + // gnuc 4.8 is using a mix of C++11 standard and pre-standard templates bool hasConstructor = std::is_default_constructible::value && !std::has_trivial_default_constructor::value; bool hasDestructor = std::is_destructible::value && !std::is_trivially_destructible::value; bool hasAssignmentOperator = std::is_copy_assignable::value && !std::has_trivial_copy_assign::value; bool hasCopyConstructor = std::is_copy_constructible::value && !std::has_trivial_copy_constructor::value; #else + // All other compilers and versions are assumed to use non C++11 compliant code until proven otherwise // Not fully C++11 compliant. The has_trivial checks were used while the standard was still // being elaborated, but were then removed in favor of the above is_trivially checks // http://stackoverflow.com/questions/12702103/writing-code-that-works-when-has-trivial-destructor-is-defined-instead-of-is @@ -1556,8 +1556,8 @@ enum asEBCInstr asBC_POWdi = 197, asBC_POWi64 = 198, asBC_POWu64 = 199, - - asBC_MAXBYTECODE = 200, + asBC_Thiscall1 = 200, + asBC_MAXBYTECODE = 201, // Temporary tokens. Can't be output to the final program asBC_VarDecl = 251, @@ -1851,8 +1851,8 @@ const asSBCInfo asBCInfo[256] = asBCINFO(POWdi, wW_rW_rW_ARG, 0), asBCINFO(POWi64, wW_rW_rW_ARG, 0), asBCINFO(POWu64, wW_rW_rW_ARG, 0), + asBCINFO(Thiscall1, DW_ARG, -AS_PTR_SIZE-1), - asBCINFO_DUMMY(200), asBCINFO_DUMMY(201), asBCINFO_DUMMY(202), asBCINFO_DUMMY(203), diff --git a/lib/angelscript/projects/cmake/CMakeLists.txt b/lib/angelscript/projects/cmake/CMakeLists.txt index 9ee6fbf0c..e93971315 100644 --- a/lib/angelscript/projects/cmake/CMakeLists.txt +++ b/lib/angelscript/projects/cmake/CMakeLists.txt @@ -14,11 +14,15 @@ if(APPLE) option(BUILD_FRAMEWORK "Build Framework bundle for OSX" OFF) endif() -set(ANGELSCRIPT_VERSION_MAJOR 2) -set(ANGELSCRIPT_VERSION_MINOR 30) -set(ANGELSCRIPT_VERSION_PATCH 0) +file(READ ../../include/angelscript.h ANGELSCRIPT_H) +string(REGEX MATCH "#define ANGELSCRIPT_VERSION_STRING \"([0-9]*).([0-9]*).([0-9]*)" ANGELSCRIPT_VERSION_REGEX ${ANGELSCRIPT_H}) +set(ANGELSCRIPT_VERSION_MAJOR ${CMAKE_MATCH_1}) +set(ANGELSCRIPT_VERSION_MINOR ${CMAKE_MATCH_2}) +set(ANGELSCRIPT_VERSION_PATCH ${CMAKE_MATCH_3}) set(PROJECT_VERSION ${ANGELSCRIPT_VERSION_MAJOR}.${ANGELSCRIPT_VERSION_MINOR}.${ANGELSCRIPT_VERSION_PATCH}) +message(STATUS "Configuring angelscript ${PROJECT_VERSION}") + find_package(Threads) set(ANGELSCRIPT_HEADERS @@ -102,12 +106,13 @@ if(MSVC AND CMAKE_CL_64) endif() endif() -if(ANDROID) +if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "^arm") enable_language(ASM) if(CMAKE_ASM_COMPILER_WORKS) set(ANGELSCRIPT_SOURCE ${ANGELSCRIPT_SOURCE} ../../source/as_callfunc_arm.cpp ../../source/as_callfunc_arm_gcc.S) + set_property(SOURCE ../../source/as_callfunc_arm_gcc.S APPEND PROPERTY COMPILE_FLAGS " -Wa,-mimplicit-it=always") else() - message(FATAL ERROR "Android target requires a working assembler") + message(FATAL ERROR "ARM target requires a working assembler") endif() endif() @@ -156,4 +161,5 @@ endif() #set(RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/../../bin) +#add_subdirectory(../../../samples/game/projects/cmake/ ./game) diff --git a/lib/angelscript/source/as_builder.cpp b/lib/angelscript/source/as_builder.cpp index a7c630411..2ff191c3a 100644 --- a/lib/angelscript/source/as_builder.cpp +++ b/lib/angelscript/source/as_builder.cpp @@ -363,9 +363,35 @@ int asCBuilder::CompileGlobalVar(const char *sectionName, const char *code, int CompileGlobalVariables(); + // It is possible that the global variable initialization included anonymous functions that must be compiled too + for( asUINT n = 0; n < functions.GetLength(); n++ ) + { + asCCompiler compiler(engine); + asCScriptFunction *func = engine->scriptFunctions[functions[n]->funcId]; + int r = compiler.CompileFunction(this, functions[n]->script, func->parameterNames, functions[n]->node, func, 0); + if( r < 0 ) + break; + } + if( numWarnings > 0 && engine->ep.compilerWarnings == 2 ) WriteError(TXT_WARNINGS_TREATED_AS_ERROR, 0, 0); + // None of the functions should be added to the module if any error occurred, + // or it was requested that the functions wouldn't be added to the scope + if( numErrors > 0 ) + { + for( asUINT n = 0; n < functions.GetLength(); n++ ) + { + asCScriptFunction *func = engine->scriptFunctions[functions[n]->funcId]; + if( module->globalFunctions.GetIndex(func) >= 0 ) + { + module->globalFunctions.Erase(module->globalFunctions.GetIndex(func)); + module->scriptFunctions.RemoveValue(func); + func->ReleaseInternal(); + } + } + } + if( numErrors > 0 ) { // Remove the variable from the module, if it was registered @@ -549,22 +575,38 @@ int asCBuilder::CompileFunction(const char *sectionName, const char *code, int l funcDesc->paramNames = func->parameterNames; funcDesc->isExistingShared = false; - asCCompiler compiler(engine); - compiler.CompileFunction(this, functions[0]->script, func->parameterNames, functions[0]->node, func, 0); + // This must be done in a loop, as it is possible that additional functions get declared as lambda's in the code + for( asUINT n = 0; n < functions.GetLength(); n++ ) + { + asCCompiler compiler(engine); + asCScriptFunction *func = engine->scriptFunctions[functions[n]->funcId]; + int r = compiler.CompileFunction(this, functions[n]->script, func->parameterNames, functions[n]->node, func, 0); + if( r < 0 ) + break; + } if( numWarnings > 0 && engine->ep.compilerWarnings == 2 ) WriteError(TXT_WARNINGS_TREATED_AS_ERROR, 0, 0); + // None of the functions should be added to the module if any error occurred, + // or it was requested that the functions wouldn't be added to the scope + if( !(compileFlags & asCOMP_ADD_TO_MODULE) || numErrors > 0 ) + { + for( asUINT n = 0; n < functions.GetLength(); n++ ) + { + asCScriptFunction *func = engine->scriptFunctions[functions[n]->funcId]; + if( module->globalFunctions.GetIndex(func) >= 0 ) + { + module->globalFunctions.Erase(module->globalFunctions.GetIndex(func)); + module->scriptFunctions.RemoveValue(func); + func->ReleaseInternal(); + } + } + } + if( numErrors > 0 ) { - // If the function was added to the module then remove it again - if( compileFlags & asCOMP_ADD_TO_MODULE ) - { - module->globalFunctions.Erase(module->globalFunctions.GetIndex(func)); - module->scriptFunctions.RemoveValue(func); - func->ReleaseInternal(); - } - + // Release the function pointer that would otherwise be returned if no errors occured func->ReleaseInternal(); return asERROR; @@ -965,15 +1007,20 @@ int asCBuilder::VerifyProperty(asCDataType *dt, const char *decl, asCString &nam if( r < 0 ) return asINVALID_DECLARATION; - // Get data type and property name + // Get data type asCScriptNode *dataType = parser.GetScriptNode()->firstChild; - asCScriptNode *nameNode = dataType->next; + // Check if the property is declared 'by reference' + bool isReference = (dataType->next->tokenType == ttAmp); + + // Get the name of the property + asCScriptNode *nameNode = isReference ? dataType->next->next : dataType->next; // If an object property is registered, then use the // object's namespace, otherwise use the specified namespace type = CreateDataTypeFromNode(dataType, &source, dt ? dt->GetObjectType()->nameSpace : ns); name.Assign(&decl[nameNode->tokenPos], nameNode->tokenLength); + type.MakeReference(isReference); // Validate that the type really can be a registered property // We cannot use CanBeInstantiated, as it is allowed to register @@ -1458,9 +1505,9 @@ sMixinClass *asCBuilder::GetMixinClass(const char *name, asSNameSpace *ns) int asCBuilder::RegisterFuncDef(asCScriptNode *node, asCScriptCode *file, asSNameSpace *ns) { - // TODO: 2.30.0: redesign: Allow funcdefs to be explicitly declared as 'shared'. In this case - // an error should be given if any of the arguments/return type is not - // shared. + // TODO: redesign: Allow funcdefs to be explicitly declared as 'shared'. In this case + // an error should be given if any of the arguments/return type is not + // shared. // Find the name asASSERT( node->firstChild->nodeType == snDataType ); @@ -1516,6 +1563,7 @@ void asCBuilder::CompleteFuncDef(sFuncDef *funcDef) asCScriptFunction *func = module->funcDefs[funcDef->idx]; asASSERT( func ); + // TODO: It should be possible to declare funcdef as shared. In this case a compiler error will be given if any of the types it uses are not shared GetParsedFunctionDetails(funcDef->node, funcDef->script, 0, funcDef->name, func->returnType, func->parameterNames, func->parameterTypes, func->inOutFlags, defaultArgs, isConstMethod, isConstructor, isDestructor, isPrivate, isProtected, isOverride, isFinal, isShared, func->nameSpace); // There should not be any defaultArgs, but if there are any we need to delete them to avoid leaks @@ -1523,27 +1571,47 @@ void asCBuilder::CompleteFuncDef(sFuncDef *funcDef) if( defaultArgs[n] ) asDELETE(defaultArgs[n], asCString); - // TODO: Should we force the use of 'shared' for this check to be done? - // Check if there is another identical funcdef from another module and if so reuse that instead - for( asUINT n = 0; n < engine->funcDefs.GetLength(); n++ ) + // All funcdefs are shared, unless one of the parameter types or return type is not shared + isShared = true; + if( func->returnType.GetObjectType() && !func->returnType.GetObjectType()->IsShared() ) + isShared = false; + if( func->returnType.GetFuncDef() && !func->returnType.GetFuncDef()->IsShared() ) + isShared = false; + for( asUINT n = 0; isShared && n < func->parameterTypes.GetLength(); n++ ) { - asCScriptFunction *f2 = engine->funcDefs[n]; - if( f2 == 0 || func == f2 ) - continue; + if( func->parameterTypes[n].GetObjectType() && !func->parameterTypes[n].GetObjectType()->IsShared() ) + isShared = false; + if( func->parameterTypes[n].GetFuncDef() && !func->parameterTypes[n].GetFuncDef()->IsShared() ) + isShared = false; + } + func->isShared = isShared; - if( f2->name == func->name && - f2->nameSpace == func->nameSpace && - f2->IsSignatureExceptNameEqual(func) ) + // Check if there is another identical funcdef from another module and if so reuse that instead + if( func->isShared ) + { + for( asUINT n = 0; n < engine->funcDefs.GetLength(); n++ ) { - // Replace our funcdef for the existing one - funcDef->idx = f2->id; - module->funcDefs[module->funcDefs.IndexOf(func)] = f2; - f2->AddRefInternal(); + asCScriptFunction *f2 = engine->funcDefs[n]; + if( f2 == 0 || func == f2 ) + continue; - engine->funcDefs.RemoveValue(func); + if( !f2->isShared ) + continue; - func->ReleaseInternal(); - break; + if( f2->name == func->name && + f2->nameSpace == func->nameSpace && + f2->IsSignatureExceptNameEqual(func) ) + { + // Replace our funcdef for the existing one + funcDef->idx = f2->id; + module->funcDefs[module->funcDefs.IndexOf(func)] = f2; + f2->AddRefInternal(); + + engine->funcDefs.RemoveValue(func); + + func->ReleaseInternal(); + break; + } } } } @@ -2463,7 +2531,7 @@ void asCBuilder::CompileInterfaces() sClassDeclaration *intfDecl = interfaceDeclarations[n]; asCObjectType *intfType = intfDecl->objType; - // TODO: 2.28.1: Is this really at the correct place? Hasn't the vfTableIdx already been set here? + // TODO: Is this really at the correct place? Hasn't the vfTableIdx already been set here? // Co-opt the vfTableIdx value in our own methods to indicate the // index the function should have in the table chunk for this interface. for( asUINT d = 0; d < intfType->methods.GetLength(); d++ ) @@ -4124,6 +4192,34 @@ int asCBuilder::RegisterScriptFunctionFromNode(asCScriptNode *node, asCScriptCod return RegisterScriptFunction(node, file, objType, isInterface, isGlobalFunction, ns, isExistingShared, isMixin, name, returnType, parameterNames, parameterTypes, inOutFlags, defaultArgs, isConstMethod, isConstructor, isDestructor, isPrivate, isProtected, isOverride, isFinal, isShared); } +asCScriptFunction *asCBuilder::RegisterLambda(asCScriptNode *node, asCScriptCode *file, asCScriptFunction *funcDef, const asCString &name, asSNameSpace *ns) +{ + // Get the parameter names from the node + asCArray parameterNames; + asCArray defaultArgs; + asCScriptNode *args = node->firstChild; + while( args && args->nodeType == snIdentifier ) + { + asCString argName; + argName.Assign(&file->code[args->tokenPos], args->tokenLength); + parameterNames.PushLast(argName); + defaultArgs.PushLast(0); + args = args->next; + } + + // The statement block for the function must be disconnected, as the builder is going to be the owner of it + args->DisconnectParent(); + + // Get the return and parameter types from the funcDef + asCString funcName = name; + int r = RegisterScriptFunction(args, file, 0, 0, true, ns, false, false, funcName, funcDef->returnType, parameterNames, funcDef->parameterTypes, funcDef->inOutFlags, defaultArgs, false, false, false, false, false, false, false, false); + if( r < 0 ) + return 0; + + // Return the function that was just created (but that will be compiled later) + return engine->scriptFunctions[functions[functions.GetLength()-1]->funcId]; +} + int asCBuilder::RegisterScriptFunction(asCScriptNode *node, asCScriptCode *file, asCObjectType *objType, bool isInterface, bool isGlobalFunction, asSNameSpace *ns, bool isExistingShared, bool isMixin, asCString &name, asCDataType &returnType, asCArray ¶meterNames, asCArray ¶meterTypes, asCArray &inOutFlags, asCArray &defaultArgs, bool isConstMethod, bool isConstructor, bool isDestructor, bool isPrivate, bool isProtected, bool isOverride, bool isFinal, bool isShared) { // Determine default namespace if not specified @@ -4734,11 +4830,15 @@ void asCBuilder::GetObjectMethodDescriptions(const char *name, asCObjectType *ob // If searching with a scope informed, then the node and script must also be informed for potential error reporting asASSERT( errNode && script ); - // If the scope contains ::identifier, then use the last identifier as the class name and the rest of is as the namespace + // If the scope contains ::identifier, then use the last identifier as the class name and the rest of it as the namespace int n = scope.FindLast("::"); asCString className = n >= 0 ? scope.SubString(n+2) : scope; asCString nsName = n >= 0 ? scope.SubString(0, n) : ""; - asSNameSpace *ns = GetNameSpaceByString(nsName, objectType->nameSpace, errNode, script); + + // Check if the namespace actually exist, if not return silently as this cannot be the referring to a base class + asSNameSpace *ns = GetNameSpaceByString(nsName, objectType->nameSpace, errNode, script, false); + if( ns == 0 ) + return; // Find the base class with the specified scope while( objectType && (objectType->name != className || objectType->nameSpace != ns) ) @@ -4894,7 +4994,7 @@ asSNameSpace *asCBuilder::GetNameSpaceFromNode(asCScriptNode *node, asCScriptCod return GetNameSpaceByString(scope, implicitNs, node, script); } -asSNameSpace *asCBuilder::GetNameSpaceByString(const asCString &nsName, asSNameSpace *implicitNs, asCScriptNode *errNode, asCScriptCode *script) +asSNameSpace *asCBuilder::GetNameSpaceByString(const asCString &nsName, asSNameSpace *implicitNs, asCScriptNode *errNode, asCScriptCode *script, bool isRequired) { asSNameSpace *ns = implicitNs; if( nsName == "::" ) @@ -4902,7 +5002,7 @@ asSNameSpace *asCBuilder::GetNameSpaceByString(const asCString &nsName, asSNameS else if( nsName != "" ) { ns = engine->FindNameSpace(nsName.AddressOf()); - if( ns == 0 ) + if( ns == 0 && isRequired ) { asCString msg; msg.Format(TXT_NAMESPACE_s_DOESNT_EXIST, nsName.AddressOf()); @@ -5073,6 +5173,12 @@ asCDataType asCBuilder::CreateDataTypeFromNode(asCScriptNode *node, asCScriptCod ot = otInstance; } } + else if( n && n->next && n->next->nodeType == snDataType ) + { + asCString str; + str.Format(TXT_TYPE_s_NOT_TEMPLATE, ot->name.AddressOf()); + WriteError(str, file, n); + } // Create object data type if( ot ) diff --git a/lib/angelscript/source/as_builder.h b/lib/angelscript/source/as_builder.h index 3a9851fec..11c9ed917 100644 --- a/lib/angelscript/source/as_builder.h +++ b/lib/angelscript/source/as_builder.h @@ -174,7 +174,7 @@ protected: asCString GetCleanExpressionString(asCScriptNode *n, asCScriptCode *file); asSNameSpace *GetNameSpaceFromNode(asCScriptNode *node, asCScriptCode *script, asSNameSpace *implicitNs, asCScriptNode **next); - asSNameSpace *GetNameSpaceByString(const asCString &nsName, asSNameSpace *implicitNs, asCScriptNode *errNode, asCScriptCode *script); + asSNameSpace *GetNameSpaceByString(const asCString &nsName, asSNameSpace *implicitNs, asCScriptNode *errNode, asCScriptCode *script, bool isRequired = true); asCString GetScopeFromNode(asCScriptNode *n, asCScriptCode *script, asCScriptNode **next = 0); asCObjectType *GetObjectType(const char *type, asSNameSpace *ns); @@ -213,6 +213,7 @@ protected: int RegisterEnum(asCScriptNode *node, asCScriptCode *file, asSNameSpace *ns); int RegisterTypedef(asCScriptNode *node, asCScriptCode *file, asSNameSpace *ns); int RegisterFuncDef(asCScriptNode *node, asCScriptCode *file, asSNameSpace *ns); + asCScriptFunction *RegisterLambda(asCScriptNode *node, asCScriptCode *file, asCScriptFunction *funcDef, const asCString &name, asSNameSpace *ns); void CompleteFuncDef(sFuncDef *funcDef); void CompileInterfaces(); void CompileClasses(asUINT originalNumTempl); diff --git a/lib/angelscript/source/as_bytecode.cpp b/lib/angelscript/source/as_bytecode.cpp index 32564f2a0..b0777d4f8 100644 --- a/lib/angelscript/source/as_bytecode.cpp +++ b/lib/angelscript/source/as_bytecode.cpp @@ -1015,6 +1015,21 @@ void asCByteCode::OptimizeLocally(const asCArray &tempVariableOffsets) ChangeFirstDeleteNext(curr, asBC_PSF); instr = GoForward(curr); } + // VAR a, GETOBJREF 0 -> PshVPtr a + else if( curr->next && curr->next->op == asBC_GETOBJREF && curr->next->wArg[0] == 0 ) + { + ChangeFirstDeleteNext(curr, asBC_PshVPtr); + instr = GoForward(curr); + } + // VAR, PSF, GETREF {PTR_SIZE} -> PSF, PSF + if( curr->next && curr->next->op == asBC_PSF && + curr->next->next && curr->next->next->op == asBC_GETREF && + curr->next->next->wArg[0] == AS_PTR_SIZE ) + { + curr->op = asBC_PSF; + DeleteInstruction(curr->next->next); + instr = GoForward(curr); + } } } @@ -2220,6 +2235,13 @@ void asCByteCode::DebugOutput(const char *name, asCScriptEngine *engine, asCScri } break; + case asBC_FuncPtr: + { + asCScriptFunction *func = *(asCScriptFunction**)ARG_DW(instr->arg); + fprintf(file, " %-8s 0x%x (func:%s)\n", asBCInfo[instr->op].name, (asUINT)*ARG_DW(instr->arg), func->GetDeclaration()); + } + break; + case asBC_PshC4: case asBC_Cast: fprintf(file, " %-8s 0x%x (i:%d, f:%g)\n", asBCInfo[instr->op].name, (asUINT)*ARG_DW(instr->arg), *((int*) ARG_DW(instr->arg)), *((float*) ARG_DW(instr->arg))); @@ -2233,6 +2255,7 @@ void asCByteCode::DebugOutput(const char *name, asCScriptEngine *engine, asCScri case asBC_CALLSYS: case asBC_CALLBND: case asBC_CALLINTF: + case asBC_Thiscall1: { int funcID = *(int*)ARG_DW(instr->arg); asCString decl = engine->GetFunctionDeclaration(funcID); @@ -2268,10 +2291,17 @@ void asCByteCode::DebugOutput(const char *name, asCScriptEngine *engine, asCScri { case asBC_OBJTYPE: { - asCObjectType *ot = *(asCObjectType**)ARG_DW(instr->arg); + asCObjectType *ot = *(asCObjectType**)ARG_QW(instr->arg); fprintf(file, " %-8s 0x%x (type:%s)\n", asBCInfo[instr->op].name, (asUINT)*ARG_QW(instr->arg), ot->GetName()); } break; + + case asBC_FuncPtr: + { + asCScriptFunction *func = *(asCScriptFunction**)ARG_QW(instr->arg); + fprintf(file, " %-8s 0x%x (func:%s)\n", asBCInfo[instr->op].name, (asUINT)*ARG_QW(instr->arg), func->GetDeclaration()); + } + break; default: #ifdef __GNUC__ @@ -2288,15 +2318,27 @@ void asCByteCode::DebugOutput(const char *name, asCScriptEngine *engine, asCScri case asBCTYPE_wW_QW_ARG: case asBCTYPE_rW_QW_ARG: + switch( instr->op ) + { + case asBC_RefCpyV: + case asBC_FREE: + { + asCObjectType *ot = *(asCObjectType**)ARG_QW(instr->arg); + fprintf(file, " %-8s v%d, 0x%x (type:%s)\n", asBCInfo[instr->op].name, instr->wArg[0], (asUINT)*ARG_QW(instr->arg), ot->GetName()); + } + break; + + default: #ifdef __GNUC__ #ifdef _LP64 - fprintf(file, " %-8s v%d, 0x%lx (i:%ld, f:%g)\n", asBCInfo[instr->op].name, instr->wArg[0], *ARG_QW(instr->arg), *((asINT64*) ARG_QW(instr->arg)), *((double*) ARG_QW(instr->arg))); + fprintf(file, " %-8s v%d, 0x%lx (i:%ld, f:%g)\n", asBCInfo[instr->op].name, instr->wArg[0], *ARG_QW(instr->arg), *((asINT64*) ARG_QW(instr->arg)), *((double*) ARG_QW(instr->arg))); #else - fprintf(file, " %-8s v%d, 0x%llx (i:%lld, f:%g)\n", asBCInfo[instr->op].name, instr->wArg[0], *ARG_QW(instr->arg), *((asINT64*) ARG_QW(instr->arg)), *((double*) ARG_QW(instr->arg))); + fprintf(file, " %-8s v%d, 0x%llx (i:%lld, f:%g)\n", asBCInfo[instr->op].name, instr->wArg[0], *ARG_QW(instr->arg), *((asINT64*) ARG_QW(instr->arg)), *((double*) ARG_QW(instr->arg))); #endif #else - fprintf(file, " %-8s v%d, 0x%I64x (i:%I64d, f:%g)\n", asBCInfo[instr->op].name, instr->wArg[0], *ARG_QW(instr->arg), *((asINT64*) ARG_QW(instr->arg)), *((double*) ARG_QW(instr->arg))); + fprintf(file, " %-8s v%d, 0x%I64x (i:%I64d, f:%g)\n", asBCInfo[instr->op].name, instr->wArg[0], *ARG_QW(instr->arg), *((asINT64*) ARG_QW(instr->arg)), *((double*) ARG_QW(instr->arg))); #endif + } break; case asBCTYPE_DW_DW_ARG: diff --git a/lib/angelscript/source/as_bytecode.h b/lib/angelscript/source/as_bytecode.h index 59d8557b5..b1bb1127b 100644 --- a/lib/angelscript/source/as_bytecode.h +++ b/lib/angelscript/source/as_bytecode.h @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2012 Andreas Jonsson + Copyright (c) 2003-2015 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -130,6 +130,8 @@ public: int InstrW_W(asEBCInstr bc, int w, int b); int InstrSHORT_DW_DW(asEBCInstr bc, short a, asDWORD b, asDWORD c); + asCScriptEngine *GetEngine() const { return engine; }; + asCArray lineNumbers; asCArray sectionIdxs; int largestStackUsed; diff --git a/lib/angelscript/source/as_callfunc.cpp b/lib/angelscript/source/as_callfunc.cpp index 723ba1c28..81095ab8b 100644 --- a/lib/angelscript/source/as_callfunc.cpp +++ b/lib/angelscript/source/as_callfunc.cpp @@ -130,10 +130,11 @@ int DetectCallingConvention(bool isMethod, const asSFuncPtr &ptr, int callConv, internal->callConv = (internalCallConv)(thisCallConv + 2); #endif internal->baseOffset = ( int )MULTI_BASE_OFFSET(ptr); -#if defined(AS_ARM) && (defined(__GNUC__) || defined(AS_PSVITA)) +#if (defined(AS_ARM) || defined(AS_MIPS)) && (defined(__GNUC__) || defined(AS_PSVITA)) // As the least significant bit in func is used to switch to THUMB mode // on ARM processors, the LSB in the __delta variable is used instead of // the one in __pfn on ARM processors. + // MIPS also appear to use the base offset to indicate virtual method. if( (size_t(internal->baseOffset) & 1) ) internal->callConv = (internalCallConv)(thisCallConv + 2); #endif @@ -577,10 +578,11 @@ int CallSystemFunction(int id, asCContext *context) } // Add the base offset for multiple inheritance -#if (defined(__GNUC__) && defined(AS_ARM)) || defined(AS_PSVITA) +#if (defined(__GNUC__) && (defined(AS_ARM) || defined(AS_MIPS))) || defined(AS_PSVITA) // On GNUC + ARM the lsb of the offset is used to indicate a virtual function // and the whole offset is thus shifted one bit left to keep the original // offset resolution + // MIPS also work like ARM in this regard obj = (void*)(asPWORD(obj) + (sysFunc->baseOffset>>1)); #else obj = (void*)(asPWORD(obj) + sysFunc->baseOffset); @@ -634,10 +636,11 @@ int CallSystemFunction(int id, asCContext *context) } // Add the base offset for multiple inheritance -#if (defined(__GNUC__) && defined(AS_ARM)) || defined(AS_PSVITA) +#if (defined(__GNUC__) && (defined(AS_ARM) || defined(AS_MIPS))) || defined(AS_PSVITA) // On GNUC + ARM the lsb of the offset is used to indicate a virtual function // and the whole offset is thus shifted one bit left to keep the original // offset resolution + // MIPS also work like ARM in this regard tempPtr = (void*)(asPWORD(tempPtr) + (sysFunc->baseOffset>>1)); #else tempPtr = (void*)(asPWORD(tempPtr) + sysFunc->baseOffset); @@ -822,7 +825,15 @@ int CallSystemFunction(int id, asCContext *context) if( cleanCount ) { args = context->m_regs.stackPointer; - if( callConv >= ICC_THISCALL ) + + // Skip the hidden argument for the return pointer + // TODO: runtime optimize: This check and increment should have been done in PrepareSystemFunction + if( descr->DoesReturnOnStack() ) + args += AS_PTR_SIZE; + + // Skip the object pointer on the stack + // TODO: runtime optimize: This check and increment should have been done in PrepareSystemFunction + if( callConv >= ICC_THISCALL && sysFunc->objForThiscall == 0 ) args += AS_PTR_SIZE; asSSystemFunctionInterface::SClean *clean = sysFunc->cleanArgs.AddressOf(); diff --git a/lib/angelscript/source/as_callfunc_arm_gcc.S b/lib/angelscript/source/as_callfunc_arm_gcc.S index 38299d453..3ce5566a4 100644 --- a/lib/angelscript/source/as_callfunc_arm_gcc.S +++ b/lib/angelscript/source/as_callfunc_arm_gcc.S @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2014 Andreas Jonsson + Copyright (c) 2003-2015 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -39,11 +39,13 @@ The assembler routines for Linux were written by Carlos Luna in December 2012 */ +#if !defined(AS_MAX_PORTABILITY) + #if defined(__arm__) || defined(__ARM__) || defined(I3D_ARCH_ARM) #if !defined(__linux__) || defined(__ANDROID__) || defined(ANDROID) || defined(__SOFTFP__) -/* iOS, Android, and Marmalade goes here */ +/* iOS, Android, Marmalade, and Linux with soft-float ABI goes here */ .global armFunc .global armFuncR0 @@ -85,7 +87,12 @@ stackargsloop: bne stackargsloop mov sp, r12 nomoreargs: +#if defined (__ARM_ARCH_4T__) || defined (__ARM_ARCH_4__) + mov lr, pc /* older ARM didn't support blx */ + mov pc, r4 +#else blx r4 +#endif add sp, sp, r8 ldmia sp!, {r4-r8, pc} @@ -133,7 +140,12 @@ stackargslooparmFuncObjLast: bne stackargslooparmFuncObjLast mov sp, r12 nomoreargsarmFuncObjLast: +#if defined (__ARM_ARCH_4T__) || defined (__ARM_ARCH_4__) + mov lr, pc /* older ARM didn't support blx */ + mov pc, r4 +#else blx r4 +#endif add sp, sp, r8 ldmia sp!, {r4-r8, pc} @@ -180,7 +192,12 @@ stackargslooparmFuncR0ObjLast: bne stackargslooparmFuncR0ObjLast mov sp, r12 nomoreargsarmFuncR0ObjLast: +#if defined (__ARM_ARCH_4T__) || defined (__ARM_ARCH_4__) + mov lr, pc /* older ARM didn't support blx */ + mov pc, r4 +#else blx r4 +#endif add sp, sp, r8 ldmia sp!, {r4-r8, pc} @@ -218,7 +235,12 @@ stackargslooparmFuncR0: bne stackargslooparmFuncR0 mov sp, r12 nomoreargsarmFuncR0: +#if defined (__ARM_ARCH_4T__) || defined (__ARM_ARCH_4__) + mov lr, pc /* older ARM didn't support blx */ + mov pc, r4 +#else blx r4 +#endif add sp, sp, r8 ldmia sp!, {r4-r8, pc} @@ -255,14 +277,19 @@ stackargslooparmFuncR0R1: bne stackargslooparmFuncR0R1 mov sp, r12 nomoreargsarmFuncR0R1: +#if defined (__ARM_ARCH_4T__) || defined (__ARM_ARCH_4__) + mov lr, pc /* older ARM didn't support blx */ + mov pc, r4 +#else blx r4 +#endif add sp, sp, r8 ldmia sp!, {r4-r8, pc} /* --------------------------------------------------------------------------------------------*/ #elif defined(__linux__) && !defined(__SOFTFP__) -/* The Linux code goes here */ +/* The Linux with hard-float ABI code goes here */ /* These codes are suitable for armeabi + vfp / armeabihf */ @@ -340,7 +367,7 @@ stackargsloop: mov sp, r12 nomoreargs: -#if defined (___ARM_ARCH_4T__) || defined (__ARM_ARCH_4__) +#if defined (__ARM_ARCH_4T__) || defined (__ARM_ARCH_4__) mov lr, pc /* older ARM didn't support blx */ mov pc, r4 #else @@ -427,8 +454,8 @@ stackargslooparmFuncObjLast: mov sp, r12 nomoreargsarmFuncObjLast: -#if defined (___ARM_ARCH_4T__) || defined (__ARM_ARCH_4__) - mov lr, pc +#if defined (__ARM_ARCH_4T__) || defined (__ARM_ARCH_4__) + mov lr, pc /* older ARM didn't support blx */ mov pc, r4 #else blx r4 @@ -521,8 +548,8 @@ stackargslooparmFuncR0ObjLast: mov sp, r12 nomoreargsarmFuncR0ObjLast: -#if defined (___ARM_ARCH_4T__) || defined (__ARM_ARCH_4__) - mov lr, pc +#if defined (__ARM_ARCH_4T__) || defined (__ARM_ARCH_4__) + mov lr, pc /* older ARM didn't support blx */ mov pc, r4 #else blx r4 @@ -597,8 +624,8 @@ stackargslooparmFuncR0: mov sp, r12 nomoreargsarmFuncR0: -#if defined (___ARM_ARCH_4T__) || defined (__ARM_ARCH_4__) - mov lr, pc +#if defined (__ARM_ARCH_4T__) || defined (__ARM_ARCH_4__) + mov lr, pc /* older ARM didn't support blx */ mov pc, r4 #else blx r4 @@ -677,8 +704,8 @@ stackargslooparmFuncR0R1: mov sp, r12 nomoreargsarmFuncR0R1: -#if defined (___ARM_ARCH_4T__) || defined (__ARM_ARCH_4__) - mov lr, pc +#if defined (__ARM_ARCH_4T__) || defined (__ARM_ARCH_4__) + mov lr, pc /* older ARM didn't support blx */ mov pc, r4 #else blx r4 @@ -697,3 +724,6 @@ nomoreargsarmFuncR0R1: #endif /* arm */ +#endif /* !AS_MAX_PORTABILITY */ + + diff --git a/lib/angelscript/source/as_callfunc_arm_vita.S b/lib/angelscript/source/as_callfunc_arm_vita.S index 3281f46f5..406db039b 100644 --- a/lib/angelscript/source/as_callfunc_arm_vita.S +++ b/lib/angelscript/source/as_callfunc_arm_vita.S @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2013 Andreas Jonsson + Copyright (c) 2003-2015 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -34,6 +34,8 @@ This code was adapted from as_callfunc_arm_gcc (ARM, Linux hard float) by Brandon Bare on October 2014. */ +#if !defined(AS_MAX_PORTABILITY) + #ifdef __psp2__ .syntax unified @@ -61,7 +63,7 @@ /* --------------------------------------------------------------------------------------------*/ armFunc: - .fnstart + .fnstart push {r4-r8, r10, r11, lr} /* sp must be 8-byte alignment for ABI compliance, so the pushed registers must be even */ @@ -81,19 +83,19 @@ armFunc: /* Load the first 4 arguments into r0-r3 */ cmp r7, #4 - it ge + it ge ldrge r0, [r6] cmp r7, #8 - it ge + it ge ldrge r1, [r6, #4] cmp r7, #12 - it ge + it ge ldrge r2, [r6, #8] cmp r7, #16 - it ge + it ge ldrge r3, [r6, #12] stackargs: @@ -128,11 +130,11 @@ nomoreargs: pop {r4-r8, r10, r11, pc} - .fnend + .fnend /* --------------------------------------------------------------------------------------------*/ armFuncObjLast: - .fnstart + .fnstart push {r4-r8, r10, r11, lr} /* Were storing r11 just to keep the stack aligned to an 8 byte boundary */ @@ -157,28 +159,28 @@ armFuncObjLast: /* Load the first 4 arguments into r0-r3 */ cmp r7, #4 - it ge + it ge ldrge r0, [r6] cmp r7, #8 - it ge + it ge ldrge r1, [r6,#4] - it lt + it lt movlt r1, r5 cmp r7, #12 - it ge + it ge ldrge r2, [r6,#8] - it lt + it lt movlt r2, r5 cmp r7, #16 - it ge + it ge ldrge r3, [r6,#12] - ittt lt + ittt lt movlt r3, r5 movlt r5, #0 /* If objlast got placed into a register, r5 = 0 */ blt stackargsFuncObjLast /* If objlast got placed into a register, go to stackargsFuncObjLast */ @@ -220,11 +222,11 @@ nomoreargsarmFuncObjLast: pop {r4-r8, r10,r11, pc} - .fnend + .fnend /* --------------------------------------------------------------------------------------------*/ armFuncR0ObjLast: - .fnstart + .fnstart push {r4-r8, r10, r11, lr} @@ -252,35 +254,35 @@ armFuncR0ObjLast: /* Load the first 3 arguments into r1-r3 */ cmp r7, #4 - it ge + it ge ldrge r1, [r6] cmp r7, #8 - it ge + it ge ldrge r2, [r6,#4] - it lt + it lt movlt r2, r5 cmp r7, #12 - it ge + it ge ldrge r3, [r6,#8] - ittt lt + ittt lt movlt r3, r5 movlt r5, #0 /* If objlast got placed into a register, r5 = 0 */ blt stackargsFuncR0ObjLast /* If objlast got placed into a register, go to stackargsFuncR0ObjLast */ cmp r7, #16 /* Else if we have one last arg set the offset accordingly and store the arg in the array */ - itt ge + itt ge ldrge r7, [r6, #12] strge r7, [r6, #8] str r5, [r6, #12] /* Put objlast in r6 + 12 */ - mov r5, #0 + mov r5, #0 - it ge + it ge movge r5, #4 /* Set r5 with an offset of #4 if theres one last arg that couldnt be placed in r registers */ add r5, r5, #4 /* Set r5 with an offset of + #4, so objlast can be loaded into the stack */ @@ -318,11 +320,11 @@ nomoreargsarmFuncR0ObjLast: pop {r4-r8, r10, r11, pc} - .fnend + .fnend /* --------------------------------------------------------------------------------------------*/ armFuncR0: - .fnstart + .fnstart push {r4-r8, r10, r11, lr} @@ -344,19 +346,19 @@ armFuncR0: /* Load the first 3 arguments into r1-r3 */ cmp r7, #4 - it ge + it ge ldrge r1, [r6] cmp r7, #8 - it ge + it ge ldrge r2, [r6, #4] cmp r7, #12 - it ge + it ge ldrge r3, [r6, #8] cmp r7, #16 - it ge + it ge movge r11, #4 /* If there is still one arg to be placed, set the offset in r11 to #4 */ stackargsarmFuncR0: @@ -393,11 +395,11 @@ nomoreargsarmFuncR0: pop {r4-r8, r10, r11, pc} - .fnend + .fnend /* --------------------------------------------------------------------------------------------*/ armFuncR0R1: - .fnstart + .fnstart push {r4-r8, r10, r11, lr} @@ -421,23 +423,23 @@ armFuncR0R1: /* Load the first 2 arguments into r2-r3 */ cmp r7, #4 - it ge + it ge ldrge r2, [r6] cmp r7, #8 - it ge + it ge ldrge r3, [r6, #4] cmp r7, #12 - it ge + it ge movge r11, #4 /* If there is a third arg to be placed, set the offset in r11 to #4 */ cmp r7, #16 - it ge + it ge movge r11, #8 /* If there is a fourth arg to be placed, set the offset in r11 to #8 */ - itt lt + itt lt ldrlt r7, [r6, #8] /* Else copy the third arg to the correct place in the array */ strlt r7, [r6, #12] @@ -475,6 +477,9 @@ nomoreargsarmFuncR0R1: pop {r4-r8, r10, r11, pc} - .fnend + .fnend + +#endif + +#endif /* !AS_MAX_PORTABILITY */ -#endif \ No newline at end of file diff --git a/lib/angelscript/source/as_callfunc_arm_xcode.S b/lib/angelscript/source/as_callfunc_arm_xcode.S index 60c4e18e6..235e242f3 100644 --- a/lib/angelscript/source/as_callfunc_arm_xcode.S +++ b/lib/angelscript/source/as_callfunc_arm_xcode.S @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2009 Andreas Jonsson + Copyright (c) 2003-2015 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -35,6 +35,8 @@ // Adapted to GNUC by darktemplar216 in September 2009 // Small fixed to work under XCode GCC by Gilad Novik in October 2009 +#if !defined(AS_MAX_PORTABILITY) + #if defined(__arm__) || defined(__ARM__) .align 2 @@ -235,3 +237,6 @@ nomoreargsarmFuncR0R1: ldmia sp!, {r4-r8, pc} #endif + +#endif /* !AS_MAX_PORTABILITY */ + diff --git a/lib/angelscript/source/as_callfunc_mips.cpp b/lib/angelscript/source/as_callfunc_mips.cpp index 2dc8e0078..0b2e3b4fc 100644 --- a/lib/angelscript/source/as_callfunc_mips.cpp +++ b/lib/angelscript/source/as_callfunc_mips.cpp @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2014 Andreas Jonsson + Copyright (c) 2003-2015 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -35,7 +35,9 @@ // These functions handle the actual calling of system functions // // This version is MIPS specific and was originally written -// by Manu Evans in April, 2006 +// by Manu Evans in April, 2006 for Playstation Portable (PSP) +// +// Support for Linux with MIPS was added by Andreas Jonsson in April, 2015 // @@ -52,10 +54,320 @@ #include #include +#if !defined(AS_ANDROID) #include +#endif BEGIN_AS_NAMESPACE +#if defined(__linux__) && defined(_ABIO32) + +// The MIPS ABI used by Linux is implemented here +// (Tested on CI20 MIPS Creator with Debian Linux) +// +// ref: SYSTEM V +// APPLICATION BINARY INTERFACE +// MIPS RISC Processor +// http://math-atlas.sourceforge.net/devel/assembly/mipsabi32.pdf +// +// ref: MIPS Instruction Reference +// http://www.mrc.uidaho.edu/mrc/people/jff/digital/MIPSir.html + +union SFloatRegs +{ + union { double d0; struct { float f0; asDWORD dummy0; };}; + union { double d1; struct { float f1; asDWORD dummy1; };}; +} ; + +extern "C" asQWORD mipsFunc(asUINT argSize, asDWORD *argBuffer, void *func, SFloatRegs &floatRegs); +asDWORD GetReturnedFloat(); +asQWORD GetReturnedDouble(); + +asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, void *obj, asDWORD *args, void *retPointer, asQWORD &/*retQW2*/, void *secondObject) +{ + asCScriptEngine *engine = context->m_engine; + asSSystemFunctionInterface *sysFunc = descr->sysFuncIntf; + int callConv = sysFunc->callConv; + + asQWORD retQW = 0; + + void *func = (void*)sysFunc->func; + void **vftable; + + asDWORD argBuffer[128]; // Ought to be big enough + asASSERT( sysFunc->paramSize < 128 ); + + asDWORD argOffset = 0; + + SFloatRegs floatRegs; + asDWORD floatOffset = 0; + + // If the application function returns the value in memory then + // the first argument must be the pointer to that memory + if( sysFunc->hostReturnInMemory ) + { + asASSERT( retPointer ); + argBuffer[argOffset++] = (asPWORD)retPointer; + } + + if( callConv == ICC_CDECL_OBJFIRST || callConv == ICC_CDECL_OBJFIRST_RETURNINMEM || + callConv == ICC_THISCALL || callConv == ICC_THISCALL_RETURNINMEM || + callConv == ICC_VIRTUAL_THISCALL || callConv == ICC_VIRTUAL_THISCALL_RETURNINMEM || + callConv == ICC_THISCALL_OBJFIRST || callConv == ICC_VIRTUAL_THISCALL_OBJFIRST || + callConv == ICC_THISCALL_OBJFIRST_RETURNINMEM || callConv == ICC_VIRTUAL_THISCALL_OBJFIRST_RETURNINMEM || + callConv == ICC_THISCALL_OBJLAST || callConv == ICC_VIRTUAL_THISCALL_OBJLAST || + callConv == ICC_THISCALL_OBJLAST_RETURNINMEM || callConv == ICC_VIRTUAL_THISCALL_OBJLAST_RETURNINMEM ) + { + // Add the object pointer as the first argument + argBuffer[argOffset++] = (asPWORD)obj; + } + + if( callConv == ICC_THISCALL_OBJFIRST || callConv == ICC_VIRTUAL_THISCALL_OBJFIRST || + callConv == ICC_THISCALL_OBJFIRST_RETURNINMEM || callConv == ICC_VIRTUAL_THISCALL_OBJFIRST_RETURNINMEM ) + { + // Add the second object pointer + argBuffer[argOffset++] = (asPWORD)secondObject; + } + + int spos = 0; + for( asUINT n = 0; n < descr->parameterTypes.GetLength(); n++ ) + { + asCDataType ¶mType = descr->parameterTypes[n]; + if( paramType.IsObject() && !paramType.IsObjectHandle() && !paramType.IsReference() ) + { + if( paramType.GetObjectType()->flags & COMPLEX_MASK ) + { + // The object is passed by reference + argBuffer[argOffset++] = args[spos++]; + } + else + { + // Ensure 8byte alignment for classes that need it + if( (paramType.GetObjectType()->flags & asOBJ_APP_CLASS_ALIGN8) && (argOffset & 1) ) + argOffset++; + + // Copy the object's memory to the buffer + memcpy(&argBuffer[argOffset], *(void**)(args+spos), paramType.GetSizeInMemoryBytes()); + // Delete the original memory + engine->CallFree(*(char**)(args+spos)); + spos++; + argOffset += paramType.GetSizeInMemoryDWords(); + } + } + else if( paramType.GetTokenType() == ttQuestion ) + { + // Copy both pointer and type id + argBuffer[argOffset++] = args[spos++]; + argBuffer[argOffset++] = args[spos++]; + } + else + { + // The first 2 floats or doubles are loaded into the float registers. + // Actually this is only done if they are the first arguments to the function, + // but it doesn't cause any harm to load them into the registers even if they + // won't be used so we don't need to check if they really are the first args. + if( floatOffset == 0 ) + { + if( paramType.GetTokenType() == ttFloat ) + floatRegs.f0 = *reinterpret_cast(&args[spos]); + else if( paramType.GetTokenType() == ttDouble ) + floatRegs.d0 = *reinterpret_cast(&args[spos]); + floatOffset++; + } + else if( floatOffset == 1 ) + { + if( paramType.GetTokenType() == ttFloat ) + floatRegs.f1 = *reinterpret_cast(&args[spos]); + else if( paramType.GetTokenType() == ttDouble ) + floatRegs.d1 = *reinterpret_cast(&args[spos]); + floatOffset++; + } + + // Copy the value directly + if( paramType.GetSizeOnStackDWords() > 1 ) + { + // Make sure the argument is 8byte aligned + if( argOffset & 1 ) + argOffset++; + *reinterpret_cast(&argBuffer[argOffset]) = *reinterpret_cast(&args[spos]); + argOffset += 2; + spos += 2; + } + else + argBuffer[argOffset++] = args[spos++]; + } + } + + if( callConv == ICC_CDECL_OBJLAST || callConv == ICC_CDECL_OBJLAST_RETURNINMEM ) + { + // Add the object pointer as the last argument + argBuffer[argOffset++] = (asPWORD)obj; + } + + if( callConv == ICC_THISCALL_OBJLAST || callConv == ICC_VIRTUAL_THISCALL_OBJLAST || + callConv == ICC_THISCALL_OBJLAST_RETURNINMEM || callConv == ICC_VIRTUAL_THISCALL_OBJLAST_RETURNINMEM ) + { + // Add the second object pointer + argBuffer[argOffset++] = (asPWORD)secondObject; + } + + switch( callConv ) + { + case ICC_CDECL: + case ICC_CDECL_RETURNINMEM: + case ICC_STDCALL: + case ICC_STDCALL_RETURNINMEM: + case ICC_CDECL_OBJLAST: + case ICC_CDECL_OBJLAST_RETURNINMEM: + case ICC_CDECL_OBJFIRST: + case ICC_CDECL_OBJFIRST_RETURNINMEM: + case ICC_THISCALL: + case ICC_THISCALL_RETURNINMEM: + case ICC_THISCALL_OBJFIRST: + case ICC_THISCALL_OBJFIRST_RETURNINMEM: + case ICC_THISCALL_OBJLAST: + case ICC_THISCALL_OBJLAST_RETURNINMEM: + retQW = mipsFunc(argOffset*4, argBuffer, func, floatRegs); + break; + + case ICC_VIRTUAL_THISCALL: + case ICC_VIRTUAL_THISCALL_RETURNINMEM: + case ICC_VIRTUAL_THISCALL_OBJFIRST: + case ICC_VIRTUAL_THISCALL_OBJFIRST_RETURNINMEM: + case ICC_VIRTUAL_THISCALL_OBJLAST: + case ICC_VIRTUAL_THISCALL_OBJLAST_RETURNINMEM: + // Get virtual function table from the object pointer + vftable = *(void***)obj; + retQW = mipsFunc(argOffset*4, argBuffer, vftable[asPWORD(func)>>2], floatRegs); + break; + default: + context->SetInternalException(TXT_INVALID_CALLING_CONVENTION); + } + + // If the return is a float value we need to get the value from the FP register + if( sysFunc->hostReturnFloat ) + { + if( sysFunc->hostReturnSize == 1 ) + *(asDWORD*)&retQW = GetReturnedFloat(); + else + retQW = GetReturnedDouble(); + } + + return retQW; +} + +asDWORD GetReturnedFloat() +{ + asDWORD f; + + asm("swc1 $f0, %0\n" : "=m"(f)); + + return f; +} + +asQWORD GetReturnedDouble() +{ + asQWORD d = 0; + + asm("sdc1 $f0, %0\n" : "=m"(d)); + + return d; +} + +// asQWORD mipsFunc(asUINT argSize, asDWORD *argBuffer, void *func, SFloatRegs &floatRegs); +// $2,$3 $4 $5 $6 $7 +asm( +" .text\n" +//" .align 2\n" +" .cfi_startproc\n" +" .global mipsFunc\n" +" .ent mipsFunc\n" +"mipsFunc:\n" +//" .frame $fp,64,$31 # vars= 0, regs= 0/0, args= 0, gp= 0\n" +//" .mask 0x00000000,0\n" +//" .fmask 0x00000000,0\n" +" .set noreorder\n" +" .set nomacro\n" + +// align the stack frame to 8 bytes +" addiu $12, $4, 7\n" // t4 ($12) = argSize ($4) + 7 +" li $13, -8\n" // t5 ($13) = 0xfffffffffffffff8 +" and $12, $12, $13\n" // t4 ($12) &= t5 ($13). t4 holds the size of the argument block +// It is required that the caller reserves space for at least 16 bytes even if there are less than 4 arguments +// and add 8 bytes for the return pointer and s0 ($16) backup +" addiu $13, $12, 24\n" // t5 = t4 + 24. t5 ($13) holds the total size of the stack frame (including return pointer) +// save the s0 register (so we can use it to remember where our return pointer is lives) +" sw $16, -4($sp)\n" // store the s0 register (so we can use it to remember how big our stack frame is) +" .cfi_offset 16, -4\n" +// store the return pointer +" sw $31, -8($sp)\n" +" .cfi_offset 31, -8\n" +// keep original stack pointer +" move $16, $sp\n" +" .cfi_def_cfa_register 16\n" +// push the stack +" subu $sp, $sp, $13\n" + +// store the argument in temporary registers +" addiu $25, $6, 0\n" // t9 ($25) holds the function pointer (must be t9 for position independent code) +" addiu $3, $4, 0\n" // v1 ($3) holds the size of the argument buffer +" move $15, $5\n" // t7 ($15) holds the pointer to the argBuffer +" move $14, $7\n" // t6 ($14) holds the values for the float registers + +// load integer registers +" lw $4, 0($15)\n" // a0 ($4) +" lw $5, 4($15)\n" // a1 ($5) +" lw $6, 8($15)\n" // a2 ($6) +" lw $7, 12($15)\n" // a3 ($7) + +// load float registers +" ldc1 $f12, 8($14)\n" +" ldc1 $f14, 0($14)\n" + +// skip stack parameters if there are 4 or less as they are moved into the registers +" addi $14, $3, -16\n" // The first 4 args were already loaded into registers +" blez $14, andCall\n" +" nop\n" + +// push stack parameters +"pushArgs:\n" +" addi $3, -4\n" +// load from $15 + stack bytes ($3) +" addu $14, $15, $3\n" +" lw $14, 0($14)\n" +// store to $sp + stack bytes ($3) +" addu $13, $sp, $3\n" +" sw $14, 0($13)\n" +// if there are more, loop... +" bne $3, $0, pushArgs\n" +" nop\n" + +// and call the function +"andCall:\n" +" jalr $25\n" +" nop\n" + +// restore original stack pointer +" move $sp, $16\n" +// restore the return pointer +" lw $31, -8($sp)\n" +// restore the original value of $16 +" lw $16, -4($sp)\n" +// and return from the function +" jr $31\n" +" nop\n" + +" .set macro\n" +" .set reorder\n" +" .end mipsFunc\n" +" .cfi_endproc\n" +" .size mipsFunc, .-mipsFunc\n" +); + +#else // !(defined(__linux__) && defined(_ABIO32)) + +// The MIPS ABI used by PSP and PS2 is implemented here + #define AS_MIPS_MAX_ARGS 32 #define AS_NUM_REG_FLOATS 8 #define AS_NUM_REG_INTS 8 @@ -191,33 +503,12 @@ asDWORD GetReturnedFloat() return f; } -/* -asDWORD GetReturnedFloat(); - -asm( -" .align 4\n" -" .global GetReturnedFloat\n" -"GetReturnedFloat:\n" -" .set noreorder\n" -" .set nomacro\n" -" j $ra\n" -" mfc1 $v0, $f0\n" -" .set macro\n" -" .set reorder\n" -" .end Func\n" -*/ - - -// sizeof(double) == 4 with sh-elf-gcc (3.4.0) -m4 -// so this isn't really used... asQWORD GetReturnedDouble() { asQWORD d = 0; - printf("Broken!!!"); -/* - asm("sw $v0, %0\n" : "=m"(d)); -*/ + asm("sdc1 $f0, %0\n" : "=m"(d)); + return d; } @@ -352,7 +643,7 @@ asm( " .set nomacro\n" // align the stack frame to 8 bytes " addiu $12, $6, 7\n" -" li $13, -8\n" // 0xfffffffffffffffc +" li $13, -8\n" // 0xfffffffffffffff8 " and $12, $12, $13\n" // t4 holds the size of the argument block // and add 8 bytes for the return pointer and s0 backup " addiu $13, $12, 8\n" // t5 holds the total size of the stack frame (including return pointer) @@ -432,6 +723,8 @@ asm( " .size mipsFunc, .-mipsFunc\n" ); +#endif // PSP and PS2 MIPS ABI + END_AS_NAMESPACE #endif // AS_MIPS diff --git a/lib/angelscript/source/as_callfunc_x64_gcc.cpp b/lib/angelscript/source/as_callfunc_x64_gcc.cpp index 5e497dec8..e05ae986f 100644 --- a/lib/angelscript/source/as_callfunc_x64_gcc.cpp +++ b/lib/angelscript/source/as_callfunc_x64_gcc.cpp @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2014 Andreas Jonsson + Copyright (c) 2003-2015 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -73,14 +73,18 @@ static asQWORD __attribute__((noinline)) X64_CallFunction(const asQWORD *args, i __asm__ __volatile__ ( - " movq %0, %%rcx \n" // rcx = cnt + " movq %0, %%rcx \n" // rcx = cnt " movq %1, %%r10 \n" // r10 = args " movq %2, %%r11 \n" // r11 = func // Backup stack pointer in R15 that is guaranteed to maintain its value over function calls " movq %%rsp, %%r15 \n" +#ifdef __OPTIMIZE__ // Make sure the stack unwind logic knows we've backed up the stack pointer in register r15 + // This should only be done if any optimization is done. If no optimization (-O0) is used, + // then the compiler already backups the rsp before entering the inline assembler code " .cfi_def_cfa_register r15 \n" +#endif // Skip the first 128 bytes on the stack frame, called "red zone", // that might be used by the compiler to store temporary values @@ -100,7 +104,7 @@ static asQWORD __attribute__((noinline)) X64_CallFunction(const asQWORD *args, i " jle endstack \n" " subl $1, %%esi \n" " xorl %%edx, %%edx \n" - " leaq 8(, %%rsi, 8), %%rcx \n" + " leaq 8(, %%rsi, 8), %%rcx \n" "loopstack: \n" " movq 112(%%r10, %%rdx), %%rax \n" " pushq %%rax \n" @@ -128,12 +132,16 @@ static asQWORD __attribute__((noinline)) X64_CallFunction(const asQWORD *args, i " movsd 56(%%rax), %%xmm7 \n" // Call the function - " call *%%r11 \n" + " call *%%r11 \n" // Restore stack pointer " mov %%r15, %%rsp \n" +#ifdef __OPTIMIZE__ // Inform the stack unwind logic that the stack pointer has been restored + // This should only be done if any optimization is done. If no optimization (-O0) is used, + // then the compiler already backups the rsp before entering the inline assembler code " .cfi_def_cfa_register rsp \n" +#endif // Put return value in retQW1 and retQW2, using either RAX:RDX or XMM0:XMM1 depending on type of return value " movl %5, %%ecx \n" diff --git a/lib/angelscript/source/as_callfunc_x86.cpp b/lib/angelscript/source/as_callfunc_x86.cpp index 21c5f40df..d6f50db81 100644 --- a/lib/angelscript/source/as_callfunc_x86.cpp +++ b/lib/angelscript/source/as_callfunc_x86.cpp @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2014 Andreas Jonsson + Copyright (c) 2003-2015 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -1263,13 +1263,13 @@ endcopy: "subl $4, %%ecx \n" "jne copyloop3 \n" "endcopy3: \n" -#if defined(__MINGW32__) && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 7) || __GNUC__ > 4) - // MinGW made some strange choices with 4.7, and the thiscall calling convention - // when returning an object in memory is completely different from when not returning - // in memory - "pushl 0(%%ebx) \n" // push obj on the stack - "movl 16(%%ebx), %%ecx \n" // move the return pointer into ECX - "call *12(%%ebx) \n" // call the function +#ifdef AS_MINGW47 + // MinGW made some strange choices with 4.7 and the thiscall calling convention, + // returning an object in memory is completely different from when not returning + // in memory + "pushl 0(%%ebx) \n" // push obj on the stack + "movl 16(%%ebx), %%ecx \n" // move the return pointer into ECX + "call *12(%%ebx) \n" // call the function #else "movl 0(%%ebx), %%ecx \n" // move obj into ECX #ifdef THISCALL_PASS_OBJECT_POINTER_ON_THE_STACK @@ -1286,7 +1286,7 @@ endcopy: "addl $4, %%esp \n" // pop the object pointer #endif #endif -#endif // MINGW +#endif // AS_MINGW47 // Pop the alignment bytes "popl %%esp \n" "popl %%ebx \n" diff --git a/lib/angelscript/source/as_callfunc_xenon.cpp b/lib/angelscript/source/as_callfunc_xenon.cpp index 34228216c..aaa4d8586 100644 --- a/lib/angelscript/source/as_callfunc_xenon.cpp +++ b/lib/angelscript/source/as_callfunc_xenon.cpp @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2014 Andreas Jonsson + Copyright (c) 2003-2015 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -43,7 +43,9 @@ // various fixes in asm ppcFunc // fix for variable arguments // - +// Modified by Anthony Clark May 2015 +// Fixed the issue where int64 and uint64 could not be passed nativly +// few minor fixes within asm ppcFunc to handle int64 and uint64 // XBox 360 calling convention @@ -87,7 +89,6 @@ // References: // https://www-01.ibm.com/chips/techlib/techlib.nsf/techdocs/852569B20050FF77852569970071B0D6/$file/eabi_app.pdf // -// TODO: The code doesn't handle int64 and uint64 parameters // TODO: The code doesn't handle objects passed by value (unless they are max 4 bytes in size) @@ -128,7 +129,7 @@ enum argTypes // pArgs is the array of the argument values // pArgTypes is an array containing a byte indicating the type (enum argTypes) for each argument. // dwFunc is the address of the function that will be called -asQWORD __declspec( naked ) ppcFunc(const asDWORD* pArgs, asDWORD dwFunc, const asBYTE* pArgTypes) +asQWORD __declspec( naked ) ppcFunc(const asQWORD* pArgs, asDWORD dwFunc, const asBYTE* pArgTypes) { __asm { @@ -202,7 +203,7 @@ ppcNextArg: ////////////////////////////////////////////////////////////////////////// ppcArgIsInteger: // Get the arg from the stack - lwz r12, 0(r26) + ld r12, 0(r26) // r23 holds the integer arg count so far cmplwi cr6, r23, 0 @@ -251,11 +252,11 @@ ppcArgIsInteger: b ppcLoadIntRegUpd ppcLoadIntRegUpd: - stw r12, 0(r31) // push on the stack + std r12, 0(r31) // push on the stack addi r31, r31, 8 // inc stack by 1 reg addi r23, r23, 1 // Increment used int register count - addi r26, r26, 4 // Increment pArgs + addi r26, r26, 8 // Increment pArgs b ppcNextArg // Call next arg ////////////////////////////////////////////////////////////////////////// @@ -511,7 +512,7 @@ asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, // Pack the arguments into an array that ppcFunc() can use to load each CPU register properly asBYTE ppcArgsType[AS_PPC_MAX_ARGS + AS_PPC_RETURNINMEM_REG + AS_PPC_THISCALL_REG + AS_PPC_ENDOFARGS]; - asDWORD ppcArgs[AS_PPC_MAX_ARGS + AS_PPC_RETURNINMEM_REG + AS_PPC_THISCALL_REG]; + asQWORD ppcArgs[AS_PPC_MAX_ARGS + AS_PPC_RETURNINMEM_REG + AS_PPC_THISCALL_REG]; int argsCnt = 0; // If the function returns an object in memory, we allocate the memory and put the ptr to the front (will go to r3) @@ -625,35 +626,34 @@ asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, } else { - // TODO: How should int64 and uint64 be passed natively? - // Currently the code doesn't handle these types - // TODO: The code also ignore the fact that large objects // passed by value has been copied to the stack // in the above loop. *pCurArgType++ = ppcINTARG; - *((int*) pCurFixedArgValue) = *((int*) pCurStackArgValue); + *((asQWORD*) pCurFixedArgValue) = *((asUINT*) pCurStackArgValue); if( !descr->parameterTypes[n].IsReference() ) { - // If the arg is less that 4 bytes, then move the - // bytes to the higher bytes within the dword + // If the arg is not 4 bytes which we coppied, lets do it again the right way asUINT numBytes = descr->parameterTypes[n].GetSizeInMemoryBytes(); if( numBytes == 1 ) { - pCurFixedArgValue[3] = pCurFixedArgValue[0]; - pCurFixedArgValue[0] = 0; + *((asQWORD*) pCurFixedArgValue) = *((asBYTE*) pCurStackArgValue); } else if( numBytes == 2 ) { - *(asWORD*)&pCurFixedArgValue[2] = *(asWORD*)&pCurFixedArgValue[0]; - *(asWORD*)&pCurFixedArgValue[0] = 0; + *((asQWORD*) pCurFixedArgValue) = *((asWORD*) pCurStackArgValue); + } + else if( numBytes == 8 ) + { + *((asQWORD*) pCurFixedArgValue) = *((asQWORD*) pCurStackArgValue); + pCurStackArgValue += 4; // Increase our cur stack arg value by 4 bytes to = 8 total later } } - pCurFixedArgValue += 4; + pCurFixedArgValue += 8; pCurStackArgValue += 4; // if it is a variable argument, account for the typeId @@ -703,7 +703,7 @@ asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, // Add the object pointer as the last argument ppcArgsType[argsCnt++] = ppcINTARG; ppcArgsType[argsCnt] = ppcENDARG; - *((asPWORD*)pCurFixedArgValue) = (asPWORD)obj; + *((asQWORD*)pCurFixedArgValue) = (asPWORD)obj; retQW = ppcFunc( ppcArgs, (asDWORD)func, ppcArgsType ); break; } diff --git a/lib/angelscript/source/as_compiler.cpp b/lib/angelscript/source/as_compiler.cpp index 62b67315d..337e209df 100644 --- a/lib/angelscript/source/as_compiler.cpp +++ b/lib/angelscript/source/as_compiler.cpp @@ -110,11 +110,14 @@ void asCCompiler::Reset(asCBuilder *builder, asCScriptCode *script, asCScriptFun m_isConstructor = false; m_isConstructorCalled = false; m_classDecl = 0; + m_globalVar = 0; nextLabel = 0; breakLabels.SetLength(0); continueLabels.SetLength(0); + numLambdas = 0; + byteCode.ClearAll(); } @@ -530,8 +533,8 @@ int asCCompiler::CompileFunction(asCBuilder *builder, asCScriptCode *script, asC // We need to parse the statement block now asCScriptNode *blockBegin; - // If the function signature was implicit, e.g. virtual property - // accessor, then the received node already is the statement block + // If the function signature was implicit, e.g. virtual property accessor or + // lambda function, then the received node already is the statement block if( func->nodeType != snStatementBlock ) blockBegin = func->lastChild; else @@ -1187,6 +1190,7 @@ void asCCompiler::CompileStatementBlock(asCScriptNode *block, bool ownVariableSc int asCCompiler::CompileGlobalVariable(asCBuilder *builder, asCScriptCode *script, asCScriptNode *node, sGlobalVariableDescription *gvar, asCScriptFunction *outFunc) { Reset(builder, script, outFunc); + m_globalVar = gvar; // Add a variable scope (even though variables can't be declared) AddVariableScope(); @@ -1354,8 +1358,8 @@ void asCCompiler::CompileInitAsCopy(asCDataType &dt, int offset, asCByteCode *bc } else { - // TODO: 2.28.1: Need to reserve variables, as the default constructor may need - // to allocate temporary variables to compute default args + // TODO: Need to reserve variables, as the default constructor may need + // to allocate temporary variables to compute default args // Allocate and construct the temporary object before whatever is already in the bytecode asCByteCode tmpBC(engine); @@ -1423,7 +1427,7 @@ int asCCompiler::PrepareArgument(asCDataType *paramType, asSExprContext *ctx, as param.MakeHandle(ctx->type.isExplicitHandle || ctx->type.IsNullConstant()); // Treat the void expression like a null handle when working with var types - if( ctx->type.IsVoidExpression() ) + if( ctx->IsVoidExpression() ) param = asCDataType::CreateNullHandle(); // If value assign is disabled for reference types, then make @@ -1484,6 +1488,17 @@ int asCCompiler::PrepareArgument(asCDataType *paramType, asSExprContext *ctx, as } else if( ctx->type.dataType.IsNullHandle() ) { + // Make sure the argument type can support handles (or is itself a handle) + if( !dt.SupportHandles() && !dt.IsObjectHandle() ) + { + asCString str; + str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, ctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), param.Format(outFunc->nameSpace).AddressOf()); + Error(str, node); + + ctx->type.Set(param); + return -1; + } + // Need to initialize a local temporary variable to // represent the null handle when passed as reference asASSERT( ctx->bc.GetLastInstr() == asBC_PshNull ); @@ -1599,42 +1614,53 @@ int asCCompiler::PrepareArgument(asCDataType *paramType, asSExprContext *ctx, as ctx->bc.AddCode(&tmpBC); } - // Make sure the variable is not used in the expression - offset = AllocateVariableNotIn(dt, true, false, ctx); - - if( dt.IsPrimitive() ) + // If the expression is marked as clean, then it can be used directly + // without the need to allocate another temporary value as it is known + // that the argument has no other value than the default + if( ctx->isCleanArg ) { - ctx->type.SetVariable(dt, offset, true); - PushVariableOnStack(ctx, true); + // Must be a local variable + asASSERT( ctx->type.isVariable ); } else { - // TODO: 2.28.1: Need to reserve variables, as the default constructor may need - // to allocate temporary variables to compute default args + // Make sure the variable is not used in the expression + offset = AllocateVariableNotIn(dt, true, false, ctx); - // Allocate and construct the temporary object - asCByteCode tmpBC(engine); - CallDefaultConstructor(dt, offset, IsVariableOnHeap(offset), &tmpBC, node); + if( dt.IsPrimitive() ) + { + ctx->type.SetVariable(dt, offset, true); + PushVariableOnStack(ctx, true); + } + else + { + // TODO: Need to reserve variables, as the default constructor may need + // to allocate temporary variables to compute default args - // Insert the code before the expression code - tmpBC.AddCode(&ctx->bc); - ctx->bc.AddCode(&tmpBC); + // Allocate and construct the temporary object + asCByteCode tmpBC(engine); + CallDefaultConstructor(dt, offset, IsVariableOnHeap(offset), &tmpBC, node); - dt.MakeReference(!dt.IsObject() || dt.IsObjectHandle()); - asCTypeInfo type; - type.Set(dt); - type.isTemporary = true; - type.stackOffset = (short)offset; + // Insert the code before the expression code + tmpBC.AddCode(&ctx->bc); + ctx->bc.AddCode(&tmpBC); - ctx->type = type; + dt.MakeReference(!dt.IsObject() || dt.IsObjectHandle()); + asCTypeInfo type; + type.Set(dt); + type.isTemporary = true; + type.stackOffset = (short)offset; - ctx->bc.InstrSHORT(asBC_PSF, (short)offset); - if( dt.IsObject() && !dt.IsObjectHandle() ) - ctx->bc.Instr(asBC_RDSPtr); + ctx->type = type; + + ctx->bc.InstrSHORT(asBC_PSF, (short)offset); + if( dt.IsObject() && !dt.IsObjectHandle() ) + ctx->bc.Instr(asBC_RDSPtr); + } + + // After the function returns the temporary variable will + // be assigned to the expression, if it is a valid lvalue } - - // After the function returns the temporary variable will - // be assigned to the expression, if it is a valid lvalue } else if( refType == asTM_INOUTREF ) { @@ -1825,14 +1851,15 @@ void asCCompiler::PrepareFunctionCall(int funcId, asCByteCode *bc, asCArrayGetFunctionDescription(funcId); + asASSERT( descr->parameterTypes.GetLength() == args.GetLength() ); + // If the function being called is the opAssign or copy constructor for the same type // as the argument, then we should avoid making temporary copy of the argument - asASSERT( descr->parameterTypes.GetLength() == args.GetLength() ); bool makingCopy = false; if( descr->parameterTypes.GetLength() == 1 && descr->parameterTypes[0].IsEqualExceptRefAndConst(args[0]->type.dataType) && - ((descr->name == "opAssign" && descr->objectType && descr->objectType == args[0]->type.dataType.GetObjectType()) || - (args[0]->type.dataType.GetObjectType() && descr->name == args[0]->type.dataType.GetObjectType()->name)) ) + (((descr->name == "opAssign" || descr->name == "$beh0") && descr->objectType && descr->objectType == args[0]->type.dataType.GetObjectType()) || + (descr->objectType == 0 && args[0]->type.dataType.GetObjectType() && descr->name == args[0]->type.dataType.GetObjectType()->name)) ) makingCopy = true; // Add code for arguments @@ -1870,8 +1897,8 @@ void asCCompiler::MoveArgsToStack(int funcId, asCByteCode *bc, asCArrayparameterTypes.GetLength() == 1 && descr->parameterTypes[0].IsEqualExceptRefAndConst(args[0]->type.dataType) && - ((descr->name == "opAssign" && descr->objectType && descr->objectType == args[0]->type.dataType.GetObjectType()) || - (args[0]->type.dataType.GetObjectType() && descr->name == args[0]->type.dataType.GetObjectType()->name)) ) + (((descr->name == "opAssign" || descr->name == "$beh0") && descr->objectType && descr->objectType == args[0]->type.dataType.GetObjectType()) || + (descr->objectType == 0 && args[0]->type.dataType.GetObjectType() && descr->name == args[0]->type.dataType.GetObjectType()->name)) ) makingCopy = true; #endif @@ -2266,7 +2293,7 @@ asUINT asCCompiler::MatchFunctions(asCArray &funcs, asCArrayfuncType == asFUNC_VIRTUAL ) desc = objectType->virtualFunctionTable[desc->vfTableIdx]; - //Match every named argument to an argument in the function + // Match every named argument to an argument in the function for( n = 0; n < namedArgs->GetLength(); ++n ) (*namedArgs)[n].match = asUINT(-1); @@ -2307,7 +2334,7 @@ asUINT asCCompiler::MatchFunctions(asCArray &funcs, asCArrayGetLength(); ++n ) @@ -2778,10 +2805,6 @@ bool asCCompiler::CompileInitialization(asCScriptNode *node, asCByteCode *bc, as { asSExprContext ctx(engine); - // TODO: copy: Here we should look for the best matching constructor, instead of - // just the copy constructor. Only if no appropriate constructor is - // available should the assignment operator be used. - // Compile the expression asSExprContext newExpr(engine); asSExprContext* expr; @@ -2797,159 +2820,294 @@ bool asCCompiler::CompileInitialization(asCScriptNode *node, asCByteCode *bc, as r = CompileAssignment(node, expr); } - // Call the default constructor here - if( isVarGlobOrMem == 0 ) - CallDefaultConstructor(type, offset, IsVariableOnHeap(offset), &ctx.bc, errNode); - else if( isVarGlobOrMem == 1 ) - CallDefaultConstructor(type, offset, true, &ctx.bc, errNode, isVarGlobOrMem); - else if( isVarGlobOrMem == 2 ) - CallDefaultConstructor(type, offset, type.IsReference(), &ctx.bc, errNode, isVarGlobOrMem); + // Look for appropriate constructor + asCArray funcs; + asCArray args; - if( r >= 0 ) + // Handles must use the handle assignment operation. + // Types that are ASHANDLE must not allow the use of the constructor in this case, + // because it is ambiguous whether a value assignment or handle assignment will be done. + // Only do this if the expression is of the same type, as the expression is an assignment + // and an initialization constructor may not have the same meaning. + // TODO: Should allow initialization constructor if it is declared as allowed for implicit conversions. + if( !type.IsObjectHandle() && !expr->type.isExplicitHandle && + !(type.GetObjectType() && (type.GetObjectType()->GetFlags() & asOBJ_ASHANDLE)) && + type.IsEqualExceptRefAndConst(expr->type.dataType) ) { - if( type.IsPrimitive() ) + asSTypeBehaviour *beh = type.GetBehaviour(); + if( beh ) { - if( type.IsReadOnly() && expr->type.isConstant ) - { - ImplicitConversion(expr, type, node, asIC_IMPLICIT_CONV); - - // Tell caller that the expression is a constant so it can mark the variable as pure constant - isConstantExpression = true; - *constantValue = expr->type.qwordValue; - } - - asSExprContext lctx(engine); - if( isVarGlobOrMem == 0 ) - lctx.type.SetVariable(type, offset, false); - else if( isVarGlobOrMem == 1 ) - { - lctx.type.Set(type); - lctx.type.dataType.MakeReference(true); - - // If it is an enum value, i.e. offset is negative, that is being compiled then - // we skip this as the bytecode won't be used anyway, only the constant value - if( offset >= 0 ) - lctx.bc.InstrPTR(asBC_LDG, engine->globalProperties[offset]->GetAddressOfValue()); - } + if( type.GetObjectType()->flags & asOBJ_REF ) + funcs = beh->factories; else - { - asASSERT( isVarGlobOrMem == 2 ); - lctx.type.Set(type); - lctx.type.dataType.MakeReference(true); - - // Load the reference of the primitive member into the register - lctx.bc.InstrSHORT(asBC_PSF, 0); - lctx.bc.Instr(asBC_RDSPtr); - lctx.bc.InstrSHORT_DW(asBC_ADDSi, (short)offset, engine->GetTypeIdFromDataType(asCDataType::CreateObject(outFunc->objectType, false))); - lctx.bc.Instr(asBC_PopRPtr); - } - lctx.type.dataType.MakeReadOnly(false); - lctx.type.isLValue = true; - - DoAssignment(&ctx, &lctx, expr, node, node, ttAssignment, node); - ProcessDeferredParams(&ctx); + funcs = beh->constructors; } - else + + asCString str = type.Format(outFunc->nameSpace); + args.PushLast(expr); + MatchFunctions(funcs, args, node, str.AddressOf(), 0, 0, 0, true); + } + + if( funcs.GetLength() == 1 ) + { + // Use the constructor + + // TODO: clean-up: A large part of this is identical to the initalization with argList above + + // Add the default values for arguments not explicitly supplied + int r = CompileDefaultAndNamedArgs(node, args, funcs[0], type.GetObjectType()); + + if( r == asSUCCESS ) { - // TODO: runtime optimize: Here we should look for the best matching constructor, instead of - // just the copy constructor. Only if no appropriate constructor is - // available should the assignment operator be used. - - asSExprContext lexpr(engine); - lexpr.type.Set(type); - if( isVarGlobOrMem == 0 ) - lexpr.type.dataType.MakeReference(IsVariableOnHeap(offset)); - else if( isVarGlobOrMem == 1 ) - lexpr.type.dataType.MakeReference(true); - else if( isVarGlobOrMem == 2 ) + asSExprContext ctx(engine); + if( type.GetObjectType() && (type.GetObjectType()->flags & asOBJ_REF) ) { - if( !lexpr.type.dataType.IsObject() || (lexpr.type.dataType.GetObjectType()->flags & asOBJ_REF) ) - lexpr.type.dataType.MakeReference(true); - } + if( isVarGlobOrMem == 0 ) + MakeFunctionCall(&ctx, funcs[0], 0, args, node, true, offset); + else + { + MakeFunctionCall(&ctx, funcs[0], 0, args, node); + ctx.bc.Instr(asBC_RDSPtr); + if( isVarGlobOrMem == 1 ) + { + // Store the returned handle in the global variable + ctx.bc.InstrPTR(asBC_PGA, engine->globalProperties[offset]->GetAddressOfValue()); + } + else + { + // Store the returned handle in the member + ctx.bc.InstrSHORT(asBC_PSF, 0); + ctx.bc.Instr(asBC_RDSPtr); + ctx.bc.InstrSHORT_DW(asBC_ADDSi, (short)offset, engine->GetTypeIdFromDataType(asCDataType::CreateObject(outFunc->objectType, false))); + } + ctx.bc.InstrPTR(asBC_REFCPY, type.GetObjectType()); + ReleaseTemporaryVariable(ctx.type.stackOffset, &ctx.bc); + } - // Allow initialization of constant variables - lexpr.type.dataType.MakeReadOnly(false); - - if( type.IsObjectHandle() ) - lexpr.type.isExplicitHandle = true; - - if( isVarGlobOrMem == 0 ) - { - lexpr.bc.InstrSHORT(asBC_PSF, (short)offset); - lexpr.type.stackOffset = (short)offset; - lexpr.type.isVariable = true; - } - else if( isVarGlobOrMem == 1 ) - { - lexpr.bc.InstrPTR(asBC_PGA, engine->globalProperties[offset]->GetAddressOfValue()); + // Pop the reference left by the function call + ctx.bc.Instr(asBC_PopPtr); } else { - lexpr.bc.InstrSHORT(asBC_PSF, 0); - lexpr.bc.Instr(asBC_RDSPtr); - lexpr.bc.InstrSHORT_DW(asBC_ADDSi, (short)offset, engine->GetTypeIdFromDataType(asCDataType::CreateObject(outFunc->objectType, false))); - lexpr.type.stackOffset = -1; - } - lexpr.type.isLValue = true; + bool onHeap = false; - - // If left expression resolves into a registered type - // check if the assignment operator is overloaded, and check - // the type of the right hand expression. If none is found - // the default action is a direct copy if it is the same type - // and a simple assignment. - bool assigned = false; - // Even though an ASHANDLE can be an explicit handle the overloaded operator needs to be called - if( lexpr.type.dataType.IsObject() && (!lexpr.type.isExplicitHandle || (lexpr.type.dataType.GetObjectType()->flags & asOBJ_ASHANDLE)) ) - { - bool useHndlAssign = lexpr.type.dataType.IsHandleToAsHandleType(); - assigned = CompileOverloadedDualOperator(node, &lexpr, expr, &ctx, useHndlAssign); - if( assigned ) + if( isVarGlobOrMem == 0 ) { - // Pop the resulting value - if( !ctx.type.dataType.IsPrimitive() ) - ctx.bc.Instr(asBC_PopPtr); + // When the object is allocated on the heap, the address where the + // reference will be stored must be pushed on the stack before the + // arguments. This reference on the stack is safe, even if the script + // is suspended during the evaluation of the arguments. + onHeap = IsVariableOnHeap(offset); + if( onHeap ) + ctx.bc.InstrSHORT(asBC_PSF, (short)offset); + } + else if( isVarGlobOrMem == 1 ) + { + // Push the address of the location where the variable will be stored on the stack. + // This reference is safe, because the addresses of the global variables cannot change. + onHeap = true; + ctx.bc.InstrPTR(asBC_PGA, engine->globalProperties[offset]->GetAddressOfValue()); + } + else + { + // Value types may be allocated inline if they are POD types + onHeap = !type.IsObject() || type.IsReference() || (type.GetObjectType()->flags & asOBJ_REF); + if( onHeap ) + { + ctx.bc.InstrSHORT(asBC_PSF, 0); + ctx.bc.Instr(asBC_RDSPtr); + ctx.bc.InstrSHORT_DW(asBC_ADDSi, (short)offset, engine->GetTypeIdFromDataType(asCDataType::CreateObject(outFunc->objectType, false))); + } + } - // Release the argument - ProcessDeferredParams(&ctx); + PrepareFunctionCall(funcs[0], &ctx.bc, args); + MoveArgsToStack(funcs[0], &ctx.bc, args, false); - // Release temporary variable that may be allocated by the overloaded operator - ReleaseTemporaryVariable(ctx.type, &ctx.bc); + // When the object is allocated on the stack, the address to the + // object is pushed on the stack after the arguments as the object pointer + if( !onHeap ) + { + if( isVarGlobOrMem == 2 ) + { + ctx.bc.InstrSHORT(asBC_PSF, 0); + ctx.bc.Instr(asBC_RDSPtr); + ctx.bc.InstrSHORT_DW(asBC_ADDSi, (short)offset, engine->GetTypeIdFromDataType(asCDataType::CreateObject(outFunc->objectType, false))); + } + else + { + ctx.bc.InstrSHORT(asBC_PSF, (short)offset); + } + } + + PerformFunctionCall(funcs[0], &ctx, onHeap, &args, type.GetObjectType()); + + if( isVarGlobOrMem == 0 ) + { + // Mark the object in the local variable as initialized + ctx.bc.ObjInfo(offset, asOBJ_INIT); } } + bc->AddCode(&ctx.bc); + } + } + else + { + // Call the default constructur, then call the assignment operator - if( !assigned ) + // Call the default constructor here + if( isVarGlobOrMem == 0 ) + CallDefaultConstructor(type, offset, IsVariableOnHeap(offset), &ctx.bc, errNode); + else if( isVarGlobOrMem == 1 ) + CallDefaultConstructor(type, offset, true, &ctx.bc, errNode, isVarGlobOrMem); + else if( isVarGlobOrMem == 2 ) + CallDefaultConstructor(type, offset, type.IsReference(), &ctx.bc, errNode, isVarGlobOrMem); + + if( r >= 0 ) + { + if( type.IsPrimitive() ) { - PrepareForAssignment(&lexpr.type.dataType, expr, node, false); - - // If the expression is constant and the variable also is constant - // then mark the variable as pure constant. This will allow the compiler - // to optimize expressions with this variable. if( type.IsReadOnly() && expr->type.isConstant ) { + ImplicitConversion(expr, type, node, asIC_IMPLICIT_CONV); + + // Tell caller that the expression is a constant so it can mark the variable as pure constant isConstantExpression = true; *constantValue = expr->type.qwordValue; } - // Add expression code to bytecode - MergeExprBytecode(&ctx, expr); + asSExprContext lctx(engine); + if( isVarGlobOrMem == 0 ) + lctx.type.SetVariable(type, offset, false); + else if( isVarGlobOrMem == 1 ) + { + lctx.type.Set(type); + lctx.type.dataType.MakeReference(true); - // Add byte code for storing value of expression in variable - ctx.bc.AddCode(&lexpr.bc); + // If it is an enum value, i.e. offset is negative, that is being compiled then + // we skip this as the bytecode won't be used anyway, only the constant value + if( offset >= 0 ) + lctx.bc.InstrPTR(asBC_LDG, engine->globalProperties[offset]->GetAddressOfValue()); + } + else + { + asASSERT( isVarGlobOrMem == 2 ); + lctx.type.Set(type); + lctx.type.dataType.MakeReference(true); - PerformAssignment(&lexpr.type, &expr->type, &ctx.bc, errNode); - - // Release temporary variables used by expression - ReleaseTemporaryVariable(expr->type, &ctx.bc); - - ctx.bc.Instr(asBC_PopPtr); + // Load the reference of the primitive member into the register + lctx.bc.InstrSHORT(asBC_PSF, 0); + lctx.bc.Instr(asBC_RDSPtr); + lctx.bc.InstrSHORT_DW(asBC_ADDSi, (short)offset, engine->GetTypeIdFromDataType(asCDataType::CreateObject(outFunc->objectType, false))); + lctx.bc.Instr(asBC_PopRPtr); + } + lctx.type.dataType.MakeReadOnly(false); + lctx.type.isLValue = true; + DoAssignment(&ctx, &lctx, expr, node, node, ttAssignment, node); ProcessDeferredParams(&ctx); } - } - } + else + { + // TODO: runtime optimize: Here we should look for the best matching constructor, instead of + // just the copy constructor. Only if no appropriate constructor is + // available should the assignment operator be used. - bc->AddCode(&ctx.bc); + asSExprContext lexpr(engine); + lexpr.type.Set(type); + if( isVarGlobOrMem == 0 ) + lexpr.type.dataType.MakeReference(IsVariableOnHeap(offset)); + else if( isVarGlobOrMem == 1 ) + lexpr.type.dataType.MakeReference(true); + else if( isVarGlobOrMem == 2 ) + { + if( !lexpr.type.dataType.IsObject() || (lexpr.type.dataType.GetObjectType()->flags & asOBJ_REF) ) + lexpr.type.dataType.MakeReference(true); + } + + // Allow initialization of constant variables + lexpr.type.dataType.MakeReadOnly(false); + + if( type.IsObjectHandle() ) + lexpr.type.isExplicitHandle = true; + + if( isVarGlobOrMem == 0 ) + { + lexpr.bc.InstrSHORT(asBC_PSF, (short)offset); + lexpr.type.stackOffset = (short)offset; + lexpr.type.isVariable = true; + } + else if( isVarGlobOrMem == 1 ) + { + lexpr.bc.InstrPTR(asBC_PGA, engine->globalProperties[offset]->GetAddressOfValue()); + } + else + { + lexpr.bc.InstrSHORT(asBC_PSF, 0); + lexpr.bc.Instr(asBC_RDSPtr); + lexpr.bc.InstrSHORT_DW(asBC_ADDSi, (short)offset, engine->GetTypeIdFromDataType(asCDataType::CreateObject(outFunc->objectType, false))); + lexpr.type.stackOffset = -1; + } + lexpr.type.isLValue = true; + + + // If left expression resolves into a registered type + // check if the assignment operator is overloaded, and check + // the type of the right hand expression. If none is found + // the default action is a direct copy if it is the same type + // and a simple assignment. + bool assigned = false; + // Even though an ASHANDLE can be an explicit handle the overloaded operator needs to be called + if( lexpr.type.dataType.IsObject() && (!lexpr.type.isExplicitHandle || (lexpr.type.dataType.GetObjectType()->flags & asOBJ_ASHANDLE)) ) + { + bool useHndlAssign = lexpr.type.dataType.IsHandleToAsHandleType(); + assigned = CompileOverloadedDualOperator(node, &lexpr, expr, &ctx, useHndlAssign); + if( assigned ) + { + // Pop the resulting value + if( !ctx.type.dataType.IsPrimitive() ) + ctx.bc.Instr(asBC_PopPtr); + + // Release the argument + ProcessDeferredParams(&ctx); + + // Release temporary variable that may be allocated by the overloaded operator + ReleaseTemporaryVariable(ctx.type, &ctx.bc); + } + } + + if( !assigned ) + { + PrepareForAssignment(&lexpr.type.dataType, expr, node, false); + + // If the expression is constant and the variable also is constant + // then mark the variable as pure constant. This will allow the compiler + // to optimize expressions with this variable. + if( type.IsReadOnly() && expr->type.isConstant ) + { + isConstantExpression = true; + *constantValue = expr->type.qwordValue; + } + + // Add expression code to bytecode + MergeExprBytecode(&ctx, expr); + + // Add byte code for storing value of expression in variable + ctx.bc.AddCode(&lexpr.bc); + + PerformAssignment(&lexpr.type, &expr->type, &ctx.bc, errNode); + + // Release temporary variables used by expression + ReleaseTemporaryVariable(expr->type, &ctx.bc); + + ctx.bc.Instr(asBC_PopPtr); + + ProcessDeferredParams(&ctx); + } + } + } + + bc->AddCode(&ctx.bc); + } } else { @@ -2976,8 +3134,7 @@ void asCCompiler::CompileInitList(asCTypeInfo *var, asCScriptNode *node, asCByte { // Check if the type supports initialization lists if( var->dataType.GetObjectType() == 0 || - var->dataType.GetBehaviour()->listFactory == 0 || - var->dataType.IsObjectHandle() ) + var->dataType.GetBehaviour()->listFactory == 0 ) { asCString str; str.Format(TXT_INIT_LIST_CANNOT_BE_USED_WITH_s, var->dataType.Format(outFunc->nameSpace).AddressOf()); @@ -4098,13 +4255,14 @@ void asCCompiler::CompileForStatement(asCScriptNode *fnode, asCByteCode *bc) } //--------------------------- - // Compile the increment statement + // Compile the increment statement(s) asCByteCode nextBC(engine); - asCScriptNode *third = second->next; - if( third->nodeType == snExpressionStatement ) + asCScriptNode *cnode = second->next; + while( cnode && cnode->nodeType == snExpressionStatement && cnode != fnode->lastChild ) { - LineInstr(&nextBC, third->tokenPos); - CompileExpressionStatement(third, &nextBC); + LineInstr(&nextBC, cnode->tokenPos); + CompileExpressionStatement(cnode, &nextBC); + cnode = cnode->next; } //------------------------------ @@ -4367,6 +4525,10 @@ void asCCompiler::CompileExpressionStatement(asCScriptNode *enode, asCByteCode * if( expr.IsClassMethod() || expr.IsGlobalFunc() ) Error(TXT_INVALID_EXPRESSION_AMBIGUOUS_NAME, enode); + // Must not have unused anonymous functions + if( expr.IsLambda() ) + Error(TXT_INVALID_EXPRESSION_LAMBDA, enode); + // If we get here and there is still an unprocessed property // accessor, then process it as a get access. Don't call if there is // already a compile error, or we might report an error that is not valid @@ -5855,11 +6017,58 @@ asUINT asCCompiler::ImplicitConvPrimitiveToPrimitive(asSExprContext *ctx, const return cost; } +asUINT asCCompiler::ImplicitConvLambdaToFunc(asSExprContext *ctx, const asCDataType &to, asCScriptNode * /*node*/, EImplicitConv /*convType*/, bool generateCode) +{ + asASSERT( to.GetFuncDef() && ctx->IsLambda() ); + + // Check that the lambda has the correct amount of arguments + asUINT count = 0; + asCScriptNode *argNode = ctx->exprNode->firstChild; + while( argNode->nodeType == snIdentifier ) + { + count++; + argNode = argNode->next; + } + asASSERT( argNode->nodeType == snStatementBlock ); + + asCScriptFunction *funcDef = to.GetFuncDef(); + if( funcDef->parameterTypes.GetLength() != count ) + return asCC_NO_CONV; + + // The Lambda can be used as this funcdef + ctx->type.dataType = to; + + if( generateCode ) + { + // Build a unique name for the anonymous function + asCString name; + if( m_globalVar ) + name.Format("$%s$%d", m_globalVar->name.AddressOf(), numLambdas++); + else + name.Format("$%s$%d", outFunc->GetDeclaration(), numLambdas++); + + // Register the lambda with the builder for later compilation + asCScriptFunction *func = builder->RegisterLambda(ctx->exprNode, script, funcDef, name, outFunc->nameSpace); + asASSERT( func == 0 || funcDef->IsSignatureExceptNameEqual(func) ); + ctx->bc.InstrPTR(asBC_FuncPtr, func); + + // Clear the expression node as it is no longer valid + ctx->exprNode = 0; + } + + return asCC_CONST_CONV; +} + asUINT asCCompiler::ImplicitConversion(asSExprContext *ctx, const asCDataType &to, asCScriptNode *node, EImplicitConv convType, bool generateCode, bool allowObjectConstruct) { asASSERT( ctx->type.dataType.GetTokenType() != ttUnrecognizedToken || ctx->type.dataType.IsNullHandle() ); + if( to.GetFuncDef() && ctx->IsLambda() ) + { + return ImplicitConvLambdaToFunc(ctx, to, node, convType, generateCode); + } + // No conversion from void to any other type if( ctx->type.dataType.GetTokenType() == ttVoid ) return asCC_NO_CONV; @@ -6329,16 +6538,25 @@ asUINT asCCompiler::ImplicitConvObjectValue(asSExprContext *ctx, const asCDataTy // Pass the reference of that variable to the function as output parameter asCDataType toRef(to); toRef.MakeReference(false); - asCArray args; asSExprContext arg(engine); arg.bc.InstrSHORT(asBC_PSF, (short)stackOffset); + + // If this an object on the heap, the pointer must be dereferenced + if( IsVariableOnHeap(stackOffset) ) + arg.bc.Instr(asBC_RDSPtr); + // Don't mark the variable as temporary, so it won't be freed too early arg.type.SetVariable(toRef, stackOffset, false); arg.type.isLValue = true; arg.exprNode = node; - args.PushLast(&arg); + + // Mark the argument as clean, so that MakeFunctionCall knows it + // doesn't have to make a copy of it in order to protect the value + arg.isCleanArg = true; // Call the behaviour method + asCArray args; + args.PushLast(&arg); MakeFunctionCall(ctx, funcs[0], ctx->type.dataType.GetObjectType(), args, node); // Use the reference to the variable as the result of the expression @@ -7876,9 +8094,9 @@ int asCCompiler::CompileCondition(asCScriptNode *expr, asSExprContext *ctx) // In case the options were to objects, it is necessary to dereference the pointer on // the stack so it will point to the actual object, instead of the variable - if( le.type.dataType.IsReference() && le.type.isVariable && le.type.dataType.IsObject() && !le.type.dataType.IsObjectHandle() ) + if( le.type.dataType.IsReference() && le.type.dataType.IsObject() && !le.type.dataType.IsObjectHandle() ) { - asASSERT( re.type.dataType.IsReference() && re.type.isVariable && re.type.dataType.IsObject() && !re.type.dataType.IsObjectHandle() ); + asASSERT( re.type.dataType.IsReference() && re.type.dataType.IsObject() && !re.type.dataType.IsObjectHandle() ); ctx->bc.Instr(asBC_RDSPtr); } @@ -8055,7 +8273,15 @@ int asCCompiler::CompileExpression(asCScriptNode *expr, asSExprContext *ctx) } // Convert to polish post fix, i.e: a+b => ab+ + asCArray postfix; + ConvertToPostFix(expr, postfix); + // Compile the postfix formatted expression + return CompilePostFixExpression(&postfix, ctx); +} + +void asCCompiler::ConvertToPostFix(asCScriptNode *expr, asCArray &postfix) +{ // The algorithm that I've implemented here is similar to // Djikstra's Shunting Yard algorithm, though I didn't know it at the time. // ref: http://en.wikipedia.org/wiki/Shunting-yard_algorithm @@ -8069,28 +8295,26 @@ int asCCompiler::CompileExpression(asCScriptNode *expr, asSExprContext *ctx) node = node->next; } - asCArray stack(count); - asCArray stack2(count); + asCArray stackA(count); + asCArray &stackB = postfix; + stackB.Allocate(count, false); node = expr->firstChild; while( node ) { int precedence = GetPrecedence(node); - while( stack.GetLength() > 0 && - precedence <= GetPrecedence(stack[stack.GetLength()-1]) ) - stack2.PushLast(stack.PopLast()); + while( stackA.GetLength() > 0 && + precedence <= GetPrecedence(stackA[stackA.GetLength()-1]) ) + stackB.PushLast(stackA.PopLast()); - stack.PushLast(node); + stackA.PushLast(node); node = node->next; } - while( stack.GetLength() > 0 ) - stack2.PushLast(stack.PopLast()); - - // Compile the postfix formatted expression - return CompilePostFixExpression(&stack2, ctx); + while( stackA.GetLength() > 0 ) + stackB.PushLast(stackA.PopLast()); } int asCCompiler::CompilePostFixExpression(asCArray *postfix, asSExprContext *ctx) @@ -8402,12 +8626,14 @@ int asCCompiler::CompileVariableAccess(const asCString &name, const asCString &s // Recursively search parent namespaces for global entities asCString currScope = scope; - if( scope == "" ) - currScope = outFunc->nameSpace->name; + + // Get the namespace for this scope. This may return null if the scope is an enum + asSNameSpace *ns = DetermineNameSpace(currScope); + if( ns && currScope != "::" ) + currScope = ns->name; + while( !found && !noGlobal && !objType ) { - asSNameSpace *ns = DetermineNameSpace(currScope); - // Is it a global property? if( !found && ns ) { @@ -8621,7 +8847,7 @@ int asCCompiler::CompileVariableAccess(const asCString &name, const asCString &s { ctx->type.SetDummy(); asCString str; - str.Format(TXT_UNKNOWN_SCOPE_s, currScope.AddressOf()); + str.Format(TXT_UNKNOWN_SCOPE_s, scope.AddressOf()); Error(str, errNode); return -1; } @@ -8639,6 +8865,9 @@ int asCCompiler::CompileVariableAccess(const asCString &name, const asCString &s currScope = currScope.SubString(0, pos); else currScope = "::"; + + if( ns ) + ns = engine->GetParentNameSpace(ns); } } @@ -8872,7 +9101,7 @@ int asCCompiler::CompileExpressionValue(asCScriptNode *node, asSExprContext *ctx } else if( vnode->nodeType == snConstructCall ) { - CompileConstructCall(vnode, ctx); + return CompileConstructCall(vnode, ctx); } else if( vnode->nodeType == snAssignment ) { @@ -8888,12 +9117,19 @@ int asCCompiler::CompileExpressionValue(asCScriptNode *node, asSExprContext *ctx else if( vnode->nodeType == snCast ) { // Implement the cast operator - CompileConversion(vnode, ctx); + return CompileConversion(vnode, ctx); } else if( vnode->nodeType == snUndefined && vnode->tokenType == ttVoid ) { // This is a void expression - ctx->type.SetVoidExpression(); + ctx->SetVoidExpression(); + } + else if( vnode->nodeType == snFunction ) + { + // This is an anonymous function + // Defer the evaluation of the function until it known where it + // will be used, which is where the signature will be defined + ctx->SetLambda(vnode); } else asASSERT(false); @@ -9149,7 +9385,7 @@ void asCCompiler::ProcessHeredocStringConstant(asCString &str, asCScriptNode *no str = tmp; } -void asCCompiler::CompileConversion(asCScriptNode *node, asSExprContext *ctx) +int asCCompiler::CompileConversion(asCScriptNode *node, asSExprContext *ctx) { asSExprContext expr(engine); asCDataType to; @@ -9221,7 +9457,7 @@ void asCCompiler::CompileConversion(asCScriptNode *node, asSExprContext *ctx) { // Assume that the error can be fixed and allow the compilation to continue ctx->type.SetConstantDW(to, 0); - return; + return -1; } ProcessPropertyGetAccessor(&expr, node); @@ -9230,7 +9466,7 @@ void asCCompiler::CompileConversion(asCScriptNode *node, asSExprContext *ctx) if( expr.IsClassMethod() ) { Error(TXT_INVALID_OP_ON_METHOD, node); - return; + return -1; } // We don't want a reference for conversion casts @@ -9252,7 +9488,7 @@ void asCCompiler::CompileConversion(asCScriptNode *node, asSExprContext *ctx) // This will keep information about constant type MergeExprBytecode(ctx, &expr); ctx->type = expr.type; - return; + return 0; } if( to.IsEqualExceptRefAndConst(expr.type.dataType) && to.IsPrimitive() ) @@ -9260,7 +9496,7 @@ void asCCompiler::CompileConversion(asCScriptNode *node, asSExprContext *ctx) MergeExprBytecode(ctx, &expr); ctx->type = expr.type; ctx->type.dataType.MakeReadOnly(true); - return; + return 0; } // The implicit conversion already does most of the conversions permitted, @@ -9284,7 +9520,7 @@ void asCCompiler::CompileConversion(asCScriptNode *node, asSExprContext *ctx) } if( conversionOK ) - return; + return 0; // Conversion not available ctx->type.SetDummy(); @@ -9298,6 +9534,7 @@ void asCCompiler::CompileConversion(asCScriptNode *node, asSExprContext *ctx) msg.Format(TXT_NO_CONVERSION_s_TO_s, strFrom.AddressOf(), strTo.AddressOf()); Error(msg, node); + return -1; } void asCCompiler::AfterFunctionCall(int funcID, asCArray &args, asSExprContext *ctx, bool deferAll) @@ -9314,12 +9551,12 @@ void asCCompiler::AfterFunctionCall(int funcID, asCArray &args, int n = (int)descr->parameterTypes.GetLength() - 1; for( ; n >= 0; n-- ) { - // All &out arguments must be deferred + // All &out arguments must be deferred, except if the argument is clean, in which case the actual reference was passed in to the function // If deferAll is set all objects passed by reference or handle must be deferred - if( (descr->parameterTypes[n].IsReference() && (descr->inOutFlags[n] & asTM_OUTREF)) || + if( (descr->parameterTypes[n].IsReference() && (descr->inOutFlags[n] & asTM_OUTREF) && !args[n]->isCleanArg) || (descr->parameterTypes[n].IsObject() && deferAll && (descr->parameterTypes[n].IsReference() || descr->parameterTypes[n].IsObjectHandle())) ) { - asASSERT( !(descr->parameterTypes[n].IsReference() && (descr->inOutFlags[n] == asTM_OUTREF)) || args[n]->origExpr ); + asASSERT( !(descr->parameterTypes[n].IsReference() && (descr->inOutFlags[n] == asTM_OUTREF) && !args[n]->isCleanArg) || args[n]->origExpr ); // For &inout, only store the argument if it is for a temporary variable if( engine->ep.allowUnsafeReferences || @@ -9408,11 +9645,11 @@ void asCCompiler::ProcessDeferredParams(asSExprContext *ctx) { // We must still evaluate the expression MergeExprBytecode(ctx, expr); - if( !expr->type.IsVoidExpression() && (!expr->type.isConstant || expr->type.IsNullConstant()) ) + if( !expr->IsVoidExpression() && (!expr->type.isConstant || expr->type.IsNullConstant()) ) ctx->bc.Instr(asBC_PopPtr); // Give an error, except if the argument is void, null or 0 which indicate the argument is explicitly to be ignored - if( !expr->type.IsVoidExpression() && !expr->type.IsNullConstant() && !(expr->type.isConstant && expr->type.qwordValue == 0) ) + if( !expr->IsVoidExpression() && !expr->type.IsNullConstant() && !(expr->type.isConstant && expr->type.qwordValue == 0) ) Error(TXT_ARG_NOT_LVALUE, outParam.argNode); ReleaseTemporaryVariable(outParam.argType, &ctx->bc); @@ -9446,13 +9683,14 @@ void asCCompiler::ProcessDeferredParams(asSExprContext *ctx) } -void asCCompiler::CompileConstructCall(asCScriptNode *node, asSExprContext *ctx) +int asCCompiler::CompileConstructCall(asCScriptNode *node, asSExprContext *ctx) { // The first node is a datatype node asCString name; asCTypeInfo tempObj; bool onHeap = true; asCArray funcs; + bool error = false; // It is possible that the name is really a constructor asCDataType dt; @@ -9460,8 +9698,7 @@ void asCCompiler::CompileConstructCall(asCScriptNode *node, asSExprContext *ctx) if( dt.IsPrimitive() ) { // This is a cast to a primitive type - CompileConversion(node, ctx); - return; + return CompileConversion(node, ctx); } if( dt.GetObjectType() && (dt.GetObjectType()->flags & asOBJ_IMPLICIT_HANDLE) ) @@ -9477,7 +9714,7 @@ void asCCompiler::CompileConstructCall(asCScriptNode *node, asSExprContext *ctx) str.Format(TXT_CANT_CONSTRUCT_s_USE_REF_CAST, dt.Format(outFunc->nameSpace).AddressOf()); Error(str, node); ctx->type.SetDummy(); - return; + return -1; } if( !dt.CanBeInstantiated() ) @@ -9492,7 +9729,7 @@ void asCCompiler::CompileConstructCall(asCScriptNode *node, asSExprContext *ctx) str.Format(TXT_DATA_TYPE_CANT_BE_s, dt.Format(outFunc->nameSpace).AddressOf()); Error(str, node); ctx->type.SetDummy(); - return; + return -1; } // Do not allow constructing non-shared types in shared functions @@ -9502,6 +9739,7 @@ void asCCompiler::CompileConstructCall(asCScriptNode *node, asSExprContext *ctx) asCString msg; msg.Format(TXT_SHARED_CANNOT_USE_NON_SHARED_TYPE_s, dt.GetObjectType()->name.AddressOf()); Error(msg, node); + return -1; } // Compile the arguments @@ -9528,7 +9766,7 @@ void asCCompiler::CompileConstructCall(asCScriptNode *node, asSExprContext *ctx) asDELETE(args[0],asSExprContext); - return; + return 0; } } @@ -9586,7 +9824,7 @@ void asCCompiler::CompileConstructCall(asCScriptNode *node, asSExprContext *ctx) // Push the reference on the stack ctx->bc.InstrSHORT(asBC_PSF, tempObj.stackOffset); - return; + return 0; } } @@ -9603,7 +9841,10 @@ void asCCompiler::CompileConstructCall(asCScriptNode *node, asSExprContext *ctx) // The delegate must be able to hold on to a reference to the object if( !args[0]->type.dataType.SupportHandles() ) + { Error(TXT_CANNOT_CREATE_DELEGATE_FOR_NOREF_TYPES, node); + error = true; + } else { // Filter the available object methods to find the one that matches the func def @@ -9652,18 +9893,22 @@ void asCCompiler::CompileConstructCall(asCScriptNode *node, asSExprContext *ctx) // Push a reference to the temporary variable on the stack ctx->bc.InstrSHORT(asBC_PSF, (short)returnOffset); + + // Clean up arguments + ReleaseTemporaryVariable(args[0]->type, &ctx->bc); } else { asCString msg; msg.Format(TXT_NO_MATCHING_SIGNATURES_TO_s, dt.GetFuncDef()->GetDeclaration()); Error(msg.AddressOf(), node); + error = true; } } // Clean-up arg asDELETE(args[0],asSExprContext); - return; + return error ? -1 : 0; } MatchFunctions(funcs, args, node, name.AddressOf(), &namedArgs, 0, false); @@ -9671,6 +9916,7 @@ void asCCompiler::CompileConstructCall(asCScriptNode *node, asSExprContext *ctx) if( funcs.GetLength() != 1 ) { // The error was reported by MatchFunctions() + error = true; // Dummy value ctx->type.SetDummy(); @@ -9726,12 +9972,15 @@ void asCCompiler::CompileConstructCall(asCScriptNode *node, asSExprContext *ctx) PerformFunctionCall(funcs[0], ctx, false, &args); } } + else + error = true; } } else { // Failed to compile the argument list, set the result to the dummy type ctx->type.SetDummy(); + error = true; } // Cleanup @@ -9745,6 +9994,8 @@ void asCCompiler::CompileConstructCall(asCScriptNode *node, asSExprContext *ctx) { asDELETE(namedArgs[n].ctx,asSExprContext); } + + return error ? -1 : 0; } @@ -9951,7 +10202,7 @@ int asCCompiler::CompileFunctionCall(asCScriptNode *node, asSExprContext *ctx, a if( CompileArgumentList(node->lastChild, args, namedArgs) >= 0 ) { // Special case: Allow calling func(void) with an expression that evaluates to no datatype, but isn't exactly 'void' - if( args.GetLength() == 1 && args[0]->type.dataType == asCDataType::CreatePrimitive(ttVoid, false) && !args[0]->type.IsVoidExpression() ) + if( args.GetLength() == 1 && args[0]->type.IsVoid() && !args[0]->IsVoidExpression() ) { // Evaluate the expression before the function call MergeExprBytecode(ctx, args[0]); @@ -10062,7 +10313,8 @@ asSNameSpace *asCCompiler::DetermineNameSpace(const asCString &scope) if( scope == "" ) { - if( outFunc->nameSpace->name != "" ) + // When compiling default argument expression the correct namespace is stored in the outFunc even for objects + if( outFunc->nameSpace->name != "" || isCompilingDefaultArg ) ns = outFunc->nameSpace; else if( outFunc->objectType && outFunc->objectType->nameSpace->name != "" ) ns = outFunc->objectType->nameSpace; @@ -10089,7 +10341,7 @@ int asCCompiler::CompileExpressionPreOp(asCScriptNode *node, asSExprContext *ctx } // Don't allow any operators on void expressions - if( ctx->type.IsVoidExpression() ) + if( ctx->IsVoidExpression() ) { Error(TXT_VOID_CANT_BE_OPERAND, node); return -1; @@ -10927,11 +11179,25 @@ int asCCompiler::ProcessPropertyGetSetAccessor(asSExprContext *ctx, asSExprConte before.bc.InstrPTR(asBC_REFCPY, func->objectType); before.bc.Instr(asBC_PopPtr); + if( lctx->type.isTemporary ) + { + // Add the release of the temporary variable as a deferred expression + asSDeferredParam deferred; + deferred.origExpr = 0; + deferred.argInOutFlags = asTM_INREF; + deferred.argNode = 0; + deferred.argType.SetVariable(ctx->type.dataType, lctx->type.stackOffset, true); + before.deferredParams.PushLast(deferred); + } + // Update the left expression to use the local variable lctx->bc.InstrSHORT(asBC_PSF, (short)offset); lctx->type.stackOffset = (short)offset; lctx->property_ref = true; + // Don't release the temporary variable too early + lctx->type.isTemporary = false; + ctx->bc.AddCode(&before.bc); } @@ -10960,6 +11226,10 @@ int asCCompiler::ProcessPropertyGetSetAccessor(asSExprContext *ctx, asSExprConte if( before.type.stackOffset ) ReleaseTemporaryVariable(before.type.stackOffset, &ctx->bc); + asASSERT( ctx->deferredParams.GetLength() == 0 ); + ctx->deferredParams = before.deferredParams; + ProcessDeferredParams(ctx); + return 0; } @@ -11044,7 +11314,7 @@ int asCCompiler::CompileExpressionPostOp(asCScriptNode *node, asSExprContext *ct } // Don't allow any operators on void expressions - if( ctx->type.IsVoidExpression() ) + if( ctx->IsVoidExpression() ) { Error(TXT_VOID_CANT_BE_OPERAND, node); return -1; @@ -11653,7 +11923,7 @@ asUINT asCCompiler::MatchArgument(asCArray &funcs, asCArraytype.IsVoidExpression() ) + if( argExpr->IsVoidExpression() ) { if( desc->inOutFlags[paramNum] == asTM_OUTREF ) return 0; @@ -11665,6 +11935,7 @@ int asCCompiler::MatchArgument(asCScriptFunction *desc, const asSExprContext *ar ti.type = argExpr->type; ti.methodName = argExpr->methodName; ti.enumValue = argExpr->enumValue; + ti.exprNode = argExpr->exprNode; if( argExpr->type.dataType.IsPrimitive() ) ti.type.dataType.MakeReference(false); int cost = ImplicitConversion(&ti, desc->parameterTypes[paramNum], 0, asIC_IMPLICIT_CONV, false, allowObjectConstruct); @@ -11723,7 +11994,8 @@ int asCCompiler::MatchArgument(asCScriptFunction *desc, const asSExprContext *ar void asCCompiler::PrepareArgument2(asSExprContext *ctx, asSExprContext *arg, asCDataType *paramType, bool isFunction, int refType, bool isMakingCopy) { // Reference parameters whose value won't be used don't evaluate the expression - if( paramType->IsReference() && !(refType & asTM_INREF) ) + // Clean arguments (i.e. default value) will be passed in directly as there is nothing to protect + if( paramType->IsReference() && !(refType & asTM_INREF) && !arg->isCleanArg ) { // Store the original bytecode so that it can be reused when processing the deferred output parameter asSExprContext *orig = asNEW(asSExprContext)(engine); @@ -12077,28 +12349,14 @@ int asCCompiler::CompileOverloadedDualOperator2(asCScriptNode *node, const char return 0; } -void asCCompiler::MakeFunctionCall(asSExprContext *ctx, int funcId, asCObjectType *objectType, asCArray &args, asCScriptNode * /*node*/, bool useVariable, int stackOffset, int funcPtrVar) +void asCCompiler::MakeFunctionCall(asSExprContext *ctx, int funcId, asCObjectType *objectType, asCArray &args, asCScriptNode *node, bool useVariable, int stackOffset, int funcPtrVar) { if( objectType ) - { Dereference(ctx, true); - // This following warning was removed as there may be valid reasons - // for calling non-const methods on temporary objects, and we shouldn't - // warn when there is no way of removing the warning. -/* - // Warn if the method is non-const and the object is temporary - // since the changes will be lost when the object is destroyed. - // If the object is accessed through a handle, then it is assumed - // the object is not temporary, even though the handle is. - if( ctx->type.isTemporary && - !ctx->type.dataType.IsObjectHandle() && - !engine->scriptFunctions[funcId]->isReadOnly ) - { - Warning("A non-const method is called on temporary object. Changes to the object may be lost.", node); - Information(engine->scriptFunctions[funcId]->GetDeclaration(), node); - } -*/ } + // Store the expression node for error reporting + if( ctx->exprNode == 0 ) + ctx->exprNode = node; asCByteCode objBC(engine); objBC.AddCode(&ctx->bc); @@ -12162,7 +12420,7 @@ int asCCompiler::CompileOperator(asCScriptNode *node, asSExprContext *lctx, asSE } // Don't allow any operators on void expressions - if( lctx->type.IsVoidExpression() || rctx->type.IsVoidExpression() ) + if( lctx->IsVoidExpression() || rctx->IsVoidExpression() ) { Error(TXT_VOID_CANT_BE_OPERAND, node); return -1; @@ -14042,7 +14300,19 @@ void asCCompiler::PerformFunctionCall(int funcId, asSExprContext *ctx, bool isCo else if( descr->funcType == asFUNC_SCRIPT ) ctx->bc.Call(asBC_CALL , descr->id, argSize); else if( descr->funcType == asFUNC_SYSTEM ) - ctx->bc.Call(asBC_CALLSYS , descr->id, argSize); + { + // Check if we can use the faster asBC_Thiscall1 instruction, i.e. one of + // type &obj::func(int) + // type &obj::func(uint) + if( descr->GetObjectType() && descr->returnType.IsReference() && + descr->parameterTypes.GetLength() == 1 && + (descr->parameterTypes[0].IsIntegerType() || descr->parameterTypes[0].IsUnsignedType()) && + descr->parameterTypes[0].GetSizeInMemoryBytes() == 4 && + !descr->parameterTypes[0].IsReference() ) + ctx->bc.Call(asBC_Thiscall1, descr->id, argSize); + else + ctx->bc.Call(asBC_CALLSYS , descr->id, argSize); + } else if( descr->funcType == asFUNC_FUNCDEF ) ctx->bc.CallPtr(asBC_CallPtr, funcPtrVar, argSize); } @@ -14201,20 +14471,7 @@ void asCCompiler::MergeExprBytecodeAndType(asSExprContext *before, asSExprContex { MergeExprBytecode(before, after); - before->type = after->type; - before->property_get = after->property_get; - before->property_set = after->property_set; - before->property_const = after->property_const; - before->property_handle = after->property_handle; - before->property_ref = after->property_ref; - before->property_arg = after->property_arg; - before->exprNode = after->exprNode; - before->methodName = after->methodName; - before->enumValue = after->enumValue; - - after->property_arg = 0; - - // Do not copy the origExpr member + before->Merge(after); } void asCCompiler::FilterConst(asCArray &funcs, bool removeConst) diff --git a/lib/angelscript/source/as_compiler.h b/lib/angelscript/source/as_compiler.h index bd0ff7fd8..f9de7e475 100644 --- a/lib/angelscript/source/as_compiler.h +++ b/lib/angelscript/source/as_compiler.h @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2014 Andreas Jonsson + Copyright (c) 2003-2015 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -73,14 +73,9 @@ struct asSExprContext { asSExprContext(asCScriptEngine *engine) : bc(engine) { - exprNode = 0; - origExpr = 0; - property_get = 0; - property_set = 0; - property_const = false; - property_handle = false; - property_ref = false; - property_arg = 0; + property_arg = 0; + + Clear(); } ~asSExprContext() { @@ -90,43 +85,95 @@ struct asSExprContext void Clear() { bc.ClearAll(); - type.SetDummy(); + type.Set(asCDataType()); + deferredParams.SetLength(0); if( property_arg ) asDELETE(property_arg, asSExprContext); - property_arg = 0; - deferredParams.SetLength(0); - exprNode = 0; - origExpr = 0; - property_get = 0; - property_set = 0; - property_const = false; - property_handle = false; - property_ref = false; - methodName = ""; - enumValue = ""; + property_arg = 0; + exprNode = 0; + origExpr = 0; + property_get = 0; + property_set = 0; + property_const = false; + property_handle = false; + property_ref = false; + methodName = ""; + enumValue = ""; + isVoidExpression = false; + isCleanArg = false; } - bool IsClassMethod() + bool IsClassMethod() const { if( type.dataType.GetObjectType() == 0 ) return false; if( methodName == "" ) return false; if( type.dataType.GetObjectType() == &type.dataType.GetObjectType()->engine->functionBehaviours ) return false; return true; } - bool IsGlobalFunc() + bool IsGlobalFunc() const { if( type.dataType.GetObjectType() == 0 ) return false; if( methodName == "" ) return false; if( type.dataType.GetObjectType() != &type.dataType.GetObjectType()->engine->functionBehaviours ) return false; return true; } + void SetLambda(asCScriptNode *funcDecl) + { + asASSERT( funcDecl && funcDecl->nodeType == snFunction ); + asASSERT( bc.GetLastInstr() == -1 ); + + Clear(); + type.SetUndefinedFuncHandle(bc.GetEngine()); + exprNode = funcDecl; + } + bool IsLambda() const + { + if( type.IsUndefinedFuncHandle() && exprNode && exprNode->nodeType == snFunction ) + return true; + + return false; + } + void SetVoidExpression() + { + Clear(); + type.SetVoid(); + isVoidExpression = true; + } + bool IsVoidExpression() const + { + if( isVoidExpression && type.IsVoid() && exprNode == 0 ) + return true; + + return false; + } + void Merge(asSExprContext *after) + { + type = after->type; + property_get = after->property_get; + property_set = after->property_set; + property_const = after->property_const; + property_handle = after->property_handle; + property_ref = after->property_ref; + property_arg = after->property_arg; + exprNode = after->exprNode; + methodName = after->methodName; + enumValue = after->enumValue; + isVoidExpression = after->isVoidExpression; + isCleanArg = after->isCleanArg; + + after->property_arg = 0; + + // Do not copy the origExpr member + } asCByteCode bc; asCTypeInfo type; int property_get; int property_set; - bool property_const; // If the object that is being accessed through property accessor is read-only - bool property_handle; // If the property accessor is called on an object stored in a handle - bool property_ref; // If the property accessor is called on a reference + bool property_const; // If the object that is being accessed through property accessor is read-only + bool property_handle; // If the property accessor is called on an object stored in a handle + bool property_ref; // If the property accessor is called on a reference + bool isVoidExpression; // Set to true if the expression is an explicit 'void', e.g. used to ignore out parameters in func calls + bool isCleanArg; // Set to true if the expression has only been initialized with default constructor asSExprContext *property_arg; asCArray deferredParams; asCScriptNode *exprNode; @@ -212,8 +259,8 @@ protected: int CompileExpressionPostOp(asCScriptNode *node, asSExprContext *out); int CompileExpressionValue(asCScriptNode *node, asSExprContext *out); int CompileFunctionCall(asCScriptNode *node, asSExprContext *out, asCObjectType *objectType, bool objIsConst, const asCString &scope = ""); - void CompileConstructCall(asCScriptNode *node, asSExprContext *out); - void CompileConversion(asCScriptNode *node, asSExprContext *out); + int CompileConstructCall(asCScriptNode *node, asSExprContext *out); + int CompileConversion(asCScriptNode *node, asSExprContext *out); int CompileOperator(asCScriptNode *node, asSExprContext *l, asSExprContext *r, asSExprContext *out, eTokenType opToken = ttUnrecognizedToken); void CompileOperatorOnHandles(asCScriptNode *node, asSExprContext *l, asSExprContext *r, asSExprContext *out, eTokenType opToken = ttUnrecognizedToken); void CompileMathOperator(asCScriptNode *node, asSExprContext *l, asSExprContext *r, asSExprContext *out, eTokenType opToken = ttUnrecognizedToken); @@ -239,6 +286,7 @@ protected: void CompileInitAsCopy(asCDataType &type, int offset, asCByteCode *bc, asSExprContext *arg, asCScriptNode *node, bool derefDestination); // Helper functions + void ConvertToPostFix(asCScriptNode *expr, asCArray &postfix); void ProcessPropertyGetAccessor(asSExprContext *ctx, asCScriptNode *node); int ProcessPropertySetAccessor(asSExprContext *ctx, asSExprContext *arg, asCScriptNode *node); int ProcessPropertyGetSetAccessor(asSExprContext *ctx, asSExprContext *lctx, asSExprContext *rctx, eTokenType op, asCScriptNode *errNode); @@ -288,6 +336,7 @@ protected: asUINT ImplicitConvObjectValue(asSExprContext *ctx, const asCDataType &to, asCScriptNode *node, EImplicitConv convType, bool generateCode); void ImplicitConversionConstant(asSExprContext *ctx, const asCDataType &to, asCScriptNode *node, EImplicitConv convType); void ImplicitConvObjectToBestMathType(asSExprContext *ctx, asCScriptNode *node); + asUINT ImplicitConvLambdaToFunc(asSExprContext *ctx, const asCDataType &to, asCScriptNode *node, EImplicitConv convType, bool generateCode = true); void LineInstr(asCByteCode *bc, size_t pos); @@ -307,16 +356,18 @@ protected: bool hasCompileErrors; int nextLabel; + int numLambdas; - asCVariableScope *variables; - asCBuilder *builder; - asCScriptEngine *engine; - asCScriptCode *script; + asCVariableScope *variables; + asCBuilder *builder; + asCScriptEngine *engine; + asCScriptCode *script; asCScriptFunction *outFunc; - bool m_isConstructor; - bool m_isConstructorCalled; - sClassDeclaration *m_classDecl; + bool m_isConstructor; + bool m_isConstructorCalled; + sClassDeclaration *m_classDecl; + sGlobalVariableDescription *m_globalVar; asCArray breakLabels; asCArray continueLabels; @@ -353,7 +404,7 @@ protected: bool isCompilingDefaultArg; bool isProcessingDeferredParams; - int noCodeOutput; + int noCodeOutput; }; END_AS_NAMESPACE diff --git a/lib/angelscript/source/as_config.h b/lib/angelscript/source/as_config.h index c9771e4ac..cb05bffbd 100644 --- a/lib/angelscript/source/as_config.h +++ b/lib/angelscript/source/as_config.h @@ -586,6 +586,7 @@ #define AS_NO_MEMORY_H #define AS_MIPS #define AS_PSP + #define AS_USE_DOUBLE_AS_FLOAT // PSVita #elif defined(__psp2__) #define AS_PSVITA @@ -799,6 +800,14 @@ // As of version 4.7 MinGW changed the ABI, presumably // to be better aligned with how MSVC works #if (__GNUC__ == 4 && __GNUC_MINOR__ >= 7) || __GNUC__ > 4 + #define AS_MINGW47 + #endif + + #if (__clang_major__ == 3 && __clang_minor__ > 4) || __clang_major > 3 + #define AS_MINGW47 + #endif + + #ifdef AS_MINGW47 #undef CALLEE_POPS_HIDDEN_RETURN_POINTER #define THISCALL_CALLEE_POPS_ARGUMENTS #else @@ -845,7 +854,7 @@ // STDCALL is not available on 64bit Linux #undef STDCALL #define STDCALL - #elif defined(__ARMEL__) || defined(__arm__) + #elif (defined(__ARMEL__) || defined(__arm__)) && !(defined(__ARM_ARCH_4__) || defined(__ARM_ARCH_4T__)) #define AS_ARM // TODO: The stack unwind on exceptions currently fails due to the assembler code in as_callfunc_arm_gcc.S @@ -884,11 +893,24 @@ #elif defined(__mips__) #define AS_MIPS - #define AS_BIG_ENDIAN - #define AS_USE_DOUBLE_AS_FLOAT + #undef STDCALL + #define STDCALL - // Native calling conventions for Linux/Mips do not work yet. - #define AS_MAX_PORTABILITY + #ifdef _ABIO32 + #define AS_MIPS + + // All structures are returned in memory regardless of size or complexity + #define THISCALL_RETURN_SIMPLE_IN_MEMORY + #define THISCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 0 + #define CDECL_RETURN_SIMPLE_IN_MEMORY + #define CDECL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 0 + #define STDCALL_RETURN_SIMPLE_IN_MEMORY + #define CDECL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 0 + #undef AS_NO_THISCALL_FUNCTOR_METHOD + #else + // For other ABIs the native calling convention is not available (yet) + #define AS_MAX_PORTABILITY + #endif #else #define AS_MAX_PORTABILITY #endif @@ -936,6 +958,7 @@ // Support native calling conventions on MIPS architecture #if (defined(_MIPS_ARCH) || defined(_mips) || defined(__MIPSEL__)) && !defined(__LP64__) #define AS_MIPS + #define AS_USE_DOUBLE_AS_FLOAT #else #define AS_MAX_PORTABILITY #endif @@ -988,6 +1011,9 @@ #if (defined(_ARM_) || defined(__arm__)) // Android ARM + // TODO: The stack unwind on exceptions currently fails due to the assembler code in as_callfunc_arm_gcc.S + #define AS_NO_EXCEPTIONS + #undef THISCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE #undef CDECL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE #undef STDCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE @@ -1013,6 +1039,26 @@ #define THISCALL_PASS_OBJECT_POINTER_ON_THE_STACK #define AS_X86 #undef AS_NO_THISCALL_FUNCTOR_METHOD + #elif defined(__mips__) + #define AS_MIPS + #undef STDCALL + #define STDCALL + + #ifdef _ABIO32 + #define AS_MIPS + + // All structures are returned in memory regardless of size or complexity + #define THISCALL_RETURN_SIMPLE_IN_MEMORY + #define THISCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 0 + #define CDECL_RETURN_SIMPLE_IN_MEMORY + #define CDECL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 0 + #define STDCALL_RETURN_SIMPLE_IN_MEMORY + #define CDECL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 0 + #undef AS_NO_THISCALL_FUNCTOR_METHOD + #else + // For other ABIs the native calling convention is not available (yet) + #define AS_MAX_PORTABILITY + #endif #endif // Haiku OS @@ -1047,7 +1093,7 @@ // Support native calling conventions on Intel 32bit CPU #define THISCALL_PASS_OBJECT_POINTER_ON_THE_STACK #define AS_X86 - #elif defined(__LP64__) + #elif defined(__x86_64__) #define AS_X64_GCC #define HAS_128_BIT_PRIMITIVES #define SPLIT_OBJS_BY_MEMBER_TYPES @@ -1112,11 +1158,6 @@ // Nothing special here #endif -// MIPS architecture (generally PS2 and PSP consoles, potentially supports N64 as well) -#if defined(_MIPS_ARCH) || defined(_mips) || defined(__MIPSEL__) || defined(__PSP__) || defined(__psp__) || defined(_EE_) || defined(_PSP) || defined(_PS2) - #define AS_USE_DOUBLE_AS_FLOAT // use 32bit floats instead of doubles -#endif - // PowerPC, e.g. Mac, GameCube, PS3, XBox 360, Wii #if defined(__PPC__) || defined(__ppc__) || defined(_PPC_) || defined(EPPC) #define AS_BIG_ENDIAN diff --git a/lib/angelscript/source/as_context.cpp b/lib/angelscript/source/as_context.cpp index 489877a0f..9096a1045 100644 --- a/lib/angelscript/source/as_context.cpp +++ b/lib/angelscript/source/as_context.cpp @@ -134,17 +134,32 @@ public: #endif +// interface AS_API asIScriptContext *asGetActiveContext() { asCThreadLocalData *tld = asCThreadManager::GetLocalData(); - if( tld->activeContexts.GetLength() == 0 ) + + // tld can be 0 if asGetActiveContext is called before any engine has been created. + + // Observe! I've seen a case where an application linked with the library twice + // and thus ended up with two separate instances of the code and global variables. + // The application somehow mixed the two instances so that a function called from + // a script ended up calling asGetActiveContext from the other instance that had + // never been initialized. + + if( tld == 0 || tld->activeContexts.GetLength() == 0 ) return 0; return tld->activeContexts[tld->activeContexts.GetLength()-1]; } +// internal +// Note: There is no asPopActiveContext(), just call tld->activeContexts.PopLast() instead asCThreadLocalData *asPushActiveContext(asIScriptContext *ctx) { asCThreadLocalData *tld = asCThreadManager::GetLocalData(); + asASSERT( tld ); + if( tld == 0 ) + return 0; tld->activeContexts.PushLast(ctx); return tld; } @@ -1304,8 +1319,9 @@ int asCContext::Execute() } // Pop the active context - asASSERT(tld->activeContexts[tld->activeContexts.GetLength()-1] == this); - tld->activeContexts.PopLast(); + asASSERT(tld && tld->activeContexts[tld->activeContexts.GetLength()-1] == this); + if( tld ) + tld->activeContexts.PopLast(); if( m_status == asEXECUTION_FINISHED ) { @@ -4301,6 +4317,67 @@ void asCContext::ExecuteNext() } l_bc += 2; break; + case asBC_Thiscall1: + // This instruction is a faster version of asBC_CALLSYS. It is faster because + // it has much less runtime overhead with determining the calling convention + // and no dynamic code for loading the parameters. The instruction can only + // be used to call functions with the following signatures: + // + // type &obj::func(int) + // type &obj::func(uint) + // void obj::func(int) + // void obj::func(uint) + { + // Get function ID from the argument + int i = asBC_INTARG(l_bc); + + // Need to move the values back to the context as the called functions + // may use the debug interface to inspect the registers + m_regs.programPointer = l_bc; + m_regs.stackPointer = l_sp; + m_regs.stackFramePointer = l_fp; + + // Pop the thispointer from the stack + void *obj = *(void**)l_sp; + l_sp += AS_PTR_SIZE; + + // Pop the int arg from the stack + int arg = *(int*)l_sp; + l_sp++; + + // Call the method + m_callingSystemFunction = m_engine->scriptFunctions[i]; + void *ptr = m_engine->CallObjectMethodRetPtr(obj, arg, m_callingSystemFunction); + m_callingSystemFunction = 0; + *(asPWORD*)&m_regs.valueRegister = (asPWORD)ptr; + + // Update the program position after the call so that line number is correct + l_bc += 2; + + if( m_regs.doProcessSuspend ) + { + // Should the execution be suspended? + if( m_doSuspend ) + { + m_regs.programPointer = l_bc; + m_regs.stackPointer = l_sp; + m_regs.stackFramePointer = l_fp; + + m_status = asEXECUTION_SUSPENDED; + return; + } + // An exception might have been raised + if( m_status != asEXECUTION_ACTIVE ) + { + m_regs.programPointer = l_bc; + m_regs.stackPointer = l_sp; + m_regs.stackFramePointer = l_fp; + + return; + } + } + } + break; // Don't let the optimizer optimize for size, // since it requires extra conditions and jumps @@ -4887,7 +4964,7 @@ int asCContext::GetExceptionLineNumber(int *column, const char **sectionName) if( sectionName ) { - // The section index can be -1 if the exception was raised in a generated function, e.g. factstub for templates + // The section index can be -1 if the exception was raised in a generated function, e.g. $fact for templates if( m_exceptionSectionIdx >= 0 ) *sectionName = m_engine->scriptSectionNames[m_exceptionSectionIdx]->AddressOf(); else diff --git a/lib/angelscript/source/as_datatype.cpp b/lib/angelscript/source/as_datatype.cpp index 05dc7b3ce..88d0067eb 100644 --- a/lib/angelscript/source/as_datatype.cpp +++ b/lib/angelscript/source/as_datatype.cpp @@ -227,7 +227,7 @@ asCString asCDataType::Format(asSNameSpace *currNs, bool includeNamespace) const str += "const"; } - if( isReference ) + if( isReference ) str += "&"; return str; diff --git a/lib/angelscript/source/as_debug.h b/lib/angelscript/source/as_debug.h index a86428d41..197887401 100644 --- a/lib/angelscript/source/as_debug.h +++ b/lib/angelscript/source/as_debug.h @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2014 Andreas Jonsson + Copyright (c) 2003-2015 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -74,11 +74,19 @@ typedef double float64_t; #include #endif +#endif // AS_PSVITA +#endif // _WIN32_WCE +#endif // AS_WII + +#endif // !defined(AS_DEBUG) + + #if defined(_MSC_VER) && defined(AS_PROFILE) // Currently only do profiling with MSVC++ #include +#include #include "as_string.h" #include "as_map.h" #include "as_string_util.h" @@ -144,7 +152,7 @@ public: return time; } - void End(const char *name, double beginTime) + void End(const char * /*name*/, double beginTime) { double time = GetTime(); @@ -247,7 +255,7 @@ protected: END_AS_NAMESPACE -#else // _MSC_VER && AS_PROFILE +#else // !(_MSC_VER && AS_PROFILE) // Define it so nothing is done #define TimeIt(x) @@ -257,20 +265,6 @@ END_AS_NAMESPACE - - - -#endif // AS_PSVITA -#endif // _WIN32_WCE -#endif // AS_WII - -#else // !defined(AS_DEBUG) - -// Define it so nothing is done -#define TimeIt(x) - -#endif // !defined(AS_DEBUG) - -#endif +#endif // defined(AS_DEBUG_H) diff --git a/lib/angelscript/source/as_gc.cpp b/lib/angelscript/source/as_gc.cpp index 36f6230db..f488a5053 100644 --- a/lib/angelscript/source/as_gc.cpp +++ b/lib/angelscript/source/as_gc.cpp @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2014 Andreas Jonsson + Copyright (c) 2003-2015 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -486,23 +486,18 @@ int asCGarbageCollector::ReportAndReleaseUndestroyedObjects() engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, msg.AddressOf()); // Add additional info for builtin types - if( gcObj.type->name == "_builtin_function_" ) + if( gcObj.type->name == "$func" ) { // Unfortunately we can't show the function declaration here, because the engine may have released the parameter list already so the declaration would only be misleading // We need to show the function type too as for example delegates do not have a name msg.Format(TXT_PREV_FUNC_IS_NAMED_s_TYPE_IS_d, reinterpret_cast(gcObj.obj)->GetName(), reinterpret_cast(gcObj.obj)->GetFuncType()); engine->WriteMessage("", 0, 0, asMSGTYPE_INFORMATION, msg.AddressOf()); } - else if( gcObj.type->name == "_builtin_objecttype_" ) + else if( gcObj.type->name == "$obj" ) { msg.Format(TXT_PREV_TYPE_IS_NAMED_s, reinterpret_cast(gcObj.obj)->GetName()); engine->WriteMessage("", 0, 0, asMSGTYPE_INFORMATION, msg.AddressOf()); } - else if( gcObj.type->name == "_builtin_globalprop_" ) - { - msg.Format(TXT_PREV_TYPE_IS_NAMED_s, reinterpret_cast(gcObj.obj)->name.AddressOf()); - engine->WriteMessage("", 0, 0, asMSGTYPE_INFORMATION, msg.AddressOf()); - } // Release the reference that the GC holds if the release functions is still available if( gcObj.type->beh.release && engine->scriptFunctions[gcObj.type->beh.release] ) diff --git a/lib/angelscript/source/as_module.cpp b/lib/angelscript/source/as_module.cpp index 232e17be1..2c81e0762 100644 --- a/lib/angelscript/source/as_module.cpp +++ b/lib/angelscript/source/as_module.cpp @@ -46,32 +46,6 @@ BEGIN_AS_NAMESPACE -// TODO: 2.30.0: redesign: Improved method for discarding modules (faster clean-up, less abuse of garbage collector) -// -// I need to separate the reference counter for internal references and outside references: -// -// - Internal references are for example, when the module refers to a function or object since it is declared in the module, or when -// a function refers to another function since it is being called in the code. -// - Outside references are for example object instances holding a reference to the object type, or a context currently -// executing a function. -// -// If no object instances are alive or no contexts are alive it is known that functions from a discarded module -// can be called, so they can be destroyed without any need to execute the complex garbage collection routines. -// -// If there are live objects, the entire module should be kept for safe keeping, though no longer visible. -// -// TODO: It may not be necessary to keep track of internal references. Without keeping track of internal references, can I still -// handle RemoveFunction and RemoveGlobalVariable correctly? -// -// TODO: How to avoid global variables keeping code alive? For example a script object, or a funcdef? -// Can I do a quick check of the object types and functions to count number of outside references, and then do another -// check over the global variables to subtract the outside references coming from these? What if the outside reference -// is added by an application type in a global variable that the engine doesn't know about? Example, a global dictionary -// holding object instances. Should discarding a module immediately destroy the content of the global variables? What if -// a live object tries to access the global variable after it has been discarded? Throwing a script exception is acceptable? -// Perhaps I need to allow the user to provide a clean-up routine that will be executed before destroying the objects. -// Or I might just put that responsibility on the application. - // internal asCModule::asCModule(const char *name, asCScriptEngine *engine) @@ -315,7 +289,7 @@ int asCModule::Build() // Don't allow the module to be rebuilt if there are still // external references that will need the previous code - // TODO: 2.30.0: interface: The asIScriptModule must have a method for querying if the module is used + // TODO: interface: The asIScriptModule must have a method for querying if the module is used if( HasExternalReferences(false) ) { engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, TXT_MODULE_IS_IN_USE); @@ -944,9 +918,6 @@ int asCModule::GetGlobalVarIndexByName(const char *name) const // interface int asCModule::RemoveGlobalVar(asUINT index) { - // TODO: 2.30.0: redesign: Before removing the variable, clear it to free the object - // The property shouldn't be orphaned. - asCGlobalProperty *prop = scriptGlobals.Get(index); if( !prop ) return asINVALID_ARG; @@ -1273,6 +1244,33 @@ int asCModule::AddScriptFunction(asCScriptFunction *func) func->AddRefInternal(); engine->AddScriptFunction(func); + // If the function that is being added is an already compiled shared function + // then it is necessary to look for anonymous functions that may be declared + // within it and add those as well + if( func->isShared && func->funcType == asFUNC_SCRIPT ) + { + // Loop through the byte code and check all the + // asBC_FuncPtr instructions for anonymous functions + asDWORD *bc = func->scriptData->byteCode.AddressOf(); + asUINT bcLength = (asUINT)func->scriptData->byteCode.GetLength(); + for( asUINT n = 0; n < bcLength; ) + { + int c = *(asBYTE*)&bc[n]; + if( c == asBC_FuncPtr ) + { + asCScriptFunction *f = reinterpret_cast(asBC_PTRARG(&bc[n])); + // Anonymous functions start with $ + // There are never two equal anonymous functions so it is not necessary to look for duplicates + if( f && f->name[0] == '$' ) + { + AddScriptFunction(f); + globalFunctions.Put(f); + } + } + n += asBCTypeSize[asBCInfo[c].type]; + } + } + return 0; } @@ -1700,16 +1698,6 @@ int asCModule::CompileFunction(const char *sectionName, const char *code, int li // interface int asCModule::RemoveFunction(asIScriptFunction *func) { - // TODO: 2.30.0: redesign: Check if there are any references before removing the function - // if there are, just hide it from the visible but do not destroy or - // remove it from the module. - // - // Only if the function has no live references, nor internal references - // can it be immediately removed, and its internal references released. - // - // Check if any previously hidden functions are without references, - // if so they should removed too. - // Find the global function asCScriptFunction *f = static_cast(func); int idx = globalFunctions.GetIndex(f); @@ -1734,6 +1722,7 @@ int asCModule::AddFuncDef(const asCString &name, asSNameSpace *ns) func->name = name; func->nameSpace = ns; + func->module = this; funcDefs.PushLast(func); diff --git a/lib/angelscript/source/as_objecttype.cpp b/lib/angelscript/source/as_objecttype.cpp index 92ceaece3..8bded269d 100644 --- a/lib/angelscript/source/as_objecttype.cpp +++ b/lib/angelscript/source/as_objecttype.cpp @@ -53,6 +53,7 @@ asCObjectType::asCObjectType() module = 0; derivedFrom = 0; size = 0; + typeId = -1; // start as -1 to signal that it hasn't been defined acceptValueSubType = true; acceptRefSubType = true; @@ -74,6 +75,7 @@ asCObjectType::asCObjectType(asCScriptEngine *engine) this->engine = engine; module = 0; derivedFrom = 0; + typeId = -1; // start as -1 to signal that it hasn't been defined acceptValueSubType = true; acceptRefSubType = true; @@ -241,7 +243,8 @@ void asCObjectType::DestroyInternal() userData.SetLength(0); // Remove the type from the engine - engine->RemoveFromTypeIdMap(this); + if( typeId != -1 ) + engine->RemoveFromTypeIdMap(this); // Clear the engine pointer to mark the object type as invalid engine = 0; @@ -252,7 +255,6 @@ asCObjectType::~asCObjectType() if( engine == 0 ) return; - // TODO: 2.30.0: redesign: Shouldn't this have been done already? DestroyInternal(); } @@ -322,12 +324,18 @@ asUINT asCObjectType::GetSize() const // interface int asCObjectType::GetTypeId() const { - // We need a non const pointer to create the asCDataType object. - // We're not breaking anything here because this function is not - // modifying the object, so this const cast is safe. - asCObjectType *ot = const_cast(this); + if( typeId == -1 ) + { + // We need a non const pointer to create the asCDataType object. + // We're not breaking anything here because this function is not + // modifying the object, so this const cast is safe. + asCObjectType *ot = const_cast(this); - return engine->GetTypeIdFromDataType(asCDataType::CreateObject(ot, false)); + // The engine will define the typeId for this object type + engine->GetTypeIdFromDataType(asCDataType::CreateObject(ot, false)); + } + + return typeId; } // interface diff --git a/lib/angelscript/source/as_objecttype.h b/lib/angelscript/source/as_objecttype.h index 2a72e514f..7c84b7831 100644 --- a/lib/angelscript/source/as_objecttype.h +++ b/lib/angelscript/source/as_objecttype.h @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2014 Andreas Jonsson + Copyright (c) 2003-2015 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -191,6 +191,7 @@ public: #ifdef WIP_16BYTE_ALIGN int alignment; #endif + mutable int typeId; asCArray properties; asCArray methods; asCArray interfaces; diff --git a/lib/angelscript/source/as_parser.cpp b/lib/angelscript/source/as_parser.cpp index eaa0768f8..fca02fe68 100644 --- a/lib/angelscript/source/as_parser.cpp +++ b/lib/angelscript/source/as_parser.cpp @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2014 Andreas Jonsson + Copyright (c) 2003-2015 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -261,6 +261,13 @@ int asCParser::ParsePropertyDeclaration(asCScriptCode *script) scriptNode->AddChildLast(ParseType(true)); if( isSyntaxError ) return -1; + // Allow optional '&' to indicate that the property is indirect, i.e. stored as reference + sToken t; + GetToken(&t); + RewindTo(&t); + if( t.type == ttAmp ) + scriptNode->AddChildLast(ParseToken(ttAmp)); + // Allow optional namespace to be defined before the identifier in case // the declaration is to be used for searching for an existing property ParseOptionalScope(scriptNode); @@ -269,7 +276,6 @@ int asCParser::ParsePropertyDeclaration(asCScriptCode *script) if( isSyntaxError ) return -1; // The declaration should end after the identifier - sToken t; GetToken(&t); if( t.type != ttEnd ) { @@ -402,9 +408,11 @@ asCScriptNode *asCParser::ParseType(bool allowConst, bool allowVariableType, boo if( isSyntaxError ) return node; // If the datatype is a template type, then parse the subtype within the < > + GetToken(&t); + RewindTo(&t); asCScriptNode *type = node->lastChild; tempString.Assign(&script->code[type->tokenPos], type->tokenLength); - if( engine->IsTemplateType(tempString.AddressOf()) ) + if( engine->IsTemplateType(tempString.AddressOf()) && t.type == ttLessThan ) { GetToken(&t); if( t.type != ttLessThan ) @@ -602,7 +610,7 @@ asCScriptNode *asCParser::ParseIdentifier() return node; } -// BNF: PARAMLIST ::= '(' ('void' | (TYPE TYPEMOD [IDENTIFIER] ['=' EXPR] {',' TYPE TYPEMOD [IDENTIFIER] ['=' EXPR]}) ')' +// BNF: PARAMLIST ::= '(' ['void' | (TYPE TYPEMOD [IDENTIFIER] ['=' EXPR] {',' TYPE TYPEMOD [IDENTIFIER] ['=' EXPR]})] ')' asCScriptNode *asCParser::ParseParameterList() { asCScriptNode *node = CreateNode(snParameterList); @@ -1104,10 +1112,13 @@ bool asCParser::CheckTemplateType(sToken &t) tempString.Assign(&script->code[t.pos], t.length); if( engine->IsTemplateType(tempString.AddressOf()) ) { - // Expect the sub type within < > + // If the next token is a < then parse the sub-type too GetToken(&t); if( t.type != ttLessThan ) - return false; + { + RewindTo(&t); + return true; + } for(;;) { @@ -1232,7 +1243,7 @@ asCScriptNode *asCParser::ParseCast() return node; } -// BNF: EXPRVALUE ::= 'void' | CONSTRUCTCALL | FUNCCALL | VARACCESS | CAST | LITERAL | '(' ASSIGN ')' +// BNF: EXPRVALUE ::= 'void' | CONSTRUCTCALL | FUNCCALL | VARACCESS | CAST | LITERAL | '(' ASSIGN ')' | LAMBDA asCScriptNode *asCParser::ParseExprValue() { asCScriptNode *node = CreateNode(snExprValue); @@ -1250,44 +1261,54 @@ asCScriptNode *asCParser::ParseExprValue() node->AddChildLast(ParseConstructCall()); else if( t1.type == ttIdentifier || t1.type == ttScope ) { - // Determine the last identifier in order to check if it is a type - sToken t; - if( t1.type == ttScope ) t = t2; else t = t1; - RewindTo(&t); - GetToken(&t2); - while( t.type == ttIdentifier ) + // Check if the expression is an anonymous function + if( IsLambda() ) { - t2 = t; - GetToken(&t); - if( t.type == ttScope ) - GetToken(&t); - else - break; + node->AddChildLast(ParseLambda()); } - - bool isDataType = IsDataType(t2); - bool isTemplateType = false; - if( isDataType ) - { - // Is this a template type? - tempString.Assign(&script->code[t2.pos], t2.length); - if( engine->IsTemplateType(tempString.AddressOf()) ) - isTemplateType = true; - } - - // Rewind so the real parsing can be done, after deciding what to parse - RewindTo(&t1); - - // Check if this is a construct call - if( isDataType && (t.type == ttOpenParanthesis || // type() - t.type == ttOpenBracket) ) // type[]() - node->AddChildLast(ParseConstructCall()); - else if( isTemplateType && t.type == ttLessThan ) // type() - node->AddChildLast(ParseConstructCall()); - else if( IsFunctionCall() ) - node->AddChildLast(ParseFunctionCall()); else - node->AddChildLast(ParseVariableAccess()); + { + // Determine the last identifier in order to check if it is a type + sToken t; + if( t1.type == ttScope ) t = t2; else t = t1; + RewindTo(&t); + GetToken(&t2); + while( t.type == ttIdentifier ) + { + t2 = t; + GetToken(&t); + if( t.type == ttScope ) + GetToken(&t); + else + break; + } + + bool isDataType = IsDataType(t2); + bool isTemplateType = false; + if( isDataType ) + { + // Is this a template type? + tempString.Assign(&script->code[t2.pos], t2.length); + if( engine->IsTemplateType(tempString.AddressOf()) ) + isTemplateType = true; + } + + GetToken(&t2); + + // Rewind so the real parsing can be done, after deciding what to parse + RewindTo(&t1); + + // Check if this is a construct call + if( isDataType && (t.type == ttOpenParanthesis || // type() + (t.type == ttOpenBracket && t2.type == ttCloseBracket)) ) // type[]() + node->AddChildLast(ParseConstructCall()); + else if( isTemplateType && t.type == ttLessThan ) // type() + node->AddChildLast(ParseConstructCall()); + else if( IsFunctionCall() ) + node->AddChildLast(ParseFunctionCall()); + else + node->AddChildLast(ParseVariableAccess()); + } } else if( t1.type == ttCast ) node->AddChildLast(ParseCast()); @@ -1355,6 +1376,83 @@ asCScriptNode *asCParser::ParseConstant() return node; } +bool asCParser::IsLambda() +{ + bool isLambda = false; + sToken t; + GetToken(&t); + if( t.type == ttIdentifier && IdentifierIs(t, FUNCTION_TOKEN) ) + { + sToken t2; + GetToken(&t2); + if( t2.type == ttOpenParanthesis ) + { + // Skip until ) + while( t2.type != ttCloseParanthesis && t2.type != ttEnd ) + GetToken(&t2); + + // The next token must be a { + GetToken(&t2); + if( t2.type == ttStartStatementBlock ) + isLambda = true; + } + } + + RewindTo(&t); + return isLambda; +} + +// BNF: LAMBDA ::= 'function' '(' [IDENTIFIER {',' IDENTIFIER}] ')' STATBLOCK +asCScriptNode *asCParser::ParseLambda() +{ + asCScriptNode *node = CreateNode(snFunction); + if( node == 0 ) return 0; + + sToken t; + GetToken(&t); + + if( t.type != ttIdentifier || !IdentifierIs(t, FUNCTION_TOKEN) ) + { + Error(ExpectedToken("function"), &t); + return node; + } + + GetToken(&t); + if( t.type != ttOpenParanthesis ) + { + Error(ExpectedToken("("), &t); + return node; + } + + GetToken(&t); + if( t.type == ttIdentifier ) + { + RewindTo(&t); + node->AddChildLast(ParseIdentifier()); + + GetToken(&t); + while( t.type == ttListSeparator ) + { + node->AddChildLast(ParseIdentifier()); + if( isSyntaxError ) return node; + + GetToken(&t); + } + } + + if( t.type != ttCloseParanthesis ) + { + Error(ExpectedToken(")"), &t); + return node; + } + + // We should just find the end of the statement block here. The statements + // will be parsed on request by the compiler once it starts the compilation. + node->AddChildLast(SuperficiallyParseStatementBlock()); + + return node; +} + asCScriptNode *asCParser::ParseStringConstant() { asCScriptNode *node = CreateNode(snConstant); @@ -1618,7 +1716,7 @@ asCScriptNode *asCParser::ParseCondition() return node; } -// BNF: EXPR ::= (TYPE '=' INILIST) | (EXPRTERM {EXPROP EXPRTERM}) +// BNF: EXPR ::= (TYPE '=' INITLIST) | (EXPRTERM {EXPROP EXPRTERM}) asCScriptNode *asCParser::ParseExpression() { asCScriptNode *node = CreateNode(snExpression); @@ -2459,7 +2557,22 @@ bool asCParser::IsVirtualPropertyDecl() // as it may wrongly identify the statement as a non-declaration if the user typed // the name incorrectly. The real type is validated in ParseDeclaration where a // proper error message can be given. - if( !IsRealType(t1.type) && t1.type != ttIdentifier ) + if( t1.type == ttScope ) + GetToken(&t1); + + if( t1.type == ttIdentifier ) + { + sToken t2; + GetToken(&t2); + while( t1.type == ttIdentifier && t2.type == ttScope ) + { + GetToken(&t1); + GetToken(&t2); + } + + RewindTo(&t2); + } + else if( !IsRealType(t1.type) ) { RewindTo(&t); return false; @@ -3201,61 +3314,37 @@ asCScriptNode *asCParser::SuperficiallyParseVarInit() if( t.type == ttAssignment ) { GetToken(&t); - if( t.type == ttStartStatementBlock ) + sToken start = t; + + // Find the end of the expression + int indentParan = 0; + int indentBrace = 0; + while( indentParan || indentBrace || (t.type != ttListSeparator && t.type != ttEndStatement && t.type != ttEndStatementBlock) ) { - sToken start = t; - - // Find the end of the initialization list - int indent = 1; - while( indent ) + if( t.type == ttOpenParanthesis ) + indentParan++; + else if( t.type == ttCloseParanthesis ) + indentParan--; + else if( t.type == ttStartStatementBlock ) + indentBrace++; + else if( t.type == ttEndStatementBlock ) + indentBrace--; + else if( t.type == ttNonTerminatedStringConstant ) { - GetToken(&t); - if( t.type == ttStartStatementBlock ) - indent++; - else if( t.type == ttEndStatementBlock ) - indent--; - else if( t.type == ttNonTerminatedStringConstant ) - { - Error(TXT_NONTERMINATED_STRING, &t); - break; - } - else if( t.type == ttEnd ) - { - Error(TXT_UNEXPECTED_END_OF_FILE, &t); - Info(TXT_WHILE_PARSING_INIT_LIST, &start); - break; - } + Error(TXT_NONTERMINATED_STRING, &t); + break; } - } - else - { - sToken start = t; - - // Find the end of the expression - int indent = 0; - while( indent || (t.type != ttListSeparator && t.type != ttEndStatement && t.type != ttEndStatementBlock) ) + else if( t.type == ttEnd ) { - if( t.type == ttOpenParanthesis ) - indent++; - else if( t.type == ttCloseParanthesis ) - indent--; - else if( t.type == ttNonTerminatedStringConstant ) - { - Error(TXT_NONTERMINATED_STRING, &t); - break; - } - else if( t.type == ttEnd ) - { - Error(TXT_UNEXPECTED_END_OF_FILE, &t); - Info(TXT_WHILE_PARSING_EXPRESSION, &start); - break; - } - GetToken(&t); + Error(TXT_UNEXPECTED_END_OF_FILE, &t); + Info(TXT_WHILE_PARSING_EXPRESSION, &start); + break; } - - // Rewind so that the next token read is the list separator, end statement, or end statement block - RewindTo(&t); + GetToken(&t); } + + // Rewind so that the next token read is the list separator, end statement, or end statement block + RewindTo(&t); } else if( t.type == ttOpenParanthesis ) { @@ -3872,7 +3961,7 @@ asCScriptNode *asCParser::ParseIf() return node; } -// BNF: FOR ::= 'for' '(' (VAR | EXPRSTAT) EXPRSTAT [ASSIGN] ')' STATEMENT +// BNF: FOR ::= 'for' '(' (VAR | EXPRSTAT) EXPRSTAT [ASSIGN {',' ASSIGN}] ')' STATEMENT asCScriptNode *asCParser::ParseFor() { asCScriptNode *node = CreateNode(snFor); @@ -3911,18 +4000,27 @@ asCScriptNode *asCParser::ParseFor() { RewindTo(&t); - asCScriptNode *n = CreateNode(snExpressionStatement); - if( n == 0 ) return 0; - node->AddChildLast(n); - n->AddChildLast(ParseAssignment()); - if( isSyntaxError ) return node; - - GetToken(&t); - if( t.type != ttCloseParanthesis ) + // Parse N increment statements separated by , + for(;;) { - Error(ExpectedToken(")"), &t); - Error(InsteadFound(t), &t); - return node; + asCScriptNode *n = CreateNode(snExpressionStatement); + if( n == 0 ) return 0; + node->AddChildLast(n); + n->AddChildLast(ParseAssignment()); + if( isSyntaxError ) return node; + + GetToken(&t); + if( t.type == ttListSeparator ) + continue; + else if( t.type == ttCloseParanthesis ) + break; + else + { + const char *tokens[] = {",", ")"}; + Error(ExpectedOneOf(tokens, 2), &t); + Error(InsteadFound(t), &t); + return node; + } } } diff --git a/lib/angelscript/source/as_parser.h b/lib/angelscript/source/as_parser.h index 76d74c0ba..06fd93ff8 100644 --- a/lib/angelscript/source/as_parser.h +++ b/lib/angelscript/source/as_parser.h @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2014 Andreas Jonsson + Copyright (c) 2003-2015 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -133,6 +133,8 @@ protected: bool IsVarDecl(); bool IsVirtualPropertyDecl(); bool IsFuncDecl(bool isMethod); + bool IsLambda(); + bool IsFunctionCall(); // Expressions asCScriptNode *ParseAssignment(); @@ -151,13 +153,13 @@ protected: asCScriptNode *ParseCast(); asCScriptNode *ParseConstant(); asCScriptNode *ParseStringConstant(); + asCScriptNode *ParseLambda(); bool IsConstant(int tokenType); bool IsOperator(int tokenType); bool IsPreOperator(int tokenType); bool IsPostOperator(int tokenType); bool IsAssignOperator(int tokenType); - bool IsFunctionCall(); bool CheckTemplateType(sToken &t); #endif diff --git a/lib/angelscript/source/as_restore.cpp b/lib/angelscript/source/as_restore.cpp index 19eacc933..4e13157b8 100644 --- a/lib/angelscript/source/as_restore.cpp +++ b/lib/angelscript/source/as_restore.cpp @@ -40,6 +40,7 @@ #include "as_bytecode.h" #include "as_scriptobject.h" #include "as_texts.h" +#include "as_debug.h" BEGIN_AS_NAMESPACE @@ -65,6 +66,8 @@ void asCReader::ReadData(void *data, asUINT size) int asCReader::Read(bool *wasDebugInfoStripped) { + TimeIt("asCReader::Read"); + // Before starting the load, make sure that // any existing resources have been freed module->InternalReset(); @@ -124,6 +127,8 @@ int asCReader::Error(const char *msg) int asCReader::ReadInner() { + TimeIt("asCReader::ReadInner"); + // This function will load each entity one by one from the stream. // If any error occurs, it will return to the caller who is // responsible for cleaning up the partially loaded entities. @@ -258,31 +263,38 @@ int asCReader::ReadInner() asCScriptFunction *func = ReadFunction(isNew, false, true); if( func ) { + func->module = module; module->funcDefs.PushLast(func); engine->funcDefs.PushLast(func); // TODO: clean up: This is also done by the builder. It should probably be moved to a method in the module // Check if there is another identical funcdef from another module and if so reuse that instead - for( asUINT n = 0; n < engine->funcDefs.GetLength(); n++ ) + if( func->isShared ) { - asCScriptFunction *f2 = engine->funcDefs[n]; - if( f2 == 0 || func == f2 ) - continue; - - if( f2->name == func->name && - f2->nameSpace == func->nameSpace && - f2->IsSignatureExceptNameEqual(func) ) + for( asUINT n = 0; n < engine->funcDefs.GetLength(); n++ ) { - // Replace our funcdef for the existing one - module->funcDefs[module->funcDefs.IndexOf(func)] = f2; - f2->AddRefInternal(); + asCScriptFunction *f2 = engine->funcDefs[n]; + if( f2 == 0 || func == f2 ) + continue; - engine->funcDefs.RemoveValue(func); + if( !f2->isShared ) + continue; - savedFunctions[savedFunctions.IndexOf(func)] = f2; + if( f2->name == func->name && + f2->nameSpace == func->nameSpace && + f2->IsSignatureExceptNameEqual(func) ) + { + // Replace our funcdef for the existing one + module->funcDefs[module->funcDefs.IndexOf(func)] = f2; + f2->AddRefInternal(); - func->ReleaseInternal(); - break; + engine->funcDefs.RemoveValue(func); + + savedFunctions[savedFunctions.IndexOf(func)] = f2; + + func->ReleaseInternal(); + break; + } } } } @@ -549,6 +561,8 @@ int asCReader::ReadInner() void asCReader::ReadUsedStringConstants() { + TimeIt("asCReader::ReadUsedStringConstants"); + asCString str; asUINT count; @@ -563,6 +577,8 @@ void asCReader::ReadUsedStringConstants() void asCReader::ReadUsedFunctions() { + TimeIt("asCReader::ReadUsedFunctions"); + asUINT count; count = ReadEncodedUInt(); usedFunctions.SetLength(count); @@ -606,25 +622,44 @@ void asCReader::ReadUsedFunctions() for( asUINT i = 0; i < module->bindInformations.GetLength(); i++ ) { asCScriptFunction *f = module->bindInformations[i]->importedFunctionSignature; - if( !func.IsSignatureEqual(f) || - func.objectType != f->objectType || + if( func.objectType != f->objectType || func.funcType != f->funcType || - func.nameSpace != f->nameSpace ) + func.nameSpace != f->nameSpace || + !func.IsSignatureEqual(f) ) continue; usedFunctions[n] = f; break; } } + else if( func.funcType == asFUNC_FUNCDEF ) + { + const asCArray &funcs = module->funcDefs; + for( asUINT i = 0; i < funcs.GetLength(); i++ ) + { + asCScriptFunction *f = funcs[i]; + if( f == 0 || func.name != f->name || !func.IsSignatureExceptNameAndObjectTypeEqual(f) ) + continue; + + // Funcdefs are always global so there is no need to compare object type + asASSERT( f->objectType == 0 ); + + usedFunctions[n] = f; + break; + } + } else { + // TODO: optimize: Global functions should be searched for in module->globalFunctions + // TODO: optimize: funcdefs should be searched for in module->funcDefs + // TODO: optimize: object methods should be searched for directly in the object type for( asUINT i = 0; i < module->scriptFunctions.GetLength(); i++ ) { asCScriptFunction *f = module->scriptFunctions[i]; - if( !func.IsSignatureEqual(f) || - func.objectType != f->objectType || + if( func.objectType != f->objectType || func.funcType != f->funcType || - func.nameSpace != f->nameSpace ) + func.nameSpace != f->nameSpace || + !func.IsSignatureEqual(f) ) continue; usedFunctions[n] = f; @@ -634,17 +669,157 @@ void asCReader::ReadUsedFunctions() } else { - for( asUINT i = 0; i < engine->scriptFunctions.GetLength(); i++ ) + if( func.funcType == asFUNC_FUNCDEF ) { - asCScriptFunction *f = engine->scriptFunctions[i]; - if( f == 0 || - !func.IsSignatureEqual(f) || - func.objectType != f->objectType || - func.nameSpace != f->nameSpace ) - continue; + // This is a funcdef (registered or shared) + const asCArray &funcs = engine->funcDefs; + for( asUINT i = 0; i < funcs.GetLength(); i++ ) + { + asCScriptFunction *f = funcs[i]; + if( f == 0 || func.name != f->name || !func.IsSignatureExceptNameAndObjectTypeEqual(f) ) + continue; - usedFunctions[n] = f; - break; + // Funcdefs are always global so there is no need to compare object type + asASSERT( f->objectType == 0 ); + + usedFunctions[n] = f; + break; + } + } + else if( func.name[0] == '$' ) + { + // This is a special function + + // Check for string factory + if( func.name == "$str" && engine->stringFactory && + func.IsSignatureExceptNameAndObjectTypeEqual(engine->stringFactory) ) + usedFunctions[n] = engine->stringFactory; + else if( func.name == "$beh0" && func.objectType ) + { + // This is a class constructor, so we can search directly in the object type's constructors + for( asUINT i = 0; i < func.objectType->beh.constructors.GetLength(); i++ ) + { + asCScriptFunction *f = engine->scriptFunctions[func.objectType->beh.constructors[i]]; + if( f == 0 || + !func.IsSignatureExceptNameAndObjectTypeEqual(f) ) + continue; + + usedFunctions[n] = f; + break; + } + } + else if( func.name == "$fact" || func.name == "$beh3" ) + { + // This is a factory (or stub), so look for the function in the return type's factories + asCObjectType *objType = func.returnType.GetObjectType(); + if( objType ) + { + for( asUINT i = 0; i < objType->beh.factories.GetLength(); i++ ) + { + asCScriptFunction *f = engine->scriptFunctions[objType->beh.factories[i]]; + if( f == 0 || + !func.IsSignatureExceptNameAndObjectTypeEqual(f) ) + continue; + + usedFunctions[n] = f; + break; + } + } + } + else if( func.name == "$list" ) + { + // listFactory is used for both factory is global and returns a handle and constructor that is a method + asCObjectType *objType = func.objectType ? func.objectType : func.returnType.GetObjectType(); + if( objType ) + { + asCScriptFunction *f = engine->scriptFunctions[objType->beh.listFactory]; + if( f && func.IsSignatureExceptNameAndObjectTypeEqual(f) ) + usedFunctions[n] = f; + } + } + else if( func.name == "$beh2" ) + { + // This is a destructor, so check the object type's destructor + asCObjectType *objType = func.objectType; + if( objType ) + { + asCScriptFunction *f = engine->scriptFunctions[objType->beh.destruct]; + if( f && func.IsSignatureExceptNameAndObjectTypeEqual(f) ) + usedFunctions[n] = f; + } + } + else if( func.name == "$beh4" ) + { + // This is a list factory, so check the return type's list factory + asCObjectType *objType = func.returnType.GetObjectType(); + if( objType ) + { + asCScriptFunction *f = engine->scriptFunctions[objType->beh.listFactory]; + if( f && func.IsSignatureExceptNameAndObjectTypeEqual(f) ) + usedFunctions[n] = f; + } + } + else if( func.name == "$dlgte" ) + { + // This is the delegate factory + asCScriptFunction *f = engine->registeredGlobalFuncs.GetFirst(engine->nameSpaces[0], DELEGATE_FACTORY); + asASSERT( f && func.IsSignatureEqual(f) ); + usedFunctions[n] = f; + } + } + else if( func.objectType == 0 ) + { + // This is a global function + const asCArray &funcs = engine->registeredGlobalFuncs.GetIndexes(func.nameSpace, func.name); + for( asUINT i = 0; i < funcs.GetLength(); i++ ) + { + asCScriptFunction *f = engine->registeredGlobalFuncs.Get(funcs[i]); + if( f == 0 || + !func.IsSignatureExceptNameAndObjectTypeEqual(f) ) + continue; + + usedFunctions[n] = f; + break; + } + } + else if( func.objectType ) + { + // It is a class member, so we can search directly in the object type's members + // TODO: virtual function is different that implemented method + for( asUINT i = 0; i < func.objectType->methods.GetLength(); i++ ) + { + asCScriptFunction *f = engine->scriptFunctions[func.objectType->methods[i]]; + if( f == 0 || + !func.IsSignatureEqual(f) ) + continue; + + usedFunctions[n] = f; + break; + } + } + + if( usedFunctions[n] == 0 ) + { + // TODO: clean up: This part of the code should never happen. All functions should + // be found in the above logic. The only valid reason to come here + // is if the bytecode is wrong and the function doesn't exist anyway. + // This loop is kept temporarily until we can be certain all scenarios + // are covered. + for( asUINT i = 0; i < engine->scriptFunctions.GetLength(); i++ ) + { + asCScriptFunction *f = engine->scriptFunctions[i]; + if( f == 0 || + func.objectType != f->objectType || + func.nameSpace != f->nameSpace || + !func.IsSignatureEqual(f) ) + continue; + + usedFunctions[n] = f; + break; + } + + // No function is expected to be found + asASSERT(usedFunctions[n] == 0); } } @@ -973,6 +1148,13 @@ asCScriptFunction *asCReader::ReadFunction(bool &isNew, bool addToModule, bool a { func->vfTableIdx = ReadEncodedUInt(); } + else if( func->funcType == asFUNC_FUNCDEF ) + { + asBYTE bits; + ReadData(&bits, 1); + if( bits ) + func->isShared = true; + } if( addToModule ) { @@ -1520,28 +1702,25 @@ asQWORD asCReader::ReadEncodedUInt64() void asCReader::ReadString(asCString* str) { - char b; - ReadData(&b, 1); - if( b == '\0' ) + asUINT len = ReadEncodedUInt(); + if( len & 1 ) { - str->SetLength(0); + asUINT idx = len/2; + if( idx < savedStrings.GetLength() ) + *str = savedStrings[idx]; + else + Error(TXT_INVALID_BYTECODE_d); } - else if( b == 'n' ) + else if( len > 0 ) { - asUINT len = ReadEncodedUInt(); + len /= 2; str->SetLength(len); stream->Read(str->AddressOf(), len); savedStrings.PushLast(*str); } else - { - asUINT n = ReadEncodedUInt(); - if( n < savedStrings.GetLength() ) - *str = savedStrings[n]; - else - Error(TXT_INVALID_BYTECODE_d); - } + str->SetLength(0); } void asCReader::ReadGlobalProperty() @@ -1625,7 +1804,7 @@ void asCReader::ReadDataType(asCDataType *dt) ReadData(&bits, 1); asCScriptFunction *funcDef = 0; - if( tokenType == ttIdentifier && objType && objType->name == "_builtin_function_" ) + if( tokenType == ttIdentifier && objType && objType->name == "$func" ) { asCScriptFunction func(engine, module, asFUNC_DUMMY); ReadFunctionSignature(&func); @@ -1790,7 +1969,7 @@ asCObjectType* asCReader::ReadObjectType() ReadString(&ns); asSNameSpace *nameSpace = engine->AddNameSpace(ns.AddressOf()); - if( typeName.GetLength() && typeName != "_builtin_object_" && typeName != "_builtin_function_" ) + if( typeName.GetLength() && typeName != "$obj" && typeName != "$func" ) { // Find the object type ot = module->GetObjectType(typeName.AddressOf(), nameSpace); @@ -1806,11 +1985,11 @@ asCObjectType* asCReader::ReadObjectType() return 0; } } - else if( typeName == "_builtin_object_" ) + else if( typeName == "$obj" ) { ot = &engine->scriptTypeBehaviours; } - else if( typeName == "_builtin_function_" ) + else if( typeName == "$func" ) { ot = &engine->functionBehaviours; } @@ -2073,6 +2252,8 @@ void asCReader::ReadByteCode(asCScriptFunction *func) void asCReader::ReadUsedTypeIds() { + TimeIt("asCReader::ReadUsedTypeIds"); + asUINT count = ReadEncodedUInt(); usedTypeIds.Allocate(count, false); for( asUINT n = 0; n < count; n++ ) @@ -2085,6 +2266,8 @@ void asCReader::ReadUsedTypeIds() void asCReader::ReadUsedGlobalProps() { + TimeIt("asCReader::ReadUsedGlobalProps"); + int c = ReadEncodedUInt(); usedGlobalProperties.Allocate(c, false); @@ -2124,6 +2307,8 @@ void asCReader::ReadUsedGlobalProps() void asCReader::ReadUsedObjectProps() { + TimeIt("asCReader::ReadUsedObjectProps"); + asUINT c = ReadEncodedUInt(); usedObjectProperties.SetLength(c); @@ -2293,7 +2478,8 @@ void asCReader::TranslateFunction(asCScriptFunction *func) } else if( c == asBC_CALL || c == asBC_CALLINTF || - c == asBC_CALLSYS ) + c == asBC_CALLSYS || + c == asBC_Thiscall1 ) { // Translate the index to the func id int *fid = (int*)&bc[n+1]; @@ -2800,7 +2986,8 @@ void asCReader::CalculateStackNeeded(asCScriptFunction *func) { // Determine the true delta from the instruction arguments if( bc == asBC_CALL || - bc == asBC_CALLSYS || + bc == asBC_CALLSYS || + bc == asBC_Thiscall1 || bc == asBC_CALLBND || bc == asBC_ALLOC || bc == asBC_CALLINTF || @@ -3030,6 +3217,7 @@ asCScriptFunction *asCReader::GetCalledFunction(asCScriptFunction *func, asDWORD if( bc == asBC_CALL || bc == asBC_CALLSYS || + bc == asBC_Thiscall1 || bc == asBC_CALLINTF ) { // Find the function from the function id in bytecode @@ -3085,11 +3273,13 @@ int asCReader::AdjustGetOffset(int offset, asCScriptFunction *func, asDWORD prog // Find out which function that will be called asCScriptFunction *calledFunc = 0; + int stackDelta = 0; for( asUINT n = programPos; func->scriptData->byteCode.GetLength(); ) { asBYTE bc = *(asBYTE*)&func->scriptData->byteCode[n]; if( bc == asBC_CALL || bc == asBC_CALLSYS || + bc == asBC_Thiscall1 || bc == asBC_CALLINTF || bc == asBC_ALLOC || bc == asBC_CALLBND || @@ -3106,6 +3296,10 @@ int asCReader::AdjustGetOffset(int offset, asCScriptFunction *func, asDWORD prog return offset - (1 - AS_PTR_SIZE); } + // Keep track of the stack size between the + // instruction that needs to be adjusted and the call + stackDelta += asBCInfo[bc].stackInc; + n += asBCTypeSize[asBCInfo[bc].type]; } @@ -3118,16 +3312,30 @@ int asCReader::AdjustGetOffset(int offset, asCScriptFunction *func, asDWORD prog // Count the number of pointers pushed on the stack above the // current offset, and then adjust the offset accordingly asUINT numPtrs = 0; - int currOffset = 0; + int currOffset = -stackDelta; if( offset > currOffset && calledFunc->GetObjectType() ) { - numPtrs++; currOffset++; + if( currOffset > 0 ) + numPtrs++; +#if AS_PTR_SIZE == 2 + // For 64bit platforms it is necessary to increment the currOffset by one more + // DWORD since the stackDelta was counting the full 64bit size of the pointer + else if( stackDelta ) + currOffset++; +#endif } if( offset > currOffset && calledFunc->DoesReturnOnStack() ) { - numPtrs++; currOffset++; + if( currOffset > 0 ) + numPtrs++; +#if AS_PTR_SIZE == 2 + // For 64bit platforms it is necessary to increment the currOffset by one more + // DWORD since the stackDelta was counting the full 64bit size of the pointer + else if( stackDelta ) + currOffset++; +#endif } for( asUINT p = 0; p < calledFunc->parameterTypes.GetLength(); p++ ) { @@ -3136,8 +3344,15 @@ int asCReader::AdjustGetOffset(int offset, asCScriptFunction *func, asDWORD prog if( !calledFunc->parameterTypes[p].IsPrimitive() || calledFunc->parameterTypes[p].IsReference() ) { - numPtrs++; currOffset++; + if( currOffset > 0 ) + numPtrs++; +#if AS_PTR_SIZE == 2 + // For 64bit platforms it is necessary to increment the currOffset by one more + // DWORD since the stackDelta was counting the full 64bit size of the pointer + else if( stackDelta ) + currOffset++; +#endif // The variable arg ? has an additiona 32bit integer with the typeid if( calledFunc->parameterTypes[p].IsAnyType() ) @@ -3197,6 +3412,8 @@ void asCWriter::WriteData(const void *data, asUINT size) int asCWriter::Write() { + TimeIt("asCWriter::Write"); + unsigned long i, count; // Store everything in the same order that the builder parses scripts @@ -3207,101 +3424,149 @@ int asCWriter::Write() WriteData(&stripDebugInfo, sizeof(stripDebugInfo)); // Store enums - count = (asUINT)module->enumTypes.GetLength(); - WriteEncodedInt64(count); - for( i = 0; i < count; i++ ) { - WriteObjectTypeDeclaration(module->enumTypes[i], 1); - WriteObjectTypeDeclaration(module->enumTypes[i], 2); + TimeIt("store enums"); + + count = (asUINT)module->enumTypes.GetLength(); + WriteEncodedInt64(count); + for( i = 0; i < count; i++ ) + { + WriteObjectTypeDeclaration(module->enumTypes[i], 1); + WriteObjectTypeDeclaration(module->enumTypes[i], 2); + } } // Store type declarations first - count = (asUINT)module->classTypes.GetLength(); - WriteEncodedInt64(count); - for( i = 0; i < count; i++ ) { - // Store only the name of the class/interface types - WriteObjectTypeDeclaration(module->classTypes[i], 1); + TimeIt("type declarations"); + + count = (asUINT)module->classTypes.GetLength(); + WriteEncodedInt64(count); + for( i = 0; i < count; i++ ) + { + // Store only the name of the class/interface types + WriteObjectTypeDeclaration(module->classTypes[i], 1); + } } // Store func defs - count = (asUINT)module->funcDefs.GetLength(); - WriteEncodedInt64(count); - for( i = 0; i < count; i++ ) - WriteFunction(module->funcDefs[i]); + { + TimeIt("func defs"); + + count = (asUINT)module->funcDefs.GetLength(); + WriteEncodedInt64(count); + for( i = 0; i < count; i++ ) + WriteFunction(module->funcDefs[i]); + } // Now store all interface methods - count = (asUINT)module->classTypes.GetLength(); - for( i = 0; i < count; i++ ) { - if( module->classTypes[i]->IsInterface() ) - WriteObjectTypeDeclaration(module->classTypes[i], 2); + TimeIt("interface methods"); + + count = (asUINT)module->classTypes.GetLength(); + for( i = 0; i < count; i++ ) + { + if( module->classTypes[i]->IsInterface() ) + WriteObjectTypeDeclaration(module->classTypes[i], 2); + } } // Then store the class methods and behaviours - for( i = 0; i < count; ++i ) { - if( !module->classTypes[i]->IsInterface() ) - WriteObjectTypeDeclaration(module->classTypes[i], 2); + TimeIt("class methods and behaviours"); + + for( i = 0; i < count; ++i ) + { + if( !module->classTypes[i]->IsInterface() ) + WriteObjectTypeDeclaration(module->classTypes[i], 2); + } } // Then store the class properties - for( i = 0; i < count; ++i ) { - if( !module->classTypes[i]->IsInterface() ) - WriteObjectTypeDeclaration(module->classTypes[i], 3); + TimeIt("class properties"); + + for( i = 0; i < count; ++i ) + { + if( !module->classTypes[i]->IsInterface() ) + WriteObjectTypeDeclaration(module->classTypes[i], 3); + } } // Store typedefs - count = (asUINT)module->typeDefs.GetLength(); - WriteEncodedInt64(count); - for( i = 0; i < count; i++ ) { - WriteObjectTypeDeclaration(module->typeDefs[i], 1); - WriteObjectTypeDeclaration(module->typeDefs[i], 2); + TimeIt("type defs"); + + count = (asUINT)module->typeDefs.GetLength(); + WriteEncodedInt64(count); + for( i = 0; i < count; i++ ) + { + WriteObjectTypeDeclaration(module->typeDefs[i], 1); + WriteObjectTypeDeclaration(module->typeDefs[i], 2); + } } // scriptGlobals[] - count = (asUINT)module->scriptGlobals.GetSize(); - WriteEncodedInt64(count); - asCSymbolTable::iterator it = module->scriptGlobals.List(); - for( ; it; it++ ) - WriteGlobalProperty(*it); + { + TimeIt("script globals"); + + count = (asUINT)module->scriptGlobals.GetSize(); + WriteEncodedInt64(count); + asCSymbolTable::iterator it = module->scriptGlobals.List(); + for( ; it; it++ ) + WriteGlobalProperty(*it); + } // scriptFunctions[] - count = 0; - for( i = 0; i < module->scriptFunctions.GetLength(); i++ ) - if( module->scriptFunctions[i]->objectType == 0 ) - count++; - WriteEncodedInt64(count); - for( i = 0; i < module->scriptFunctions.GetLength(); ++i ) - if( module->scriptFunctions[i]->objectType == 0 ) - WriteFunction(module->scriptFunctions[i]); + { + TimeIt("scriptFunctions"); + + count = 0; + for( i = 0; i < module->scriptFunctions.GetLength(); i++ ) + if( module->scriptFunctions[i]->objectType == 0 ) + count++; + WriteEncodedInt64(count); + for( i = 0; i < module->scriptFunctions.GetLength(); ++i ) + if( module->scriptFunctions[i]->objectType == 0 ) + WriteFunction(module->scriptFunctions[i]); + } // globalFunctions[] - count = (int)module->globalFunctions.GetSize(); - asCSymbolTable::iterator funcIt = module->globalFunctions.List(); - WriteEncodedInt64(count); - while( funcIt ) { - WriteFunction(*funcIt); - funcIt++; + TimeIt("globalFunctions"); + + count = (int)module->globalFunctions.GetSize(); + asCSymbolTable::iterator funcIt = module->globalFunctions.List(); + WriteEncodedInt64(count); + while( funcIt ) + { + WriteFunction(*funcIt); + funcIt++; + } } // bindInformations[] - count = (asUINT)module->bindInformations.GetLength(); - WriteEncodedInt64(count); - for( i = 0; i < count; ++i ) { - WriteFunction(module->bindInformations[i]->importedFunctionSignature); - WriteString(&module->bindInformations[i]->importFromModule); + TimeIt("bindInformations"); + + count = (asUINT)module->bindInformations.GetLength(); + WriteEncodedInt64(count); + for( i = 0; i < count; ++i ) + { + WriteFunction(module->bindInformations[i]->importedFunctionSignature); + WriteString(&module->bindInformations[i]->importFromModule); + } } // usedTypes[] - count = (asUINT)usedTypes.GetLength(); - WriteEncodedInt64(count); - for( i = 0; i < count; ++i ) - WriteObjectType(usedTypes[i]); + { + TimeIt("usedTypes"); + + count = (asUINT)usedTypes.GetLength(); + WriteEncodedInt64(count); + for( i = 0; i < count; ++i ) + WriteObjectType(usedTypes[i]); + } // usedTypeIds[] WriteUsedTypeIds(); @@ -3335,6 +3600,8 @@ int asCWriter::FindStringConstantIndex(int id) void asCWriter::WriteUsedStringConstants() { + TimeIt("asCWriter::WriteUsedStringConstants"); + asUINT count = (asUINT)usedStringConstants.GetLength(); WriteEncodedInt64(count); for( asUINT i = 0; i < count; ++i ) @@ -3343,6 +3610,8 @@ void asCWriter::WriteUsedStringConstants() void asCWriter::WriteUsedFunctions() { + TimeIt("asCWriter::WriteUsedFunctions"); + asUINT count = (asUINT)usedFunctions.GetLength(); WriteEncodedInt64(count); @@ -3572,6 +3841,12 @@ void asCWriter::WriteFunction(asCScriptFunction* func) // TODO: Do we really need to store this? It can probably be reconstructed by the reader WriteEncodedInt64(func->vfTableIdx); } + else if( func->funcType == asFUNC_FUNCDEF ) + { + char bits = 0; + bits += func->isShared ? 1 : 0; + WriteData(&bits,1); + } } void asCWriter::WriteObjectTypeDeclaration(asCObjectType *ot, int phase) @@ -3755,39 +4030,28 @@ void asCWriter::WriteEncodedInt64(asINT64 i) void asCWriter::WriteString(asCString* str) { - // TODO: All strings should be stored in a separate section, and when - // they are used an offset into that section should be stored. - // This will make it unnecessary to store the extra byte to - // identify new versus old strings. - - if( str->GetLength() == 0 ) - { - char z = '\0'; - WriteData(&z, 1); - return; - } - // First check if the string hasn't been saved already asSMapNode *cursor = 0; if (stringToIdMap.MoveTo(&cursor, asCStringPointer(str))) { // Save a reference to the existing string - char b = 'r'; - WriteData(&b, 1); - WriteEncodedInt64(cursor->value); + // The lowest bit is set to 1 to indicate a reference + WriteEncodedInt64(cursor->value*2+1); return; } // Save a new string - char b = 'n'; - WriteData(&b, 1); - + // The lowest bit is set to 0 to indicate a new string asUINT len = (asUINT)str->GetLength(); - WriteEncodedInt64(len); - stream->Write(str->AddressOf(), (asUINT)len); + WriteEncodedInt64(len*2); - savedStrings.PushLast(*str); - stringToIdMap.Insert(asCStringPointer(str), int(savedStrings.GetLength()) - 1); + if( len > 0 ) + { + stream->Write(str->AddressOf(), (asUINT)len); + + savedStrings.PushLast(*str); + stringToIdMap.Insert(asCStringPointer(str), int(savedStrings.GetLength()) - 1); + } } void asCWriter::WriteGlobalProperty(asCGlobalProperty* prop) @@ -3851,7 +4115,7 @@ void asCWriter::WriteDataType(const asCDataType *dt) bits.isReadOnly = dt->IsReadOnly(); WriteData(&bits, 1); - if( t == ttIdentifier && dt->GetObjectType()->name == "_builtin_function_" ) + if( t == ttIdentifier && dt->GetObjectType()->name == "$func" ) { WriteFunctionSignature(dt->GetFuncDef()); } @@ -3883,17 +4147,17 @@ void asCWriter::WriteObjectType(asCObjectType* ot) WriteEncodedInt64(ot->templateSubTypes.GetLength()); for( asUINT n = 0; n < ot->templateSubTypes.GetLength(); n++ ) { - if( ot->templateSubTypes[0].IsObject() || ot->templateSubTypes[0].IsEnumType() ) + if( ot->templateSubTypes[n].IsObject() || ot->templateSubTypes[n].IsEnumType() ) { ch = 's'; WriteData(&ch, 1); - WriteDataType(&ot->templateSubTypes[0]); + WriteDataType(&ot->templateSubTypes[n]); } else { ch = 't'; WriteData(&ch, 1); - eTokenType t = ot->templateSubTypes[0].GetTokenType(); + eTokenType t = ot->templateSubTypes[n].GetTokenType(); WriteEncodedInt64(t); } } @@ -4053,11 +4317,13 @@ int asCWriter::AdjustGetOffset(int offset, asCScriptFunction *func, asDWORD prog // Find out which function that will be called asCScriptFunction *calledFunc = 0; + int stackDelta = 0; for( asUINT n = programPos; n < func->scriptData->byteCode.GetLength(); ) { asBYTE bc = *(asBYTE*)&func->scriptData->byteCode[n]; if( bc == asBC_CALL || bc == asBC_CALLSYS || + bc == asBC_Thiscall1 || bc == asBC_CALLINTF ) { // Find the function from the function id in bytecode @@ -4120,6 +4386,10 @@ int asCWriter::AdjustGetOffset(int offset, asCScriptFunction *func, asDWORD prog return offset + (1 - AS_PTR_SIZE); } + // Keep track of the stack size between the + // instruction that needs to be adjusted and the call + stackDelta += asBCInfo[bc].stackInc; + n += asBCTypeSize[asBCInfo[bc].type]; } @@ -4128,16 +4398,18 @@ int asCWriter::AdjustGetOffset(int offset, asCScriptFunction *func, asDWORD prog // Count the number of pointers pushed on the stack above the // current offset, and then adjust the offset accordingly asUINT numPtrs = 0; - int currOffset = 0; + int currOffset = -stackDelta; if( offset > currOffset && calledFunc->GetObjectType() ) { - numPtrs++; currOffset += AS_PTR_SIZE; + if( currOffset > 0 ) + numPtrs++; } if( offset > currOffset && calledFunc->DoesReturnOnStack() ) { - numPtrs++; currOffset += AS_PTR_SIZE; + if( currOffset > 0 ) + numPtrs++; } for( asUINT p = 0; p < calledFunc->parameterTypes.GetLength(); p++ ) { @@ -4147,8 +4419,9 @@ int asCWriter::AdjustGetOffset(int offset, asCScriptFunction *func, asDWORD prog calledFunc->parameterTypes[p].IsReference() ) { // objects and references are passed by pointer - numPtrs++; currOffset += AS_PTR_SIZE; + if( currOffset > 0 ) + numPtrs++; // The variable arg ? has an additional 32bit int with the typeid if( calledFunc->parameterTypes[p].IsAnyType() ) @@ -4262,7 +4535,8 @@ void asCWriter::WriteByteCode(asCScriptFunction *func) } else if( c == asBC_CALL || // DW_ARG c == asBC_CALLINTF || // DW_ARG - c == asBC_CALLSYS ) // DW_ARG + c == asBC_CALLSYS || // DW_ARG + c == asBC_Thiscall1 ) // DW_ARG { // Translate the function id *(int*)(tmp+1) = FindFunctionIndex(engine->scriptFunctions[*(int*)(tmp+1)]); @@ -4772,6 +5046,8 @@ void asCWriter::SListAdjuster::SetNextType(int typeId) void asCWriter::WriteUsedTypeIds() { + TimeIt("asCWriter::WriteUsedTypeIds"); + asUINT count = (asUINT)usedTypeIds.GetLength(); WriteEncodedInt64(count); for( asUINT n = 0; n < count; n++ ) @@ -4792,6 +5068,8 @@ int asCWriter::FindGlobalPropPtrIndex(void *ptr) void asCWriter::WriteUsedGlobalProps() { + TimeIt("asCWriter::WriteUsedGlobalProps"); + int c = (int)usedGlobalProperties.GetLength(); WriteEncodedInt64(c); @@ -4799,32 +5077,12 @@ void asCWriter::WriteUsedGlobalProps() { asPWORD *p = (asPWORD*)usedGlobalProperties[n]; - // First search for the global in the module - char moduleProp = 0; + // Find the property descriptor from the address asCGlobalProperty *prop = 0; - asCSymbolTable::iterator it = module->scriptGlobals.List(); - for( ; it; it++ ) + asSMapNode *cursor; + if( engine->varAddressMap.MoveTo(&cursor, p) ) { - if( p == (*it)->GetAddressOfValue() ) - { - prop = (*it); - moduleProp = 1; - break; - } - } - - // If it is not in the module, it must be an application registered property - if( !prop ) - { - asCSymbolTable::iterator it = engine->registeredGlobalProps.List(); - for( ; it; it++ ) - { - if( it->GetAddressOfValue() == p ) - { - prop = *it; - break; - } - } + prop = engine->varAddressMap.GetValue(cursor); } asASSERT(prop); @@ -4835,12 +5093,17 @@ void asCWriter::WriteUsedGlobalProps() WriteDataType(&prop->type); // Also store whether the property is a module property or a registered property + char moduleProp = 0; + if( prop->realAddress == 0 ) + moduleProp = 1; WriteData(&moduleProp, 1); } } void asCWriter::WriteUsedObjectProps() { + TimeIt("asCWriter::WriteUsedObjectProps"); + int c = (int)usedObjectProperties.GetLength(); WriteEncodedInt64(c); diff --git a/lib/angelscript/source/as_scriptengine.cpp b/lib/angelscript/source/as_scriptengine.cpp index 85cf0cd75..ecc489840 100644 --- a/lib/angelscript/source/as_scriptengine.cpp +++ b/lib/angelscript/source/as_scriptengine.cpp @@ -118,6 +118,9 @@ AS_API const char * asGetLibraryOptions() #ifdef WIP_16BYTE_ALIGN "WIP_16BYTE_ALIGN " #endif +#ifdef AS_BIG_ENDIAN + "AS_BIG_ENDIAN " +#endif // Target system #ifdef AS_WIN @@ -584,21 +587,22 @@ asCScriptEngine::asCScriptEngine() // Reserve function id 0 for no function scriptFunctions.PushLast(0); + // Reserve the first typeIds for the primitive types + typeIdSeqNbr = asTYPEID_DOUBLE + 1; + // Make sure typeId for the built-in primitives are defined according to asETypeIdFlags - int id = 0; - UNUSED_VAR(id); // It is only used in debug mode - id = GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttVoid, false)); asASSERT( id == asTYPEID_VOID ); - id = GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttBool, false)); asASSERT( id == asTYPEID_BOOL ); - id = GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttInt8, false)); asASSERT( id == asTYPEID_INT8 ); - id = GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttInt16, false)); asASSERT( id == asTYPEID_INT16 ); - id = GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttInt, false)); asASSERT( id == asTYPEID_INT32 ); - id = GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttInt64, false)); asASSERT( id == asTYPEID_INT64 ); - id = GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttUInt8, false)); asASSERT( id == asTYPEID_UINT8 ); - id = GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttUInt16, false)); asASSERT( id == asTYPEID_UINT16 ); - id = GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttUInt, false)); asASSERT( id == asTYPEID_UINT32 ); - id = GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttUInt64, false)); asASSERT( id == asTYPEID_UINT64 ); - id = GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttFloat, false)); asASSERT( id == asTYPEID_FLOAT ); - id = GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttDouble, false)); asASSERT( id == asTYPEID_DOUBLE ); + asASSERT( GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttVoid, false)) == asTYPEID_VOID ); + asASSERT( GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttBool, false)) == asTYPEID_BOOL ); + asASSERT( GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttInt8, false)) == asTYPEID_INT8 ); + asASSERT( GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttInt16, false)) == asTYPEID_INT16 ); + asASSERT( GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttInt, false)) == asTYPEID_INT32 ); + asASSERT( GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttInt64, false)) == asTYPEID_INT64 ); + asASSERT( GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttUInt8, false)) == asTYPEID_UINT8 ); + asASSERT( GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttUInt16, false)) == asTYPEID_UINT16 ); + asASSERT( GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttUInt, false)) == asTYPEID_UINT32 ); + asASSERT( GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttUInt64, false)) == asTYPEID_UINT64 ); + asASSERT( GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttFloat, false)) == asTYPEID_FLOAT ); + asASSERT( GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttDouble, false)) == asTYPEID_DOUBLE ); defaultArrayObjectType = 0; @@ -608,9 +612,9 @@ asCScriptEngine::asCScriptEngine() void asCScriptEngine::DeleteDiscardedModules() { - // TODO: 2.30.0: redesign: Prevent more than one thread from entering this function at the same time. - // If a thread is already doing the work for the clean-up the other thread should - // simply return, as the first thread will continue. + // TODO: redesign: Prevent more than one thread from entering this function at the same time. + // If a thread is already doing the work for the clean-up the other thread should + // simply return, as the first thread will continue. ACQUIRESHARED(engineRWLock); asUINT maxCount = discardedModules.GetLength(); @@ -646,7 +650,7 @@ void asCScriptEngine::DeleteDiscardedModules() asCScriptEngine::~asCScriptEngine() { - // TODO: 2.30.0: redesign: Clean up redundant code + // TODO: clean-up: Clean up redundant code asUINT n = 0; inDestructor = true; @@ -690,12 +694,8 @@ asCScriptEngine::~asCScriptEngine() if( refCount.get() > 0 ) WriteMessage("", 0, 0, asMSGTYPE_ERROR, TXT_ENGINE_REF_COUNT_ERROR_DURING_SHUTDOWN); - asSMapNode *cursor = 0; - while( mapTypeIdToDataType.MoveFirst(&cursor) ) - { - asDELETE(mapTypeIdToDataType.GetValue(cursor),asCDataType); - mapTypeIdToDataType.Erase(cursor); - } + mapTypeIdToObjectType.EraseAll(); + mapTypeIdToFunction.EraseAll(); // First remove what is not used, so that other groups can be deleted safely defaultGroup.RemoveConfiguration(this, true); @@ -1976,7 +1976,7 @@ int asCScriptEngine::RegisterBehaviourToObjectType(asCObjectType *objectType, as listPattern->Destroy(this); return ConfigError(asINVALID_DECLARATION, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); } - func.name.Format("_beh_%d_", behaviour); + func.name.Format("$beh%d", behaviour); if( behaviour != asBEHAVE_FACTORY && behaviour != asBEHAVE_LIST_FACTORY ) { @@ -3229,7 +3229,7 @@ int asCScriptEngine::RegisterStringFactory(const char *datatype, const asSFuncPt return ConfigError(asOUT_OF_MEMORY, "RegisterStringFactory", datatype, 0); } - func->name = "_string_factory_"; + func->name = "$str"; func->sysFuncIntf = newInterface; asCBuilder bld(this, 0); @@ -3590,6 +3590,9 @@ asCObjectType *asCScriptEngine::GetTemplateInstanceType(asCObjectType *templateT if( templateType->beh.listFactory ) { asCScriptFunction *func = GenerateTemplateFactoryStub(templateType, ot, templateType->beh.listFactory); + + // Rename the function to easily identify it in LoadByteCode + func->name = "$list"; ot->beh.listFactory = func->id; } @@ -3787,7 +3790,7 @@ asCScriptFunction *asCScriptEngine::GenerateTemplateFactoryStub(asCObjectType *t func->funcType = asFUNC_SCRIPT; func->AllocateScriptFunctionData(); - func->name = "factstub"; + func->name = "$fact"; func->id = GetNextScriptFunctionId(); AddScriptFunction(func); @@ -4230,6 +4233,62 @@ void *asCScriptEngine::CallObjectMethodRetPtr(void *obj, int func) const #endif } +void *asCScriptEngine::CallObjectMethodRetPtr(void *obj, int param1, asCScriptFunction *func) const +{ + asASSERT( func != 0 ); + asSSystemFunctionInterface *i = func->sysFuncIntf; + +#ifndef AS_NO_CLASS_METHODS + if( i->callConv == ICC_THISCALL || i->callConv == ICC_VIRTUAL_THISCALL ) + { +#if defined(__GNUC__) || defined(AS_PSVITA) + // For virtual thiscalls we must call the method as a true class method so that the compiler will lookup the function address in the vftable + union + { + asSIMPLEMETHOD_t mthd; + struct + { + asFUNCTION_t func; + asPWORD baseOffset; + } f; + } p; + p.f.func = (asFUNCTION_t)(i->func); + p.f.baseOffset = asPWORD(i->baseOffset); + void *(asCSimpleDummy::*f)(int) = (void *(asCSimpleDummy::*)(int))(p.mthd); + return (((asCSimpleDummy*)obj)->*f)(param1); +#else + union + { + asSIMPLEMETHOD_t mthd; + asFUNCTION_t func; + } p; + p.func = (asFUNCTION_t)(i->func); + void *(asCSimpleDummy::*f)(int) = (void *(asCSimpleDummy::*)(int))p.mthd; + obj = (void*)(asPWORD(obj) + i->baseOffset); + return (((asCSimpleDummy*)obj)->*f)(param1); +#endif + } + else +#endif + if( i->callConv == ICC_GENERIC_METHOD ) + { + asCGeneric gen(const_cast(this), func, obj, reinterpret_cast(¶m1)); + void (*f)(asIScriptGeneric *) = (void (*)(asIScriptGeneric *))(i->func); + f(&gen); + return *(void **)gen.GetReturnPointer(); + } + else if( i->callConv == ICC_CDECL_OBJLAST ) + { + void *(*f)(int, void *) = (void *(*)(int, void *))(i->func); + return f(param1, obj); + } + else /*if( i->callConv == ICC_CDECL_OBJFIRST )*/ + { + void *(*f)(void *, int) = (void *(*)(void *, int))(i->func); + return f(obj, param1); + } +} + void *asCScriptEngine::CallGlobalFunctionRetPtr(int func) const { asCScriptFunction *s = scriptFunctions[func]; @@ -4496,82 +4555,154 @@ void asCScriptEngine::GCEnumCallback(void *reference) } -// TODO: multithread: The mapTypeIdToDataType must be protected with critical sections in all functions that access it int asCScriptEngine::GetTypeIdFromDataType(const asCDataType &dtIn) const { - if( dtIn.IsNullHandle() ) return 0; + if( dtIn.IsNullHandle() ) return asTYPEID_VOID; - // Register the base form - asCDataType dt(dtIn); - if( dt.GetObjectType() ) - dt.MakeHandle(false); - - // Find the existing type id - asSMapNode *cursor = 0; - mapTypeIdToDataType.MoveFirst(&cursor); - while( cursor ) + if( dtIn.GetObjectType() == 0 ) { - if( mapTypeIdToDataType.GetValue(cursor)->IsEqualExceptRefAndConst(dt) ) + // Primitives have pre-fixed typeIds + switch( dtIn.GetTokenType() ) { - int typeId = mapTypeIdToDataType.GetKey(cursor); - if( dtIn.GetObjectType() && !(dtIn.GetObjectType()->flags & asOBJ_ASHANDLE) ) + case ttVoid: return asTYPEID_VOID; + case ttBool: return asTYPEID_BOOL; + case ttInt8: return asTYPEID_INT8; + case ttInt16: return asTYPEID_INT16; + case ttInt: return asTYPEID_INT32; + case ttInt64: return asTYPEID_INT64; + case ttUInt8: return asTYPEID_UINT8; + case ttUInt16: return asTYPEID_UINT16; + case ttUInt: return asTYPEID_UINT32; + case ttUInt64: return asTYPEID_UINT64; + case ttFloat: return asTYPEID_FLOAT; + case ttDouble: return asTYPEID_DOUBLE; + default: + // All types should be covered by the above. The variable type is not really a type + asASSERT(dtIn.GetTokenType() == ttQuestion); + return -1; + } + } + + int typeId = -1; + asCObjectType *ot = dtIn.GetObjectType(); + if( ot != &functionBehaviours ) + { + // Object's hold the typeId themselves + typeId = ot->typeId; + + if( typeId == -1 ) + { + ACQUIREEXCLUSIVE(engineRWLock); + // Make sure another thread didn't determine the typeId while we were waiting for the lock + if( ot->typeId == -1 ) { - // The ASHANDLE types behave like handles, but are really - // value types so the typeId is never returned as a handle - if( dtIn.IsObjectHandle() ) - typeId |= asTYPEID_OBJHANDLE; - if( dtIn.IsHandleToConst() ) - typeId |= asTYPEID_HANDLETOCONST; + typeId = typeIdSeqNbr++; + if( ot->flags & asOBJ_SCRIPT_OBJECT ) typeId |= asTYPEID_SCRIPTOBJECT; + else if( ot->flags & asOBJ_TEMPLATE ) typeId |= asTYPEID_TEMPLATE; + else if( ot->flags & asOBJ_ENUM ) {} // TODO: Should we have a specific bit for this? + else typeId |= asTYPEID_APPOBJECT; + + ot->typeId = typeId; + + mapTypeIdToObjectType.Insert(typeId, ot); + } + RELEASEEXCLUSIVE(engineRWLock); + } + } + else + { + // This a funcdef, so we'll need to look in the map for the funcdef + + // TODO: optimize: It shouldn't be necessary to exclusive lock when the typeId already exists + ACQUIREEXCLUSIVE(engineRWLock); + + // Find the existing type id + asCScriptFunction *func = dtIn.GetFuncDef(); + asASSERT(func); + asSMapNode *cursor = 0; + mapTypeIdToFunction.MoveFirst(&cursor); + while( cursor ) + { + if( mapTypeIdToFunction.GetValue(cursor) == func ) + { + typeId = mapTypeIdToFunction.GetKey(cursor); + break; } - return typeId; + mapTypeIdToFunction.MoveNext(&cursor, cursor); } - mapTypeIdToDataType.MoveNext(&cursor, cursor); + // The type id doesn't exist, create it + if( typeId == -1 ) + { + // Setup the type id for the funcdef + typeId = typeIdSeqNbr++; + typeId |= asTYPEID_APPOBJECT; + mapTypeIdToFunction.Insert(typeId, func); + } + + RELEASEEXCLUSIVE(engineRWLock); } - // The type id doesn't exist, create it - - // Setup the basic type id - int typeId = typeIdSeqNbr++; - if( dt.GetObjectType() ) + // Add flags according to the requested type + if( dtIn.GetObjectType() && !(dtIn.GetObjectType()->flags & asOBJ_ASHANDLE) ) { - if( dt.GetObjectType()->flags & asOBJ_SCRIPT_OBJECT ) typeId |= asTYPEID_SCRIPTOBJECT; - else if( dt.GetObjectType()->flags & asOBJ_TEMPLATE ) typeId |= asTYPEID_TEMPLATE; - else if( dt.GetObjectType()->flags & asOBJ_ENUM ) {} // TODO: Should we have a specific bit for this? - else typeId |= asTYPEID_APPOBJECT; + // The ASHANDLE types behave like handles, but are really + // value types so the typeId is never returned as a handle + if( dtIn.IsObjectHandle() ) + typeId |= asTYPEID_OBJHANDLE; + if( dtIn.IsHandleToConst() ) + typeId |= asTYPEID_HANDLETOCONST; } - // Insert the basic object type - asCDataType *newDt = asNEW(asCDataType)(dt); - if( newDt == 0 ) - { - // Out of memory - return 0; - } - - newDt->MakeReference(false); - newDt->MakeReadOnly(false); - newDt->MakeHandle(false); - - mapTypeIdToDataType.Insert(typeId, newDt); - - // Call recursively to get the correct typeId - return GetTypeIdFromDataType(dtIn); + return typeId; } asCDataType asCScriptEngine::GetDataTypeFromTypeId(int typeId) const { int baseId = typeId & (asTYPEID_MASK_OBJECT | asTYPEID_MASK_SEQNBR); - asSMapNode *cursor = 0; - if( mapTypeIdToDataType.MoveTo(&cursor, baseId) ) + if( typeId <= asTYPEID_DOUBLE ) { - asCDataType dt(*mapTypeIdToDataType.GetValue(cursor)); + eTokenType type[] = {ttVoid, ttBool, ttInt8, ttInt16, ttInt, ttInt64, ttUInt8, ttUInt16, ttUInt, ttUInt64, ttFloat, ttDouble}; + return asCDataType::CreatePrimitive(type[typeId], false); + } + + // First check if the typeId is an object type + asCObjectType *ot = 0; + ACQUIRESHARED(engineRWLock); + asSMapNode *cursor = 0; + if( mapTypeIdToObjectType.MoveTo(&cursor, baseId) ) + ot = mapTypeIdToObjectType.GetValue(cursor); + RELEASESHARED(engineRWLock); + + if( ot ) + { + asCDataType dt = asCDataType::CreateObject(ot, false); if( typeId & asTYPEID_OBJHANDLE ) dt.MakeHandle(true, true); if( typeId & asTYPEID_HANDLETOCONST ) dt.MakeHandleToConst(true); + + return dt; + } + + // Then check if it is a funcdef + asCScriptFunction *func = 0; + ACQUIRESHARED(engineRWLock); + asSMapNode *cursor2 = 0; + if( mapTypeIdToFunction.MoveTo(&cursor2, baseId) ) + func = mapTypeIdToFunction.GetValue(cursor2); + RELEASESHARED(engineRWLock); + + if( func ) + { + asCDataType dt = asCDataType::CreateFuncDef(func); + if( typeId & asTYPEID_OBJHANDLE ) + dt.MakeHandle(true, true); + if( typeId & asTYPEID_HANDLETOCONST ) + dt.MakeHandleToConst(true); + return dt; } @@ -4586,19 +4717,19 @@ asCObjectType *asCScriptEngine::GetObjectTypeFromTypeId(int typeId) const void asCScriptEngine::RemoveFromTypeIdMap(asCObjectType *type) { - asSMapNode *cursor = 0; - mapTypeIdToDataType.MoveFirst(&cursor); + ACQUIREEXCLUSIVE(engineRWLock); + asSMapNode *cursor = 0; + mapTypeIdToObjectType.MoveFirst(&cursor); while( cursor ) { - asCDataType *dt = mapTypeIdToDataType.GetValue(cursor); - asSMapNode *old = cursor; - mapTypeIdToDataType.MoveNext(&cursor, cursor); - if( dt->GetObjectType() == type ) + if( mapTypeIdToObjectType.GetValue(cursor) == type ) { - asDELETE(dt,asCDataType); - mapTypeIdToDataType.Erase(old); + mapTypeIdToObjectType.Erase(cursor); + break; } + mapTypeIdToObjectType.MoveNext(&cursor, cursor); } + RELEASEEXCLUSIVE(engineRWLock); } // interface @@ -5318,7 +5449,6 @@ void asCScriptEngine::RemoveScriptFunction(asCScriptFunction *func) // internal void asCScriptEngine::RemoveFuncdef(asCScriptFunction *funcdef) { - // TODO: 2.30.0: redesign: How to avoid removing a funcdef that is shared by multiple modules? funcDefs.RemoveValue(funcdef); } diff --git a/lib/angelscript/source/as_scriptengine.h b/lib/angelscript/source/as_scriptengine.h index 161d22781..c9a2a487f 100644 --- a/lib/angelscript/source/as_scriptengine.h +++ b/lib/angelscript/source/as_scriptengine.h @@ -238,6 +238,7 @@ public: bool CallObjectMethodRetBool(void *obj, int func) const; int CallObjectMethodRetInt(void *obj, int func) const; void *CallObjectMethodRetPtr(void *obj, int func) const; + void *CallObjectMethodRetPtr(void *obj, int param1, asCScriptFunction *func) const; void CallGlobalFunction(void *param1, void *param2, asSSystemFunctionInterface *func, asCScriptFunction *desc) const; bool CallGlobalFunctionRetBool(void *param1, void *param2, asSSystemFunctionInterface *func, asCScriptFunction *desc) const; @@ -400,16 +401,17 @@ public: // This array stores the template instances types that have been automatically generated from template types asCArray generatedTemplateTypes; // Stores the funcdefs - // TODO: 2.30.0: redesign: Only shared funcdefs should be stored here - // a funcdef becomes shared if all arguments and the return type are shared (or application registered) + // TODO: redesign: Only shared funcdefs should be stored here + // a funcdef becomes shared if all arguments and the return type are shared (or application registered) asCArray funcDefs; // doesn't increase ref count // Stores the names of the script sections for debugging purposes asCArray scriptSectionNames; // Type identifiers - mutable int typeIdSeqNbr; - mutable asCMap mapTypeIdToDataType; + mutable int typeIdSeqNbr; + mutable asCMap mapTypeIdToObjectType; + mutable asCMap mapTypeIdToFunction; // Garbage collector asCGarbageCollector gc; diff --git a/lib/angelscript/source/as_scriptfunction.cpp b/lib/angelscript/source/as_scriptfunction.cpp index 874cab558..efb4a5c07 100644 --- a/lib/angelscript/source/as_scriptfunction.cpp +++ b/lib/angelscript/source/as_scriptfunction.cpp @@ -105,7 +105,7 @@ static void ScriptFunction_CreateDelegate_Generic(asIScriptGeneric *gen) gen->SetReturnAddress(CreateDelegate(func, obj)); } -// TODO: 2.29.0: operator== +// TODO: operator== /*static void ScriptFunction_opEquals_Generic(asIScriptGeneric *gen) { asCScriptFunction *funcSelf = (asCScriptFunction*)gen->GetObject(); @@ -124,7 +124,7 @@ void RegisterScriptFunction(asCScriptEngine *engine) UNUSED_VAR(r); // It is only used in debug mode engine->functionBehaviours.engine = engine; engine->functionBehaviours.flags = asOBJ_REF | asOBJ_GC | asOBJ_SCRIPT_FUNCTION; - engine->functionBehaviours.name = "_builtin_function_"; + engine->functionBehaviours.name = "$func"; #ifndef AS_MAX_PORTABILITY r = engine->RegisterBehaviourToObjectType(&engine->functionBehaviours, asBEHAVE_ADDREF, "void f()", asMETHOD(asCScriptFunction,AddRef), asCALL_THISCALL, 0); asASSERT( r >= 0 ); r = engine->RegisterBehaviourToObjectType(&engine->functionBehaviours, asBEHAVE_RELEASE, "void f()", asMETHOD(asCScriptFunction,Release), asCALL_THISCALL, 0); asASSERT( r >= 0 ); @@ -133,7 +133,7 @@ void RegisterScriptFunction(asCScriptEngine *engine) r = engine->RegisterBehaviourToObjectType(&engine->functionBehaviours, asBEHAVE_GETGCFLAG, "bool f()", asMETHOD(asCScriptFunction,GetFlag), asCALL_THISCALL, 0); asASSERT( r >= 0 ); r = engine->RegisterBehaviourToObjectType(&engine->functionBehaviours, asBEHAVE_ENUMREFS, "void f(int&in)", asMETHOD(asCScriptFunction,EnumReferences), asCALL_THISCALL, 0); asASSERT( r >= 0 ); r = engine->RegisterBehaviourToObjectType(&engine->functionBehaviours, asBEHAVE_RELEASEREFS, "void f(int&in)", asMETHOD(asCScriptFunction,ReleaseAllHandles), asCALL_THISCALL, 0); asASSERT( r >= 0 ); - // TODO: 2.29.0: Need some way to allow the arg type to adapt when the funcdefs are instantiated + // TODO: Need some way to allow the arg type to adapt when the funcdefs are instantiated // r = engine->RegisterMethodToObjectType(&engine->functionBehaviours, "bool opEquals(const int &in)", asMETHOD(asCScriptFunction,operator==), asCALL_THISCALL); asASSERT( r >= 0 ); #else r = engine->RegisterBehaviourToObjectType(&engine->functionBehaviours, asBEHAVE_ADDREF, "void f()", asFUNCTION(ScriptFunction_AddRef_Generic), asCALL_GENERIC, 0); asASSERT( r >= 0 ); @@ -226,7 +226,7 @@ asIScriptFunction *asCScriptFunction::GetDelegateFunction() const return funcForDelegate; } -// TODO: 2.29.0: operator== +// TODO: operator== /* // internal bool asCScriptFunction::operator==(const asCScriptFunction &other) const @@ -419,7 +419,6 @@ asCScriptFunction::~asCScriptFunction() // If the engine pointer is 0, then DestroyInternal has already been called and there is nothing more to do if( engine == 0 ) return; - // TODO: 2.30.0: redesign: Shouldn't this have been done already? DestroyInternal(); // Finally set the engine pointer to 0 because it must not be accessed again @@ -678,7 +677,7 @@ asCString asCScriptFunction::GetDeclarationStr(bool includeObjectName, bool incl if( !(returnType.GetTokenType() == ttVoid && objectType && (name == objectType->name || (name.GetLength() > 0 && name[0] == '~') || - name == "_beh_0_" || name == "_beh_2_")) ) + name == "$beh0" || name == "$beh2")) ) { str = returnType.Format(nameSpace, includeNamespace); str += " "; @@ -699,13 +698,13 @@ asCString asCScriptFunction::GetDeclarationStr(bool includeObjectName, bool incl } if( name == "" ) str += "_unnamed_function_("; - else if( name.SubString(0,5) == "_beh_" && name.GetLength() == 7 ) + else if( name.SubString(0,4) == "$beh" && name.GetLength() == 5 ) { - if( name[5] == '0' + asBEHAVE_CONSTRUCT ) + if( name[4] == '0' + asBEHAVE_CONSTRUCT ) str += objectType->name + "("; - else if( name[5] == '0' + asBEHAVE_FACTORY ) + else if( name[4] == '0' + asBEHAVE_FACTORY ) str += returnType.GetObjectType()->name + "("; - else if( name[5] == '0' + asBEHAVE_DESTRUCT ) + else if( name[4] == '0' + asBEHAVE_DESTRUCT ) str += "~" + objectType->name + "("; else str += name + "("; @@ -1008,7 +1007,7 @@ void asCScriptFunction::ComputeSignatureId() // internal bool asCScriptFunction::IsSignatureEqual(const asCScriptFunction *func) const { - if( !IsSignatureExceptNameEqual(func) || name != func->name ) return false; + if( name != func->name || !IsSignatureExceptNameEqual(func) ) return false; return true; } @@ -1043,9 +1042,9 @@ bool asCScriptFunction::IsSignatureExceptNameAndReturnTypeEqual(const asCScriptF bool asCScriptFunction::IsSignatureExceptNameAndReturnTypeEqual(const asCArray ¶mTypes, const asCArray ¶mInOut, const asCObjectType *objType, bool readOnly) const { if( this->isReadOnly != readOnly ) return false; + if( (this->objectType != 0) != (objType != 0) ) return false; if( this->inOutFlags != paramInOut ) return false; if( this->parameterTypes != paramTypes ) return false; - if( (this->objectType != 0) != (objType != 0) ) return false; return true; } @@ -1299,7 +1298,11 @@ void asCScriptFunction::ReleaseReferences() if( group != 0 ) group->Release(); if( funcId ) - engine->scriptFunctions[funcId]->ReleaseInternal(); + { + asCScriptFunction *fptr = engine->scriptFunctions[funcId]; + if( fptr ) + fptr->ReleaseInternal(); + } } break; diff --git a/lib/angelscript/source/as_scriptfunction.h b/lib/angelscript/source/as_scriptfunction.h index a5044d859..1ab9a75e3 100644 --- a/lib/angelscript/source/as_scriptfunction.h +++ b/lib/angelscript/source/as_scriptfunction.h @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2014 Andreas Jonsson + Copyright (c) 2003-2015 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -186,12 +186,12 @@ public: void DestroyHalfCreated(); - // TODO: 2.29.0: operator== - // TODO: 2.29.0: The asIScriptFunction should provide operator== and operator!= that should do a - // a value comparison. Two delegate objects that point to the same object and class method should compare as equal - // TODO: 2.29.0: The operator== should also be provided in script as opEquals to allow the same comparison in script - // To do this we'll need some way to adapt the argtype for opEquals for each funcdef, preferrably without instantiating lots of different methods - // Perhaps reusing 'auto' to mean the same type as the object + // TODO: operator== + // TODO: The asIScriptFunction should provide operator== and operator!= that should do a + // a value comparison. Two delegate objects that point to the same object and class method should compare as equal + // TODO: The operator== should also be provided in script as opEquals to allow the same comparison in script + // To do this we'll need some way to adapt the argtype for opEquals for each funcdef, preferrably without instantiating lots of different methods + // Perhaps reusing 'auto' to mean the same type as the object //bool operator==(const asCScriptFunction &other) const; void DestroyInternal(); @@ -329,7 +329,7 @@ public: asSSystemFunctionInterface *sysFuncIntf; }; -const char * const DELEGATE_FACTORY = "%delegate_factory"; +const char * const DELEGATE_FACTORY = "$dlgte"; asCScriptFunction *CreateDelegate(asCScriptFunction *func, void *obj); END_AS_NAMESPACE diff --git a/lib/angelscript/source/as_scriptobject.cpp b/lib/angelscript/source/as_scriptobject.cpp index 89cc4d16e..80af81e83 100644 --- a/lib/angelscript/source/as_scriptobject.cpp +++ b/lib/angelscript/source/as_scriptobject.cpp @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2014 Andreas Jonsson + Copyright (c) 2003-2015 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -173,6 +173,24 @@ static void ScriptObject_ReleaseAllHandles_Generic(asIScriptGeneric *gen) self->ReleaseAllHandles(engine); } +static void ScriptObject_Assignment_Generic(asIScriptGeneric *gen) +{ + asCScriptObject *other = *(asCScriptObject**)gen->GetAddressOfArg(0); + asCScriptObject *self = (asCScriptObject*)gen->GetObject(); + + *self = *other; + + *(asCScriptObject**)gen->GetAddressOfReturnLocation() = self; +} + +static void ScriptObject_Construct_Generic(asIScriptGeneric *gen) +{ + asCObjectType *objType = *(asCObjectType**)gen->GetAddressOfArg(0); + asCScriptObject *self = (asCScriptObject*)gen->GetObject(); + + ScriptObject_Construct(objType, self); +} + #endif void RegisterScriptObject(asCScriptEngine *engine) @@ -182,7 +200,7 @@ void RegisterScriptObject(asCScriptEngine *engine) UNUSED_VAR(r); // It is only used in debug mode engine->scriptTypeBehaviours.engine = engine; engine->scriptTypeBehaviours.flags = asOBJ_SCRIPT_OBJECT | asOBJ_REF | asOBJ_GC; - engine->scriptTypeBehaviours.name = "_builtin_object_"; + engine->scriptTypeBehaviours.name = "$obj"; #ifndef AS_MAX_PORTABILITY r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_CONSTRUCT, "void f(int&in)", asFUNCTION(ScriptObject_Construct), asCALL_CDECL_OBJLAST, 0); asASSERT( r >= 0 ); r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_ADDREF, "void f()", asMETHOD(asCScriptObject,AddRef), asCALL_THISCALL, 0); asASSERT( r >= 0 ); @@ -216,14 +234,6 @@ void RegisterScriptObject(asCScriptEngine *engine) #endif } -void ScriptObject_Construct_Generic(asIScriptGeneric *gen) -{ - asCObjectType *objType = *(asCObjectType**)gen->GetAddressOfArg(0); - asCScriptObject *self = (asCScriptObject*)gen->GetObject(); - - ScriptObject_Construct(objType, self); -} - void ScriptObject_Construct(asCObjectType *objType, asCScriptObject *self) { new(self) asCScriptObject(objType); @@ -744,16 +754,6 @@ void asCScriptObject::ReleaseAllHandles(asIScriptEngine *engine) } } -void ScriptObject_Assignment_Generic(asIScriptGeneric *gen) -{ - asCScriptObject *other = *(asCScriptObject**)gen->GetAddressOfArg(0); - asCScriptObject *self = (asCScriptObject*)gen->GetObject(); - - *self = *other; - - *(asCScriptObject**)gen->GetAddressOfReturnLocation() = self; -} - asCScriptObject &ScriptObject_Assignment(asCScriptObject *other, asCScriptObject *self) { return (*self = *other); diff --git a/lib/angelscript/source/as_scriptobject.h b/lib/angelscript/source/as_scriptobject.h index eab7ca64b..dcdc5f371 100644 --- a/lib/angelscript/source/as_scriptobject.h +++ b/lib/angelscript/source/as_scriptobject.h @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2014 Andreas Jonsson + Copyright (c) 2003-2015 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -154,9 +154,6 @@ asCScriptObject &ScriptObject_Assignment(asCScriptObject *other, asCScriptObject void ScriptObject_ConstructUnitialized(asCObjectType *objType, asCScriptObject *self); -void ScriptObject_Construct_Generic(asIScriptGeneric *gen); -void ScriptObject_Assignment_Generic(asIScriptGeneric *gen); - void RegisterScriptObject(asCScriptEngine *engine); asIScriptObject *ScriptObjectFactory(const asCObjectType *objType, asCScriptEngine *engine); diff --git a/lib/angelscript/source/as_texts.h b/lib/angelscript/source/as_texts.h index 808e1644c..b43648b16 100644 --- a/lib/angelscript/source/as_texts.h +++ b/lib/angelscript/source/as_texts.h @@ -142,6 +142,7 @@ #define TXT_INVALID_CONTINUE "Invalid 'continue'" #define TXT_INVALID_ESCAPE_SEQUENCE "Invalid escape sequence" #define TXT_INVALID_EXPRESSION_AMBIGUOUS_NAME "Invalid expression: ambiguous name" +#define TXT_INVALID_EXPRESSION_LAMBDA "Invalid expression: stand-alone anonymous function" #define TXT_INVALID_OP_ON_METHOD "Invalid operation on method" #define TXT_INVALID_REF_PROP_ACCESS "Invalid reference. Property accessors cannot be used in combined read/write operations" #define TXT_INVALID_SCOPE "Invalid scope resolution" @@ -252,6 +253,7 @@ #define TXT_TOO_MANY_JUMP_LABELS "The function has too many jump labels to handle. Split the function into smaller ones." #define TXT_TOO_MANY_VALUES_FOR_LIST "Too many values to match pattern" #define TXT_TYPE_s_NOT_AVAILABLE_FOR_MODULE "Type '%s' is not available for this module" +#define TXT_TYPE_s_NOT_TEMPLATE "Type '%s' is not a template type" #define TXT_UNEXPECTED_END_OF_FILE "Unexpected end of file" #define TXT_UNEXPECTED_TOKEN_s "Unexpected token '%s'" diff --git a/lib/angelscript/source/as_tokendef.h b/lib/angelscript/source/as_tokendef.h index 6e08650a3..a88d2605a 100644 --- a/lib/angelscript/source/as_tokendef.h +++ b/lib/angelscript/source/as_tokendef.h @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2014 Andreas Jonsson + Copyright (c) 2003-2015 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -311,6 +311,7 @@ const char * const OVERRIDE_TOKEN = "override"; const char * const GET_TOKEN = "get"; const char * const SET_TOKEN = "set"; const char * const ABSTRACT_TOKEN = "abstract"; +const char * const FUNCTION_TOKEN = "function"; END_AS_NAMESPACE diff --git a/lib/angelscript/source/as_typeinfo.cpp b/lib/angelscript/source/as_typeinfo.cpp index 534ac0b53..230aff05f 100644 --- a/lib/angelscript/source/as_typeinfo.cpp +++ b/lib/angelscript/source/as_typeinfo.cpp @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2014 Andreas Jonsson + Copyright (c) 2003-2015 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -53,7 +53,6 @@ asCTypeInfo::asCTypeInfo() isExplicitHandle = false; qwordValue = 0; isLValue = false; - isVoidExpression = false; isRefToLocal = false; } @@ -68,7 +67,6 @@ void asCTypeInfo::Set(const asCDataType &dt) isExplicitHandle = false; qwordValue = 0; isLValue = false; - isVoidExpression = false; isRefToLocal = false; } @@ -135,6 +133,18 @@ void asCTypeInfo::SetUndefinedFuncHandle(asCScriptEngine *engine) isLValue = false; } +bool asCTypeInfo::IsUndefinedFuncHandle() const +{ + if( isConstant == false ) return false; + if( qwordValue == 0 ) return false; + if( isLValue ) return false; + if( dataType.GetObjectType() == 0 ) return false; + if( dataType.GetObjectType()->name != "$func" ) return false; + if( dataType.GetFuncDef() ) return false; + + return true; +} + void asCTypeInfo::SetNullConstant() { Set(asCDataType::CreateNullHandle()); @@ -153,17 +163,19 @@ bool asCTypeInfo::IsNullConstant() const return false; } -void asCTypeInfo::SetVoidExpression() +void asCTypeInfo::SetVoid() { Set(asCDataType::CreatePrimitive(ttVoid, false)); isLValue = false; - isConstant = false; - isVoidExpression = true; + isConstant = true; } -bool asCTypeInfo::IsVoidExpression() const +bool asCTypeInfo::IsVoid() const { - return isVoidExpression; + if( dataType.GetTokenType() == ttVoid ) + return true; + + return false; } void asCTypeInfo::SetDummy() diff --git a/lib/angelscript/source/as_typeinfo.h b/lib/angelscript/source/as_typeinfo.h index aa8ad528c..724411e82 100644 --- a/lib/angelscript/source/as_typeinfo.h +++ b/lib/angelscript/source/as_typeinfo.h @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2014 Andreas Jonsson + Copyright (c) 2003-2015 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -61,11 +61,12 @@ struct asCTypeInfo void SetConstantD(const asCDataType &dataType, double value); void SetNullConstant(); void SetUndefinedFuncHandle(asCScriptEngine *engine); - void SetVoidExpression(); + void SetVoid(); void SetDummy(); + bool IsUndefinedFuncHandle() const; bool IsNullConstant() const; - bool IsVoidExpression() const; + bool IsVoid() const; asCDataType dataType; bool isLValue : 1; // Can this value be updated in assignment, or increment operators, etc @@ -73,9 +74,8 @@ struct asCTypeInfo bool isConstant : 1; bool isVariable : 1; bool isExplicitHandle : 1; - bool isVoidExpression : 1; bool isRefToLocal : 1; // The reference may be to a local variable - short dummy : 9; + short dummy : 10; short stackOffset; union { diff --git a/src/config/user_config.hpp b/src/config/user_config.hpp index d4d859992..5b932d901 100644 --- a/src/config/user_config.hpp +++ b/src/config/user_config.hpp @@ -593,9 +593,9 @@ namespace UserConfigParams "stun.voxgratia.org", "stun.xten.com") ); - PARAM_PREFIX StringUserConfigParam m_packets_log_filename - PARAM_DEFAULT( StringUserConfigParam("packets_log.txt", "packets_log_filename", - "Where to log received and sent packets.") ); + PARAM_PREFIX BoolUserConfigParam m_log_packets + PARAM_DEFAULT( BoolUserConfigParam(false, "log-network-packets", + "If all network packets should be logged") ); // ---- Graphic Quality PARAM_PREFIX GroupUserConfigParam m_graphics_quality diff --git a/src/graphics/hit_sfx.hpp b/src/graphics/hit_sfx.hpp index 0fe6431d4..b30343ea9 100644 --- a/src/graphics/hit_sfx.hpp +++ b/src/graphics/hit_sfx.hpp @@ -36,7 +36,7 @@ private: public: HitSFX(const Vec3& coord, const char* explosion_sound); ~HitSFX(); - virtual bool updateAndDelete(float dt); + virtual bool updateAndDelete(float dt) OVERRIDE; virtual void setLocalPlayerKartHit() OVERRIDE; }; // HitSFX diff --git a/src/graphics/shaders.hpp b/src/graphics/shaders.hpp index 8b051c7de..6953f11fc 100644 --- a/src/graphics/shaders.hpp +++ b/src/graphics/shaders.hpp @@ -18,7 +18,6 @@ #ifndef HEADER_SHADERS_HPP #define HEADER_SHADERS_HPP -#include "config/user_config.hpp" #include "graphics/shader.hpp" #include "graphics/shared_gpu_objects.hpp" #include "graphics/texture_shader.hpp" diff --git a/src/graphics/shadow_matrices.cpp b/src/graphics/shadow_matrices.cpp index 0b4dc1597..9350c68df 100644 --- a/src/graphics/shadow_matrices.cpp +++ b/src/graphics/shadow_matrices.cpp @@ -17,6 +17,7 @@ #include "graphics/shadow_matrices.hpp" +#include "config/user_config.hpp" #include "graphics/central_settings.hpp" #include "graphics/glwrap.hpp" #include "graphics/irr_driver.hpp" diff --git a/src/graphics/stk_text_billboard.hpp b/src/graphics/stk_text_billboard.hpp index 6bcce436f..986e0d9e2 100644 --- a/src/graphics/stk_text_billboard.hpp +++ b/src/graphics/stk_text_billboard.hpp @@ -84,7 +84,7 @@ public: virtual void collectChar(irr::video::ITexture* texture, const irr::core::rect& destRect, const irr::core::rect& sourceRect, - const irr::video::SColor* const colors); + const irr::video::SColor* const colors) OVERRIDE; virtual void updateAbsolutePosition() OVERRIDE; }; diff --git a/src/guiengine/message_queue.cpp b/src/guiengine/message_queue.cpp index ebe684fda..548c5a86c 100644 --- a/src/guiengine/message_queue.cpp +++ b/src/guiengine/message_queue.cpp @@ -54,6 +54,8 @@ public: m_render_type = "achievement-message::neutral"; else if (mt==MessageQueue::MT_ERROR) m_render_type = "error-message::neutral"; + else if (mt==MessageQueue::MT_GENERIC) + m_render_type = "generic-message::neutral"; else m_render_type = "friend-message::neutral"; } // Message diff --git a/src/guiengine/message_queue.hpp b/src/guiengine/message_queue.hpp index 3abeb5874..e6f027522 100644 --- a/src/guiengine/message_queue.hpp +++ b/src/guiengine/message_queue.hpp @@ -34,7 +34,7 @@ namespace MessageQueue * different look. This type is used to sort the messages, so it is * important that messages that need to be shown as early as possible * will be listed last (i.e. have highest priority). */ - enum MessageType { MT_FRIEND, MT_ACHIEVEMENT, MT_ERROR}; + enum MessageType { MT_FRIEND, MT_ACHIEVEMENT, MT_ERROR, MT_GENERIC}; void add(MessageType mt, const core::stringw &message); void updatePosition(); diff --git a/src/input/gamepad_config.cpp b/src/input/gamepad_config.cpp index 2dacb758a..0be20e5e8 100644 --- a/src/input/gamepad_config.cpp +++ b/src/input/gamepad_config.cpp @@ -126,14 +126,14 @@ void GamepadConfig::setDefaultBinds () { setBinding(PA_STEER_LEFT, Input::IT_STICKMOTION, 0, Input::AD_NEGATIVE); setBinding(PA_STEER_RIGHT, Input::IT_STICKMOTION, 0, Input::AD_POSITIVE); - setBinding(PA_ACCEL, Input::IT_STICKMOTION, 1, Input::AD_NEGATIVE); - setBinding(PA_BRAKE, Input::IT_STICKMOTION, 1, Input::AD_POSITIVE); - setBinding(PA_FIRE, Input::IT_STICKBUTTON, 0); - setBinding(PA_NITRO, Input::IT_STICKBUTTON, 1); - setBinding(PA_DRIFT, Input::IT_STICKBUTTON, 2); - setBinding(PA_RESCUE, Input::IT_STICKBUTTON, 3); - setBinding(PA_LOOK_BACK, Input::IT_STICKBUTTON, 4); - setBinding(PA_PAUSE_RACE, Input::IT_STICKBUTTON, 5); + setBinding(PA_ACCEL, Input::IT_STICKBUTTON, 0, Input::AD_NEGATIVE); + setBinding(PA_BRAKE, Input::IT_STICKBUTTON, 3, Input::AD_POSITIVE); + setBinding(PA_FIRE, Input::IT_STICKBUTTON, 1); + setBinding(PA_NITRO, Input::IT_STICKBUTTON, 4); + setBinding(PA_DRIFT, Input::IT_STICKBUTTON, 5); + setBinding(PA_RESCUE, Input::IT_STICKBUTTON, 8); + setBinding(PA_LOOK_BACK, Input::IT_STICKBUTTON, 6); + setBinding(PA_PAUSE_RACE, Input::IT_STICKBUTTON, 9); setBinding(PA_MENU_UP, Input::IT_STICKMOTION, 1, Input::AD_NEGATIVE); setBinding(PA_MENU_DOWN, Input::IT_STICKMOTION, 1, Input::AD_POSITIVE); diff --git a/src/input/gamepad_config.hpp b/src/input/gamepad_config.hpp index 42a8fb478..b62c77d57 100644 --- a/src/input/gamepad_config.hpp +++ b/src/input/gamepad_config.hpp @@ -70,7 +70,7 @@ public: core::stringw toString(); - virtual void save(std::ofstream& stream); + virtual void save(std::ofstream& stream) OVERRIDE; void setDefaultBinds (); virtual core::stringw getBindingAsString(const PlayerAction action) const OVERRIDE; virtual bool load(const XMLNode *config) OVERRIDE; @@ -81,7 +81,7 @@ public: // ------------------------------------------------------------------------ /** Returns true if this device should desensitize its input at values * close to 0 (to avoid 'oversteering'). */ - virtual bool desensitize() const { return m_desensitize;} + virtual bool desensitize() const OVERRIDE { return m_desensitize;} // ------------------------------------------------------------------------ /** Returns the number of buttons in this configuration. */ @@ -103,9 +103,9 @@ public: /** Return deadzone of this configuration. */ int getDeadzone() const { return m_deadzone; } // ------------------------------------------------------------------------ - virtual bool isGamePad() const { return true; } + virtual bool isGamePad() const OVERRIDE { return true; } // ------------------------------------------------------------------------ - virtual bool isKeyboard() const { return false; } + virtual bool isKeyboard() const OVERRIDE { return false; } }; // class GamepadConfig diff --git a/src/input/input_manager.cpp b/src/input/input_manager.cpp index 31b8793b3..a0c969a57 100644 --- a/src/input/input_manager.cpp +++ b/src/input/input_manager.cpp @@ -367,8 +367,8 @@ void InputManager::handleStaticAction(int key, int value) case KEY_F10: if(world && value) { - if(control_is_pressed && ReplayRecorder::get()) - ReplayRecorder::get()->Save(); + if(control_is_pressed) + ReplayRecorder::get()->save(); else history->Save(); } diff --git a/src/io/file_manager.cpp b/src/io/file_manager.cpp index b173f94b2..748fd38d4 100644 --- a/src/io/file_manager.cpp +++ b/src/io/file_manager.cpp @@ -213,6 +213,7 @@ FileManager::FileManager() checkAndCreateConfigDir(); checkAndCreateAddonsDir(); checkAndCreateScreenshotDir(); + checkAndCreateReplayDir(); checkAndCreateCachedTexturesDir(); checkAndCreateGPDir(); @@ -641,6 +642,14 @@ std::string FileManager::getScreenshotDir() const return m_screenshot_dir; } // getScreenshotDir +//----------------------------------------------------------------------------- +/** Returns the directory in which replay file should be stored. + */ +std::string FileManager::getReplayDir() const +{ + return m_replay_dir; +} // getReplayDir + //----------------------------------------------------------------------------- /** Returns the directory in which resized textures should be cached. */ @@ -910,6 +919,32 @@ void FileManager::checkAndCreateScreenshotDir() } // checkAndCreateScreenshotDir +// ---------------------------------------------------------------------------- +/** Creates the directories for replay recorded. This will set m_replay_dir + * with the appropriate path. + */ +void FileManager::checkAndCreateReplayDir() +{ +#if defined(WIN32) || defined(__CYGWIN__) + m_replay_dir = m_user_config_dir + "replay/"; +#elif defined(__APPLE__) + m_replay_dir = getenv("HOME"); + m_replay_dir += "/Library/Application Support/SuperTuxKart/replay/"; +#else + m_replay_dir = checkAndCreateLinuxDir("XDG_DATA_HOME", "supertuxkart", + ".local/share", ".supertuxkart"); + m_replay_dir += "replay/"; +#endif + + if(!checkAndCreateDirectory(m_replay_dir)) + { + Log::error("FileManager", "Can not create replay directory '%s', " + "falling back to '.'.", m_replay_dir.c_str()); + m_replay_dir = "."; + } + +} // checkAndCreateReplayDir + // ---------------------------------------------------------------------------- /** Creates the directories for cached textures. This will set * m_cached_textures_dir with the appropriate path. diff --git a/src/io/file_manager.hpp b/src/io/file_manager.hpp index 003507f9c..a836c6406 100644 --- a/src/io/file_manager.hpp +++ b/src/io/file_manager.hpp @@ -75,6 +75,9 @@ private: /** Directory to store screenshots in. */ std::string m_screenshot_dir; + /** Directory to store replays in. */ + std::string m_replay_dir; + /** Directory where resized textures are cached. */ std::string m_cached_textures_dir; @@ -97,6 +100,7 @@ private: bool isDirectory(const std::string &path) const; void checkAndCreateAddonsDir(); void checkAndCreateScreenshotDir(); + void checkAndCreateReplayDir(); void checkAndCreateCachedTexturesDir(); void checkAndCreateGPDir(); void discoverPaths(); @@ -118,6 +122,7 @@ public: XMLNode *createXMLTreeFromString(const std::string & content); std::string getScreenshotDir() const; + std::string getReplayDir() const; std::string getCachedTexturesDir() const; std::string getGPDir() const; std::string getTextureCacheLocation(const std::string& filename); @@ -146,6 +151,9 @@ public: return fileExists(std::string(prefix) + path); } // ------------------------------------------------------------------------ + /** Returns the name of the stdout file for log messages. */ + static const std::string& getStdoutName() { return m_stdout_filename; } + // ------------------------------------------------------------------------ void listFiles (std::set& result, const std::string& dir, bool make_full_path=false) const; diff --git a/src/items/flyable.cpp b/src/items/flyable.cpp index 39a5161bf..82839730a 100644 --- a/src/items/flyable.cpp +++ b/src/items/flyable.cpp @@ -480,6 +480,7 @@ void Flyable::explode(AbstractKart *kart_hit, PhysicalObject *object, ->getKartTeam(m_owner->getWorldKartId())) continue; } + if (kart->isGhostKart()) continue; // If no secondary hits should be done, only hit the // direct hit kart. diff --git a/src/items/powerup.cpp b/src/items/powerup.cpp index 6e5d5ca94..93ef5e802 100644 --- a/src/items/powerup.cpp +++ b/src/items/powerup.cpp @@ -294,7 +294,7 @@ void Powerup::use() for(unsigned int i = 0 ; i < world->getNumKarts(); ++i) { AbstractKart *kart=world->getKart(i); - if(kart->isEliminated()) continue; + if(kart->isEliminated() || kart->isInvulnerable()) continue; if(kart == m_owner) continue; if(kart->getPosition() == 1) { @@ -328,7 +328,7 @@ void Powerup::use() for(unsigned int i = 0 ; i < world->getNumKarts(); ++i) { AbstractKart *kart=world->getKart(i); - if(kart->isEliminated() || kart== m_owner) continue; + if(kart->isEliminated() || kart== m_owner || kart->isInvulnerable()) continue; if(kart->isShielded()) { kart->decreaseShieldTime(); diff --git a/src/karts/abstract_kart.cpp b/src/karts/abstract_kart.cpp index 75eea4cbc..796e0e514 100644 --- a/src/karts/abstract_kart.cpp +++ b/src/karts/abstract_kart.cpp @@ -40,7 +40,14 @@ AbstractKart::AbstractKart(const std::string& ident, { m_world_kart_id = world_kart_id; m_kart_properties.reset(new KartProperties()); - m_kart_properties->copyForPlayer(kart_properties_manager->getKart(ident)); + const KartProperties* kp = kart_properties_manager->getKart(ident); + if (kp == NULL) + { + Log::warn("Abstract_Kart", "Unknown kart %s, fallback to tux", + ident.c_str()); + kp = kart_properties_manager->getKart(std::string("tux")); + } + m_kart_properties->copyForPlayer(kp); m_difficulty = difficulty; m_kart_animation = NULL; assert(m_kart_properties); diff --git a/src/karts/abstract_kart.hpp b/src/karts/abstract_kart.hpp index 6dffa33f8..70558028f 100644 --- a/src/karts/abstract_kart.hpp +++ b/src/karts/abstract_kart.hpp @@ -46,6 +46,7 @@ class Material; class Powerup; class Skidding; class SlipStream; +class TerrainInfo; /** An abstract interface for the actual karts. Some functions are actually * implemented here in order to allow inlining. @@ -421,6 +422,9 @@ public: /** Shows the star effect for a certain time. */ virtual void showStarEffect(float t) = 0; // ------------------------------------------------------------------------ + /** Returns the terrain info oject. */ + virtual const TerrainInfo *getTerrainInfo() const = 0; + // ------------------------------------------------------------------------ /** Called when the kart crashes against another kart. * \param k The kart that was hit. * \param update_attachments If true the attachment of this kart and the @@ -450,6 +454,12 @@ public: // ------------------------------------------------------------------------ /** Returns whether this kart wins or loses. */ virtual bool getRaceResult() const = 0; + // ------------------------------------------------------------------------ + /** Returns whether this kart is a ghost (replay) kart. */ + virtual bool isGhostKart() const = 0; + // ------------------------------------------------------------------------ + /** Returns whether this kart is jumping. */ + virtual bool isJumping() const = 0; }; // AbstractKart diff --git a/src/karts/controller/ai_base_controller.hpp b/src/karts/controller/ai_base_controller.hpp index c88b31f57..2f7cf3ba5 100644 --- a/src/karts/controller/ai_base_controller.hpp +++ b/src/karts/controller/ai_base_controller.hpp @@ -63,7 +63,7 @@ protected: void setControllerName(const std::string &name); float steerToPoint(const Vec3 &point); float normalizeAngle(float angle); - virtual void update (float delta) ; + virtual void update (float delta); virtual void setSteering (float angle, float dt); virtual bool canSkid(float steer_fraction) = 0; // ------------------------------------------------------------------------ diff --git a/src/karts/controller/ghost_controller.cpp b/src/karts/controller/ghost_controller.cpp new file mode 100644 index 000000000..3e9604742 --- /dev/null +++ b/src/karts/controller/ghost_controller.cpp @@ -0,0 +1,60 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2016 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "karts/controller/ghost_controller.hpp" +#include "modes/world.hpp" + +GhostController::GhostController(AbstractKart *kart) + : Controller(kart) +{ +} // GhostController + +//----------------------------------------------------------------------------- +void GhostController::reset() +{ + m_current_index = 0; + m_current_time = 0.0f; +} // reset + +//----------------------------------------------------------------------------- +void GhostController::update(float dt) +{ + m_current_time = World::getWorld()->getTime(); + // Find (if necessary) the next index to use + if (m_current_time != 0.0f) + { + while (m_current_index + 1 < m_all_times.size() && + m_current_time >= m_all_times[m_current_index + 1]) + { + m_current_index++; + } + } + +} // update + +//----------------------------------------------------------------------------- +void GhostController::addReplayTime(float time) +{ + // FIXME: for now avoid that transforms for the same time are set + // twice (to avoid division by zero in update). This should be + // done when saving in replay + if (m_all_times.size() > 0 && m_all_times.back() == time) + return; + m_all_times.push_back(time); + +} // addReplayTime diff --git a/src/karts/controller/ghost_controller.hpp b/src/karts/controller/ghost_controller.hpp new file mode 100644 index 000000000..e16fe1f06 --- /dev/null +++ b/src/karts/controller/ghost_controller.hpp @@ -0,0 +1,78 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2016 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef HEADER_GHOST_CONTROLLER_HPP +#define HEADER_GHOST_CONTROLLER_HPP + +#include "karts/controller/controller.hpp" +#include "states_screens/state_manager.hpp" + +#include + +/** A class for Ghost controller. + * \ingroup controller + */ +class GhostController : public Controller +{ +private: + /** Pointer to the last index in m_all_times that is smaller than + * the current world time. */ + unsigned int m_current_index; + + /** The current world time. */ + float m_current_time; + + /** The list of the times at which the events of kart were reached. */ + std::vector m_all_times; + +public: + GhostController(AbstractKart *kart); + virtual ~GhostController() {}; + virtual void reset(); + virtual void update (float dt); + virtual bool disableSlipstreamBonus() const { return true; } + virtual void crashed(const Material *m) {}; + virtual void crashed(const AbstractKart *k) {}; + virtual void handleZipper(bool play_sound) {}; + virtual void finishedRace(float time) {}; + virtual void collectedItem(const Item &item, int add_info=-1, + float previous_energy=0) {}; + virtual void setPosition(int p) {}; + virtual bool isPlayerController() const { return false; } + virtual bool isLocalPlayerController() const { return false; } + virtual void action(PlayerAction action, int value) {}; + virtual void skidBonusTriggered() {}; + virtual void newLap(int lap) {}; + void addReplayTime(float time); + // ------------------------------------------------------------------------ + bool isReplayEnd() const + { return m_current_index + 1 >= m_all_times.size(); } + // ------------------------------------------------------------------------ + float getReplayDelta() const + { + assert(m_current_index < m_all_times.size()); + return ((m_current_time - m_all_times[m_current_index]) / + (m_all_times[m_current_index + 1] - m_all_times[m_current_index])); + } + // ------------------------------------------------------------------------ + unsigned int getCurrentReplayIndex() const + { return m_current_index; } + // ------------------------------------------------------------------------ +}; // GhostController + +#endif diff --git a/src/karts/controller/local_player_controller.hpp b/src/karts/controller/local_player_controller.hpp index 39b9dcc4a..932b11f8d 100644 --- a/src/karts/controller/local_player_controller.hpp +++ b/src/karts/controller/local_player_controller.hpp @@ -75,7 +75,7 @@ public: virtual bool isLocalPlayerController() const OVERRIDE {return true;} // ------------------------------------------------------------------------ /** Returns the name of the player profile. */ - core::stringw getName() const { return m_player->getProfile()->getName(); } + core::stringw getName() const OVERRIDE { return m_player->getProfile()->getName(); } }; // LocalPlayerController diff --git a/src/karts/controller/player_controller.hpp b/src/karts/controller/player_controller.hpp index 7db23f2b0..08640c9bb 100644 --- a/src/karts/controller/player_controller.hpp +++ b/src/karts/controller/player_controller.hpp @@ -64,22 +64,22 @@ public: { } // setPosition // ------------------------------------------------------------------------ - virtual void crashed(const AbstractKart *k) + virtual void crashed(const AbstractKart *k) OVERRIDE { } // crashed(AbstractKart) // ------------------------------------------------------------------------ - virtual void crashed(const Material *m) + virtual void crashed(const Material *m) OVERRIDE { } // crashed(Material) // ------------------------------------------------------------------------ /** Callback whenever a new lap is triggered. Used by the AI * to trigger a recomputation of the way to use, not used for players. */ - virtual void newLap(int lap) + virtual void newLap(int lap) OVERRIDE { } // ------------------------------------------------------------------------ /** Player will always be able to get a slipstream bonus. */ - virtual bool disableSlipstreamBonus() const + virtual bool disableSlipstreamBonus() const OVERRIDE { return false; } diff --git a/src/karts/controller/skidding_ai.cpp b/src/karts/controller/skidding_ai.cpp index fa2ce5784..0e3440cac 100644 --- a/src/karts/controller/skidding_ai.cpp +++ b/src/karts/controller/skidding_ai.cpp @@ -333,7 +333,8 @@ void SkiddingAI::update(float dt) // If we are faster, try to predict the point where we will hit // the other kart - if(m_kart_ahead->getSpeed() < m_kart->getSpeed()) + if((m_kart_ahead->getSpeed() < m_kart->getSpeed()) && + !m_kart_ahead->isGhostKart()) { float time_till_hit = m_distance_ahead / (m_kart->getSpeed()-m_kart_ahead->getSpeed()); @@ -1757,7 +1758,7 @@ void SkiddingAI::checkCrashes(const Vec3& pos ) { const AbstractKart* kart = m_world->getKart(j); // Ignore eliminated karts - if(kart==m_kart||kart->isEliminated()) continue; + if(kart==m_kart||kart->isEliminated()||kart->isGhostKart()) continue; const AbstractKart *other_kart = m_world->getKart(j); // Ignore karts ahead that are faster than this kart. if(m_kart->getVelocityLC().getZ() < other_kart->getVelocityLC().getZ()) diff --git a/src/karts/ghost_kart.cpp b/src/karts/ghost_kart.cpp index 885e3dd73..56ddfc82f 100644 --- a/src/karts/ghost_kart.cpp +++ b/src/karts/ghost_kart.cpp @@ -17,17 +17,19 @@ // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "karts/ghost_kart.hpp" +#include "karts/controller/ghost_controller.hpp" +#include "karts/kart_gfx.hpp" +#include "karts/kart_model.hpp" #include "modes/world.hpp" #include "LinearMath/btQuaternion.h" -#include "utils/log.hpp" -GhostKart::GhostKart(const std::string& ident) - : Kart(ident, /*world kart id*/99999, - /*position*/-1, btTransform(), PLAYER_DIFFICULTY_NORMAL) +GhostKart::GhostKart(const std::string& ident, unsigned int world_kart_id, + int position) + : Kart(ident, world_kart_id, + position, btTransform(btQuaternion(0, 0, 0, 1)), + PLAYER_DIFFICULTY_NORMAL) { - m_current_transform = 0; - m_next_event = 0; } // GhostKart // ---------------------------------------------------------------------------- @@ -35,84 +37,108 @@ void GhostKart::reset() { m_node->setVisible(true); Kart::reset(); - m_current_transform = 0; - m_next_event = 0; // This will set the correct start position update(0); } // reset // ---------------------------------------------------------------------------- -/** Sets the next time and transform. The current time and transform becomes - * the previous time and transform. - * \param - */ -void GhostKart::addTransform(float time, const btTransform &trans) +void GhostKart::addReplayEvent(float time, + const btTransform &trans, + const ReplayBase::PhysicInfo &pi, + const ReplayBase::KartReplayEvent &kre) { - // FIXME: for now avoid that transforms for the same time are set - // twice (to avoid division by zero in update). This should be - // done when saving in replay - if(m_all_times.size()>0 && m_all_times.back()==time) - return; - m_all_times.push_back(time); - m_all_transform.push_back(trans); -} // addTransform + GhostController* gc = dynamic_cast(getController()); + gc->addReplayTime(time); + + m_all_transform.push_back(trans); + m_all_physic_info.push_back(pi); + m_all_replay_events.push_back(kre); + + // Use first frame of replay to calculate default suspension + if (m_all_physic_info.size() == 1) + { + float f = 0; + for (int i = 0; i < 4; i++) + f += m_all_physic_info[0].m_suspension_length[i]; + m_graphical_y_offset = -f / 4 + getKartModel()->getLowestPoint(); + m_kart_model->setDefaultSuspension(); + } -// ---------------------------------------------------------------------------- -/** Adds a replay event for this kart. - */ -void GhostKart::addReplayEvent(const ReplayBase::KartReplayEvent &kre) -{ - m_replay_events.push_back(kre); } // addReplayEvent // ---------------------------------------------------------------------------- -/** Updates the ghost data each time step. It uses interpolation to get a new - * position and rotation. +/** Updates the current event of the ghost kart using interpolation * \param dt Time step size. */ void GhostKart::update(float dt) { - float t = World::getWorld()->getTime(); - // Don't do anything at startup - if(t==0) return; - updateTransform(t, dt); - while(m_next_event < m_replay_events.size() && - m_replay_events[m_next_event].m_time <= t) - { - Log::debug("Ghost_Kart", "Handling event %d", m_next_event); - // Handle the next event now - m_next_event++; - } -} -// ---------------------------------------------------------------------------- -/** Updates the current transform of the ghost kart using interpolation - * \param t Current world time. - * \param dt Time step size. - */ -void GhostKart::updateTransform(float t, float dt) -{ + GhostController* gc = dynamic_cast(getController()); + if (gc == NULL) return; - // Find (if necessary) the next index to use - while(m_current_transform+1 < m_all_times.size() && - t>=m_all_times[m_current_transform+1]) - { - m_current_transform ++; - } - if(m_current_transform+1>=m_all_times.size()) + gc->update(dt); + if (gc->isReplayEnd()) { m_node->setVisible(false); return; } - float f =(t - m_all_times[m_current_transform]) - / ( m_all_times[m_current_transform+1] - - m_all_times[m_current_transform] ); - setXYZ((1-f)*m_all_transform[m_current_transform ].getOrigin() - + f *m_all_transform[m_current_transform+1].getOrigin() ); - const btQuaternion q = m_all_transform[m_current_transform].getRotation() - .slerp(m_all_transform[m_current_transform+1] - .getRotation(), - f); + const unsigned int idx = gc->getCurrentReplayIndex(); + const float rd = gc->getReplayDelta(); + assert(idx < m_all_transform.size()); + + float nitro_frac = 0; + if (m_all_replay_events[idx].m_on_nitro) + { + nitro_frac = fabsf(m_all_physic_info[idx].m_speed) / + (m_kart_properties->getEngineMaxSpeed()); + + if (nitro_frac > 1.0f) + nitro_frac = 1.0f; + } + getKartGFX()->updateNitroGraphics(nitro_frac); + + if (m_all_replay_events[idx].m_on_zipper) + showZipperFire(); + + setXYZ((1- rd)*m_all_transform[idx ].getOrigin() + + rd *m_all_transform[idx + 1].getOrigin() ); + + const btQuaternion q = m_all_transform[idx].getRotation() + .slerp(m_all_transform[idx + 1].getRotation(), rd); setRotation(q); - Moveable::updateGraphics(dt, Vec3(0,0,0), btQuaternion(0, 0, 0, 1)); + + Vec3 center_shift(0, 0, 0); + center_shift.setY(m_graphical_y_offset); + center_shift = getTrans().getBasis() * center_shift; + + Moveable::updateGraphics(dt, center_shift, btQuaternion(0, 0, 0, 1)); + getKartModel()->update(dt, dt*(m_all_physic_info[idx].m_speed), + m_all_physic_info[idx].m_steer, m_all_physic_info[idx].m_speed, idx); + getKartGFX()->update(dt); + + Vec3 front(0, 0, getKartLength()*0.5f); + m_xyz_front = getTrans()(front); + + if (m_all_replay_events[idx].m_jumping && !m_is_jumping) + { + m_is_jumping = true; + getKartModel()->setAnimation(KartModel::AF_JUMP_START); + } + else if (!m_all_replay_events[idx].m_jumping && m_is_jumping) + { + m_is_jumping = false; + getKartModel()->setAnimation(KartModel::AF_DEFAULT); + } + } // update + +// ---------------------------------------------------------------------------- +/** Returns the speed of the kart in meters/second. */ +float GhostKart::getSpeed() const +{ + const GhostController* gc = + dynamic_cast(getController()); + + assert(gc->getCurrentReplayIndex() < m_all_physic_info.size()); + return m_all_physic_info[gc->getCurrentReplayIndex()].m_speed; +} // getSpeed diff --git a/src/karts/ghost_kart.hpp b/src/karts/ghost_kart.hpp index f08742a1a..009baa00d 100644 --- a/src/karts/ghost_kart.hpp +++ b/src/karts/ghost_kart.hpp @@ -36,37 +36,46 @@ class GhostKart : public Kart { private: - /** The list of the times at which the transform were reached. */ - std::vector m_all_times; - /** The transforms to assume at the corresponding time in m_all_times. */ - std::vector m_all_transform; + std::vector m_all_transform; - std::vector m_replay_events; + std::vector m_all_physic_info; - /** Pointer to the last index in m_all_times that is smaller than - * the current world time. */ - unsigned int m_current_transform; + std::vector m_all_replay_events; - /** Index of the next kart replay event. */ - unsigned int m_next_event; - - void updateTransform(float t, float dt); public: - GhostKart(const std::string& ident); - virtual void update (float dt); - virtual void addTransform(float time, const btTransform &trans); - virtual void addReplayEvent(const ReplayBase::KartReplayEvent &kre); - virtual void reset(); + GhostKart(const std::string& ident, + unsigned int world_kart_id, int position); + virtual void update (float dt); + virtual void reset(); // ------------------------------------------------------------------------ /** No physics body for ghost kart, so nothing to adjust. */ - virtual void updateWeight() {}; + virtual void updateWeight() {}; // ------------------------------------------------------------------------ /** No physics for ghost kart. */ - virtual void applyEngineForce (float force) {} + virtual void applyEngineForce (float force) {}; // ------------------------------------------------------------------------ // Not needed to create any physics for a ghost kart. - virtual void createPhysics() {} + virtual void createPhysics() {}; + // ------------------------------------------------------------------------ + const float getSuspensionLength(int index, int wheel) const + { return m_all_physic_info[index].m_suspension_length[wheel]; } + // ------------------------------------------------------------------------ + void addReplayEvent(float time, + const btTransform &trans, + const ReplayBase::PhysicInfo &pi, + const ReplayBase::KartReplayEvent &kre); + // ------------------------------------------------------------------------ + /** Returns whether this kart is a ghost (replay) kart. */ + virtual bool isGhostKart() const { return true; } + // ------------------------------------------------------------------------ + /** Ghost can't be hunted. */ + virtual bool isInvulnerable() const { return true; } + // ------------------------------------------------------------------------ + /** Returns the speed of the kart in meters/second. */ + virtual float getSpeed() const; + // ------------------------------------------------------------------------ + virtual void kartIsInRestNow() {}; // ------------------------------------------------------------------------ }; // GhostKart diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index d3d958134..accaec692 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -837,7 +837,7 @@ void Kart::finishedRace(float time, bool from_server) // it would trigger a race end again. if(m_finished_race) return; - if(!from_server) +/* if(!from_server) { if(NetworkConfig::get()->isServer()) { @@ -851,7 +851,7 @@ void Kart::finishedRace(float time, bool from_server) return; } } // !from_server - +*/ m_finished_race = true; m_finish_time = time; m_controller->finishedRace(time); @@ -889,9 +889,12 @@ void Kart::finishedRace(float time, bool from_server) { // Save for music handling in race result gui setRaceResult(); - setController(new EndController(this, m_controller)); + if(!isGhostKart()) + { + setController(new EndController(this, m_controller)); + } // Skip animation if this kart is eliminated - if (m_eliminated) return; + if (m_eliminated || isGhostKart()) return; m_kart_model->setAnimation(m_race_result ? KartModel::AF_WIN_START : KartModel::AF_LOSE_START); diff --git a/src/karts/kart.hpp b/src/karts/kart.hpp index b687689a2..76fbb6258 100644 --- a/src/karts/kart.hpp +++ b/src/karts/kart.hpp @@ -64,6 +64,17 @@ class TerrainInfo; class Kart : public AbstractKart { friend class Skidding; +protected: + /** Offset of the graphical kart chassis from the physical chassis. */ + float m_graphical_y_offset; + + /** The coordinates of the front of the kart, used to determine when a + * new lap is triggered. */ + Vec3 m_xyz_front; + + /** Is time flying activated */ + bool m_is_jumping; + private: /** Handles speed increase and capping due to powerup, terrain, ... */ MaxSpeed *m_max_speed; @@ -102,13 +113,6 @@ private: /** Current race position (1-num_karts). */ int m_race_position; - /** The coordinates of the front of the kart, used to determine when a - * new lap is triggered. */ - Vec3 m_xyz_front; - - /** Offset of the graphical kart chassis from the physical chassis. */ - float m_graphical_y_offset; - /** True if the kart wins, false otherwise. */ bool m_race_result; @@ -169,9 +173,6 @@ private: // Graphical effects // ----------------- - /** Is time flying activated */ - bool m_is_jumping; - /** The shadow of a kart. */ Shadow *m_shadow; @@ -430,7 +431,7 @@ public: virtual void showStarEffect(float t); // ------------------------------------------------------------------------ /** Returns the terrain info oject. */ - TerrainInfo *getTerrainInfo() { return m_terrain_info; } + virtual const TerrainInfo *getTerrainInfo() const { return m_terrain_info; } // ------------------------------------------------------------------------ virtual void setOnScreenText(const wchar_t *text); // ------------------------------------------------------------------------ @@ -447,6 +448,12 @@ public: // ------------------------------------------------------------------------ /** Set this kart race result. */ void setRaceResult(); + // ------------------------------------------------------------------------ + /** Returns whether this kart is a ghost (replay) kart. */ + virtual bool isGhostKart() const { return false; } + // ------------------------------------------------------------------------ + /** Returns whether this kart is jumping. */ + virtual bool isJumping() const { return m_is_jumping; }; }; // Kart diff --git a/src/karts/kart_model.cpp b/src/karts/kart_model.cpp index 8f78041da..12af928f7 100644 --- a/src/karts/kart_model.cpp +++ b/src/karts/kart_model.cpp @@ -717,6 +717,11 @@ void KartModel::setAnimation(AnimationFrameType type, bool play_non_loop) m_animated_node->setLoopMode(false); m_animated_node->setAnimationEndCallback(this); } + else + { + // Special animation not found, revert to default + m_current_animation = AF_DEFAULT; + } } // setAnimation // ---------------------------------------------------------------------------- @@ -769,6 +774,14 @@ void KartModel::OnAnimationEnd(scene::IAnimatedMeshSceneNode *node) // ---------------------------------------------------------------------------- void KartModel::setDefaultSuspension() { + GhostKart* gk = dynamic_cast(m_kart); + if (gk) + { + for (int i = 0; i < 4; i++) + m_default_physics_suspension[i] = gk->getSuspensionLength(0, i); + return; + } + for(int i=0; igetVehicle()->getNumWheels(); i++) { const btWheelInfo &wi = m_kart->getVehicle()->getWheelInfo(i); @@ -786,30 +799,45 @@ void KartModel::setDefaultSuspension() * \param suspension Suspension height for all four wheels. * \param speed The speed of the kart in meters/sec, used for the * speed-weighted objects' animations + * \param gt_replay_index The index to get replay data, used by ghost kart */ -void KartModel::update(float dt, float distance, float steer, float speed) +void KartModel::update(float dt, float distance, float steer, float speed, + int gt_replay_index) { core::vector3df wheel_steer(0, steer*30.0f, 0); for(unsigned int i=0; i<4; i++) { - if (!m_kart || !m_wheel_node[i]) continue; + if (!m_kart || !m_wheel_node[i]) continue; #ifdef DEBUG - if (UserConfigParams::m_physics_debug && - !dynamic_cast(m_kart) ) - { - const btWheelInfo &wi = m_kart->getVehicle()->getWheelInfo(i); - // Make wheels that are not touching the ground invisible - m_wheel_node[i]->setVisible(wi.m_raycastInfo.m_isInContact); - } + if (UserConfigParams::m_physics_debug && + !m_kart->isGhostKart()) + { + const btWheelInfo &wi = m_kart->getVehicle()->getWheelInfo(i); + // Make wheels that are not touching the ground invisible + m_wheel_node[i]->setVisible(wi.m_raycastInfo.m_isInContact); + } #endif core::vector3df pos = m_wheel_graphics_position[i].toIrrVector(); - const btWheelInfo &wi = m_kart->getVehicle()->getWheelInfo(i); + float suspension_length = 0.0f; + GhostKart* gk = dynamic_cast(m_kart); + // Prevent using m_default_physics_suspension uninitialized + if (gk && gt_replay_index == -1) break; + + if (gk) + { + suspension_length = gk->getSuspensionLength(gt_replay_index, i); + } + else + { + suspension_length = m_kart->getVehicle()->getWheelInfo(i). + m_raycastInfo.m_suspensionLength; + } // Check documentation of Kart::updateGraphics for the following line pos.Y += m_default_physics_suspension[i] - - wi.m_raycastInfo.m_suspensionLength + - suspension_length - m_kart_lowest_point; m_wheel_node[i]->setPosition(pos); diff --git a/src/karts/kart_model.hpp b/src/karts/kart_model.hpp index 6180cb89a..27be3190f 100644 --- a/src/karts/kart_model.hpp +++ b/src/karts/kart_model.hpp @@ -237,8 +237,8 @@ public: void loadInfo(const XMLNode &node); bool loadModels(const KartProperties &kart_properties); void setDefaultSuspension(); - void update(float dt, float distance, float steer, - float speed); + void update(float dt, float distance, float steer, float speed, + int gt_replay_index = -1); void finishedRace(); scene::ISceneNode* attachModel(bool animatedModels, bool always_animated); diff --git a/src/karts/skidding.cpp b/src/karts/skidding.cpp index 3b8dab760..58e4466f2 100644 --- a/src/karts/skidding.cpp +++ b/src/karts/skidding.cpp @@ -23,7 +23,6 @@ #endif #include "achievements/achievement_info.hpp" #include "config/player_manager.hpp" -#include "karts/ghost_kart.hpp" #include "karts/kart.hpp" #include "karts/kart_gfx.hpp" #include "karts/kart_properties.hpp" @@ -82,7 +81,7 @@ void Skidding::reset() btVector3 rot(0, 0, 0); // Only access the vehicle if the kart is not a ghost - if (dynamic_cast(m_kart)==NULL) + if (!m_kart->isGhostKart()) m_kart->getVehicle()->setTimedRotation(0, rot); } // reset diff --git a/src/main.cpp b/src/main.cpp index a534e1e6c..24d39242d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -527,7 +527,6 @@ void cmdLineHelp() " spaces are allowed in the track names.\n" " --demo-laps=n Number of laps in a demo.\n" " --demo-karts=n Number of karts to use in a demo.\n" - " --ghost Replay ghost data together with one player kart.\n" // " --history Replay history file 'history.dat'.\n" // " --history=n Replay history file 'history.dat' using:\n" // " n=1: recorded positions\n" @@ -1004,9 +1003,6 @@ int handleCmdLine() } } // --with-profile - if(CommandLine::has("--ghost")) - ReplayPlay::create(); - if(CommandLine::has("--history", &n)) { history->doReplayHistory( (History::HistoryReplayMode)n); @@ -1157,6 +1153,7 @@ void initRest() // The order here can be important, e.g. KartPropertiesManager needs // defaultKartProperties, which are defined in stk_config. history = new History (); + ReplayPlay::create(); ReplayRecorder::create(); material_manager = new MaterialManager (); track_manager = new TrackManager (); @@ -1581,7 +1578,6 @@ static void cleanSuperTuxKart() irr_driver->updateConfigIfRelevant(); AchievementsManager::destroy(); Referee::cleanup(); - if(ReplayPlay::get()) ReplayPlay::destroy(); if(race_manager) delete race_manager; if(grand_prix_manager) delete grand_prix_manager; if(highscore_manager) delete highscore_manager; @@ -1593,6 +1589,7 @@ static void cleanSuperTuxKart() if(track_manager) delete track_manager; if(material_manager) delete material_manager; if(history) delete history; + ReplayPlay::destroy(); ReplayRecorder::destroy(); delete ParticleKindManager::get(); PlayerManager::destroy(); diff --git a/src/modes/easter_egg_hunt.hpp b/src/modes/easter_egg_hunt.hpp index 00eb70db5..83dde6dbc 100644 --- a/src/modes/easter_egg_hunt.hpp +++ b/src/modes/easter_egg_hunt.hpp @@ -46,20 +46,20 @@ public: EasterEggHunt(); virtual ~EasterEggHunt(); - virtual void init(); + virtual void init() OVERRIDE; - virtual bool isRaceOver(); + virtual bool isRaceOver() OVERRIDE; // overriding World methods - virtual void reset(); + virtual void reset() OVERRIDE; - virtual bool raceHasLaps(){ return false; } + virtual bool raceHasLaps() OVERRIDE { return false; } - virtual const std::string& getIdent() const; - virtual void terminateRace(); - virtual void update(float dt); + virtual const std::string& getIdent() const OVERRIDE; + virtual void terminateRace() OVERRIDE; + virtual void update(float dt) OVERRIDE; virtual void getKartsDisplayInfo( - std::vector *info); + std::vector *info) OVERRIDE; void updateKartRanks(); void collectedEasterEgg(const AbstractKart *kart); diff --git a/src/modes/follow_the_leader.hpp b/src/modes/follow_the_leader.hpp index da5fe1f6d..37e395b8f 100644 --- a/src/modes/follow_the_leader.hpp +++ b/src/modes/follow_the_leader.hpp @@ -48,7 +48,7 @@ public: // overriding World methods virtual void reset() OVERRIDE; virtual const std::string& getIdent() const OVERRIDE; - virtual const btTransform &getStartTransform(int index); + virtual const btTransform &getStartTransform(int index) OVERRIDE; virtual float getClockStartTime() const; virtual void getKartsDisplayInfo( std::vector *info) OVERRIDE; diff --git a/src/modes/linear_world.cpp b/src/modes/linear_world.cpp index cca42320c..468574f30 100644 --- a/src/modes/linear_world.cpp +++ b/src/modes/linear_world.cpp @@ -178,9 +178,10 @@ void LinearWorld::update(float dt) // in the position of the kart (e.g. while falling the kart // might get too close to another part of the track, shortly // jump to position one, then on reset fall back to last) - if (!kart_info.getTrackSector()->isOnRoad() && + if ((!kart_info.getTrackSector()->isOnRoad() && (!kart->getMaterial() || - kart->getMaterial()->isDriveReset()) ) + kart->getMaterial()->isDriveReset())) && + !kart->isGhostKart()) continue; kart_info.getTrackSector()->update(kart->getFrontXYZ()); kart_info.m_overall_distance = kart_info.m_race_lap diff --git a/src/modes/linear_world.hpp b/src/modes/linear_world.hpp index 76959f402..b2d416cd5 100644 --- a/src/modes/linear_world.hpp +++ b/src/modes/linear_world.hpp @@ -160,7 +160,7 @@ public: // ------------------------------------------------------------------------ /** Returns the number of laps a kart has completed. * \param kart_index World index of the kart. */ - int getKartLaps(unsigned int kart_index) const + int getKartLaps(unsigned int kart_index) const OVERRIDE { assert(kart_index < m_kart_info.size()); return m_kart_info[kart_index].m_race_lap; diff --git a/src/modes/overworld.hpp b/src/modes/overworld.hpp index 62d510994..a0bb6a0b3 100644 --- a/src/modes/overworld.hpp +++ b/src/modes/overworld.hpp @@ -37,7 +37,7 @@ class OverWorld : public WorldWithRank protected: /** Override from base class */ - virtual void createRaceGUI(); + virtual void createRaceGUI() OVERRIDE; bool m_return_to_garage; diff --git a/src/modes/soccer_world.hpp b/src/modes/soccer_world.hpp index 368c7d8e4..0e87a3bc8 100644 --- a/src/modes/soccer_world.hpp +++ b/src/modes/soccer_world.hpp @@ -50,7 +50,7 @@ protected: virtual AbstractKart *createKart(const std::string &kart_ident, int index, int local_player_id, int global_player_id, RaceManager::KartType type, - PerPlayerDifficulty difficulty); + PerPlayerDifficulty difficulty) OVERRIDE; private: /** Keep a pointer to the track object of soccer ball */ @@ -113,27 +113,27 @@ public: SoccerWorld(); virtual ~SoccerWorld(); - virtual void init(); + virtual void init() OVERRIDE; // clock events - virtual bool isRaceOver(); - virtual void terminateRace(); + virtual bool isRaceOver() OVERRIDE; + virtual void terminateRace() OVERRIDE; virtual void countdownReachedZero() OVERRIDE; // overriding World methods - virtual void reset(); + virtual void reset() OVERRIDE; virtual unsigned int getRescuePositionIndex(AbstractKart *kart) OVERRIDE; - virtual bool useFastMusicNearEnd() const { return false; } + virtual bool useFastMusicNearEnd() const OVERRIDE { return false; } virtual void getKartsDisplayInfo( - std::vector *info) {} + std::vector *info) OVERRIDE {} - virtual bool raceHasLaps() { return false; } + virtual bool raceHasLaps() OVERRIDE { return false; } - virtual const std::string& getIdent() const; + virtual const std::string& getIdent() const OVERRIDE; - virtual void update(float dt); + virtual void update(float dt) OVERRIDE; // ------------------------------------------------------------------------ void onCheckGoalTriggered(bool first_goal); // ------------------------------------------------------------------------ @@ -157,23 +157,23 @@ public: m_blue_score_times : m_red_score_times); } // ------------------------------------------------------------------------ - const int& getKartNode(unsigned int kart_id) const + const int getKartNode(unsigned int kart_id) const { return m_kart_on_node[kart_id]; } // ------------------------------------------------------------------------ - const int& getBallNode() const + const int getBallNode() const { return m_ball_on_node; } // ------------------------------------------------------------------------ const Vec3& getBallPosition() const { return m_ball_position; } // ------------------------------------------------------------------------ - const int& getGoalNode(SoccerTeam team) const + const int getGoalNode(SoccerTeam team) const { return (team == SOCCER_TEAM_BLUE ? m_blue_goal_node : m_red_goal_node); } // ------------------------------------------------------------------------ bool isCorrectGoal(unsigned int kart_id, bool first_goal) const; // ------------------------------------------------------------------------ - const int& getDefender(SoccerTeam team) const + const int getDefender(SoccerTeam team) const { return (team == SOCCER_TEAM_BLUE ? m_blue_defender : m_red_defender); } diff --git a/src/modes/standard_race.hpp b/src/modes/standard_race.hpp index d94933183..88d58c410 100644 --- a/src/modes/standard_race.hpp +++ b/src/modes/standard_race.hpp @@ -29,7 +29,7 @@ class StandardRace : public LinearWorld { protected: // clock events - virtual bool isRaceOver(); + virtual bool isRaceOver() OVERRIDE; public: StandardRace(); diff --git a/src/modes/world.cpp b/src/modes/world.cpp index 99373ffd1..74b9bfd54 100644 --- a/src/modes/world.cpp +++ b/src/modes/world.cpp @@ -146,6 +146,9 @@ void World::init() m_eliminated_karts = 0; m_eliminated_players = 0; m_num_players = 0; + unsigned int gk = 0; + if (race_manager->hasGhostKarts()) + gk = ReplayPlay::get()->getNumGhostKart(); // Create the race gui before anything else is attached to the scene node // (which happens when the track is loaded). This allows the race gui to @@ -179,8 +182,16 @@ void World::init() // karts can be positioned properly on (and not in) the tracks. m_track->loadTrackModel(race_manager->getReverseTrack()); + if (gk > 0) + { + ReplayPlay::get()->load(); + for (unsigned int k = 0; k < gk; k++) + m_karts.push_back(ReplayPlay::get()->getGhostKart(k)); + } + for(unsigned int i=0; igetKartType(i) == RaceManager::KT_GHOST) continue; std::string kart_ident = history->replayHistory() ? history->getKartIdent(i) : race_manager->getKartIdent(i); @@ -201,11 +212,8 @@ void World::init() // Must be called after all karts are created m_race_gui->init(); - if(ReplayPlay::get()) - ReplayPlay::get()->Load(); - powerup_manager->updateWeightsForRace(num_karts); - + if (UserConfigParams::m_weather_effects) { m_weather = new Weather(m_track->getWeatherLightning(), @@ -249,7 +257,7 @@ void World::reset() Camera::getCamera(i)->reset(); } - if(ReplayPlay::get()) + if(race_manager->hasGhostKarts()) ReplayPlay::get()->reset(); resetAllKarts(); @@ -271,7 +279,11 @@ void World::reset() race_manager->reset(); // Make sure to overwrite the data from the previous race. if(!history->replayHistory()) history->initRecording(); - if(ReplayRecorder::get()) ReplayRecorder::get()->init(); + if(race_manager->willRecordRace()) + { + Log::info("World", "Start Recording race."); + ReplayRecorder::get()->init(); + } // Reset all data structures that depend on number of karts. irr_driver->reset(); @@ -381,16 +393,6 @@ World::~World() { irr_driver->onUnloadWorld(); - if(ReplayPlay::get()) - { - // Destroy the old replay object, which also stored the ghost - // karts, and create a new one (which means that in further - // races the usage of ghosts will still be enabled). - ReplayPlay::destroy(); - ReplayPlay::create(); - } - - // In case that a race is aborted (e.g. track not found) m_track is 0. if(m_track) m_track->cleanup(); @@ -415,9 +417,24 @@ World::~World() delete m_weather; for ( unsigned int i = 0 ; i < m_karts.size() ; i++ ) + { + // Let ReplayPlay destroy the ghost karts + if (m_karts[i]->isGhostKart()) continue; delete m_karts[i]; + } + if(race_manager->hasGhostKarts()) + { + // Destroy the old replay object, which also stored the ghost + // karts, and create a new one (which means that in further + // races the usage of ghosts will still be enabled). + ReplayPlay::destroy(); + ReplayPlay::create(); + } m_karts.clear(); + race_manager->setRaceGhostKarts(false); + race_manager->setRecordRace(false); + Camera::removeAllCameras(); projectile_manager->cleanup(); @@ -446,6 +463,7 @@ void World::onGo() // from sliding downhill) for(unsigned int i=0; iisGhostKart()) continue; m_karts[i]->getVehicle()->setAllBrakes(0); } } // onGo @@ -586,12 +604,13 @@ void World::resetAllKarts() getPhysics()->getPhysicsWorld()->resetLocalTime(); // If track checking is requested, check all rescue positions if - // they are heigh enough. + // they are high enough. if(UserConfigParams::m_track_debug) { // Loop over all karts, in case that some karts are dfferent for(unsigned int kart_id=0; kart_id<(unsigned int)m_karts.size(); kart_id++) { + if (m_karts[kart_id]->isGhostKart()) continue; for(unsigned int rescue_pos=0; rescue_posisGhostKart()) continue; Vec3 xyz = (*i)->getXYZ(); //start projection from top of kart Vec3 up_offset(0, 0.5f * ((*i)->getKartHeight()), 0); @@ -667,6 +687,7 @@ void World::resetAllKarts() all_finished=true; for ( KartList::iterator i=m_karts.begin(); i!=m_karts.end(); i++) { + if ((*i)->isGhostKart()) continue; if(!(*i)->isInRest()) { Vec3 normal; @@ -945,8 +966,7 @@ void World::update(float dt) PROFILER_PUSH_CPU_MARKER("World::update (sub-updates)", 0x20, 0x7F, 0x00); history->update(dt); - if(ReplayRecorder::get()) ReplayRecorder::get()->update(dt); - if(ReplayPlay::get()) ReplayPlay::get()->update(dt); + if(race_manager->willRecordRace()) ReplayRecorder::get()->update(dt); if(history->replayHistory()) dt=history->getNextDelta(); WorldStatus::update(dt); if (m_script_engine) m_script_engine->update(dt); @@ -1146,6 +1166,7 @@ AbstractKart *World::getLocalPlayerKart(unsigned int n) const void World::eliminateKart(int kart_id, bool notify_of_elimination) { AbstractKart *kart = m_karts[kart_id]; + if (kart->isGhostKart()) return; // Display a message about the eliminated kart in the race guia if (notify_of_elimination) diff --git a/src/modes/world.hpp b/src/modes/world.hpp index 0a76b39d3..3a03d0bf2 100644 --- a/src/modes/world.hpp +++ b/src/modes/world.hpp @@ -175,7 +175,7 @@ protected: Weather* m_weather; - virtual void onGo(); + virtual void onGo() OVERRIDE; /** Returns true if the race is over. Must be defined by all modes. */ virtual bool isRaceOver() = 0; virtual void update(float dt); diff --git a/src/modes/world_with_rank.hpp b/src/modes/world_with_rank.hpp index fd768bc46..7c159be89 100644 --- a/src/modes/world_with_rank.hpp +++ b/src/modes/world_with_rank.hpp @@ -65,7 +65,7 @@ public: /** call just after instanciating. can't be moved to the contructor as child classes must be instanciated, otherwise polymorphism will fail and the results will be incorrect */ - virtual void init(); + virtual void init() OVERRIDE; bool displayRank() const { return m_display_rank; } diff --git a/src/network/network.cpp b/src/network/network.cpp index e988497cd..281e5b9be 100755 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -172,10 +172,10 @@ void Network::broadcastPacket(NetworkString *data, bool reliable) void Network::openLog() { m_log_file.setAtomic(NULL); - if (UserConfigParams::m_packets_log_filename.toString() != "") + if (UserConfigParams::m_log_packets) { std::string s = file_manager - ->getUserConfigFile(UserConfigParams::m_packets_log_filename); + ->getUserConfigFile(FileManager::getStdoutName()+".packet"); m_log_file.setAtomic(fopen(s.c_str(), "w+")); } if (!m_log_file.getData()) @@ -198,8 +198,10 @@ void Network::logPacket(const BareNetworkString &ns, bool incoming) m_log_file.lock(); fprintf(m_log_file.getData(), "[%d\t] %s ", (int)(StkTime::getRealTime()), arrow); - - fprintf(m_log_file.getData(), ns.getLogMessage().c_str()); + // Indentation for all lines after the first, so that the dump + // is nicely aligned. + std::string indent(" "); + fprintf(m_log_file.getData(), "%s", ns.getLogMessage(indent).c_str()); m_log_file.unlock(); } // logPacket // ---------------------------------------------------------------------------- diff --git a/src/network/network_console.cpp b/src/network/network_console.cpp index 52aecbe15..82a54418c 100644 --- a/src/network/network_console.cpp +++ b/src/network/network_console.cpp @@ -171,9 +171,3 @@ void NetworkConsole::kickAllPlayers() peers[i]->disconnect(); } } // kickAllPlayers - -// ---------------------------------------------------------------------------- -void NetworkConsole::sendPacket(NetworkString *data, bool reliable) -{ - m_localhost->broadcastPacket(data, reliable); -} // sendPacket diff --git a/src/network/network_console.hpp b/src/network/network_console.hpp index 5e0614926..4a429d1fd 100644 --- a/src/network/network_console.hpp +++ b/src/network/network_console.hpp @@ -44,8 +44,6 @@ public: virtual void run(); void kickAllPlayers(); - virtual void sendPacket(NetworkString *data, - bool reliable = true); // ------------------------------------------------------------------------ void setMaxPlayers(uint8_t count) { m_max_players = count; } // ------------------------------------------------------------------------ diff --git a/src/network/network_string.cpp b/src/network/network_string.cpp index 581f24921..63558e639 100644 --- a/src/network/network_string.cpp +++ b/src/network/network_string.cpp @@ -64,7 +64,7 @@ void NetworkString::unitTesting() for(unsigned int i=0; i<28; i++) slog.addUInt8(i); std::string log = slog.getLogMessage(); - assert(log=="0x000 | 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f | ................\n" + assert(log=="0x000 | 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f | ................\n" "0x010 | 10 11 12 13 14 15 16 17 18 19 1a 1b | ............\n"); } // unitTesting @@ -128,7 +128,7 @@ int BareNetworkString::decodeStringW(int pos, irr::core::stringw *out) const * to stdout or via the Log mechanism. Format * 0000 : 1234 5678 9abc ... ASCII- */ -std::string BareNetworkString::getLogMessage() const +std::string BareNetworkString::getLogMessage(const std::string &indent) const { std::ostringstream oss; for(unsigned int line=0; line=128, which are often shown + // as more than one character. + if(isprint(c) && c!=0x09 && c<=0x80) oss << char(c); else oss << '.'; } // for i oss << "\n"; + // If it's not the last line, add the indentation in front + // of the next line + if(line+16broadcastPacket(message, reliable); -} // broadcastToClients - // ---------------------------------------------------------------------------- /** Sends a message from a client to the server. */ diff --git a/src/network/protocol.hpp b/src/network/protocol.hpp index 1c6e99bad..5f063d18d 100644 --- a/src/network/protocol.hpp +++ b/src/network/protocol.hpp @@ -127,7 +127,6 @@ public: bool checkDataSize(Event* event, unsigned int minimum_size); void sendMessageToPeersChangingToken(NetworkString *message, bool reliable = true); - void broadcastToClients(NetworkString *message, bool reliable=true); void sendToServer(NetworkString *message, bool reliable = true); void requestStart(); diff --git a/src/network/protocol_manager.cpp b/src/network/protocol_manager.cpp index bc71013d7..73f6e1e45 100644 --- a/src/network/protocol_manager.cpp +++ b/src/network/protocol_manager.cpp @@ -140,7 +140,8 @@ void ProtocolManager::propagateEvent(Event* event) m_protocols.unlock(); // no protocol was aimed, show the msg to debug - if (searched_protocol == PROTOCOL_NONE) + if (searched_protocol == PROTOCOL_NONE && + event->getType() != EVENT_TYPE_DISCONNECTED) { if(event->getType()==EVENT_TYPE_MESSAGE) { @@ -175,14 +176,6 @@ void ProtocolManager::propagateEvent(Event* event) m_events_to_process.unlock(); } // propagateEvent -// ---------------------------------------------------------------------------- -void ProtocolManager::sendMessageExcept(STKPeer *peer, - NetworkString *message, - bool reliable) -{ - STKHost::get()->sendPacketExcept(peer, message, reliable); -} // sendMessageExcept - // ---------------------------------------------------------------------------- /** \brief Asks the manager to start a protocol. * This function will store the request, and process it at a time it is diff --git a/src/network/protocol_manager.hpp b/src/network/protocol_manager.hpp index f38df807c..42d50f3a4 100644 --- a/src/network/protocol_manager.hpp +++ b/src/network/protocol_manager.hpp @@ -162,9 +162,6 @@ private: public: virtual void abort(); virtual void propagateEvent(Event* event); - virtual void sendMessageExcept(STKPeer* peer, - NetworkString *message, - bool reliable = true); virtual uint32_t requestStart(Protocol* protocol); virtual void requestPause(Protocol* protocol); virtual void requestUnpause(Protocol* protocol); diff --git a/src/network/protocols/client_lobby_room_protocol.cpp b/src/network/protocols/client_lobby_room_protocol.cpp index 038445403..5abfec610 100644 --- a/src/network/protocols/client_lobby_room_protocol.cpp +++ b/src/network/protocols/client_lobby_room_protocol.cpp @@ -125,6 +125,7 @@ void ClientLobbyRoomProtocol::voteLaps(uint8_t laps, uint8_t track_nb) NetworkString *request = getNetworkString(10); request->addUInt8(LE_VOTE_LAPS).addUInt8(laps).addUInt8(track_nb); sendToServer(request, true); + delete request; } // voteLaps //----------------------------------------------------------------------------- diff --git a/src/network/protocols/connect_to_peer.hpp b/src/network/protocols/connect_to_peer.hpp index 29099ac4e..054c6ea62 100644 --- a/src/network/protocols/connect_to_peer.hpp +++ b/src/network/protocols/connect_to_peer.hpp @@ -54,10 +54,10 @@ public: ConnectToPeer(const TransportAddress &address); virtual ~ConnectToPeer(); - virtual bool notifyEventAsynchronous(Event* event); - virtual void setup(); - virtual void update() {} - virtual void asynchronousUpdate(); + virtual bool notifyEventAsynchronous(Event* event) OVERRIDE; + virtual void setup() OVERRIDE; + virtual void update() OVERRIDE {} + virtual void asynchronousUpdate() OVERRIDE; virtual void callback(Protocol *protocol) OVERRIDE; }; // class ConnectToPeer diff --git a/src/network/protocols/connect_to_server.hpp b/src/network/protocols/connect_to_server.hpp index 6d6c9597f..0fb020ac0 100644 --- a/src/network/protocols/connect_to_server.hpp +++ b/src/network/protocols/connect_to_server.hpp @@ -61,8 +61,8 @@ public: virtual bool notifyEventAsynchronous(Event* event) OVERRIDE; virtual void setup() OVERRIDE; - virtual void asynchronousUpdate(); - virtual void callback(Protocol *protocol); + virtual void asynchronousUpdate() OVERRIDE; + virtual void callback(Protocol *protocol) OVERRIDE; virtual void update() OVERRIDE {} void setServerAddress(const TransportAddress &address); }; // class ConnectToServer diff --git a/src/network/protocols/controller_events_protocol.cpp b/src/network/protocols/controller_events_protocol.cpp index 217e42370..5b9c71bfe 100644 --- a/src/network/protocols/controller_events_protocol.cpp +++ b/src/network/protocols/controller_events_protocol.cpp @@ -105,18 +105,9 @@ bool ControllerEventsProtocol::notifyEventAsynchronous(Event* event) } if (NetworkConfig::get()->isServer()) { - const std::vector &peers = STKHost::get()->getPeers(); - for(unsigned int i=0; igetPeer()) - { - pure_message.setToken(peer->getClientServerToken()); - peer->sendPacket(&pure_message, false); - } // if peer != event->getPeer() - } // for i in peers + // Send update to all clients except the original sender. + STKHost::get()->sendPacketExcept(event->getPeer(), + &pure_message, false); } // if server return true; } // notifyEventAsynchronous @@ -157,7 +148,6 @@ void ControllerEventsProtocol::controllerAction(Controller* controller, uint8_t serialized_3 = (uint8_t)(controls->m_steer*127.0); NetworkString *ns = getNetworkString(17); - ns->setToken(STKHost::get()->getPeers()[0]->getClientServerToken()); ns->addFloat(World::getWorld()->getTime()); ns->addUInt8(controller->getKart()->getWorldKartId()); ns->addUInt8(serialized_1).addUInt8(serialized_2).addUInt8(serialized_3); diff --git a/src/network/protocols/game_events_protocol.cpp b/src/network/protocols/game_events_protocol.cpp index fb72fd1bb..1d23ec00d 100644 --- a/src/network/protocols/game_events_protocol.cpp +++ b/src/network/protocols/game_events_protocol.cpp @@ -85,8 +85,7 @@ void GameEventsProtocol::collectedItem(Item* item, AbstractKart* kart) const std::vector &peers = STKHost::get()->getPeers(); for (unsigned int i = 0; i < peers.size(); i++) { - NetworkString *ns = getNetworkString(11); - ns->setToken(peers[i]->getClientServerToken()); + NetworkString *ns = getNetworkString(7); ns->setSynchronous(true); // Item picked : send item id, powerup type and kart race id uint8_t powerup = 0; @@ -136,17 +135,10 @@ void GameEventsProtocol::kartFinishedRace(AbstractKart *kart, float time) { NetworkString *ns = getNetworkString(20); ns->setSynchronous(true); - const std::vector &peers = STKHost::get()->getPeers(); - - // FIXME - TODO THIS APPEARS COMPLETELY BROKEN!!! - for (unsigned int i = 0; i < peers.size(); i++) - { - ns->addUInt32(peers[i]->getClientServerToken()) - .addUInt8(GE_KART_FINISHED_RACE) - .addUInt8(kart->getWorldKartId()).addFloat(time); - peers[i]->sendPacket(ns, /*reliable*/true); - delete ns; - } // for i in peers + ns->addUInt8(GE_KART_FINISHED_RACE).addUInt8(kart->getWorldKartId()) + .addFloat(time); + sendMessageToPeersChangingToken(ns, /*reliable*/true); + delete ns; } // kartFinishedRace // ---------------------------------------------------------------------------- diff --git a/src/network/protocols/kart_update_protocol.cpp b/src/network/protocols/kart_update_protocol.cpp index 848b2a3f6..a514ba73a 100644 --- a/src/network/protocols/kart_update_protocol.cpp +++ b/src/network/protocols/kart_update_protocol.cpp @@ -93,7 +93,7 @@ void KartUpdateProtocol::update() "Sending %d's positions %f %f %f", kart->getWorldKartId(), xyz[0], xyz[1], xyz[2]); } - broadcastToClients(ns, /*reliable*/false); + sendMessageToPeersChangingToken(ns, /*reliable*/false); delete ns; } else diff --git a/src/network/protocols/server_lobby_room_protocol.cpp b/src/network/protocols/server_lobby_room_protocol.cpp index 249f7ca81..5e6da1d92 100644 --- a/src/network/protocols/server_lobby_room_protocol.cpp +++ b/src/network/protocols/server_lobby_room_protocol.cpp @@ -392,7 +392,7 @@ void ServerLobbyRoomProtocol::kartDisconnected(Event* event) NetworkString *msg = getNetworkString(2); msg->addUInt8(LE_PLAYER_DISCONNECTED) .addUInt8(peer->getPlayerProfile()->getGlobalPlayerId()); - broadcastToClients(msg); + sendMessageToPeersChangingToken(msg, /*reliable*/true); delete msg; Log::info("ServerLobbyRoomProtocol", "Player disconnected : id %d", peer->getPlayerProfile()->getGlobalPlayerId()); @@ -465,7 +465,7 @@ void ServerLobbyRoomProtocol::connectionRequested(Event* event) // size of id -- id -- size of local id -- local id; message->addUInt8(LE_NEW_PLAYER_CONNECTED).addUInt8(new_player_id) .addUInt8(new_host_id).encodeString(name_u8); - ProtocolManager::getInstance()->sendMessageExcept(peer, message); + STKHost::get()->sendPacketExcept(peer, message); delete message; // Now answer to the peer that just connected diff --git a/src/network/protocols/server_lobby_room_protocol.hpp b/src/network/protocols/server_lobby_room_protocol.hpp index f07a0ce44..1abfa1814 100644 --- a/src/network/protocols/server_lobby_room_protocol.hpp +++ b/src/network/protocols/server_lobby_room_protocol.hpp @@ -45,10 +45,10 @@ public: ServerLobbyRoomProtocol(); virtual ~ServerLobbyRoomProtocol(); - virtual bool notifyEventAsynchronous(Event* event); - virtual void setup(); - virtual void update(); - virtual void asynchronousUpdate() {}; + virtual bool notifyEventAsynchronous(Event* event) OVERRIDE; + virtual void setup() OVERRIDE; + virtual void update() OVERRIDE; + virtual void asynchronousUpdate() OVERRIDE {}; void startGame(); void startSelection(const Event *event=NULL); diff --git a/src/network/servers_manager.cpp b/src/network/servers_manager.cpp index d8a35cb5a..31ecab705 100644 --- a/src/network/servers_manager.cpp +++ b/src/network/servers_manager.cpp @@ -148,7 +148,7 @@ Online::XMLRequest* ServersManager::getLANRefreshRequest() const { } // prepareOperation // -------------------------------------------------------------------- - virtual void operation() + virtual void operation() OVERRIDE { Network *broadcast = new Network(1, 1, 0, 0); diff --git a/src/network/stk_host.cpp b/src/network/stk_host.cpp index de76d2f1a..565d02bc2 100644 --- a/src/network/stk_host.cpp +++ b/src/network/stk_host.cpp @@ -547,7 +547,7 @@ void* STKHost::mainLoop(void* self) Log::verbose("NetworkManager", "Message, Sender : %s, message:", stk_addr.toString(/*show port*/false).c_str()); - Log::verbose("NetworkManager", + Log::verbose("NetworkManager", "%s", stk_event->data().getLogMessage().c_str()); } // if message event diff --git a/src/network/stk_host.hpp b/src/network/stk_host.hpp index f6ca1e96d..6bc6e0a5b 100644 --- a/src/network/stk_host.hpp +++ b/src/network/stk_host.hpp @@ -180,13 +180,6 @@ public: max_tries); } // receiveRawPacket - // -------------------------------------------------------------------- - void broadcastPacket(NetworkString *data, - bool reliable = true) - { - m_network->broadcastPacket(data, reliable); - } // broadcastPacket - // -------------------------------------------------------------------- void sendRawPacket(const BareNetworkString &buffer, const TransportAddress& dst) diff --git a/src/race/race_manager.cpp b/src/race/race_manager.cpp index 26318bc5f..683049cfe 100644 --- a/src/race/race_manager.cpp +++ b/src/race/race_manager.cpp @@ -47,6 +47,7 @@ #include "network/network_config.hpp" #include "network/protocols/start_game_protocol.hpp" #include "network/race_event_manager.hpp" +#include "replay/replay_play.hpp" #include "scriptengine/property_animator.hpp" #include "states_screens/grand_prix_cutscene.hpp" #include "states_screens/grand_prix_lose.hpp" @@ -75,6 +76,8 @@ RaceManager::RaceManager() m_started_from_overworld = false; m_have_kart_last_position_on_overworld = false; setReverseTrack(false); + setRecordRace(false); + setRaceGhostKarts(false); setTrack("jungle"); m_default_ai_list.clear(); setNumPlayers(0); @@ -308,6 +311,10 @@ void RaceManager::computeRandomKartList() */ void RaceManager::startNew(bool from_overworld) { + unsigned int gk = 0; + if (m_has_ghost_karts) + gk = ReplayPlay::get()->getNumGhostKart(); + m_started_from_overworld = from_overworld; m_saved_gp = NULL; // There will be checks for this being NULL done later @@ -359,17 +366,30 @@ void RaceManager::startNew(bool from_overworld) // Create the kart status data structure to keep track of scores, times, ... // ========================================================================== m_kart_status.clear(); - Log::verbose("RaceManager", "Nb of karts=%u, ai:%lu players:%lu\n", - (unsigned int) m_num_karts, m_ai_kart_list.size(), m_player_karts.size()); + if (gk > 0) + m_num_karts += gk; - assert((unsigned int)m_num_karts == m_ai_kart_list.size() - + m_player_karts.size() ); + Log::verbose("RaceManager", "Nb of karts=%u, ghost karts:%u ai:%lu players:%lu\n", + (unsigned int) m_num_karts, gk, m_ai_kart_list.size(), m_player_karts.size()); - // First add the AI karts (randomly chosen) + assert((unsigned int)m_num_karts == gk+m_ai_kart_list.size()+m_player_karts.size()); + + // First add the ghost karts (if any) // ---------------------------------------- - // GP ranks start with -1 for the leader. int init_gp_rank = getMinorMode()==MINOR_MODE_FOLLOW_LEADER ? -1 : 0; + if (gk > 0) + { + for(unsigned int i = 0; i < gk; i++) + { + m_kart_status.push_back(KartStatus(ReplayPlay::get()->getGhostKartName(i), + i, -1, -1, init_gp_rank, KT_GHOST, PLAYER_DIFFICULTY_NORMAL)); + init_gp_rank ++; + } + } + + // Then add the AI karts (randomly chosen) + // ---------------------------------------- const unsigned int ai_kart_count = m_ai_kart_list.size(); for(unsigned int i = 0; i < ai_kart_count; i++) { @@ -383,7 +403,7 @@ void RaceManager::startNew(bool from_overworld) } } - // Then add the players, which start behind the AI karts + // Finally add the players, which start behind the AI karts // ----------------------------------------------------- for(unsigned int i = 0; i < m_player_karts.size(); i++) { diff --git a/src/race/race_manager.hpp b/src/race/race_manager.hpp index 8ae4046be..8d2e59fea 100644 --- a/src/race/race_manager.hpp +++ b/src/race/race_manager.hpp @@ -45,6 +45,7 @@ static const std::string IDENT_FTL ("FOLLOW_LEADER" ); static const std::string IDENT_STRIKES ("BATTLE_3_STRIKES"); static const std::string IDENT_EASTER ("EASTER_EGG_HUNT" ); static const std::string IDENT_SOCCER ("SOCCER" ); +static const std::string IDENT_GHOST ("GHOST" ); static const std::string IDENT_OVERWORLD("OVERWORLD" ); static const std::string IDENT_CUTSCENE ("CUTSCENE" ); @@ -350,6 +351,9 @@ private: /** Determines if saved GP should be continued or not*/ bool m_continue_saved_gp; + bool m_will_record_race; + + bool m_has_ghost_karts; public: RaceManager(); ~RaceManager(); @@ -697,6 +701,26 @@ public: { return m_kart_last_position_on_overworld; } // getKartLastPositionOnOverworld + // ------------------------------------------------------------------------ + void setRecordRace(bool record) + { + m_will_record_race = record; + } // setRecordRace + // ------------------------------------------------------------------------ + void setRaceGhostKarts(bool ghost) + { + m_has_ghost_karts = ghost; + } // setRaceGhostKarts + // ------------------------------------------------------------------------ + bool willRecordRace() const + { + return m_will_record_race; + } // willRecordRace + // ------------------------------------------------------------------------ + bool hasGhostKarts() const + { + return m_has_ghost_karts; + } // hasGhostKarts }; // RaceManager diff --git a/src/replay/replay_base.cpp b/src/replay/replay_base.cpp index 6efda19c5..bcf450c5f 100644 --- a/src/replay/replay_base.cpp +++ b/src/replay/replay_base.cpp @@ -18,28 +18,23 @@ #include "replay/replay_base.hpp" #include "io/file_manager.hpp" -#include "race/race_manager.hpp" // ----------------------------------------------------------------------------- ReplayBase::ReplayBase() { - m_filename = ""; } // ReplayBaese // ----------------------------------------------------------------------------- -/** Opens a replay file (depending on the track name, which is taken from - * the race manager). +/** Opens a replay file which is determined by sub classes. * \param writeable True if the file should be opened for writing. * \return A FILE *, or NULL if the file could not be opened. */ FILE* ReplayBase::openReplayFile(bool writeable) { - m_filename = file_manager->getUserConfigFile( - race_manager->getTrackName()+".replay"); - FILE *fd = fopen(m_filename.c_str(), writeable ? "w" : "r"); - if(!fd) + FILE *fd = fopen((file_manager->getReplayDir() + + getReplayFilename()).c_str(), writeable ? "w" : "r"); + if (!fd) { - m_filename = race_manager->getTrackName()+".replay"; - fd = fopen(m_filename.c_str(), writeable ? "w" : "r"); + return NULL; } return fd; diff --git a/src/replay/replay_base.hpp b/src/replay/replay_base.hpp index 6b48bdc24..fac18a16a 100644 --- a/src/replay/replay_base.hpp +++ b/src/replay/replay_base.hpp @@ -24,6 +24,7 @@ #include #include +#include /** * \ingroup race @@ -32,46 +33,54 @@ class ReplayBase : public NoCopy { // Needs access to KartReplayEvent friend class GhostKart; -private: - /** The filename of the replay file. Only defined after calling - * openReplayFile. */ - std::string m_filename; + protected: /** Stores a transform event, i.e. a position and rotation of a kart * at a certain time. */ struct TransformEvent { /** Time at which this event happens. */ - float m_time; + float m_time; /** The transform at a certain time. */ - btTransform m_transform; + btTransform m_transform; }; // TransformEvent // ------------------------------------------------------------------------ - /** Records all other events - atm start and end skidding. */ + struct PhysicInfo + { + /** The speed at a certain time. */ + float m_speed; + /** The steering at a certain time. */ + float m_steer; + /** The suspension length of 4 wheels at a certain time. */ + float m_suspension_length[4]; + }; // PhysicInfo + + // ------------------------------------------------------------------------ + /** Records all other events - atm nitro, zipper and jumping handling. */ struct KartReplayEvent { - /** The type of event. */ - enum KartReplayEventType {KRE_NONE, - KRE_SKID_LEFT, - KRE_SKID_MIN = KRE_SKID_LEFT, - KRE_SKID_RIGHT, KRE_SKID_RELEASE} m_type; - - /** Time at which this event happens. */ - float m_time; + /** True if the kart recorded is using nitro/zipper or jumping. + * If true, a suitable GFX or animation will be replayed. */ + bool m_on_nitro; + bool m_on_zipper; + bool m_jumping; }; // KartReplayEvent // ------------------------------------------------------------------------ - ReplayBase(); FILE *openReplayFile(bool writeable); - // ---------------------------------------------------------------------- + // ------------------------------------------------------------------------ /** Returns the filename that was opened. */ - const std::string &getReplayFilename() const { return m_filename;} - // ---------------------------------------------------------------------- + virtual const std::string& getReplayFilename() const = 0; + // ------------------------------------------------------------------------ /** Returns the version number of the replay file. This is used to check * that a loaded replay file can still be understood by this * executable. */ - unsigned int getReplayVersion() const { return 1; } + unsigned int getReplayVersion() const { return 2; } + +public: + ReplayBase(); + virtual ~ReplayBase() {}; }; // ReplayBase #endif diff --git a/src/replay/replay_play.cpp b/src/replay/replay_play.cpp index 3a9c5fc83..467481700 100644 --- a/src/replay/replay_play.cpp +++ b/src/replay/replay_play.cpp @@ -21,13 +21,17 @@ #include "config/stk_config.hpp" #include "io/file_manager.hpp" #include "karts/ghost_kart.hpp" +#include "karts/controller/ghost_controller.hpp" #include "modes/world.hpp" #include "race/race_manager.hpp" #include "tracks/track.hpp" +#include "tracks/track_manager.hpp" +#include #include #include +ReplayPlay::SortOrder ReplayPlay::m_sort_order = ReplayPlay::SO_DEFAULT; ReplayPlay *ReplayPlay::m_replay_play = NULL; //----------------------------------------------------------------------------- @@ -35,7 +39,7 @@ ReplayPlay *ReplayPlay::m_replay_play = NULL; */ ReplayPlay::ReplayPlay() { - m_next = 0; + m_current_replay_file = 0; } // ReplayPlay //----------------------------------------------------------------------------- @@ -44,21 +48,11 @@ ReplayPlay::~ReplayPlay() { } // ~Replay -//----------------------------------------------------------------------------- -/** Starts replay from the replay file in the current directory. - */ -void ReplayPlay::init() -{ - m_next = 0; - Load(); -} // init - //----------------------------------------------------------------------------- /** Resets all ghost karts back to start position. */ void ReplayPlay::reset() { - m_next = 0; for(unsigned int i=0; i<(unsigned int)m_ghost_karts.size(); i++) { m_ghost_karts[i].reset(); @@ -66,24 +60,118 @@ void ReplayPlay::reset() } // reset //----------------------------------------------------------------------------- -/** Updates all ghost karts. - * \param dt Time step size. - */ -void ReplayPlay::update(float dt) +void ReplayPlay::loadAllReplayFile() { - // First update all ghost karts - for(unsigned int i=0; i<(unsigned int)m_ghost_karts.size(); i++) - m_ghost_karts[i].update(dt); + m_replay_file_list.clear(); + std::set files; + file_manager->listFiles(files, file_manager->getReplayDir(), + /*is_full_path*/ false); -} // update + char s[1024], s1[1024]; + for (std::set::iterator i = files.begin(); + i != files.end(); ++i) + { + if (StringUtils::getExtension(*i) != "replay") continue; + FILE *fd = fopen((file_manager->getReplayDir() + (*i)).c_str(), "r"); + if (fd == NULL) continue; + ReplayData rd; + + rd.m_filename = *i; + + fgets(s, 1023, fd); + unsigned int version; + if (sscanf(s,"version: %u", &version) != 1) + { + Log::warn("Replay", "No Version information " + "found in replay file (bogus replay file)."); + fclose(fd); + continue; + } + if (version != getReplayVersion()) + { + Log::warn("Replay", "Replay is version '%d'", version); + Log::warn("Replay", "STK version is '%d'", getReplayVersion()); + Log::warn("Replay", "Skipped '%s'", i->c_str()); + fclose(fd); + continue; + } + + while(true) + { + fgets(s, 1023, fd); + core::stringc is_end(s); + is_end.trim(); + if (is_end == "kart_list_end") break; + char s1[1024]; + + if (sscanf(s,"kart: %s", s1) != 1) + { + Log::warn("Replay", "Could not read ghost karts info!"); + break; + } + rd.m_kart_list.push_back(std::string(s1)); + } + + int reverse = 0; + fgets(s, 1023, fd); + if(sscanf(s, "reverse: %d", &reverse) != 1) + { + Log::warn("Replay", "Reverse info found in replay file."); + fclose(fd); + continue; + } + rd.m_reverse = reverse!=0; + + fgets(s, 1023, fd); + if (sscanf(s, "difficulty: %u", &rd.m_difficulty) != 1) + { + Log::warn("Replay", " No difficulty found in replay file."); + fclose(fd); + continue; + } + + fgets(s, 1023, fd); + if (sscanf(s, "track: %s", s1) != 1) + { + Log::warn("Replay", "Track info not found in replay file."); + fclose(fd); + continue; + } + rd.m_track_name = std::string(s1); + Track* t = track_manager->getTrack(rd.m_track_name); + if (t == NULL) + { + Log::warn("Replay", "Track '%s' used in replay not found in STK!", + rd.m_track_name.c_str()); + fclose(fd); + continue; + } + + fgets(s, 1023, fd); + if (sscanf(s, "laps: %u", &rd.m_laps) != 1) + { + Log::warn("Replay", "No number of laps found in replay file."); + fclose(fd); + continue; + } + + fgets(s, 1023, fd); + if (sscanf(s, "min_time: %f", &rd.m_min_time) != 1) + { + Log::warn("Replay", "Finish time not found in replay file."); + fclose(fd); + continue; + } + fclose(fd); + m_replay_file_list.push_back(rd); + } +} // loadAllReplayFile //----------------------------------------------------------------------------- -/** Loads a replay data from file called 'trackname'.replay. - */ -void ReplayPlay::Load() +void ReplayPlay::load() { m_ghost_karts.clearAndDeleteAll(); - char s[1024], s1[1024]; + char s[1024]; FILE *fd = openReplayFile(/*writeable*/false); if(!fd) @@ -96,44 +184,9 @@ void ReplayPlay::Load() Log::info("Replay", "Reading replay file '%s'.", getReplayFilename().c_str()); - if (fgets(s, 1023, fd) == NULL) - Log::fatal("Replay", "Could not read '%s'.", getReplayFilename().c_str()); - - unsigned int version; - if (sscanf(s,"Version: %u", &version) != 1) - Log::fatal("Replay", "No Version information found in replay file (bogus replay file)."); - - if (version != getReplayVersion()) - { - Log::warn("Replay", "Replay is version '%d'",version); - Log::warn("Replay", "STK version is '%d'",getReplayVersion()); - Log::warn("Replay", "We try to proceed, but it may fail."); - } - - if (fgets(s, 1023, fd) == NULL) - Log::fatal("Replay", "Could not read '%s'.", getReplayFilename().c_str()); - - int n; - if(sscanf(s, "difficulty: %d", &n) != 1) - Log::fatal("Replay", " No difficulty found in replay file."); - - if(race_manager->getDifficulty()!=(RaceManager::Difficulty)n) - Log::warn("Replay", "Difficulty of replay is '%d', " - "while '%d' is selected.", - race_manager->getDifficulty(), n); - - fgets(s, 1023, fd); - if(sscanf(s, "track: %s", s1) != 1) - Log::warn("Replay", "Track not found in replay file."); - assert(std::string(s1)==race_manager->getTrackName()); - race_manager->setTrack(s1); - - unsigned int num_laps; - fgets(s, 1023, fd); - if(sscanf(s, "Laps: %u", &num_laps) != 1) - Log::fatal("Replay", "No number of laps found in replay file."); - - race_manager->setNumLaps(num_laps); + const unsigned int line_skipped = getNumGhostKart() + 7; + for (unsigned int i = 0; i < line_skipped; i++) + fgets(s, 1023, fd); // eof actually doesn't trigger here, since it requires first to try // reading behind eof, but still it's clearer this way. @@ -142,11 +195,11 @@ void ReplayPlay::Load() if(fgets(s, 1023, fd)==NULL) // eof reached break; readKartData(fd, s); - } // for ksetController(controller); - m_ghost_karts.push_back(new GhostKart(std::string(s))); - m_ghost_karts[m_ghost_karts.size()-1].init(RaceManager::KT_GHOST); - - fgets(s, 1023, fd); unsigned int size; - if(sscanf(s,"size: %u",&size)!=1) + if(sscanf(next_line,"size: %u",&size)!=1) Log::fatal("Replay", "Number of records not found in replay file " - "for kart %d.", - m_ghost_karts.size()-1); + "for kart %d.", kart_num); for(unsigned int i=0; i #include #include @@ -32,33 +34,127 @@ class GhostKart; */ class ReplayPlay : public ReplayBase { -private: - static ReplayPlay *m_replay_play; - /** Points to the next free entry. */ - unsigned int m_next; +public: + /** Order of sort for ReplayData */ + enum SortOrder + { + SO_DEFAULT, + SO_TRACK = SO_DEFAULT, + SO_REV, + SO_KART_NUM, + SO_DIFF, + SO_LAPS, + SO_TIME + }; + + class ReplayData + { + public: + std::string m_filename; + std::string m_track_name; + std::vector m_kart_list; + bool m_reverse; + unsigned int m_difficulty; + unsigned int m_laps; + float m_min_time; + + bool operator < (const ReplayData& r) const + { + switch (m_sort_order) + { + case SO_TRACK: + return m_track_name < r.m_track_name; + break; + case SO_KART_NUM: + return m_kart_list.size() < r.m_kart_list.size(); + break; + case SO_REV: + return m_reverse < r.m_reverse; + break; + case SO_DIFF: + return m_difficulty < r.m_difficulty; + break; + case SO_LAPS: + return m_laps < r.m_laps; + break; + case SO_TIME: + return m_min_time < r.m_min_time; + break; + } // switch + return true; + } // operator < + }; // ReplayData + +private: + static ReplayPlay *m_replay_play; + + static SortOrder m_sort_order; + + unsigned int m_current_replay_file; + + std::vector m_replay_file_list; /** All ghost karts. */ - PtrVector m_ghost_karts; + PtrVector m_ghost_karts; ReplayPlay(); ~ReplayPlay(); void readKartData(FILE *fd, char *next_line); public: - void init(); - void update(float dt); void reset(); - void Load(); - + void load(); + void loadAllReplayFile(); + // ------------------------------------------------------------------------ + static void setSortOrder(SortOrder so) { m_sort_order = so; } + // ------------------------------------------------------------------------ + void sortReplay(bool reverse) + { + (reverse ? std::sort(m_replay_file_list.rbegin(), + m_replay_file_list.rend()) : std::sort(m_replay_file_list.begin(), + m_replay_file_list.end())); + } + // ------------------------------------------------------------------------ + void setReplayFile(unsigned int n) + { m_current_replay_file = n; } + // ------------------------------------------------------------------------ + const ReplayData& getReplayData(unsigned int n) const + { return m_replay_file_list.at(n); } + // ------------------------------------------------------------------------ + const unsigned int getNumReplayFile() const + { return m_replay_file_list.size(); } + // ------------------------------------------------------------------------ + GhostKart* getGhostKart(int n) { return m_ghost_karts.get(n); } + // ------------------------------------------------------------------------ + const unsigned int getNumGhostKart() const + { + assert(m_replay_file_list.size() > 0); + return m_replay_file_list.at(m_current_replay_file).m_kart_list.size(); + } + // ------------------------------------------------------------------------ + const std::string& getGhostKartName(int n) const + { + assert(m_replay_file_list.size() > 0); + return m_replay_file_list.at(m_current_replay_file).m_kart_list.at(n); + } // ------------------------------------------------------------------------ /** Creates a new instance of the replay object. */ - static void create() { m_replay_play = new ReplayPlay(); } + static void create() { m_replay_play = new ReplayPlay(); } // ------------------------------------------------------------------------ /** Returns the instance of the replay object. */ - static ReplayPlay *get() { return m_replay_play; } + static ReplayPlay *get() { return m_replay_play; } // ------------------------------------------------------------------------ /** Delete the instance of the replay object. */ - static void destroy() { delete m_replay_play; m_replay_play=NULL; } + static void destroy() + { delete m_replay_play; m_replay_play = NULL; } + // ------------------------------------------------------------------------ + /** Returns the filename that was opened. */ + virtual const std::string& getReplayFilename() const + { + assert(m_replay_file_list.size() > 0); + return m_replay_file_list.at(m_current_replay_file).m_filename; + } + // ------------------------------------------------------------------------ }; // Replay #endif diff --git a/src/replay/replay_recorder.cpp b/src/replay/replay_recorder.cpp index 07a5c1e9a..21c51a622 100644 --- a/src/replay/replay_recorder.cpp +++ b/src/replay/replay_recorder.cpp @@ -20,9 +20,12 @@ #include "config/stk_config.hpp" #include "io/file_manager.hpp" +#include "guiengine/message_queue.hpp" #include "karts/ghost_kart.hpp" #include "modes/world.hpp" +#include "physics/btKart.hpp" #include "race/race_manager.hpp" +#include "tracks/terrain_info.hpp" #include "tracks/track.hpp" #include @@ -36,13 +39,14 @@ ReplayRecorder *ReplayRecorder::m_replay_recorder = NULL; */ ReplayRecorder::ReplayRecorder() { + m_complete_replay = false; + m_incorrect_replay = false; } // ReplayRecorder //----------------------------------------------------------------------------- /** Frees all stored data. */ ReplayRecorder::~ReplayRecorder() { - m_transform_events.clear(); } // ~Replay //----------------------------------------------------------------------------- @@ -51,17 +55,21 @@ ReplayRecorder::~ReplayRecorder() */ void ReplayRecorder::init() { + m_complete_replay = false; + m_incorrect_replay = false; m_transform_events.clear(); + m_physic_info.clear(); + m_kart_replay_event.clear(); m_transform_events.resize(race_manager->getNumberOfKarts()); - m_skid_control.resize(race_manager->getNumberOfKarts()); + m_physic_info.resize(race_manager->getNumberOfKarts()); m_kart_replay_event.resize(race_manager->getNumberOfKarts()); unsigned int max_frames = (unsigned int)( stk_config->m_replay_max_time / stk_config->m_replay_dt); for(unsigned int i=0; igetNumberOfKarts(); i++) { m_transform_events[i].resize(max_frames); - // Rather arbitraritly sized, it will be added with push_back - m_kart_replay_event[i].reserve(500); + m_physic_info[i].resize(max_frames); + m_kart_replay_event[i].resize(max_frames); } m_count_transforms.clear(); m_count_transforms.resize(race_manager->getNumberOfKarts(), 0); @@ -75,117 +83,182 @@ void ReplayRecorder::init() #endif } // init -//----------------------------------------------------------------------------- -/** Resets all ghost karts back to start position. - */ -void ReplayRecorder::reset() -{ -} // reset - //----------------------------------------------------------------------------- /** Saves the current replay data. * \param dt Time step size. */ void ReplayRecorder::update(float dt) { + if (m_incorrect_replay || m_complete_replay) return; + const World *world = World::getWorld(); + const bool single_player = race_manager->getNumPlayers() == 1; unsigned int num_karts = world->getNumKarts(); float time = world->getTime(); - // Once we use interpolate results, we don't have to increase - // m_next by num_karts, so count how often to increase - for(unsigned int i=0; igetKart(i); + // If a single player give up in game menu, stop recording + if (kart->isEliminated() && single_player) return; - // Check if skidding state has changed. If so, store this - if(kart->getControls().m_skid != m_skid_control[i]) - { - KartReplayEvent kre; - kre.m_time = World::getWorld()->getTime(); - if(kart->getControls().m_skid==KartControl::SC_LEFT) - kre.m_type = KartReplayEvent::KRE_SKID_LEFT; - else if(kart->getControls().m_skid==KartControl::SC_RIGHT) - kre.m_type = KartReplayEvent::KRE_SKID_RIGHT; - else - kre.m_type = KartReplayEvent::KRE_NONE; - m_kart_replay_event[i].push_back(kre); - if(m_skid_control[i]!=KartControl::SC_NONE) - m_skid_control[i] = KartControl::SC_NONE; - else - m_skid_control[i] = kart->getControls().m_skid; - } + if (kart->isGhostKart()) continue; #ifdef DEBUG - m_count ++; + m_count++; #endif - if(time - m_last_saved_time[i]m_replay_dt) + if (time - m_last_saved_time[i] < stk_config->m_replay_dt) { #ifdef DEBUG - m_count_skipped_time ++; + m_count_skipped_time++; #endif continue; } m_last_saved_time[i] = time; m_count_transforms[i]++; - if(m_count_transforms[i]>=m_transform_events[i].size()) + if (m_count_transforms[i] >= m_transform_events[i].size()) { // Only print this message once. - if(m_count_transforms[i]==m_transform_events[i].size()) + if (m_count_transforms[i] == m_transform_events[i].size()) { char buffer[100]; sprintf(buffer, "Can't store more events for kart %s.", kart->getIdent().c_str()); Log::warn("ReplayRecorder", buffer); + m_incorrect_replay = single_player; } continue; } - TransformEvent *p = &(m_transform_events[i][m_count_transforms[i]-1]); - p->m_time = World::getWorld()->getTime(); + TransformEvent *p = &(m_transform_events[i][m_count_transforms[i]-1]); + PhysicInfo *q = &(m_physic_info[i][m_count_transforms[i]-1]); + KartReplayEvent *r = &(m_kart_replay_event[i][m_count_transforms[i]-1]); + + p->m_time = World::getWorld()->getTime(); p->m_transform.setOrigin(kart->getXYZ()); p->m_transform.setRotation(kart->getVisualRotation()); + + q->m_speed = kart->getSpeed(); + q->m_steer = kart->getSteerPercent(); + const int num_wheels = kart->getVehicle()->getNumWheels(); + for (int j = 0; j < 4; j++) + { + if (j > num_wheels || num_wheels == 0) + q->m_suspension_length[j] = 0.0f; + else + { + q->m_suspension_length[j] = kart->getVehicle() + ->getWheelInfo(j).m_raycastInfo.m_suspensionLength; + } + } + + bool nitro = false; + bool zipper = false; + const KartControl kc = kart->getControls(); + const Material* m = kart->getTerrainInfo()->getMaterial(); + if (kc.m_nitro && kart->isOnGround() && + kart->isOnMinNitroTime() > 0.0f && kart->getEnergy() > 0.0f) + { + nitro = true; + } + if (m) + { + if (m->isZipper() && kart->isOnGround()) + zipper = true; + } + if (kc.m_fire && + kart->getPowerup()->getType() == PowerupManager::POWERUP_ZIPPER) + { + zipper = true; + } + r->m_on_nitro = nitro; + r->m_on_zipper = zipper; + r->m_jumping = kart->isJumping(); } // for i + + if (world->getPhase() == World::RESULT_DISPLAY_PHASE && !m_complete_replay) + { + m_complete_replay = true; + save(); + } } // update //----------------------------------------------------------------------------- /** Saves the replay data stored in the internal data structures. */ -void ReplayRecorder::Save() +void ReplayRecorder::save() { -#ifdef DEBUG - printf("%d frames, %d removed because of frequency compression\n", - m_count, m_count_skipped_time); -#endif - FILE *fd = openReplayFile(/*writeable*/true); - if(!fd) + if (m_incorrect_replay || !m_complete_replay) { - Log::error("ReplayRecorder", "Can't open '%s' for writing - can't save replay data.", - getReplayFilename().c_str()); + MessageQueue::add(MessageQueue::MT_ERROR, + _("Incomplete replay file will not be saved.")); return; } - Log::info("ReplayRecorder", "Replay saved in '%s'.\n", getReplayFilename().c_str()); +#ifdef DEBUG + Log::debug("ReplayRecorder", "%d frames, %d removed because of" + "frequency compression", m_count, m_count_skipped_time); +#endif + const World *world = World::getWorld(); + const unsigned int num_karts = world->getNumKarts(); + float min_time = 99999.99f; + for (unsigned int k = 0; k < num_karts; k++) + { + if (world->getKart(k)->isGhostKart()) continue; + float cur_time = m_transform_events[k][m_count_transforms[k]-1].m_time; + if (cur_time < min_time) + min_time = cur_time; + } - World *world = World::getWorld(); - unsigned int num_karts = world->getNumKarts(); - fprintf(fd, "Version: %d\n", getReplayVersion()); + int day, month, year; + StkTime::getDate(&day, &month, &year); + std::string time = StringUtils::toString(min_time); + std::replace(time.begin(), time.end(), '.', '_'); + std::ostringstream oss; + oss << world->getTrack()->getIdent() << "_" << year << month << day + << "_" << num_karts << "_" << time << ".replay"; + m_filename = oss.str(); + + FILE *fd = openReplayFile(/*writeable*/true); + if (!fd) + { + Log::error("ReplayRecorder", "Can't open '%s' for writing - " + "can't save replay data.", getReplayFilename().c_str()); + return; + } + + core::stringw msg = _("Replay saved in \"%s\".", + (file_manager->getReplayDir() + getReplayFilename()).c_str()); + MessageQueue::add(MessageQueue::MT_GENERIC, msg); + + fprintf(fd, "version: %d\n", getReplayVersion()); + for (unsigned int real_karts = 0; real_karts < num_karts; real_karts++) + { + if (world->getKart(real_karts)->isGhostKart()) continue; + fprintf(fd, "kart: %s\n", + world->getKart(real_karts)->getIdent().c_str()); + } + + fprintf(fd, "kart_list_end\n"); + fprintf(fd, "reverse: %d\n", (int)race_manager->getReverseTrack()); fprintf(fd, "difficulty: %d\n", race_manager->getDifficulty()); fprintf(fd, "track: %s\n", world->getTrack()->getIdent().c_str()); - fprintf(fd, "Laps: %d\n", race_manager->getNumLaps()); + fprintf(fd, "laps: %d\n", race_manager->getNumLaps()); + fprintf(fd, "min_time: %f\n", min_time); unsigned int max_frames = (unsigned int)( stk_config->m_replay_max_time / stk_config->m_replay_dt ); - for(unsigned int k=0; kgetKart(k)->getIdent().c_str()); + if (world->getKart(k)->isGhostKart()) continue; fprintf(fd, "size: %d\n", m_count_transforms[k]); unsigned int num_transforms = std::min(max_frames, m_count_transforms[k]); - for(unsigned int i=0; im_time, p->m_transform.getOrigin().getX(), p->m_transform.getOrigin().getY(), @@ -193,16 +266,18 @@ void ReplayRecorder::Save() p->m_transform.getRotation().getX(), p->m_transform.getRotation().getY(), p->m_transform.getRotation().getZ(), - p->m_transform.getRotation().getW() + p->m_transform.getRotation().getW(), + q->m_speed, + q->m_steer, + q->m_suspension_length[0], + q->m_suspension_length[1], + q->m_suspension_length[2], + q->m_suspension_length[3], + (int)r->m_on_nitro, + (int)r->m_on_zipper, + (int)r->m_jumping ); } // for i - fprintf(fd, "events: %d\n", (int)m_kart_replay_event[k].size()); - for(unsigned int i=0; im_time, p->m_type); - } } fclose(fd); -} // Save - +} // save diff --git a/src/replay/replay_recorder.hpp b/src/replay/replay_recorder.hpp index c77c2015c..9cf828376 100644 --- a/src/replay/replay_recorder.hpp +++ b/src/replay/replay_recorder.hpp @@ -30,24 +30,30 @@ class ReplayRecorder : public ReplayBase { private: + std::string m_filename; /** A separate vector of Replay Events for all transforms. */ std::vector< std::vector > m_transform_events; + /** A separate vector of Replay Events for all physic info. */ + std::vector< std::vector > m_physic_info; + + /** A separate vector of Replay Events for all other events. */ + std::vector< std::vector > m_kart_replay_event; + /** Time at which a transform was saved for the last time. */ std::vector m_last_saved_time; /** Counts the number of transform events for each kart. */ std::vector m_count_transforms; - /** Stores the last skid state. */ - std::vector m_skid_control; - - std::vector< std::vector > m_kart_replay_event; - /** Static pointer to the one instance of the replay object. */ static ReplayRecorder *m_replay_recorder; + bool m_complete_replay; + + bool m_incorrect_replay; + #ifdef DEBUG /** Counts overall number of events stored. */ unsigned int m_count; @@ -65,8 +71,7 @@ private: public: void init(); void update(float dt); - void reset(); - void Save(); + void save(); // ------------------------------------------------------------------------ /** Creates a new instance of the replay object. */ @@ -81,6 +86,10 @@ public: // ------------------------------------------------------------------------ /** Delete the instance of the replay object. */ static void destroy() { delete m_replay_recorder; m_replay_recorder=NULL; } + // ------------------------------------------------------------------------ + /** Returns the filename that was opened. */ + virtual const std::string& getReplayFilename() const { return m_filename; } + // ------------------------------------------------------------------------ }; // ReplayRecorder #endif diff --git a/src/states_screens/addons_screen.hpp b/src/states_screens/addons_screen.hpp index 63c0f3371..8b4c1be5b 100644 --- a/src/states_screens/addons_screen.hpp +++ b/src/states_screens/addons_screen.hpp @@ -101,7 +101,7 @@ public: /** \brief implement callback from parent class GUIEngine::Screen */ virtual void beforeAddingWidget() OVERRIDE; - virtual void onColumnClicked(int columnId); + virtual void onColumnClicked(int columnId) OVERRIDE; virtual void init() OVERRIDE; virtual void tearDown() OVERRIDE; diff --git a/src/states_screens/create_server_screen.cpp b/src/states_screens/create_server_screen.cpp index 297f3e9a8..39a2d6159 100644 --- a/src/states_screens/create_server_screen.cpp +++ b/src/states_screens/create_server_screen.cpp @@ -26,7 +26,6 @@ #include "network/network_config.hpp" #include "network/servers_manager.hpp" #include "network/stk_host.hpp" -#include "states_screens/online_screen.hpp" #include "states_screens/state_manager.hpp" #include "states_screens/dialogs/message_dialog.hpp" #include "states_screens/networking_lobby.hpp" diff --git a/src/states_screens/credits.hpp b/src/states_screens/credits.hpp index 7c594bf6c..b454832cd 100644 --- a/src/states_screens/credits.hpp +++ b/src/states_screens/credits.hpp @@ -84,7 +84,7 @@ public: void setVictoryMusic(bool isVictory) { m_is_victory_music = isVictory; } - virtual MusicInformation* getMusic() const + virtual MusicInformation* getMusic() const OVERRIDE { if (m_is_victory_music) return music_manager->getMusicInformation("win_theme.music"); diff --git a/src/states_screens/cutscene_gui.hpp b/src/states_screens/cutscene_gui.hpp index 45ac0073c..9925a38f7 100644 --- a/src/states_screens/cutscene_gui.hpp +++ b/src/states_screens/cutscene_gui.hpp @@ -52,8 +52,8 @@ public: void setFadeLevel(float level) { m_fade_level = level; } void setSubtitle(const core::stringw& subtitle) { m_subtitle = subtitle; } - virtual void renderGlobal(float dt); - virtual void renderPlayerView(const Camera *camera, float dt){} + virtual void renderGlobal(float dt) OVERRIDE; + virtual void renderPlayerView(const Camera *camera, float dt) OVERRIDE {} virtual const core::dimension2du getMiniMapSize() const OVERRIDE { diff --git a/src/states_screens/dialogs/addons_loading.hpp b/src/states_screens/dialogs/addons_loading.hpp index 25e3fbdd3..cdc91c6e8 100644 --- a/src/states_screens/dialogs/addons_loading.hpp +++ b/src/states_screens/dialogs/addons_loading.hpp @@ -59,15 +59,15 @@ public: ~AddonsLoading(); - virtual GUIEngine::EventPropagation processEvent(const std::string& event_source); - virtual void beforeAddingWidgets(); - virtual void init(); + virtual GUIEngine::EventPropagation processEvent(const std::string& event_source) OVERRIDE; + virtual void beforeAddingWidgets() OVERRIDE; + virtual void init() OVERRIDE; /** This function is called by the GUI, all the frame (or somthing like * that). It checks the flags (m_can_load_icon and * and do the necessary. * */ - void onUpdate(float delta); + void onUpdate(float delta) OVERRIDE; void voteClicked(); virtual bool onEscapePressed() OVERRIDE; diff --git a/src/states_screens/dialogs/confirm_resolution_dialog.hpp b/src/states_screens/dialogs/confirm_resolution_dialog.hpp index 9d8b97b48..a1fab1ddb 100644 --- a/src/states_screens/dialogs/confirm_resolution_dialog.hpp +++ b/src/states_screens/dialogs/confirm_resolution_dialog.hpp @@ -38,10 +38,10 @@ private: public: ConfirmResolutionDialog(); - void onEnterPressedInternal(); - GUIEngine::EventPropagation processEvent(const std::string& eventSource); + void onEnterPressedInternal() OVERRIDE; + GUIEngine::EventPropagation processEvent(const std::string& eventSource) OVERRIDE; - virtual void onUpdate(float dt); + virtual void onUpdate(float dt) OVERRIDE; virtual bool onEscapePressed() OVERRIDE; }; diff --git a/src/states_screens/dialogs/debug_slider.hpp b/src/states_screens/dialogs/debug_slider.hpp index 2a8be11cf..8e0768db6 100644 --- a/src/states_screens/dialogs/debug_slider.hpp +++ b/src/states_screens/dialogs/debug_slider.hpp @@ -49,7 +49,7 @@ public: virtual void onEnterPressedInternal() OVERRIDE; virtual void onUpdate(float dt) OVERRIDE; - GUIEngine::EventPropagation processEvent(const std::string& eventSource); + GUIEngine::EventPropagation processEvent(const std::string& eventSource) OVERRIDE; }; diff --git a/src/states_screens/dialogs/ghost_replay_info_dialog.cpp b/src/states_screens/dialogs/ghost_replay_info_dialog.cpp new file mode 100644 index 000000000..c5fb4f01d --- /dev/null +++ b/src/states_screens/dialogs/ghost_replay_info_dialog.cpp @@ -0,0 +1,114 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2016 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "states_screens/dialogs/ghost_replay_info_dialog.hpp" + +#include "config/player_manager.hpp" +#include "replay/replay_play.hpp" +#include "states_screens/ghost_replay_selection.hpp" +#include "states_screens/state_manager.hpp" + +using namespace GUIEngine; +using namespace irr::core; + +// ----------------------------------------------------------------------------- +GhostReplayInfoDialog::GhostReplayInfoDialog(unsigned int replay_id) + : ModalDialog(0.8f,0.5f), m_replay_id(replay_id) +{ + m_self_destroy = false; + m_rd = ReplayPlay::get()->getReplayData(m_replay_id); + loadFromFile("ghost_replay_info_dialog.stkgui"); + + LabelWidget *name = getWidget("name"); + assert(name); + name->setText(stringw((m_rd.m_filename).c_str()), false); + + m_back_widget = getWidget("back"); + m_back_widget->setFocusForPlayer(PLAYER_ID_GAME_MASTER); + m_action_widget = getWidget("actions"); +} // GhostReplayInfoDialog + +// ----------------------------------------------------------------------------- +GhostReplayInfoDialog::~GhostReplayInfoDialog() +{ +} // ~GhostReplayInfoDialog + +// ----------------------------------------------------------------------------- +GUIEngine::EventPropagation + GhostReplayInfoDialog::processEvent(const std::string& event_source) +{ + + if (event_source == "actions") + { + const std::string& selection = + m_action_widget->getSelectionIDString(PLAYER_ID_GAME_MASTER); + + if(selection == "start") + { + bool reverse = m_rd.m_reverse; + std::string track_name = m_rd.m_track_name; + int laps = m_rd.m_laps; + int replay_id = m_replay_id; + + ModalDialog::dismiss(); + ReplayPlay::get()->setReplayFile(replay_id); + race_manager->setRaceGhostKarts(true); + race_manager->setNumKarts(race_manager->getNumLocalPlayers()); + + // Disable accidentally unlocking of a challenge + PlayerManager::getCurrentPlayer()->setCurrentChallenge(""); + + race_manager->setReverseTrack(reverse); + race_manager->startSingleRace(track_name, laps, false); + return GUIEngine::EVENT_BLOCK; + } + else if(selection == "remove") + { + std::string fn = m_rd.m_filename; + ModalDialog::dismiss(); + + dynamic_cast(GUIEngine::getCurrentScreen()) + ->onDeleteReplay(fn); + return GUIEngine::EVENT_BLOCK; + } + else if (selection == "back") + { + m_self_destroy = true; + return GUIEngine::EVENT_BLOCK; + } + } + return GUIEngine::EVENT_LET; +} // processEvent + +// ----------------------------------------------------------------------------- +bool GhostReplayInfoDialog::onEscapePressed() +{ + if (m_back_widget->isActivated()) + m_self_destroy = true; + return false; +} // onEscapePressed + +// ----------------------------------------------------------------------------- +void GhostReplayInfoDialog::onUpdate(float dt) +{ + if (m_self_destroy) + { + ModalDialog::dismiss(); + return; + } +} // onUpdate diff --git a/src/states_screens/dialogs/ghost_replay_info_dialog.hpp b/src/states_screens/dialogs/ghost_replay_info_dialog.hpp new file mode 100644 index 000000000..af4949b02 --- /dev/null +++ b/src/states_screens/dialogs/ghost_replay_info_dialog.hpp @@ -0,0 +1,53 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2016 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef HEADER_GHOST_REPLAY_INFO_DIALOG_HPP +#define HEADER_GHOST_REPLAY_INFO_DIALOG_HPP + +#include "guiengine/modaldialog.hpp" +#include "guiengine/widgets/icon_button_widget.hpp" +#include "guiengine/widgets/ribbon_widget.hpp" +#include "replay/replay_play.hpp" + +/** \brief Dialog that allows a user to do action with ghost replay file + * \ingroup states_screens + */ +class GhostReplayInfoDialog : public GUIEngine::ModalDialog +{ + +private: + + bool m_self_destroy; + + const unsigned int m_replay_id; + ReplayPlay::ReplayData m_rd; + + GUIEngine::RibbonWidget* m_action_widget; + GUIEngine::IconButtonWidget* m_back_widget; + +public: + GhostReplayInfoDialog(unsigned int replay_id); + ~GhostReplayInfoDialog(); + + GUIEngine::EventPropagation processEvent(const std::string& eventSource); + + virtual bool onEscapePressed(); + virtual void onUpdate(float dt); +}; // class GhostReplayInfoDialog + +#endif diff --git a/src/states_screens/dialogs/message_dialog.hpp b/src/states_screens/dialogs/message_dialog.hpp index 7f4627124..923a761dd 100644 --- a/src/states_screens/dialogs/message_dialog.hpp +++ b/src/states_screens/dialogs/message_dialog.hpp @@ -20,6 +20,7 @@ #define HEADER_CONFIRM_DIALOG_HPP #include "guiengine/modaldialog.hpp" +#include "utils/cpp2011.hpp" #include "utils/leak_check.hpp" /** @@ -93,13 +94,13 @@ public: ~MessageDialog(); - virtual void onEnterPressedInternal(); - virtual void onUpdate(float dt); - virtual void load(); + virtual void onEnterPressedInternal() OVERRIDE; + virtual void onUpdate(float dt) OVERRIDE; + virtual void load() OVERRIDE; - GUIEngine::EventPropagation processEvent(const std::string& eventSource); + GUIEngine::EventPropagation processEvent(const std::string& eventSource) OVERRIDE; - virtual void loadedFromFile(); + virtual void loadedFromFile() OVERRIDE; }; diff --git a/src/states_screens/dialogs/scripting_console.hpp b/src/states_screens/dialogs/scripting_console.hpp index f4200a715..c248a008c 100644 --- a/src/states_screens/dialogs/scripting_console.hpp +++ b/src/states_screens/dialogs/scripting_console.hpp @@ -45,7 +45,7 @@ public: virtual void onEnterPressedInternal() OVERRIDE{ runScript(); } void runScript(); - GUIEngine::EventPropagation processEvent(const std::string& eventSource); + GUIEngine::EventPropagation processEvent(const std::string& eventSource) OVERRIDE; }; #endif diff --git a/src/states_screens/dialogs/tutorial_message_dialog.hpp b/src/states_screens/dialogs/tutorial_message_dialog.hpp index 743300465..79c0f4427 100644 --- a/src/states_screens/dialogs/tutorial_message_dialog.hpp +++ b/src/states_screens/dialogs/tutorial_message_dialog.hpp @@ -44,7 +44,7 @@ public: virtual void onUpdate(float dt) OVERRIDE; - GUIEngine::EventPropagation processEvent(const std::string& eventSource); + GUIEngine::EventPropagation processEvent(const std::string& eventSource) OVERRIDE; }; diff --git a/src/states_screens/edit_gp_screen.hpp b/src/states_screens/edit_gp_screen.hpp index 5f3941437..69456585b 100644 --- a/src/states_screens/edit_gp_screen.hpp +++ b/src/states_screens/edit_gp_screen.hpp @@ -43,8 +43,8 @@ class EditGPScreen : EditGPScreen(); - void onConfirm(); - void onCancel(); + void onConfirm() OVERRIDE; + void onCancel() OVERRIDE; void loadList(const int selected); void setModified(const bool modified); diff --git a/src/states_screens/ghost_replay_selection.cpp b/src/states_screens/ghost_replay_selection.cpp new file mode 100644 index 000000000..291e1852f --- /dev/null +++ b/src/states_screens/ghost_replay_selection.cpp @@ -0,0 +1,202 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2016 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "states_screens/ghost_replay_selection.hpp" + +#include "states_screens/dialogs/ghost_replay_info_dialog.hpp" +#include "states_screens/state_manager.hpp" +#include "tracks/track.hpp" +#include "tracks/track_manager.hpp" +#include "utils/translation.hpp" + +using namespace GUIEngine; + +DEFINE_SCREEN_SINGLETON( GhostReplaySelection ); + +// ---------------------------------------------------------------------------- +/** Constructor, which loads the stkgui file. + */ +GhostReplaySelection::GhostReplaySelection() : Screen("ghost_replay_selection.stkgui") +{ + m_sort_desc = true; +} // GhostReplaySelection + +// ---------------------------------------------------------------------------- +/** Destructor. + */ +GhostReplaySelection::~GhostReplaySelection() +{ +} // GhostReplaySelection + +// ---------------------------------------------------------------------------- +/** Triggers a refresh of the replay file list. + */ +void GhostReplaySelection::refresh(bool forced_update) +{ + if (ReplayPlay::get()->getNumReplayFile() == 0 || forced_update) + ReplayPlay::get()->loadAllReplayFile(); + loadList(); +} // refresh + +// ---------------------------------------------------------------------------- +/** Set pointers to the various widgets. + */ +void GhostReplaySelection::loadedFromFile() +{ + m_replay_list_widget = getWidget("replay_list"); + assert(m_replay_list_widget != NULL); + m_replay_list_widget->setColumnListener(this); +} // loadedFromFile + +// ---------------------------------------------------------------------------- +/** Clear the replay file list, which will be reloaded. + */ +void GhostReplaySelection::beforeAddingWidget() +{ + m_replay_list_widget->clearColumns(); + m_replay_list_widget->addColumn( _("Track"), 3 ); + m_replay_list_widget->addColumn( _("Players"), 1); + m_replay_list_widget->addColumn( _("Reverse"), 1); + m_replay_list_widget->addColumn( _("Difficulty"), 1); + m_replay_list_widget->addColumn( _("Laps"), 1); + m_replay_list_widget->addColumn( _("Finish Time"), 1); +} // beforeAddingWidget + +// ---------------------------------------------------------------------------- +void GhostReplaySelection::init() +{ + Screen::init(); + refresh(/*forced_update*/false); +} // init + +// ---------------------------------------------------------------------------- +/** Loads the list of all replay files. The gui element will be + * updated. + */ +void GhostReplaySelection::loadList() +{ + ReplayPlay::get()->sortReplay(m_sort_desc); + m_replay_list_widget->clear(); + for (unsigned int i = 0; i < ReplayPlay::get()->getNumReplayFile() ; i++) + { + const ReplayPlay::ReplayData& rd = ReplayPlay::get()->getReplayData(i); + + std::vector row; + Track* t = track_manager->getTrack(rd.m_track_name); + row.push_back(GUIEngine::ListWidget::ListCell + (translations->fribidize(t->getName()) , -1, 3)); + row.push_back(GUIEngine::ListWidget::ListCell + (StringUtils::toWString(rd.m_kart_list.size()), -1, 1, true)); + row.push_back(GUIEngine::ListWidget::ListCell + (rd.m_reverse ? _("Yes") : _("No"), -1, 1, true)); + row.push_back(GUIEngine::ListWidget::ListCell + (rd.m_difficulty == 3 ? _("Supertux") : rd.m_difficulty == 2 ? + _("Expert") : rd.m_difficulty == 1 ? + _("Intermediate") : _("Novice") , -1, 1, true)); + row.push_back(GUIEngine::ListWidget::ListCell + (StringUtils::toWString(rd.m_laps), -1, 1, true)); + row.push_back(GUIEngine::ListWidget::ListCell + (StringUtils::toWString(rd.m_min_time) + L"s", -1, 1, true)); + m_replay_list_widget->addItem("replay", row); + } +} // loadList + +// ---------------------------------------------------------------------------- +void GhostReplaySelection::eventCallback(GUIEngine::Widget* widget, + const std::string& name, + const int playerID) +{ + if (name == "back") + { + StateManager::get()->escapePressed(); + } + else if (name == "reload") + { + refresh(); + } + else if (name == m_replay_list_widget->m_properties[GUIEngine::PROP_ID]) + { + int selected_index = m_replay_list_widget->getSelectionID(); + // This can happen e.g. when the list is empty and the user + // clicks somewhere. + if (selected_index >= (signed)ReplayPlay::get()->getNumReplayFile() || + selected_index < 0) + { + return; + } + new GhostReplayInfoDialog(selected_index); + } // click on replay file + +} // eventCallback + +// ---------------------------------------------------------------------------- +void GhostReplaySelection::onDeleteReplay(std::string& filename) +{ + m_file_to_be_deleted = filename; + new MessageDialog( _("Are you sure you want to remove '%s'?", + m_file_to_be_deleted.c_str()), MessageDialog::MESSAGE_DIALOG_CONFIRM, + this, false); +} // onDeleteReplay + +// ---------------------------------------------------------------------------- +void GhostReplaySelection::onConfirm() +{ + if (!file_manager + ->removeFile(file_manager->getReplayDir() + m_file_to_be_deleted)) + Log::warn("GhostReplayInfoDialog", "Failed to delete file."); + + ModalDialog::dismiss(); + GhostReplaySelection::getInstance()->refresh(); +} // onConfirm + +// ---------------------------------------------------------------------------- +/** Change the sort order if a column was clicked. + * \param column_id ID of the column that was clicked. + */ +void GhostReplaySelection::onColumnClicked(int column_id) +{ + switch (column_id) + { + case 0: + ReplayPlay::setSortOrder(ReplayPlay::SO_TRACK); + break; + case 1: + ReplayPlay::setSortOrder(ReplayPlay::SO_KART_NUM); + break; + case 2: + ReplayPlay::setSortOrder(ReplayPlay::SO_REV); + break; + case 3: + ReplayPlay::setSortOrder(ReplayPlay::SO_DIFF); + break; + case 4: + ReplayPlay::setSortOrder(ReplayPlay::SO_LAPS); + break; + case 5: + ReplayPlay::setSortOrder(ReplayPlay::SO_TIME); + break; + default: + assert(0); + break; + } // switch + /** \brief Toggle the sort order after column click **/ + m_sort_desc = !m_sort_desc; + loadList(); +} // onColumnClicked + +// ---------------------------------------------------------------------------- \ No newline at end of file diff --git a/src/states_screens/ghost_replay_selection.hpp b/src/states_screens/ghost_replay_selection.hpp new file mode 100644 index 000000000..2e357eb8d --- /dev/null +++ b/src/states_screens/ghost_replay_selection.hpp @@ -0,0 +1,78 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2016 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef HEADER_GHOST_REPLAY_SELECTION_HPP +#define HEADER_GHOST_REPLAY_SELECTION_HPP + +#include "guiengine/screen.hpp" +#include "guiengine/widgets.hpp" +#include "states_screens/dialogs/message_dialog.hpp" + +namespace GUIEngine { class Widget; } + +/** + * \brief GhostReplaySelection + * \ingroup states_screens + */ +class GhostReplaySelection : public GUIEngine::Screen, + public GUIEngine::ScreenSingleton, + public GUIEngine::IListWidgetHeaderListener, + public MessageDialog::IConfirmDialogListener + +{ + friend class GUIEngine::ScreenSingleton; + +private: + GhostReplaySelection(); + ~GhostReplaySelection(); + + GUIEngine::ListWidget* m_replay_list_widget; + std::string m_file_to_be_deleted; + bool m_sort_desc; + +public: + + void refresh(bool forced_update = true); + + /** Load the addons into the main list.*/ + void loadList(); + + void onDeleteReplay(std::string& filename); + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void loadedFromFile() OVERRIDE; + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void eventCallback(GUIEngine::Widget* widget, const std::string& name, + const int playerID) OVERRIDE; + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void beforeAddingWidget() OVERRIDE; + + virtual void onColumnClicked(int columnId) OVERRIDE; + + virtual void init() OVERRIDE; + + virtual void tearDown() OVERRIDE {}; + + /** \brief Implement IConfirmDialogListener callback */ + virtual void onConfirm() OVERRIDE; + +}; // GhostReplaySelection + +#endif diff --git a/src/states_screens/gp_info_screen.hpp b/src/states_screens/gp_info_screen.hpp index 25a08d9c8..da2cca82f 100644 --- a/src/states_screens/gp_info_screen.hpp +++ b/src/states_screens/gp_info_screen.hpp @@ -88,12 +88,12 @@ public: void onEnterPressedInternal(); virtual void eventCallback(GUIEngine::Widget *, const std::string &name, - const int player_id); + const int player_id) OVERRIDE; virtual void loadedFromFile() OVERRIDE; virtual void init() OVERRIDE; virtual void beforeAddingWidget() OVERRIDE; - virtual void onUpdate(float dt); + virtual void onUpdate(float dt) OVERRIDE; void setGP(const std::string &gp_ident); }; // GPInfoScreen diff --git a/src/states_screens/grand_prix_cutscene.hpp b/src/states_screens/grand_prix_cutscene.hpp index adf8b129a..cb94f8b4a 100644 --- a/src/states_screens/grand_prix_cutscene.hpp +++ b/src/states_screens/grand_prix_cutscene.hpp @@ -35,7 +35,7 @@ protected: void saveGPButton(); /** implement callback from INewGPListener */ - void onNewGPWithName(const irr::core::stringw& name); + void onNewGPWithName(const irr::core::stringw& name) OVERRIDE; // implement callbacks from parent class GUIEngine::Screen void eventCallback(GUIEngine::Widget* widget, diff --git a/src/states_screens/grand_prix_editor_screen.hpp b/src/states_screens/grand_prix_editor_screen.hpp index 9999092c6..15ab93754 100644 --- a/src/states_screens/grand_prix_editor_screen.hpp +++ b/src/states_screens/grand_prix_editor_screen.hpp @@ -48,8 +48,8 @@ class GrandPrixEditorScreen : void showEditScreen(GrandPrixData* gp); void enableButtons(); - void onNewGPWithName(const irr::core::stringw& newName); - void onConfirm(); + void onNewGPWithName(const irr::core::stringw& newName) OVERRIDE; + void onConfirm() OVERRIDE; static const core::stringw getGroupName(enum GrandPrixData::GPGroupType group); diff --git a/src/states_screens/grand_prix_win.cpp b/src/states_screens/grand_prix_win.cpp index df01b25b9..a7a86a1fa 100644 --- a/src/states_screens/grand_prix_win.cpp +++ b/src/states_screens/grand_prix_win.cpp @@ -62,7 +62,7 @@ const float KARTS_DELTA_Y = -0.55f; const float KARTS_INITIAL_Z = -10.0f; const float KARTS_DEST_Z = -1.8f; const float INITIAL_Y = 0.0f; -const float INITIAL_PODIUM_Y = -1.27f; +const float INITIAL_PODIUM_Y = -1.33f; const float PODIUM_HEIGHT[3] = { 0.650f, 1.0f, 0.30f }; DEFINE_SCREEN_SINGLETON( GrandPrixWin ); @@ -231,6 +231,7 @@ void GrandPrixWin::onUpdate(float dt) m_kart_node[k]->move(kart_pos, kart_rot, kart_scale, false, true); core::vector3df podium_pos = m_podium_steps[k]->getInitXYZ(); + podium_pos.Y = INITIAL_PODIUM_Y; core::vector3df podium_rot(0, m_kart_rotation[k], 0); m_podium_steps[k]->move(podium_pos, podium_rot, core::vector3df(1.0f, 1.0f, 1.0f), false, true); @@ -342,6 +343,13 @@ void GrandPrixWin::setKarts(const std::string idents_arg[3]) } } + for (int k=0; k<3; k++) + { + core::vector3df podium_pos = m_podium_steps[k]->getInitXYZ(); + podium_pos.Y = INITIAL_PODIUM_Y; + m_podium_steps[k]->move(podium_pos, core::vector3df(0, 0, 0), core::vector3df(1.0f, 1.0f, 1.0f), false, true); + } + assert(m_podium_steps[0] != NULL); assert(m_podium_steps[1] != NULL); assert(m_podium_steps[2] != NULL); diff --git a/src/states_screens/main_menu_screen.cpp b/src/states_screens/main_menu_screen.cpp index 0bc4f0e9b..9c9c12b14 100644 --- a/src/states_screens/main_menu_screen.cpp +++ b/src/states_screens/main_menu_screen.cpp @@ -44,7 +44,7 @@ #include "states_screens/help_screen_1.hpp" #include "states_screens/offline_kart_selection.hpp" #include "states_screens/online_profile_achievements.hpp" -#include "states_screens/online_screen.hpp" +#include "states_screens/online_profile_servers.hpp" #include "states_screens/options_screen_video.hpp" #include "states_screens/state_manager.hpp" #include "states_screens/user_screen.hpp" @@ -245,7 +245,7 @@ void MainMenuScreen::eventCallback(Widget* widget, const std::string& name, race_manager->setMinorMode(RaceManager::MINOR_MODE_CUTSCENE); race_manager->setNumKarts( 0 ); race_manager->setNumPlayers(0); - race_manager->setNumLocalPlayers(0); + race_manager->setNumPlayers(0); race_manager->startSingleRace("endcutscene", 999, false); std::vector parts; @@ -268,7 +268,7 @@ void MainMenuScreen::eventCallback(Widget* widget, const std::string& name, race_manager->setMinorMode(RaceManager::MINOR_MODE_CUTSCENE); race_manager->setNumKarts(0); race_manager->setNumPlayers(0); - race_manager->setNumLocalPlayers(0); + race_manager->setNumPlayers(0); race_manager->startSingleRace("gpwin", 999, false); GrandPrixWin* scene = GrandPrixWin::getInstance(); scene->push(); @@ -281,7 +281,7 @@ void MainMenuScreen::eventCallback(Widget* widget, const std::string& name, race_manager->setMinorMode(RaceManager::MINOR_MODE_CUTSCENE); race_manager->setNumKarts(0); race_manager->setNumPlayers(0); - race_manager->setNumLocalPlayers(0); + race_manager->setNumPlayers(0); race_manager->startSingleRace("gplose", 999, false); GrandPrixLose* scene = GrandPrixLose::getInstance(); scene->push(); @@ -302,7 +302,7 @@ void MainMenuScreen::eventCallback(Widget* widget, const std::string& name, race_manager->setMinorMode(RaceManager::MINOR_MODE_CUTSCENE); race_manager->setNumKarts(0); race_manager->setNumPlayers(0); - race_manager->setNumLocalPlayers(0); + race_manager->setNumPlayers(0); race_manager->startSingleRace("featunlocked", 999, false); FeatureUnlockedCutScene* scene = @@ -355,7 +355,7 @@ void MainMenuScreen::eventCallback(Widget* widget, const std::string& name, race_manager->setMinorMode(RaceManager::MINOR_MODE_CUTSCENE); race_manager->setNumKarts(0); race_manager->setNumPlayers(0); - race_manager->setNumLocalPlayers(0); + race_manager->setNumPlayers(0); race_manager->startSingleRace("introcutscene", 999, false); std::vector parts; @@ -372,7 +372,7 @@ void MainMenuScreen::eventCallback(Widget* widget, const std::string& name, race_manager->setMinorMode(RaceManager::MINOR_MODE_CUTSCENE); race_manager->setNumKarts(0); race_manager->setNumPlayers(0); - race_manager->setNumLocalPlayers(0); + race_manager->setNumPlayers(0); race_manager->startSingleRace("endcutscene", 999, false); std::vector parts; @@ -490,15 +490,11 @@ void MainMenuScreen::eventCallback(Widget* widget, const std::string& name, "\"Connect to the Internet\".")); return; } + if (PlayerManager::getCurrentOnlineId()) { - // For 0.8.2 disable the server menu, instead go to online profile - #ifdef ENABLE_NETWORK_MULTIPLAYER_SCREEN - OnlineScreen::getInstance()->push(); - #else ProfileManager::get()->setVisiting(PlayerManager::getCurrentOnlineId()); - TabOnlineProfileAchievements::getInstance()->push(); - #endif + OnlineProfileServers::getInstance()->push(); } else { diff --git a/src/states_screens/network_kart_selection.hpp b/src/states_screens/network_kart_selection.hpp index ec2ea21c4..8c7777e56 100644 --- a/src/states_screens/network_kart_selection.hpp +++ b/src/states_screens/network_kart_selection.hpp @@ -32,7 +32,7 @@ protected: NetworkKartSelectionScreen(); virtual ~NetworkKartSelectionScreen(); - virtual void playerConfirm(const int playerID); + virtual void playerConfirm(const int playerID) OVERRIDE; public: virtual void init() OVERRIDE; virtual bool onEscapePressed() OVERRIDE; diff --git a/src/states_screens/networking_lobby.cpp b/src/states_screens/networking_lobby.cpp index b8ce415c7..dfb652b4a 100644 --- a/src/states_screens/networking_lobby.cpp +++ b/src/states_screens/networking_lobby.cpp @@ -23,6 +23,7 @@ #include #include "challenges/unlock_manager.hpp" +#include "config/player_manager.hpp" #include "graphics/irr_driver.hpp" #include "guiengine/scalable_font.hpp" #include "guiengine/widgets/icon_button_widget.hpp" @@ -37,7 +38,6 @@ #include "network/protocols/client_lobby_room_protocol.hpp" #include "network/servers_manager.hpp" #include "network/stk_host.hpp" -#include "states_screens/online_screen.hpp" #include "states_screens/state_manager.hpp" #include "states_screens/dialogs/message_dialog.hpp" #include "utils/translation.hpp" diff --git a/src/states_screens/online_profile_achievements.cpp b/src/states_screens/online_profile_achievements.cpp index 7e660ca5f..9bfdfeeb7 100644 --- a/src/states_screens/online_profile_achievements.cpp +++ b/src/states_screens/online_profile_achievements.cpp @@ -87,8 +87,11 @@ void BaseOnlineProfileAchievements::init() { OnlineProfileBase::init(); if (m_profile_tabs) + { m_profile_tabs->select(m_achievements_tab->m_properties[PROP_ID], - PLAYER_ID_GAME_MASTER); + PLAYER_ID_GAME_MASTER); + m_profile_tabs->setFocusForPlayer(PLAYER_ID_GAME_MASTER); + } // For current user add the progrss information. // m_visiting_profile is NULL if the user is not logged in. diff --git a/src/states_screens/online_profile_base.cpp b/src/states_screens/online_profile_base.cpp index 605c09984..ffd5f4176 100644 --- a/src/states_screens/online_profile_base.cpp +++ b/src/states_screens/online_profile_base.cpp @@ -27,6 +27,7 @@ #include "utils/translation.hpp" #include "states_screens/online_profile_friends.hpp" #include "states_screens/online_profile_achievements.hpp" +#include "states_screens/online_profile_servers.hpp" #include "states_screens/online_profile_settings.hpp" #include @@ -41,6 +42,10 @@ using namespace Online; OnlineProfileBase::OnlineProfileBase(const std::string &filename) : Screen(filename.c_str()) { + m_servers_tab = NULL; + m_friends_tab = NULL; + m_achievements_tab = NULL; + m_settings_tab = NULL; } // OnlineProfileBase // ----------------------------------------------------------------------------- @@ -53,15 +58,20 @@ void OnlineProfileBase::loadedFromFile() m_header = getWidget("title"); assert(m_header != NULL); - m_friends_tab = !m_profile_tabs ? NULL : - (IconButtonWidget *) m_profile_tabs->findWidgetNamed("tab_friends"); - assert(m_profile_tabs == NULL || m_friends_tab != NULL); - m_achievements_tab = !m_profile_tabs ? NULL : - (IconButtonWidget*)m_profile_tabs->findWidgetNamed("tab_achievements"); - assert(m_profile_tabs == NULL || m_achievements_tab != NULL); - m_settings_tab = !m_profile_tabs ? NULL : - (IconButtonWidget *) m_profile_tabs->findWidgetNamed("tab_settings"); - assert(m_profile_tabs == NULL || m_settings_tab != NULL); + if (m_profile_tabs != NULL) + { + m_friends_tab = (IconButtonWidget *)m_profile_tabs->findWidgetNamed("tab_friends"); + assert(m_friends_tab != NULL); + + m_servers_tab = (IconButtonWidget *)m_profile_tabs->findWidgetNamed("tab_servers"); + assert(m_servers_tab != NULL); + + m_achievements_tab = (IconButtonWidget*)m_profile_tabs->findWidgetNamed("tab_achievements"); + assert(m_profile_tabs == NULL || m_achievements_tab != NULL); + + m_settings_tab = (IconButtonWidget *)m_profile_tabs->findWidgetNamed("tab_settings"); + assert(m_settings_tab != NULL); + } } // loadedFromFile // ----------------------------------------------------------------------------- @@ -96,6 +106,7 @@ void OnlineProfileBase::init() if (m_profile_tabs) { + m_servers_tab->setTooltip(_("Servers")); m_friends_tab->setTooltip(_("Friends")); m_achievements_tab->setTooltip(_("Achievements")); m_settings_tab->setTooltip(_("Account Settings")); @@ -147,6 +158,8 @@ void OnlineProfileBase::eventCallback(Widget* widget, const std::string& name, sm->replaceTopMostScreen(TabOnlineProfileAchievements::getInstance()); else if (selection == m_settings_tab->m_properties[PROP_ID]) sm->replaceTopMostScreen(OnlineProfileSettings::getInstance()); + else if (selection == m_servers_tab->m_properties[PROP_ID]) + sm->replaceTopMostScreen(OnlineProfileServers::getInstance()); } else if (name == "back") { diff --git a/src/states_screens/online_profile_base.hpp b/src/states_screens/online_profile_base.hpp index 0285edb4b..95a6906fa 100644 --- a/src/states_screens/online_profile_base.hpp +++ b/src/states_screens/online_profile_base.hpp @@ -42,6 +42,7 @@ protected: /** Pointer to the various widgets on the screen. */ GUIEngine::LabelWidget * m_header; GUIEngine::RibbonWidget* m_profile_tabs; + GUIEngine::IconButtonWidget * m_servers_tab; GUIEngine::IconButtonWidget * m_friends_tab; GUIEngine::IconButtonWidget * m_achievements_tab; GUIEngine::IconButtonWidget * m_settings_tab; diff --git a/src/states_screens/online_profile_friends.cpp b/src/states_screens/online_profile_friends.cpp index 7b534595d..593b974a3 100644 --- a/src/states_screens/online_profile_friends.cpp +++ b/src/states_screens/online_profile_friends.cpp @@ -87,6 +87,7 @@ void OnlineProfileFriends::init() m_sort_increasing = true; m_profile_tabs->select( m_friends_tab->m_properties[PROP_ID], PLAYER_ID_GAME_MASTER ); + m_profile_tabs->setFocusForPlayer(PLAYER_ID_GAME_MASTER); assert(m_visiting_profile != NULL); m_visiting_profile->fetchFriends(); m_waiting_for_friends = true; diff --git a/src/states_screens/online_profile_friends.hpp b/src/states_screens/online_profile_friends.hpp index acd55c1ba..6aa7d7b97 100644 --- a/src/states_screens/online_profile_friends.hpp +++ b/src/states_screens/online_profile_friends.hpp @@ -72,7 +72,7 @@ public: virtual void onUpdate(float delta) OVERRIDE; virtual void beforeAddingWidget() OVERRIDE; - virtual void onColumnClicked(int columnId); + virtual void onColumnClicked(int columnId) OVERRIDE; // ------------------------------------------------------------------------ /** Triggers a reload of the friend list next time this menu is shown. */ diff --git a/src/states_screens/online_profile_servers.cpp b/src/states_screens/online_profile_servers.cpp new file mode 100644 index 000000000..61235f1b0 --- /dev/null +++ b/src/states_screens/online_profile_servers.cpp @@ -0,0 +1,170 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2010-2015 Glenn De Jonghe +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "states_screens/online_profile_servers.hpp" + +#include "audio/sfx_manager.hpp" +#include "config/player_manager.hpp" +#include "guiengine/engine.hpp" +#include "guiengine/scalable_font.hpp" +#include "guiengine/screen.hpp" +#include "guiengine/widget.hpp" +#include "network/network_config.hpp" +#include "network/protocol_manager.hpp" +#include "network/protocols/connect_to_server.hpp" +#include "network/protocols/request_connection.hpp" +#include "network/servers_manager.hpp" +#include "states_screens/state_manager.hpp" +#include "states_screens/create_server_screen.hpp" +#include "states_screens/networking_lobby.hpp" +#include "states_screens/server_selection.hpp" +#include "utils/translation.hpp" + +#include + +#include +#include + +using namespace GUIEngine; +using namespace irr::core; +using namespace irr::gui; +using namespace Online; + +DEFINE_SCREEN_SINGLETON( OnlineProfileServers ); + +// ----------------------------------------------------------------------------- + +OnlineProfileServers::OnlineProfileServers() : OnlineProfileBase("online/profile_servers.stkgui") +{ +} // OnlineProfileServers + +// ----------------------------------------------------------------------------- + +void OnlineProfileServers::loadedFromFile() +{ + OnlineProfileBase::loadedFromFile(); +} // loadedFromFile + +// ----------------------------------------------------------------------------- + +void OnlineProfileServers::init() +{ + OnlineProfileBase::init(); + m_profile_tabs->select( m_servers_tab->m_properties[PROP_ID], PLAYER_ID_GAME_MASTER ); + m_servers_tab->setFocusForPlayer(PLAYER_ID_GAME_MASTER); + // OnlineScreen::getInstance()->push(); +} // init + +// ----------------------------------------------------------------------------- + +void OnlineProfileServers::eventCallback(Widget* widget, const std::string& name, const int playerID) +{ + OnlineProfileBase::eventCallback( widget, name, playerID); + + if (name == "lan") + { + RibbonWidget* ribbon = dynamic_cast(widget); + std::string selection = ribbon->getSelectionIDString(PLAYER_ID_GAME_MASTER); + if (selection == "create_lan_server") + { + NetworkConfig::get()->setIsLAN(); + NetworkConfig::get()->setIsServer(true); + CreateServerScreen::getInstance()->push(); + // TODO: create lan server + } + else if (selection == "find_lan_server") + { + NetworkConfig::get()->setIsLAN(); + NetworkConfig::get()->setIsServer(false); + ServerSelection::getInstance()->push(); + } + } + else if (name == "wan") + { + RibbonWidget* ribbon = dynamic_cast(widget); + std::string selection = ribbon->getSelectionIDString(PLAYER_ID_GAME_MASTER); + if (selection == "find_wan_server") + { + NetworkConfig::get()->setIsWAN(); + NetworkConfig::get()->setIsServer(false); + ServerSelection::getInstance()->push(); + } + else if (selection == "create_wan_server") + { + NetworkConfig::get()->setIsWAN(); + NetworkConfig::get()->setIsServer(true); + CreateServerScreen::getInstance()->push(); + } + else if (selection == "quick_wan_play") + { + doQuickPlay(); + } + } + + +} // eventCallback + +// ---------------------------------------------------------------------------- + +void OnlineProfileServers::doQuickPlay() +{ + // Refresh server list. + HTTPRequest* refresh_request = ServersManager::get()->getRefreshRequest(false); + if (refresh_request != NULL) // consider request done + { + refresh_request->executeNow(); + delete refresh_request; + } + else + { + Log::error("OnlineScreen", "Could not get the server list."); + return; + } + + // select first one + const Server *server = ServersManager::get()->getQuickPlay(); + + // do a join request + XMLRequest *join_request = new RequestConnection::ServerJoinRequest(); + if (!join_request) + { + SFXManager::get()->quickSound("anvil"); + return; + } + + PlayerManager::setUserDetails(join_request, "request-connection", + Online::API::SERVER_PATH); + join_request->addParameter("server_id", server->getServerId()); + + join_request->executeNow(); + if (join_request->isSuccess()) + { + delete join_request; + NetworkingLobby::getInstance()->push(); + ConnectToServer *cts = new ConnectToServer(server->getServerId(), + server->getHostId()); + ProtocolManager::getInstance()->requestStart(cts); + } + else + { + SFXManager::get()->quickSound("anvil"); + } + +} // doQuickPlay + +// ---------------------------------------------------------------------------- + diff --git a/src/states_screens/online_profile_servers.hpp b/src/states_screens/online_profile_servers.hpp new file mode 100644 index 000000000..26816e6de --- /dev/null +++ b/src/states_screens/online_profile_servers.hpp @@ -0,0 +1,57 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013-2015 Glenn De Jonghe +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + +#ifndef __HEADER_ONLINE_PROFILE_SERVERS_HPP__ +#define __HEADER_ONLINE_PROFILE_SERVERS_HPP__ + +#include +#include + +#include "guiengine/screen.hpp" +#include "guiengine/widgets.hpp" +#include "states_screens/online_profile_base.hpp" + +namespace GUIEngine { class Widget; } + + +/** + * \brief Online profiel overview screen + * \ingroup states_screens + */ +class OnlineProfileServers : public OnlineProfileBase, public GUIEngine::ScreenSingleton +{ +protected: + OnlineProfileServers(); + + void doQuickPlay(); + +public: + friend class GUIEngine::ScreenSingleton; + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void loadedFromFile() OVERRIDE; + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void eventCallback(GUIEngine::Widget* widget, const std::string& name, + const int playerID) OVERRIDE; + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void init() OVERRIDE; +}; + +#endif diff --git a/src/states_screens/online_profile_settings.cpp b/src/states_screens/online_profile_settings.cpp index 1231b2c16..98c83129c 100644 --- a/src/states_screens/online_profile_settings.cpp +++ b/src/states_screens/online_profile_settings.cpp @@ -58,6 +58,7 @@ void OnlineProfileSettings::init() { OnlineProfileBase::init(); m_profile_tabs->select( m_settings_tab->m_properties[PROP_ID], PLAYER_ID_GAME_MASTER ); + m_settings_tab->setFocusForPlayer(PLAYER_ID_GAME_MASTER); } // init // ----------------------------------------------------------------------------- diff --git a/src/states_screens/online_screen.cpp b/src/states_screens/online_screen.cpp deleted file mode 100644 index 2aeb35aad..000000000 --- a/src/states_screens/online_screen.cpp +++ /dev/null @@ -1,315 +0,0 @@ -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2013-2015 Glenn De Jonghe -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 3 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#define DEBUG_MENU_ITEM 0 - -#include "states_screens/online_screen.hpp" - -#include "audio/sfx_manager.hpp" -#include "config/player_manager.hpp" -#include "graphics/irr_driver.hpp" -#include "guiengine/scalable_font.hpp" -#include "input/device_manager.hpp" -#include "input/input_manager.hpp" -#include "io/file_manager.hpp" -#include "modes/demo_world.hpp" -#include "network/network_config.hpp" -#include "network/protocol_manager.hpp" -#include "network/protocols/connect_to_server.hpp" -#include "network/protocols/request_connection.hpp" -#include "network/servers_manager.hpp" -#include "online/profile_manager.hpp" -#include "online/request.hpp" -#include "states_screens/create_server_screen.hpp" -#include "states_screens/dialogs/message_dialog.hpp" -#include "states_screens/networking_lobby.hpp" -#include "states_screens/online_profile_achievements.hpp" -#include "states_screens/server_selection.hpp" -#include "states_screens/state_manager.hpp" -#include "states_screens/user_screen.hpp" - -#include -#include - -using namespace GUIEngine; -using namespace Online; - - -DEFINE_SCREEN_SINGLETON( OnlineScreen ); - -// ---------------------------------------------------------------------------- - -OnlineScreen::OnlineScreen() : Screen("online/online_screen.stkgui") -{ - m_recorded_state = PlayerProfile::OS_SIGNED_OUT; -} // OnlineScreen - -// ---------------------------------------------------------------------------- - -OnlineScreen::~OnlineScreen() -{ -} - -// ---------------------------------------------------------------------------- - -void OnlineScreen::loadedFromFile() -{ - m_user_id = getWidget("user-id"); - assert(m_user_id); - - m_back_widget = getWidget("back"); - assert(m_back_widget != NULL); - - m_top_menu_widget = getWidget("menu_top_row"); - assert(m_top_menu_widget != NULL); - - m_find_lan_server_widget = getWidget("find_lan_server"); - assert(m_find_lan_server_widget != NULL); - m_create_lan_server_widget = getWidget("create_lan_server"); - assert(m_create_lan_server_widget != NULL); - m_manage_user = getWidget("manage_user"); - assert(m_manage_user); - - m_find_wan_server_widget = getWidget("find_wan_server"); - assert(m_find_wan_server_widget != NULL); - m_create_wan_server_widget = getWidget("create_wan_server"); - assert(m_create_wan_server_widget != NULL); - m_quick_wan_play_widget = getWidget("quick_wan_play"); - assert(m_quick_wan_play_widget != NULL); - - m_bottom_menu_widget = getWidget("menu_bottomrow"); - assert(m_bottom_menu_widget != NULL); - m_profile_widget = getWidget("profile"); - assert(m_profile_widget != NULL); - m_sign_out_widget = getWidget("sign_out"); - assert(m_sign_out_widget != NULL); - -} // loadedFromFile - -// ---------------------------------------------------------------------------- -/** Checks if the recorded state differs from the actual state and sets it. - */ -bool OnlineScreen::hasStateChanged() -{ - PlayerProfile::OnlineState previous_state = m_recorded_state; - m_recorded_state = PlayerManager::getCurrentOnlineState(); - if (previous_state != m_recorded_state) - return true; - return false; -} // hasStateChanged - -// ---------------------------------------------------------------------------- -void OnlineScreen::beforeAddingWidget() -{ - // Set everything that could be set invisible or deactivated - // to active and visible - m_bottom_menu_widget->setVisible(true); - m_top_menu_widget->setVisible(true); - hasStateChanged(); - if (m_recorded_state == PlayerProfile::OS_SIGNED_OUT || - m_recorded_state == PlayerProfile::OS_SIGNING_IN || - m_recorded_state == PlayerProfile::OS_SIGNING_OUT) - { - m_quick_wan_play_widget->setActive(false); - m_find_wan_server_widget->setActive(false); - m_create_wan_server_widget->setActive(false); - m_sign_out_widget->setVisible(false); - m_profile_widget->setVisible(false); - } - else if (m_recorded_state == PlayerProfile::OS_GUEST) - { - m_find_wan_server_widget->setActive(false); - m_create_wan_server_widget->setActive(false); - m_profile_widget->setVisible(false); - } - else - { - m_quick_wan_play_widget->setActive(true); - m_find_wan_server_widget->setActive(true); - m_create_wan_server_widget->setActive(true); - m_sign_out_widget->setVisible(true); - m_profile_widget->setVisible(true); - } - -} // beforeAddingWidget - -// ---------------------------------------------------------------------------- -void OnlineScreen::init() -{ - Screen::init(); - setInitialFocus(); - DemoWorld::resetIdleTime(); - -} // init - -// ---------------------------------------------------------------------------- -void OnlineScreen::onUpdate(float delta) -{ - PlayerProfile *player = PlayerManager::getCurrentPlayer(); - if (PlayerManager::getCurrentOnlineState() == PlayerProfile::OS_SIGNED_IN) - { - m_user_id->setText(player->getLastOnlineName() + "@stk"); - } - else - { - m_user_id->setText(player->getName()); - } - - if (hasStateChanged()) - { - GUIEngine::reshowCurrentScreen(); - return; - } - -} // onUpdate - -// ---------------------------------------------------------------------------- -/** Executes the quick play selection. Atm this is all blocking. - */ -void OnlineScreen::doQuickPlay() -{ - // Refresh server list. - HTTPRequest* refresh_request = ServersManager::get()->getRefreshRequest(false); - if (refresh_request != NULL) // consider request done - { - refresh_request->executeNow(); - delete refresh_request; - } - else - { - Log::error("OnlineScreen", "Could not get the server list."); - return; - } - - // select first one - const Server *server = ServersManager::get()->getQuickPlay(); - - // do a join request - XMLRequest *join_request = new RequestConnection::ServerJoinRequest(); - if (!join_request) - { - SFXManager::get()->quickSound("anvil"); - return; - } - - PlayerManager::setUserDetails(join_request, "request-connection", - Online::API::SERVER_PATH); - join_request->addParameter("server_id", server->getServerId()); - - join_request->executeNow(); - if (join_request->isSuccess()) - { - delete join_request; - NetworkingLobby::getInstance()->push(); - ConnectToServer *cts = new ConnectToServer(server->getServerId(), - server->getHostId()); - ProtocolManager::getInstance()->requestStart(cts); - } - else - { - SFXManager::get()->quickSound("anvil"); - } - -} // doQuickPlay - -// ---------------------------------------------------------------------------- - -void OnlineScreen::eventCallback(Widget* widget, const std::string& name, - const int playerID) -{ - if (name == m_back_widget->m_properties[PROP_ID]) - { - StateManager::get()->escapePressed(); - return; - } - - RibbonWidget* ribbon = dynamic_cast(widget); - if (ribbon == NULL) return; - std::string selection = ribbon->getSelectionIDString(PLAYER_ID_GAME_MASTER); - - if (selection == m_sign_out_widget->m_properties[PROP_ID]) - { - PlayerManager::requestSignOut(); - StateManager::get()->popMenu(); - } - else if (selection == m_create_lan_server_widget->m_properties[PROP_ID]) - { - NetworkConfig::get()->setIsLAN(); - NetworkConfig::get()->setIsServer(true); - CreateServerScreen::getInstance()->push(); - // TODO: create lan server - } - else if (selection == m_find_lan_server_widget->m_properties[PROP_ID]) - { - NetworkConfig::get()->setIsLAN(); - NetworkConfig::get()->setIsServer(false); - ServerSelection::getInstance()->push(); - } - else if (selection == m_manage_user->m_properties[PROP_ID]) - { - UserScreen::getInstance()->push(); - } - else if (selection == m_profile_widget->m_properties[PROP_ID]) - { - ProfileManager::get()->setVisiting(PlayerManager::getCurrentOnlineId()); - OnlineProfileAchievements::getInstance()->push(); - } - else if (selection == m_find_wan_server_widget->m_properties[PROP_ID]) - { - NetworkConfig::get()->setIsWAN(); - NetworkConfig::get()->setIsServer(false); - ServerSelection::getInstance()->push(); - } - else if (selection == m_create_wan_server_widget->m_properties[PROP_ID]) - { - NetworkConfig::get()->setIsWAN(); - NetworkConfig::get()->setIsServer(true); - CreateServerScreen::getInstance()->push(); - } - else if (selection == m_quick_wan_play_widget->m_properties[PROP_ID]) - { - doQuickPlay(); - } - -} // eventCallback - -// ---------------------------------------------------------------------------- -void OnlineScreen::tearDown() -{ -} - -// ---------------------------------------------------------------------------- -/** Sets which widget has to be focused. Depends on the user state. - */ -void OnlineScreen::setInitialFocus() -{ - if (m_recorded_state == PlayerProfile::OS_SIGNED_IN) - m_top_menu_widget->setFocusForPlayer(PLAYER_ID_GAME_MASTER); - else - m_bottom_menu_widget->setFocusForPlayer(PLAYER_ID_GAME_MASTER); - -} // setInitialFocus - -// ---------------------------------------------------------------------------- -void OnlineScreen::onDialogClose() -{ - if (hasStateChanged()) - GUIEngine::reshowCurrentScreen(); - else - setInitialFocus(); -} // onDialogClose() - diff --git a/src/states_screens/online_screen.hpp b/src/states_screens/online_screen.hpp deleted file mode 100644 index b306baa23..000000000 --- a/src/states_screens/online_screen.hpp +++ /dev/null @@ -1,92 +0,0 @@ -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2013-2015 Glenn De Jonghe -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 3 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#ifndef HEADER_ONLINE_SCREEN_HPP -#define HEADER_ONLINE_SCREEN_HPP - -#include "config/player_manager.hpp" -#include "guiengine/screen.hpp" -#include "guiengine/widgets/label_widget.hpp" -#include "guiengine/widgets/ribbon_widget.hpp" -#include "guiengine/widgets/icon_button_widget.hpp" -#include "utils/ptr_vector.hpp" - -namespace GUIEngine { class Widget; class ListWidget; class ButtonWidget; } - -/** - * \brief Handles the main menu - * \ingroup states_screens - */ -class OnlineScreen : public GUIEngine::Screen, - public GUIEngine::ScreenSingleton -{ -private: - friend class GUIEngine::ScreenSingleton; - - OnlineScreen(); - ~OnlineScreen(); - - GUIEngine::IconButtonWidget *m_back_widget; - - GUIEngine::RibbonWidget *m_top_menu_widget; - GUIEngine::IconButtonWidget *m_find_lan_server_widget; - GUIEngine::IconButtonWidget *m_create_lan_server_widget; - GUIEngine::IconButtonWidget *m_manage_user; - - GUIEngine::IconButtonWidget *m_find_wan_server_widget; - GUIEngine::IconButtonWidget *m_create_wan_server_widget; - GUIEngine::IconButtonWidget *m_quick_wan_play_widget; - - GUIEngine::RibbonWidget *m_bottom_menu_widget; - GUIEngine::IconButtonWidget *m_register_widget; - GUIEngine::IconButtonWidget *m_profile_widget; - GUIEngine::IconButtonWidget *m_sign_out_widget; - - /** Keep the widget to to the user name. */ - GUIEngine::ButtonWidget *m_user_id; - - PlayerProfile::OnlineState m_recorded_state; - - bool hasStateChanged(); - void setInitialFocus(); - - void doQuickPlay(); -public: - - virtual void onUpdate(float delta) OVERRIDE; - - /** \brief implement callback from parent class GUIEngine::Screen */ - virtual void loadedFromFile() OVERRIDE; - - /** \brief implement callback from parent class GUIEngine::Screen */ - virtual void eventCallback(GUIEngine::Widget* widget, const std::string& name, - const int playerID) OVERRIDE; - - /** \brief implement callback from parent class GUIEngine::Screen */ - virtual void beforeAddingWidget() OVERRIDE; - - /** \brief implement callback from parent class GUIEngine::Screen */ - virtual void init() OVERRIDE; - - /** \brief implement callback from parent class GUIEngine::Screen */ - virtual void tearDown() OVERRIDE; - - /** \brief Implements the callback when a dialog gets closed. */ - virtual void onDialogClose() OVERRIDE; -}; - -#endif diff --git a/src/states_screens/options_screen_ui.cpp b/src/states_screens/options_screen_ui.cpp index 7b6c76315..0e37693cb 100644 --- a/src/states_screens/options_screen_ui.cpp +++ b/src/states_screens/options_screen_ui.cpp @@ -152,7 +152,7 @@ void OptionsScreenUI::init() CheckBoxWidget* difficulty = getWidget("perPlayerDifficulty"); assert( difficulty != NULL ); difficulty->setState( UserConfigParams::m_per_player_difficulty ); - difficulty->setTooltip(_("Players can select handicapped (more difficult) profiles on the kart selection screen")); + difficulty->setTooltip(_("In multiplayer mode, players can select handicapped (more difficult) profiles on the kart selection screen")); CheckBoxWidget* show_login = getWidget("show-login"); assert( show_login!= NULL ); @@ -197,8 +197,9 @@ void OptionsScreenUI::init() for (int n=0; nfribidize(StringUtils::utf8ToWide(s_name)); nice_lang_list.push_back(nice_name); nice_name_2_id[nice_name] = code_name; } diff --git a/src/states_screens/race_result_gui.cpp b/src/states_screens/race_result_gui.cpp index 630aa7e55..c751e3335 100644 --- a/src/states_screens/race_result_gui.cpp +++ b/src/states_screens/race_result_gui.cpp @@ -48,7 +48,7 @@ #include "states_screens/main_menu_screen.hpp" #include "states_screens/networking_lobby.hpp" #include "states_screens/network_kart_selection.hpp" -#include "states_screens/online_screen.hpp" +#include "states_screens/online_profile_servers.hpp" #include "states_screens/race_setup_screen.hpp" #include "states_screens/server_selection.hpp" #include "tracks/track.hpp" @@ -339,7 +339,7 @@ void RaceResultGUI::eventCallback(GUIEngine::Widget* widget, race_manager->exitRace(); race_manager->setAIKartOverride(""); Screen* newStack[] = {MainMenuScreen::getInstance(), - OnlineScreen::getInstance(), + OnlineProfileServers::getInstance(), ServerSelection::getInstance(), NetworkingLobby::getInstance(), NULL}; diff --git a/src/states_screens/race_result_gui.hpp b/src/states_screens/race_result_gui.hpp index 8b6a4e37e..cef78b92e 100644 --- a/src/states_screens/race_result_gui.hpp +++ b/src/states_screens/race_result_gui.hpp @@ -200,10 +200,10 @@ private: public: RaceResultGUI(); - virtual void renderGlobal(float dt); + virtual void renderGlobal(float dt) OVERRIDE; /** \brief Implement callback from parent class GUIEngine::Screen */ - virtual void loadedFromFile() {}; + virtual void loadedFromFile() OVERRIDE {}; virtual void init() OVERRIDE; virtual void tearDown() OVERRIDE; @@ -218,11 +218,11 @@ public: friend class GUIEngine::ScreenSingleton; /** Should not be called anymore. */ - const core::dimension2du getMiniMapSize() const + const core::dimension2du getMiniMapSize() const OVERRIDE { assert(false); return core::dimension2du(0, 0); } /** No kart specific view needs to be rendered in the result gui. */ - virtual void renderPlayerView(const Camera *camera, float dt) {} + virtual void renderPlayerView(const Camera *camera, float dt) OVERRIDE {} virtual void onUpdate(float dt) OVERRIDE; @@ -236,7 +236,7 @@ public: const video::SColor &color= video::SColor(255, 255, 0, 255), bool important=true, - bool big_font=false) { } + bool big_font=false) OVERRIDE { } void nextPhase(); @@ -252,7 +252,7 @@ public: */ void setHighscore(int rank); - virtual void onConfirm(); + virtual void onConfirm() OVERRIDE; }; // RaceResultGUI #endif diff --git a/src/states_screens/race_setup_screen.cpp b/src/states_screens/race_setup_screen.cpp index eef8ffdb0..6d49ce8f0 100644 --- a/src/states_screens/race_setup_screen.cpp +++ b/src/states_screens/race_setup_screen.cpp @@ -28,6 +28,7 @@ #include "race/race_manager.hpp" #include "states_screens/arenas_screen.hpp" #include "states_screens/easter_egg_screen.hpp" +#include "states_screens/ghost_replay_selection.hpp" #include "states_screens/soccer_setup_screen.hpp" #include "states_screens/state_manager.hpp" #include "states_screens/tracks_screen.hpp" @@ -39,6 +40,7 @@ const int CONFIG_CODE_FTL = 2; const int CONFIG_CODE_3STRIKES = 3; const int CONFIG_CODE_EASTER = 4; const int CONFIG_CODE_SOCCER = 5; +const int CONFIG_CODE_GHOST = 6; using namespace GUIEngine; DEFINE_SCREEN_SINGLETON( RaceSetupScreen ); @@ -109,12 +111,12 @@ void RaceSetupScreen::init() irr::core::stringw name4 = irr::core::stringw( RaceManager::getNameOf(RaceManager::MINOR_MODE_3_STRIKES)) + L"\n"; //FIXME: avoid duplicating descriptions from the help menu! - name4 += _("Hit others with weapons until they lose all their lives (only in multiplayer games)."); + name4 += _("Hit others with weapons until they lose all their lives."); w2->addItem( name4, IDENT_STRIKES, RaceManager::getIconOf(RaceManager::MINOR_MODE_3_STRIKES)); irr::core::stringw name5 = irr::core::stringw( RaceManager::getNameOf(RaceManager::MINOR_MODE_SOCCER)) + L"\n"; - name5 += _("Push the ball to the opposite cage to score goals (only in multiplayer games)."); + name5 += _("Push the ball to the opposite cage to score goals."); w2->addItem( name5, IDENT_SOCCER, RaceManager::getIconOf(RaceManager::MINOR_MODE_SOCCER)); #define ENABLE_EASTER_EGG_MODE @@ -131,6 +133,10 @@ void RaceSetupScreen::init() } #endif + irr::core::stringw name6 = irr::core::stringw( _("Ghost replay race")) + L"\n"; + name6 += _("Race against ghost karts and try to beat them!"); + w2->addItem( name6, IDENT_GHOST, "/gui/mode_ghost.png"); + w2->updateItemDisplay(); // restore saved game mode @@ -154,6 +160,9 @@ void RaceSetupScreen::init() case CONFIG_CODE_SOCCER : w2->setSelection(IDENT_SOCCER, PLAYER_ID_GAME_MASTER, true); break; + case CONFIG_CODE_GHOST : + w2->setSelection(IDENT_GHOST, PLAYER_ID_GAME_MASTER, true); + break; } { @@ -232,6 +241,12 @@ void RaceSetupScreen::eventCallback(Widget* widget, const std::string& name, UserConfigParams::m_game_mode = CONFIG_CODE_SOCCER; SoccerSetupScreen::getInstance()->push(); } + else if (selectedMode == IDENT_GHOST) + { + race_manager->setMinorMode(RaceManager::MINOR_MODE_TIME_TRIAL); + UserConfigParams::m_game_mode = CONFIG_CODE_GHOST; + GhostReplaySelection::getInstance()->push(); + } else if (selectedMode == "locked") { unlock_manager->playLockSound(); diff --git a/src/states_screens/register_screen.hpp b/src/states_screens/register_screen.hpp index edf935f6c..6eb500739 100644 --- a/src/states_screens/register_screen.hpp +++ b/src/states_screens/register_screen.hpp @@ -40,7 +40,7 @@ private: void makeEntryFieldsVisible(); void handleLocalName(const irr::core::stringw &local_name); void doRegister(); - void init(); + void init() OVERRIDE; RegisterScreen(); /** Save the pointer to the info widget, it is widely used. */ @@ -81,7 +81,7 @@ public: virtual void onDialogClose() OVERRIDE; virtual void onFocusChanged(GUIEngine::Widget *previous, GUIEngine::Widget *focus, - int playerID); + int playerID) OVERRIDE; void setRename(PlayerProfile *player); void acceptTerms(); diff --git a/src/states_screens/server_selection.cpp b/src/states_screens/server_selection.cpp index dbf2f89e6..c3fa90a3e 100644 --- a/src/states_screens/server_selection.cpp +++ b/src/states_screens/server_selection.cpp @@ -200,7 +200,7 @@ void ServerSelection::eventCallback( GUIEngine::Widget* widget, */ void ServerSelection::onUpdate(float dt) { - if(!m_refresh_request) return; + if (!m_refresh_request) return; if (m_refresh_request->isDone()) { diff --git a/src/states_screens/server_selection.hpp b/src/states_screens/server_selection.hpp index 1d3c9a1b0..40b92fac2 100644 --- a/src/states_screens/server_selection.hpp +++ b/src/states_screens/server_selection.hpp @@ -67,7 +67,7 @@ public: /** \brief implement callback from parent class GUIEngine::Screen */ virtual void beforeAddingWidget() OVERRIDE; - virtual void onColumnClicked(int columnId); + virtual void onColumnClicked(int columnId) OVERRIDE; virtual void init() OVERRIDE; diff --git a/src/states_screens/track_info_screen.cpp b/src/states_screens/track_info_screen.cpp index 34877139b..99f5c6c6d 100644 --- a/src/states_screens/track_info_screen.cpp +++ b/src/states_screens/track_info_screen.cpp @@ -68,7 +68,9 @@ void TrackInfoScreen::loadedFromFile() m_lap_spinner = getWidget("lap-spinner"); m_ai_kart_spinner = getWidget("ai-spinner"); m_reverse = getWidget("reverse"); + m_record_race = getWidget("record"); m_reverse->setState(false); + m_record_race->setState(false); m_highscore_label = getWidget("highscores"); @@ -209,6 +211,14 @@ void TrackInfoScreen::init() else m_reverse->setState(false); + // Record race or not + // ------------- + const bool record_available = race_manager->getMinorMode() == RaceManager::MINOR_MODE_TIME_TRIAL; + m_record_race->setVisible(record_available); + getWidget("record-race-text")->setVisible(record_available); + if (record_available) + m_record_race->setState(false); + // ---- High Scores m_highscore_label->setVisible(has_highscores); @@ -358,6 +368,23 @@ void TrackInfoScreen::eventCallback(Widget* widget, const std::string& name, // checkbox. updateHighScores(); } + else if (name == "record") + { + const bool record = m_record_race->getState(); + race_manager->setRecordRace(record); + m_ai_kart_spinner->setValue(0); + // Disable AI when recording ghost race + if (record) + { + m_ai_kart_spinner->setActive(false); + race_manager->setNumKarts(race_manager->getNumLocalPlayers()); + UserConfigParams::m_num_karts = race_manager->getNumLocalPlayers(); + } + else + { + m_ai_kart_spinner->setActive(true); + } + } else if (name == "lap-spinner") { assert(race_manager->modeHasLaps()); diff --git a/src/states_screens/track_info_screen.hpp b/src/states_screens/track_info_screen.hpp index b4583bdd9..b64157d2d 100644 --- a/src/states_screens/track_info_screen.hpp +++ b/src/states_screens/track_info_screen.hpp @@ -55,6 +55,9 @@ class TrackInfoScreen : public GUIEngine::Screen, /** Check box for reverse mode. */ GUIEngine::CheckBoxWidget* m_reverse; + /** Check box for record race. */ + GUIEngine::CheckBoxWidget* m_record_race; + /** The label of the highscore list. */ GUIEngine::LabelWidget* m_highscore_label; diff --git a/src/states_screens/tracks_screen.cpp b/src/states_screens/tracks_screen.cpp index 2ba4dda63..0353416e5 100644 --- a/src/states_screens/tracks_screen.cpp +++ b/src/states_screens/tracks_screen.cpp @@ -33,6 +33,7 @@ #include "states_screens/state_manager.hpp" #include "states_screens/track_info_screen.hpp" #include "states_screens/gp_info_screen.hpp" +#include "states_screens/waiting_for_others.hpp" #include "tracks/track.hpp" #include "tracks/track_manager.hpp" #include "utils/translation.hpp" @@ -96,7 +97,7 @@ void TracksScreen::eventCallback(Widget* widget, const std::string& name, ClientLobbyRoomProtocol* clrp = static_cast(protocol); clrp->voteTrack(selection); - + WaitingForOthersScreen::getInstance()->push(); } else { diff --git a/src/states_screens/user_screen.hpp b/src/states_screens/user_screen.hpp index 502a27596..7b98bdfb5 100644 --- a/src/states_screens/user_screen.hpp +++ b/src/states_screens/user_screen.hpp @@ -103,20 +103,20 @@ private: public: /** \brief implement callback from parent class GUIEngine::Screen */ - virtual void loadedFromFile(); + virtual void loadedFromFile() OVERRIDE; /** \brief implement callback from parent class GUIEngine::Screen */ virtual void eventCallback(GUIEngine::Widget* widget, - const std::string& name, const int playerID); + const std::string& name, const int playerID) OVERRIDE; /** \brief implement callback from parent class GUIEngine::Screen */ - virtual void init(); + virtual void init() OVERRIDE; /** \brief implement callback from parent class GUIEngine::Screen */ - virtual void tearDown(); + virtual void tearDown() OVERRIDE; /** \brief implement optional callback from parent class GUIEngine::Screen */ - virtual void unloaded(); + virtual void unloaded() OVERRIDE; void setNewAccountData(bool online, bool auto_login, const core::stringw &online_name="", @@ -149,9 +149,9 @@ private: public: friend class GUIEngine::ScreenSingleton; - virtual void init(); + virtual void init() OVERRIDE; virtual void eventCallback(GUIEngine::Widget* widget, - const std::string& name, const int playerID); + const std::string& name, const int playerID) OVERRIDE; }; // class TabbedUserScreen #endif diff --git a/src/states_screens/waiting_for_others.cpp b/src/states_screens/waiting_for_others.cpp new file mode 100644 index 000000000..3ea5eb2f2 --- /dev/null +++ b/src/states_screens/waiting_for_others.cpp @@ -0,0 +1,60 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2009-2015 Marianne Gagnon +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "states_screens/waiting_for_others.hpp" + +#include "config/user_config.hpp" +#include "guiengine/widget.hpp" +#include "guiengine/widgets/list_widget.hpp" +#include "guiengine/widgets/ribbon_widget.hpp" +#include "input/device_manager.hpp" +#include "input/input_manager.hpp" +#include "input/keyboard_device.hpp" +#include "karts/kart_properties_manager.hpp" +#include "race/race_manager.hpp" +#include "states_screens/state_manager.hpp" + +using namespace GUIEngine; + +DEFINE_SCREEN_SINGLETON( WaitingForOthersScreen ); + +// ----------------------------------------------------------------------------- + +WaitingForOthersScreen::WaitingForOthersScreen() : Screen("online/waiting_for_others.stkgui") +{ +} // WaitingForOthersScreen + +// ----------------------------------------------------------------------------- + +void WaitingForOthersScreen::loadedFromFile() +{ +} // loadedFromFile + +// ----------------------------------------------------------------------------- + +void WaitingForOthersScreen::eventCallback(Widget* widget, const std::string& name, const int playerID) +{ +} // eventCallback + +// ----------------------------------------------------------------------------- + +void WaitingForOthersScreen::init() +{ + Screen::init(); +} //init + +// ----------------------------------------------------------------------------- diff --git a/src/states_screens/waiting_for_others.hpp b/src/states_screens/waiting_for_others.hpp new file mode 100644 index 000000000..8d4131286 --- /dev/null +++ b/src/states_screens/waiting_for_others.hpp @@ -0,0 +1,47 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2009-2015 Marianne Gagnon +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef HEADER_WAITING_FOR_OTHERS_HPP +#define HEADER_WAITING_FOR_OTHERS_HPP + +#include "guiengine/screen.hpp" + +namespace GUIEngine { class Widget; } + +/** + * \brief Help screen, part 1 + * \ingroup states_screens + */ +class WaitingForOthersScreen : public GUIEngine::Screen, public GUIEngine::ScreenSingleton +{ + friend class GUIEngine::ScreenSingleton; + WaitingForOthersScreen(); + +public: + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void loadedFromFile() OVERRIDE; + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void eventCallback(GUIEngine::Widget* widget, const std::string& name, + const int playerID) OVERRIDE; + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void init() OVERRIDE; +}; + +#endif diff --git a/src/tinygettext/language.cpp b/src/tinygettext/language.cpp index aefed84b7..99024d9b8 100644 --- a/src/tinygettext/language.cpp +++ b/src/tinygettext/language.cpp @@ -38,251 +38,254 @@ struct LanguageSpec { /** Language name: "German", "English", "French", ... */ const char* name; + + /** Language name in the specified language */ + const char* name_localized; }; /** Language Definitions */ //*{ static const LanguageSpec languages[] = { - { "aa", 0, 0, "Afar" }, - { "af", 0, 0, "Afrikaans" }, - { "af", "ZA", 0, "Afrikaans (South Africa)" }, - { "am", 0, 0, "Amharic" }, - { "ar", 0, 0, "Arabic" }, - { "ar", "AR", 0, "Arabic (Argentina)" }, - { "ar", "OM", 0, "Arabic (Oman)" }, - { "ar", "SA", 0, "Arabic (Saudi Arabia)" }, - { "ar", "SY", 0, "Arabic (Syrian Arab Republic)" }, - { "ar", "TN", 0, "Arabic (Tunisia)" }, - { "as", 0, 0, "Assamese" }, - { "ast",0, 0, "Asturian" }, - { "ay", 0, 0, "Aymara" }, - { "az", 0, 0, "Azerbaijani" }, - { "az", "IR", 0, "Azerbaijani (Iran)" }, - { "be", 0, 0, "Belarusian" }, - { "be", 0, "latin", "Belarusian" }, - { "bg", 0, 0, "Bulgarian" }, - { "bg", "BG", 0, "Bulgarian (Bulgaria)" }, - { "bn", 0, 0, "Bengali" }, - { "bn", "BD", 0, "Bengali (Bangladesh)" }, - { "bn", "IN", 0, "Bengali (India)" }, - { "bo", 0, 0, "Tibetan" }, - { "br", 0, 0, "Breton" }, - { "bs", 0, 0, "Bosnian" }, - { "bs", "BA", 0, "Bosnian (Bosnia/Herzegovina)"}, - { "bs", "BS", 0, "Bosnian (Bahamas)" }, - { "ca", "ES", "valencia", "Catalan (valencia)" }, - { "ca", "ES", 0, "Catalan (Spain)" }, - { "ca", 0, "valencia", "Catalan (valencia)" }, - { "ca", 0, 0, "Catalan" }, - { "co", 0, 0, "Corsican" }, - { "cs", 0, 0, "Czech" }, - { "cs", "CZ", 0, "Czech (Czech Republic)" }, - { "cy", 0, 0, "Welsh" }, - { "cy", "GB", 0, "Welsh (Great Britain)" }, - { "cz", 0, 0, "Unknown language" }, - { "da", 0, 0, "Danish" }, - { "da", "DK", 0, "Danish (Denmark)" }, - { "de", 0, 0, "German" }, - { "de", "AT", 0, "German (Austria)" }, - { "de", "CH", 0, "German (Switzerland)" }, - { "de", "DE", 0, "German (Germany)" }, - { "dk", 0, 0, "Unknown language" }, - { "dz", 0, 0, "Dzongkha" }, - { "el", 0, 0, "Greek" }, - { "el", "GR", 0, "Greek (Greece)" }, - { "en", 0, 0, "English" }, - { "en", "AU", 0, "English (Australia)" }, - { "en", "CA", 0, "English (Canada)" }, - { "en", "GB", 0, "English (Great Britain)" }, - { "en", "US", 0, "English (United States)" }, - { "en", "ZA", 0, "English (South Africa)" }, - { "en", 0, "boldquot", "English" }, - { "en", 0, "quot", "English" }, - { "en", "US", "piglatin", "English" }, - { "eo", 0, 0, "Esperanto" }, - { "es", 0, 0, "Spanish" }, - { "es", "AR", 0, "Spanish (Argentina)" }, - { "es", "CL", 0, "Spanish (Chile)" }, - { "es", "CO", 0, "Spanish (Colombia)" }, - { "es", "CR", 0, "Spanish (Costa Rica)" }, - { "es", "DO", 0, "Spanish (Dominican Republic)"}, - { "es", "EC", 0, "Spanish (Ecuador)" }, - { "es", "ES", 0, "Spanish (Spain)" }, - { "es", "GT", 0, "Spanish (Guatemala)" }, - { "es", "HN", 0, "Spanish (Honduras)" }, - { "es", "LA", 0, "Spanish (Laos)" }, - { "es", "MX", 0, "Spanish (Mexico)" }, - { "es", "NI", 0, "Spanish (Nicaragua)" }, - { "es", "PA", 0, "Spanish (Panama)" }, - { "es", "PE", 0, "Spanish (Peru)" }, - { "es", "PR", 0, "Spanish (Puerto Rico)" }, - { "es", "SV", 0, "Spanish (El Salvador)" }, - { "es", "UY", 0, "Spanish (Uruguay)" }, - { "es", "VE", 0, "Spanish (Venezuela)" }, - { "et", 0, 0, "Estonian" }, - { "et", "EE", 0, "Estonian (Estonia)" }, - { "et", "ET", 0, "Estonian (Ethiopia)" }, - { "eu", 0, 0, "Basque" }, - { "eu", "ES", 0, "Basque (Spain)" }, - { "fa", 0, 0, "Persian" }, - { "fa", "AF", 0, "Persian (Afghanistan)" }, - { "fa", "IR", 0, "Persian (Iran)" }, - { "fi", 0, 0, "Finnish" }, - { "fi", "FI", 0, "Finnish (Finland)" }, - { "fo", 0, 0, "Faroese" }, - { "fo", "FO", 0, "Faeroese (Faroe Islands)" }, - { "fr", 0, 0, "French" }, - { "fr", "CA", 0, "French (Canada)" }, - { "fr", "CH", 0, "French (Switzerland)" }, - { "fr", "FR", 0, "French (France)" }, - { "fr", "LU", 0, "French (Luxembourg)" }, - { "fy", 0, 0, "Frisian" }, - { "ga", 0, 0, "Irish" }, - { "gd", 0, 0, "Gaelic Scots" }, - { "gl", 0, 0, "Galician" }, - { "gl", "ES", 0, "Galician (Spain)" }, - { "gn", 0, 0, "Guarani" }, - { "gu", 0, 0, "Gujarati" }, - { "gv", 0, 0, "Manx" }, - { "ha", 0, 0, "Hausa" }, - { "he", 0, 0, "Hebrew" }, - { "he", "IL", 0, "Hebrew (Israel)" }, - { "hi", 0, 0, "Hindi" }, - { "hr", 0, 0, "Croatian" }, - { "hr", "HR", 0, "Croatian (Croatia)" }, - { "hu", 0, 0, "Hungarian" }, - { "hu", "HU", 0, "Hungarian (Hungary)" }, - { "hy", 0, 0, "Armenian" }, - { "ia", 0, 0, "Interlingua" }, - { "id", 0, 0, "Indonesian" }, - { "id", "ID", 0, "Indonesian (Indonesia)" }, - { "is", 0, 0, "Icelandic" }, - { "is", "IS", 0, "Icelandic (Iceland)" }, - { "it", 0, 0, "Italian" }, - { "it", "CH", 0, "Italian (Switzerland)" }, - { "it", "IT", 0, "Italian (Italy)" }, - { "iu", 0, 0, "Inuktitut" }, - { "ja", 0, 0, "Japanese" }, - { "ja", "JP", 0, "Japanese (Japan)" }, - { "ka", 0, 0, "Georgian" }, - { "kk", 0, 0, "Kazakh" }, - { "kl", 0, 0, "Kalaallisut" }, - { "km", 0, 0, "Khmer" }, - { "km", "KH", 0, "Khmer (Cambodia)" }, - { "kn", 0, 0, "Kannada" }, - { "ko", 0, 0, "Korean" }, - { "ko", "KR", 0, "Korean (Korea)" }, - { "ku", 0, 0, "Kurdish" }, - { "kw", 0, 0, "Cornish" }, - { "ky", 0, 0, "Kirghiz" }, - { "la", 0, 0, "Latin" }, - { "lo", 0, 0, "Lao" }, - { "lt", 0, 0, "Lithuanian" }, - { "lt", "LT", 0, "Lithuanian (Lithuania)" }, - { "lv", 0, 0, "Latvian" }, - { "lv", "LV", 0, "Latvian (Latvia)" }, - { "mg", 0, 0, "Malagasy" }, - { "mi", 0, 0, "Maori" }, - { "mk", 0, 0, "Macedonian" }, - { "mk", "MK", 0, "Macedonian (Macedonia)" }, - { "ml", 0, 0, "Malayalam" }, - { "mn", 0, 0, "Mongolian" }, - { "mr", 0, 0, "Marathi" }, - { "ms", 0, 0, "Malay" }, - { "ms", "MY", 0, "Malay (Malaysia)" }, - { "mt", 0, 0, "Maltese" }, - { "my", 0, 0, "Burmese" }, - { "my", "MM", 0, "Burmese (Myanmar)" }, - { "nb", 0, 0, "Norwegian Bokmal" }, - { "nb", "NO", 0, "Norwegian Bokmål (Norway)" }, - { "ne", 0, 0, "Nepali" }, - { "nl", 0, 0, "Dutch" }, - { "nl", "BE", 0, "Dutch (Belgium)" }, - { "nl", "NL", 0, "Dutch (Netherlands)" }, - { "nn", 0, 0, "Norwegian Nynorsk" }, - { "nn", "NO", 0, "Norwegian Nynorsk (Norway)" }, - // DEPRECATED - //{ "no", 0, 0, "Norwegian" }, - //{ "no", "NO", 0, "Norwegian (Norway)" }, - //{ "no", "NY", 0, "Norwegian (NY)" }, - { "nr", 0, 0, "Ndebele, South" }, - { "oc", 0, 0, "Occitan post 1500" }, - { "om", 0, 0, "Oromo" }, - { "or", 0, 0, "Oriya" }, - { "os", 0, 0, "Ossetian" }, - { "pa", 0, 0, "Punjabi" }, - { "pl", 0, 0, "Polish" }, - { "pl", "PL", 0, "Polish (Poland)" }, - { "ps", 0, 0, "Pashto" }, - { "pt", 0, 0, "Portuguese" }, - { "pt", "BR", 0, "Portuguese (Brazil)" }, - { "pt", "PT", 0, "Portuguese (Portugal)" }, - { "qu", 0, 0, "Quechua" }, - { "rm", 0, 0, "Rhaeto-Romance" }, - { "ro", 0, 0, "Romanian" }, - { "ro", "RO", 0, "Romanian (Romania)" }, - { "ru", 0, 0, "Russian" }, - { "ru", "RU", 0, "Russian (Russia" }, - { "rw", 0, 0, "Kinyarwanda" }, - { "sa", 0, 0, "Sanskrit" }, - { "sd", 0, 0, "Sindhi" }, - { "se", 0, 0, "Sami" }, - { "se", "NO", 0, "Sami (Norway)" }, - { "si", 0, 0, "Sinhalese" }, - { "sk", 0, 0, "Slovak" }, - { "sk", "SK", 0, "Slovak (Slovakia)" }, - { "sl", 0, 0, "Slovenian" }, - { "sl", "SI", 0, "Slovenian (Slovenia)" }, - { "sl", "SL", 0, "Slovenian (Sierra Leone)" }, - { "sm", 0, 0, "Samoan" }, - { "so", 0, 0, "Somali" }, - { "sp", 0, 0, "Unknown language" }, - { "sq", 0, 0, "Albanian" }, - { "sq", "AL", 0, "Albanian (Albania)" }, - { "sr", 0, 0, "Serbian" }, - { "sr", "YU", 0, "Serbian (Yugoslavia)" }, - { "sr", 0,"ije", "Serbian" }, - { "sr", 0, "latin", "Serbian" }, - { "sr", 0, "Latn", "Serbian" }, - { "ss", 0, 0, "Swati" }, - { "st", 0, 0, "Sotho" }, - { "sv", 0, 0, "Swedish" }, - { "sv", "SE", 0, "Swedish (Sweden)" }, - { "sv", "SV", 0, "Swedish (El Salvador)" }, - { "sw", 0, 0, "Swahili" }, - { "ta", 0, 0, "Tamil" }, - { "te", 0, 0, "Telugu" }, - { "tg", 0, 0, "Tajik" }, - { "th", 0, 0, "Thai" }, - { "th", "TH", 0, "Thai (Thailand)" }, - { "ti", 0, 0, "Tigrinya" }, - { "tk", 0, 0, "Turkmen" }, - { "tl", 0, 0, "Tagalog" }, - { "to", 0, 0, "Tonga" }, - { "tr", 0, 0, "Turkish" }, - { "tr", "TR", 0, "Turkish (Turkey)" }, - { "ts", 0, 0, "Tsonga" }, - { "tt", 0, 0, "Tatar" }, - { "ug", 0, 0, "Uighur" }, - { "uk", 0, 0, "Ukrainian" }, - { "uk", "UA", 0, "Ukrainian (Ukraine)" }, - { "ur", 0, 0, "Urdu" }, - { "ur", "PK", 0, "Urdu (Pakistan)" }, - { "uz", 0, 0, "Uzbek" }, - { "uz", 0, "cyrillic", "Uzbek" }, - { "vi", 0, 0, "Vietnamese" }, - { "vi", "VN", 0, "Vietnamese (Vietnam)" }, - { "wa", 0, 0, "Walloon" }, - { "wo", 0, 0, "Wolof" }, - { "xh", 0, 0, "Xhosa" }, - { "yi", 0, 0, "Yiddish" }, - { "yo", 0, 0, "Yoruba" }, - { "zh", 0, 0, "Chinese" }, - { "zh", "CN", 0, "Chinese (simplified)" }, - { "zh", "HK", 0, "Chinese (Hong Kong)" }, - { "zh", "TW", 0, "Chinese (traditional)" }, - { "zu", 0, 0, "Zulu" }, - { NULL, 0, 0, NULL } + { "aa", 0, 0, "Afar" , "ʿAfár af" }, + { "af", 0, 0, "Afrikaans" , "Afrikaans" }, + { "af", "ZA", 0, "Afrikaans (South Africa)" , 0 }, + { "am", 0, 0, "Amharic" , "ኣማርኛ" }, + { "ar", 0, 0, "Arabic" , "العربية" }, + { "ar", "AR", 0, "Arabic (Argentina)" , 0 }, + { "ar", "OM", 0, "Arabic (Oman)" , 0 }, + { "ar", "SA", 0, "Arabic (Saudi Arabia)" , 0 }, + { "ar", "SY", 0, "Arabic (Syrian Arab Republic)", 0 }, + { "ar", "TN", 0, "Arabic (Tunisia)" , 0 }, + { "as", 0, 0, "Assamese" , "অসমীয়া" }, + { "ast",0, 0, "Asturian" , "Asturianu" }, + { "ay", 0, 0, "Aymara" , "aymar aru" }, + { "az", 0, 0, "Azerbaijani" , "Azərbaycanca" }, + { "az", "IR", 0, "Azerbaijani (Iran)" , 0 }, + { "be", 0, 0, "Belarusian" , "Беларуская мова" }, + { "be", 0, "latin", "Belarusian" , "Беларуская мова" }, + { "bg", 0, 0, "Bulgarian" , "български" }, + { "bg", "BG", 0, "Bulgarian (Bulgaria)" , 0 }, + { "bn", 0, 0, "Bengali" , "বাংলা" }, + { "bn", "BD", 0, "Bengali (Bangladesh)" , 0 }, + { "bn", "IN", 0, "Bengali (India)" , 0 }, + { "bo", 0, 0, "Tibetan" , "བོད་སྐད་" }, + { "br", 0, 0, "Breton" , "brezhoneg" }, + { "bs", 0, 0, "Bosnian" , "Bosanski" }, + { "bs", "BA", 0, "Bosnian (Bosnia/Herzegovina)", 0 }, + { "bs", "BS", 0, "Bosnian (Bahamas)" , 0 }, + { "ca", "ES", "valencia", "Catalan (valencia)" , 0 }, + { "ca", "ES", 0, "Catalan (Spain)" , 0 }, + { "ca", 0, "valencia", "Catalan (valencia)" , 0 }, + { "ca", 0, 0, "Catalan" , 0 }, + { "cmn", 0, 0, "Mandarin" , 0 }, + { "co", 0, 0, "Corsican" , "corsu" }, + { "cs", 0, 0, "Czech" , "Čeština" }, + { "cs", "CZ", 0, "Czech (Czech Republic)" , "Čeština (Česká Republika)"}, + { "cy", 0, 0, "Welsh" , "Welsh" }, + { "cy", "GB", 0, "Welsh (Great Britain)" , "Welsh (Great Britain)" }, + { "cz", 0, 0, "Unknown language" , "Unknown language" }, + { "da", 0, 0, "Danish" , "Dansk" }, + { "da", "DK", 0, "Danish (Denmark)" , "Dansk (Danmark)" }, + { "de", 0, 0, "German" , "Deutsch" }, + { "de", "AT", 0, "German (Austria)" , "Deutsch (Österreich)" }, + { "de", "CH", 0, "German (Switzerland)" , "Deutsch (Schweiz)" }, + { "de", "DE", 0, "German (Germany)" , "Deutsch (Deutschland)" }, + { "dk", 0, 0, "Unknown language" , "Unknown language" }, + { "dz", 0, 0, "Dzongkha" , "རྫོང་ཁ" }, + { "el", 0, 0, "Greek" , "ελληνικά" }, + { "el", "GR", 0, "Greek (Greece)" , 0 }, + { "en", 0, 0, "English" , "English" }, + { "en", "AU", 0, "English (Australia)" , "English (Australia)" }, + { "en", "CA", 0, "English (Canada)" , "English (Canada)" }, + { "en", "GB", 0, "English (Great Britain)" , "English (Great Britain)" }, + { "en", "US", 0, "English (United States)" , "English (United States)" }, + { "en", "ZA", 0, "English (South Africa)" , "English (South Africa)" }, + { "en", 0, "boldquot", "English" , "English" }, + { "en", 0, "quot", "English" , "English" }, + { "en", "US", "piglatin", "English" , "English" }, + { "eo", 0, 0, "Esperanto" , "Esperanto" }, + { "es", 0, 0, "Spanish" , "Español" }, + { "es", "AR", 0, "Spanish (Argentina)" , 0 }, + { "es", "CL", 0, "Spanish (Chile)" , 0 }, + { "es", "CO", 0, "Spanish (Colombia)" , 0 }, + { "es", "CR", 0, "Spanish (Costa Rica)" , 0 }, + { "es", "DO", 0, "Spanish (Dominican Republic)", 0 }, + { "es", "EC", 0, "Spanish (Ecuador)" , 0 }, + { "es", "ES", 0, "Spanish (Spain)" , 0 }, + { "es", "GT", 0, "Spanish (Guatemala)" , 0 }, + { "es", "HN", 0, "Spanish (Honduras)" , 0 }, + { "es", "LA", 0, "Spanish (Laos)" , 0 }, + { "es", "MX", 0, "Spanish (Mexico)" , 0 }, + { "es", "NI", 0, "Spanish (Nicaragua)" , 0 }, + { "es", "PA", 0, "Spanish (Panama)" , 0 }, + { "es", "PE", 0, "Spanish (Peru)" , 0 }, + { "es", "PR", 0, "Spanish (Puerto Rico)" , 0 }, + { "es", "SV", 0, "Spanish (El Salvador)" , 0 }, + { "es", "UY", 0, "Spanish (Uruguay)" , 0 }, + { "es", "VE", 0, "Spanish (Venezuela)" , 0 }, + { "et", 0, 0, "Estonian" , "eesti keel" }, + { "et", "EE", 0, "Estonian (Estonia)" , 0 }, + { "et", "ET", 0, "Estonian (Ethiopia)" , 0 }, + { "eu", 0, 0, "Basque" , "euskara" }, + { "eu", "ES", 0, "Basque (Spain)" , 0 }, + { "fa", 0, 0, "Persian" , "فارسى" }, + { "fa", "AF", 0, "Persian (Afghanistan)" , 0 }, + { "fa", "IR", 0, "Persian (Iran)" , 0 }, + { "fi", 0, 0, "Finnish" , "suomi" }, + { "fi", "FI", 0, "Finnish (Finland)" , 0 }, + { "fo", 0, 0, "Faroese" , "Føroyskt" }, + { "fo", "FO", 0, "Faeroese (Faroe Islands)" , 0 }, + { "fr", 0, 0, "French" , "Français" }, + { "fr", "CA", 0, "French (Canada)" , "Français (Canada)" }, + { "fr", "CH", 0, "French (Switzerland)" , "Français (Suisse)" }, + { "fr", "FR", 0, "French (France)" , "Français (France)" }, + { "fr", "LU", 0, "French (Luxembourg)" , "Français (Luxembourg)" }, + { "fy", 0, 0, "Frisian" , "Frysk" }, + { "ga", 0, 0, "Irish" , "Gaeilge" }, + { "gd", 0, 0, "Gaelic Scots" , "Gàidhlig" }, + { "gl", 0, 0, "Galician" , "Galego" }, + { "gl", "ES", 0, "Galician (Spain)" , 0 }, + { "gn", 0, 0, "Guarani" , "Avañe'ẽ" }, + { "gu", 0, 0, "Gujarati" , "ગુજરાતી" }, + { "gv", 0, 0, "Manx" , "Gaelg" }, + { "ha", 0, 0, "Hausa" , "حَوْسَ" }, + { "he", 0, 0, "Hebrew" , "עברית" }, + { "he", "IL", 0, "Hebrew (Israel)" , 0 }, + { "hi", 0, 0, "Hindi" , "हिन्दी" }, + { "hr", 0, 0, "Croatian" , "Hrvatski" }, + { "hr", "HR", 0, "Croatian (Croatia)" , 0 }, + { "hu", 0, 0, "Hungarian" , "magyar" }, + { "hu", "HU", 0, "Hungarian (Hungary)" , 0 }, + { "hy", 0, 0, "Armenian" , "Հայերեն" }, + { "ia", 0, 0, "Interlingua" , "Interlingua" }, + { "id", 0, 0, "Indonesian" , "Bahasa Indonesia" }, + { "id", "ID", 0, "Indonesian (Indonesia)" , 0 }, + { "is", 0, 0, "Icelandic" , "Íslenska" }, + { "is", "IS", 0, "Icelandic (Iceland)" , 0 }, + { "it", 0, 0, "Italian" , "Italiano" }, + { "it", "CH", 0, "Italian (Switzerland)" , 0 }, + { "it", "IT", 0, "Italian (Italy)" , 0 }, + { "iu", 0, 0, "Inuktitut" , "ᐃᓄᒃᑎᑐᑦ/inuktitut" }, + { "ja", 0, 0, "Japanese" , "日本語" }, + { "ja", "JP", 0, "Japanese (Japan)" , 0 }, + { "ka", 0, 0, "Georgian" , "ქართული" }, + { "kk", 0, 0, "Kazakh" , "Қазақша" }, + { "kl", 0, 0, "Kalaallisut" , "Kalaallisut" }, + { "km", 0, 0, "Khmer" , "ភាសាខ្មែរ" }, + { "km", "KH", 0, "Khmer (Cambodia)" , 0 }, + { "kn", 0, 0, "Kannada" , "ಕನ್ನಡ" }, + { "ko", 0, 0, "Korean" , "한국어" }, + { "ko", "KR", 0, "Korean (Korea)" , 0 }, + { "ku", 0, 0, "Kurdish" , "Kurdî" }, + { "kw", 0, 0, "Cornish" , "Kernowek" }, + { "ky", 0, 0, "Kirghiz" , 0 }, + { "la", 0, 0, "Latin" , "Latina" }, + { "lo", 0, 0, "Lao" , "ລາວ" }, + { "lt", 0, 0, "Lithuanian" , "Lietuvių" }, + { "lt", "LT", 0, "Lithuanian (Lithuania)" , 0 }, + { "lv", 0, 0, "Latvian" , "Latviešu" }, + { "lv", "LV", 0, "Latvian (Latvia)" , 0 }, + { "jbo", 0, 0, "Lojban" , "La .lojban." }, + { "mg", 0, 0, "Malagasy" , "Malagasy" }, + { "mi", 0, 0, "Maori" , "Māori" }, + { "mk", 0, 0, "Macedonian" , "Македонски" }, + { "mk", "MK", 0, "Macedonian (Macedonia)" , 0 }, + { "ml", 0, 0, "Malayalam" , "മലയാളം" }, + { "mn", 0, 0, "Mongolian" , "Монгол" }, + { "mr", 0, 0, "Marathi" , "मराठी" }, + { "ms", 0, 0, "Malay" , "Bahasa Melayu" }, + { "ms", "MY", 0, "Malay (Malaysia)" , 0 }, + { "mt", 0, 0, "Maltese" , "Malti" }, + { "my", 0, 0, "Burmese" , "မြန်မာဘာသာ" }, + { "my", "MM", 0, "Burmese (Myanmar)" , 0 }, + { "nb", 0, 0, "Norwegian Bokmal" , 0 }, + { "nb", "NO", 0, "Norwegian Bokmål (Norway)" , 0 }, + { "ne", 0, 0, "Nepali" , 0 }, + { "nl", 0, 0, "Dutch" , "Nederlands" }, + { "nl", "BE", 0, "Dutch (Belgium)" , 0 }, + { "nl", "NL", 0, "Dutch (Netherlands)" , 0 }, + { "nn", 0, 0, "Norwegian Nynorsk" , "Norsk nynorsk" }, + { "nn", "NO", 0, "Norwegian Nynorsk (Norway)" , 0 }, + { "no", 0, 0, "Norwegian" , "Norsk bokmål" }, + { "no", "NO", 0, "Norwegian (Norway)" , 0 }, + { "no", "NY", 0, "Norwegian (NY)" , 0 }, + { "nr", 0, 0, "Ndebele, South" , 0 }, + { "oc", 0, 0, "Occitan post 1500" , "Occitan" }, + { "om", 0, 0, "Oromo" , "Oromoo" }, + { "or", 0, 0, "Oriya" , "ଓଡ଼ିଆ" }, + { "pa", 0, 0, "Punjabi" , "ਪੰਜਾਬੀ" }, + { "pl", 0, 0, "Polish" , "Polski" }, + { "pl", "PL", 0, "Polish (Poland)" , 0 }, + { "ps", 0, 0, "Pashto" , "پښتو" }, + { "pt", 0, 0, "Portuguese" , "Português" }, + { "pt", "BR", 0, "Portuguese (Brazil)" , 0 }, + { "pt", "PT", 0, "Portuguese (Portugal)" , 0 }, + { "qu", 0, 0, "Quechua" , "Runa Simi" }, + { "rm", 0, 0, "Rhaeto-Romance" , "Rumantsch" }, + { "ro", 0, 0, "Romanian" , "Română" }, + { "ro", "RO", 0, "Romanian (Romania)" , 0 }, + { "ru", 0, 0, "Russian" ,"Русский" }, + { "ru", "RU", 0, "Russian (Russia" , 0 }, + { "rw", 0, 0, "Kinyarwanda" , "Kinyarwanda" }, + { "sa", 0, 0, "Sanskrit" , 0 }, + { "sd", 0, 0, "Sindhi" , 0 }, + { "se", 0, 0, "Sami" , "Sámegiella" }, + { "se", "NO", 0, "Sami (Norway)" , 0 }, + { "si", 0, 0, "Sinhalese" , 0 }, + { "sk", 0, 0, "Slovak" , "Slovenčina" }, + { "sk", "SK", 0, "Slovak (Slovakia)" , 0 }, + { "sl", 0, 0, "Slovenian" , "Slovenščina" }, + { "sl", "SI", 0, "Slovenian (Slovenia)" , 0 }, + { "sl", "SL", 0, "Slovenian (Sierra Leone)" , 0 }, + { "sm", 0, 0, "Samoan" , 0 }, + { "so", 0, 0, "Somali" , 0 }, + { "sp", 0, 0, "Unknown language" , 0 }, + { "sq", 0, 0, "Albanian" , "Shqip" }, + { "sq", "AL", 0, "Albanian (Albania)" , 0 }, + { "sr", 0, 0, "Serbian" , "Српски / srpski" }, + { "sr", "YU", 0, "Serbian (Yugoslavia)" , 0 }, + { "sr", 0,"ije", "Serbian" , 0 }, + { "sr", 0, "latin", "Serbian" , 0 }, + { "sr", 0, "Latn", "Serbian" , 0 }, + { "ss", 0, 0, "Swati" , 0 }, + { "st", 0, 0, "Sotho" , 0 }, + { "sv", 0, 0, "Swedish" , "Svenska" }, + { "sv", "SE", 0, "Swedish (Sweden)" , 0 }, + { "sv", "SV", 0, "Swedish (El Salvador)" , 0 }, + { "sw", 0, 0, "Swahili" , 0 }, + { "ta", 0, 0, "Tamil" , 0 }, + { "te", 0, 0, "Telugu" , 0 }, + { "tg", 0, 0, "Tajik" , 0 }, + { "th", 0, 0, "Thai" , "ไทย" }, + { "th", "TH", 0, "Thai (Thailand)" , 0 }, + { "ti", 0, 0, "Tigrinya" , 0 }, + { "tk", 0, 0, "Turkmen" , 0 }, + { "tl", 0, 0, "Tagalog" , 0 }, + { "to", 0, 0, "Tonga" , 0 }, + { "tr", 0, 0, "Turkish" , "Türkçe" }, + { "tr", "TR", 0, "Turkish (Turkey)" , 0 }, + { "ts", 0, 0, "Tsonga" , 0 }, + { "tt", 0, 0, "Tatar" , 0 }, + { "ug", 0, 0, "Uighur" , 0 }, + { "uk", 0, 0, "Ukrainian" , "Українська" }, + { "uk", "UA", 0, "Ukrainian (Ukraine)" , 0 }, + { "ur", 0, 0, "Urdu" , 0 }, + { "ur", "PK", 0, "Urdu (Pakistan)" , 0 }, + { "uz", 0, 0, "Uzbek" , 0 }, + { "uz", 0, "cyrillic", "Uzbek" , 0 }, + { "vi", 0, 0, "Vietnamese" , "Tiếng Việt" }, + { "vi", "VN", 0, "Vietnamese (Vietnam)" , 0 }, + { "wa", 0, 0, "Walloon" , 0 }, + { "wo", 0, 0, "Wolof" , 0 }, + { "xh", 0, 0, "Xhosa" , 0 }, + { "yi", 0, 0, "Yiddish" , "ייִדיש" }, + { "yo", 0, 0, "Yoruba" , 0 }, + { "zh", 0, 0, "Chinese" , "中文" }, + { "zh", "CN", 0, "Chinese (simplified)" , "中文(简体)" }, + { "zh", "HK", 0, "Chinese (Hong Kong)" , "中文(香港)" }, + { "zh", "TW", 0, "Chinese (traditional)" , "中文(繁體)" }, + { "zu", 0, 0, "Zulu" , 0 }, + { NULL, 0, 0, NULL , 0 } }; //*} @@ -532,6 +535,15 @@ Language::get_name() const return ""; } +std::string +Language::get_localized_name() const +{ + if(language_spec && language_spec->name_localized) + return language_spec->name_localized; + else + return this->get_name(); +} + std::string Language::str() const { diff --git a/src/tinygettext/language.hpp b/src/tinygettext/language.hpp index 46f47d299..98e31a22c 100644 --- a/src/tinygettext/language.hpp +++ b/src/tinygettext/language.hpp @@ -70,6 +70,9 @@ public: /** Returns the human readable name of the Language */ std::string get_name() const; + /** Returns the human readable name of the language in the language itself */ + std::string get_localized_name() const; + /** Returns the Language as string in the form of an environment variable: {language}_{country}@{modifier} */ std::string str() const; diff --git a/src/tracks/battle_graph.cpp b/src/tracks/battle_graph.cpp index 5ecca6832..1cddf3cc2 100644 --- a/src/tracks/battle_graph.cpp +++ b/src/tracks/battle_graph.cpp @@ -216,7 +216,7 @@ int BattleGraph::pointToNode(const int cur_node, // ----------------------------------------------------------------------------- -const int & BattleGraph::getNextShortestPathPoly(int i, int j) const +const int BattleGraph::getNextShortestPathPoly(int i, int j) const { if (i == BattleGraph::UNKNOWN_POLY || j == BattleGraph::UNKNOWN_POLY) return BattleGraph::UNKNOWN_POLY; diff --git a/src/tracks/battle_graph.hpp b/src/tracks/battle_graph.hpp index 6b872f1f2..fd32d7e94 100644 --- a/src/tracks/battle_graph.hpp +++ b/src/tracks/battle_graph.hpp @@ -120,7 +120,7 @@ public: /** Returns the next polygon on the shortest path from i to j. * Note: m_parent_poly[j][i] contains the parent of i on path from j to i, * which is the next node on the path from i to j (undirected graph) */ - const int & getNextShortestPathPoly(int i, int j) const; + const int getNextShortestPathPoly(int i, int j) const; const std::vector < std::pair >& getItemList() { return m_items_on_graph; } diff --git a/src/tracks/check_goal.hpp b/src/tracks/check_goal.hpp index d355d797d..85f3d2307 100644 --- a/src/tracks/check_goal.hpp +++ b/src/tracks/check_goal.hpp @@ -51,7 +51,7 @@ public: CheckGoal(const XMLNode &node, unsigned int index); virtual ~CheckGoal() {} virtual void update(float dt) OVERRIDE; - virtual void trigger(unsigned int kart_index); + virtual void trigger(unsigned int kart_index) OVERRIDE; virtual bool isTriggered(const Vec3 &old_pos, const Vec3 &new_pos, unsigned int indx) OVERRIDE; virtual void reset(const Track &track) OVERRIDE; diff --git a/src/tracks/track.cpp b/src/tracks/track.cpp index 0f7cb37a6..b50579225 100644 --- a/src/tracks/track.cpp +++ b/src/tracks/track.cpp @@ -493,6 +493,7 @@ void Track::loadTrackInfo() if(!root || root->getName()!="track") { + delete root; std::ostringstream o; o<<"Can't load track '"<Save(); + ReplayRecorder::get()->save(); } else if (cmdID == DEBUG_SAVE_HISTORY) { diff --git a/src/utils/translation.cpp b/src/utils/translation.cpp index ee6f05f6e..7204fc81f 100644 --- a/src/utils/translation.cpp +++ b/src/utils/translation.cpp @@ -416,26 +416,23 @@ const wchar_t* Translations::fribidize(const wchar_t* in_ptr) bool Translations::isRTLText(const wchar_t *in_ptr) { #if ENABLE_BIDI - if (this->isRTLLanguage()) + std::size_t length = wcslen(in_ptr); + FriBidiChar *fribidiInput = toFribidiChar(in_ptr); + + FriBidiCharType *types = new FriBidiCharType[length]; + fribidi_get_bidi_types(fribidiInput, length, types); + freeFribidiChar(fribidiInput); + + // Declare as RTL if one character is RTL + for (std::size_t i = 0; i < length; i++) { - std::size_t length = wcslen(in_ptr); - FriBidiChar *fribidiInput = toFribidiChar(in_ptr); - - FriBidiCharType *types = new FriBidiCharType[length]; - fribidi_get_bidi_types(fribidiInput, length, types); - freeFribidiChar(fribidiInput); - - // Declare as RTL if one character is RTL - for (std::size_t i = 0; i < length; i++) + if (types[i] & FRIBIDI_MASK_RTL) { - if (types[i] & FRIBIDI_MASK_RTL) - { - delete[] types; - return true; - } + delete[] types; + return true; } - delete[] types; } + delete[] types; return false; #else return false;