Index: ps/trunk/source/ps/Vector2D.h =================================================================== --- ps/trunk/source/ps/Vector2D.h (revision 7838) +++ ps/trunk/source/ps/Vector2D.h (nonexistent) @@ -1,131 +0,0 @@ -/* Copyright (C) 2009 Wildfire Games. - * This file is part of 0 A.D. - * - * 0 A.D. 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 2 of the License, or - * (at your option) any later version. - * - * 0 A.D. 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 0 A.D. If not, see . - */ - -// Vector2D.h -// -// 2-dimensional vector class, primarily for use by simulation code. - -#ifndef INCLUDED_VECTOR2D -#define INCLUDED_VECTOR2D - -#include -#include "maths/Vector3D.h" - -class CVector2D -{ -public: - float x; - float y; - inline CVector2D() { x = 0.0f; y = 0.0f; } - inline CVector2D( float _x, float _y ) - { - x = _x; y = _y; - } - inline CVector2D( const CVector3D& v3 ) // This is done an awful lot. - { - x = v3.X; y = v3.Z; - } - inline operator CVector3D() const - { - return( CVector3D( x, 0, y ) ); - } - inline bool operator==( const CVector2D& rhs ) const - { - return( x == rhs.x && y == rhs.y ); - } - static inline float Dot( const CVector2D& u, const CVector2D& v ) - { - return( u.x * v.x + u.y * v.y ); - } - static inline float betadot( const CVector2D& u, const CVector2D& v ) - { - // Beta-dot product. I have no idea if that's its correct name - // but use of it tends to simplify collision formulae. - // At the moment I think all of my code uses separate vectors - // and dots them together, though. - return( u.x * v.y - u.y * v.x ); - } - inline CVector2D beta() const - { - return( CVector2D( y, -x ) ); - } - inline float Dot( const CVector2D& u ) const - { - return( Dot( *this, u ) ); - } - inline float betadot( const CVector2D& u ) const - { - return( betadot( *this, u ) ); - } - inline CVector2D operator+( const CVector2D& u ) const - { - return( CVector2D( x + u.x, y + u.y ) ); - } - inline CVector2D operator-( const CVector2D& u ) const - { - return( CVector2D( x - u.x, y - u.y ) ); - } - inline CVector2D& operator+=( const CVector2D& u ) - { - x += u.x; y += u.y; - return( *this ); - } - inline CVector2D& operator-=( const CVector2D& u ) - { - x -= u.x; y -= u.y; - return( *this ); - } - inline CVector2D operator*( const float scale ) const - { - return( CVector2D( x * scale, y * scale ) ); - } - inline CVector2D operator/( const float scale ) const - { - return( CVector2D( x / scale, y / scale ) ); - } - inline CVector2D& operator*=( const float scale ) - { - x *= scale; y *= scale; - return( *this ); - } - inline CVector2D& operator/=( const float scale ) - { - x /= scale; y /= scale; - return( *this ); - } - inline float Length() const - { - return( sqrt( x * x + y * y ) ); - } - inline float length2() const - { - return( x * x + y * y ); - } - CVector2D Normalize() const - { - float l = Length(); - if( l < 0.00001 ) return( CVector2D( 1.0f, 0.0f ) ); - l = 1 / l; - return( CVector2D( x * l, y * l ) ); - } - inline bool within( const float dist ) const - { - return( ( x * x + y * y ) <= ( dist * dist ) ); - } -}; - -#endif Property changes on: ps/trunk/source/ps/Vector2D.h ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Index: ps/trunk/source/scripting/EventTypes.h =================================================================== --- ps/trunk/source/scripting/EventTypes.h (revision 7838) +++ ps/trunk/source/scripting/EventTypes.h (nonexistent) @@ -1,80 +0,0 @@ -/* Copyright (C) 2009 Wildfire Games. - * This file is part of 0 A.D. - * - * 0 A.D. 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 2 of the License, or - * (at your option) any later version. - * - * 0 A.D. 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 0 A.D. If not, see . - */ - -// EventTypes.h -// Fairly game-specific event declarations for use with DOMEvent. -// Creates unique (for the current target) names for each event. -// DOMEvent currently uses a preallocated array of EVENT_LAST elements, -// so these must be consecutive integers starting with 0. - -#ifndef INCLUDED_EVENTTYPES -#define INCLUDED_EVENTTYPES - -enum EEventType -{ - // Entity events - EVENT_INITIALIZE = 0, - EVENT_DEATH, - EVENT_TICK, - EVENT_CONTACT_ACTION, - EVENT_TARGET_EXHAUSTED, - EVENT_START_CONSTRUCTION, - EVENT_START_PRODUCTION, - EVENT_CANCEL_PRODUCTION, - EVENT_FINISH_PRODUCTION, - EVENT_TARGET_CHANGED, - EVENT_PREPARE_ORDER, - EVENT_ORDER_TRANSITION, - EVENT_NOTIFICATION, - EVENT_FORMATION, - EVENT_IDLE, - EVENT_LAST, - - // Projectile events - EVENT_IMPACT = 0, - EVENT_MISS, - - // General events - EVENT_GAME_START = 0, - EVENT_GAME_TICK, - EVENT_SELECTION_CHANGED, - EVENT_WORLD_CLICK, -}; - -// Only used for entity events... (adds them as a property) -static const wchar_t* const EventNames[EVENT_LAST] = -{ - /* EVENT_INITIALIZE */ L"onInitialize", - /* EVENT_DEATH */ L"onDeath", - /* EVENT_TICK */ L"onTick", - /* EVENT_CONTACT_ACTION */ L"onContactAction", /* For generic contact actions on a target unit, like attack or gather */ - /* EVENT_TARGET_EXHAUSTED*/ L"onTargetExhausted", /* Called when the target of a generic action dies */ - /* EVENT_START_CONSTRUCTION */ L"onStartConstruction", /* We were selected when the user placed a building */ - /* EVENT_START_PRODUCTION */ L"onStartProduction", /* We're about to start training/researching something (deduct resources, etc) */ - /* EVENT_CANCEL_PRODUCTION */ L"onCancelProduction", /* Something in production has been cancelled */ - /* EVENT_FINISH_PRODUCTION */ L"onFinishProduction", /* We've finished something in production */ - /* EVENT_TARGET_CHANGED */ L"onTargetChanged", /* If this unit is selected and the mouseover object changes */ - /* EVENT_PREPARE_ORDER */ L"onPrepareOrder", /* To check if a unit can execute a given order */ - /* EVENT_ORDER_TRANSITION */ L"onOrderTransition", /* When we change orders (sometimes...) */ - /* EVENT_NOTIFICATION */ L"onNotification", /*When we receive a notification */ - /* EVENT_FORMATION */ L"onFormation", /* When this unit does something with a formation */ - /* EVENT_IDLE */ L"onIdle", /* When this unit becomes idle, do something */ -}; - -#endif // #ifndef INCLUDED_EVENTTYPES - - Property changes on: ps/trunk/source/scripting/EventTypes.h ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Index: ps/trunk/source/scripting/ScriptObject.cpp =================================================================== --- ps/trunk/source/scripting/ScriptObject.cpp (revision 7838) +++ ps/trunk/source/scripting/ScriptObject.cpp (nonexistent) @@ -1,143 +0,0 @@ -/* Copyright (C) 2009 Wildfire Games. - * This file is part of 0 A.D. - * - * 0 A.D. 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 2 of the License, or - * (at your option) any later version. - * - * 0 A.D. 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 0 A.D. If not, see . - */ - -#include "precompiled.h" - -#include "ScriptObject.h" -#include "ScriptingHost.h" -#include "JSConversions.h" -#include "DOMEvent.h" - -CScriptObject::CScriptObject() -{ - Function = NULL; -} - -CScriptObject::~CScriptObject() -{ - Uproot(); -} - -void CScriptObject::Root() -{ - if( !Function ) - return; - - FunctionObject = JS_GetFunctionObject( Function ); - - JS_AddRoot( g_ScriptingHost.GetContext(), &FunctionObject ); -} - -void CScriptObject::Uproot() -{ - if( Function ) - JS_RemoveRoot( g_ScriptingHost.GetContext(), &FunctionObject ); -} - -CScriptObject::CScriptObject( JSFunction* _Function ) -{ - Function = NULL; - SetFunction( _Function ); -} - -CScriptObject::CScriptObject( jsval v ) -{ - Function = NULL; - SetJSVal( v ); -} - -CScriptObject::CScriptObject( const CScriptObject& copy ) -{ - Function = NULL; - SetFunction( copy.Function ); -} - -void CScriptObject::SetFunction( JSFunction* _Function ) -{ - Uproot(); - - Function = _Function; - - Root(); -} - -void CScriptObject::SetJSVal( jsval v ) -{ - CStrW Source; - switch( JS_TypeOfValue( g_ScriptingHost.GetContext(), v ) ) - { - case JSTYPE_STRING: - Source = g_ScriptingHost.ValueToUCString( v ); - Compile( L"unknown", Source ); - break; - case JSTYPE_FUNCTION: - SetFunction( JS_ValueToFunction( g_ScriptingHost.GetContext(), v ) ); - break; - default: - Function = NULL; - } -} - -JSObject* CScriptObject::GetFunctionObject() -{ - if( Function ) - return( FunctionObject ); - return( NULL ); -} - -// Executes a script attached to a JS object. -// Returns false if the script isn't defined, if the script can't be executed, -// otherwise true. Script return value is in rval. -bool CScriptObject::Run( JSObject* Context, jsval* rval, uintN argc, jsval* argv ) -{ - if( !Function ) - return( false ); - return( JS_TRUE == JS_CallFunction( g_ScriptingHost.GetContext(), Context, Function, argc, argv, rval ) ); -} - -// This variant casts script return value to a boolean, and passes it back. -bool CScriptObject::Run( JSObject* Context, uintN argc, jsval* argv ) -{ - jsval Temp; - if( !Run( Context, &Temp, argc, argv ) ) - return( false ); - return( ToPrimitive( Temp ) ); -} - -// Treat this as an event handler and dispatch an event to it. Return !evt->m_cancelled, as a convenience. -bool CScriptObject::DispatchEvent( JSObject* Context, CScriptEvent* evt ) -{ - if( Function ) - { - jsval Temp; - jsval EventObject = OBJECT_TO_JSVAL( evt->GetScript() ); - JS_CallFunction( g_ScriptingHost.GetContext(), Context, Function, 1, &EventObject, &Temp ); - } - return( evt->m_Blocked ); -} - -void CScriptObject::Compile( const CStrW& FileNameTag, const CStrW& FunctionBody ) -{ - if( Function ) - JS_RemoveRoot( g_ScriptingHost.GetContext(), &Function ); - - const char* argnames[] = { "evt" }; - utf16string str16=FunctionBody.utf16(); - Function = JS_CompileUCFunction( g_ScriptingHost.GetContext(), NULL, NULL, 1, argnames, str16.c_str(), str16.size(), CStr(FileNameTag), 0 ); - - Root(); -} Property changes on: ps/trunk/source/scripting/ScriptObject.cpp ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Index: ps/trunk/source/scripting/ScriptCustomTypes.cpp =================================================================== --- ps/trunk/source/scripting/ScriptCustomTypes.cpp (revision 7838) +++ ps/trunk/source/scripting/ScriptCustomTypes.cpp (nonexistent) @@ -1,111 +0,0 @@ -/* Copyright (C) 2009 Wildfire Games. - * This file is part of 0 A.D. - * - * 0 A.D. 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 2 of the License, or - * (at your option) any later version. - * - * 0 A.D. 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 0 A.D. If not, see . - */ - -#include "precompiled.h" - -#include "ScriptingHost.h" -#include "ScriptCustomTypes.h" - -// POINT2D - -JSClass Point2dClass = -{ - "Point2d", 0, - JS_PropertyStub, JS_PropertyStub, - JS_PropertyStub, JS_PropertyStub, - JS_EnumerateStub, JS_ResolveStub, - JS_ConvertStub, JS_FinalizeStub -}; - -JSPropertySpec Point2dProperties[] = -{ - {"x", 0, JSPROP_ENUMERATE}, - {"y", 1, JSPROP_ENUMERATE}, - {0} -}; - -JSBool Point2d_Constructor(JSContext* UNUSED(cx), JSObject* obj, uintN argc, jsval* argv, jsval* UNUSED(rval)) -{ - if (argc == 2) - { - g_ScriptingHost.SetObjectProperty(obj, "x", argv[0]); - g_ScriptingHost.SetObjectProperty(obj, "y", argv[1]); - } - else - { - jsval zero = INT_TO_JSVAL(0); - g_ScriptingHost.SetObjectProperty(obj, "x", zero); - g_ScriptingHost.SetObjectProperty(obj, "y", zero); - } - - return JS_TRUE; -} - -// Colour - -void SColour::SColourInit( float _r, float _g, float _b, float _a ) -{ - r = _r; g = _g; b = _b; a = _a; -} - -void SColour::ScriptingInit() -{ - AddMethod( "toString", 0 ); - AddProperty( L"r", (float IJSObject::*)&SColour::r ); - AddProperty( L"g", (float IJSObject::*)&SColour::g ); - AddProperty( L"b", (float IJSObject::*)&SColour::b ); - AddProperty( L"a", (float IJSObject::*)&SColour::a ); - - CJSObject::ScriptingInit( "Colour", SColour::Construct, 3 ); -} - -CStr SColour::ToString( JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv) ) -{ - return "[object Colour: ( " + CStr(r) + ", " + CStr(g) + ", " + CStr(b) + ", " + CStr(a) + " )]"; -} - - -JSBool SColour::Construct( JSContext* UNUSED(cx), JSObject* UNUSED(obj), uintN argc, jsval* argv, jsval* rval ) -{ - debug_assert( argc >= 3 ); - float alpha = 1.0; - if( argc >= 4 ) alpha = ToPrimitive( argv[3] ); - - SColour* col = new SColour( ToPrimitive( argv[0] ), - ToPrimitive( argv[1] ), - ToPrimitive( argv[2] ), - alpha ); - - col->m_EngineOwned = false; - - *rval = OBJECT_TO_JSVAL( col->GetScript() ); - - return( JS_TRUE ); -} - -// (Simon) Added this to prevent a deep copy, which evidently makes direct -// copies of the heap allocated objects within CJSObject, which eventually -// goes boom -SColour &SColour::operator = (const SColour &o) -{ - r=o.r; - g=o.g; - b=o.b; - a=o.a; - - return *this; -} Property changes on: ps/trunk/source/scripting/ScriptCustomTypes.cpp ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Index: ps/trunk/source/scripting/ScriptableComplex.h =================================================================== --- ps/trunk/source/scripting/ScriptableComplex.h (revision 7838) +++ ps/trunk/source/scripting/ScriptableComplex.h (nonexistent) @@ -1,312 +0,0 @@ -/* Copyright (C) 2009 Wildfire Games. - * This file is part of 0 A.D. - * - * 0 A.D. 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 2 of the License, or - * (at your option) any later version. - * - * 0 A.D. 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 0 A.D. If not, see . - */ - -/* -ScriptableComplex.h - -The version of CJSObject<> that retains the ability to use inheritance -in its objects. Shouldn't be used any more for anything but entity code. - -This file contains only declarations of class CJSComplex and its methods. -Their implementations are in ScriptableComplex.inl. Because CJSComplex is -a templated class, any source file that uses these methods directly must -#include ScritpableComplex.inl to link to them. However, files that -only need to know that something is a CJSComplex need not do this. This -was done to speed up compile times after modifying CJSComplex's internals: -before, 30+ files had to be recompiled because they #included Entity.h -which #includes ScriptableComplex.h. -*/ - -#ifndef INCLUDED_SCRIPTABLECOMPLEX -#define INCLUDED_SCRIPTABLECOMPLEX - -#include "scripting/ScriptingHost.h" -#include "scripting/ScriptObject.h" -#include "JSConversions.h" - -#include "lib/sysdep/stl.h" - -#include - -class IJSComplex; - -class IJSComplexProperty -{ -public: - - bool m_AllowsInheritance; - bool m_Inherited; - bool m_Intrinsic; - - // This is to make sure that all the fields are initialized at construction - inline IJSComplexProperty(): - m_AllowsInheritance(true), - m_Inherited(true), - m_Intrinsic(true) - {} - - virtual jsval Get( JSContext* cx, IJSComplex* owner ) = 0; - virtual void Set( JSContext* cx, IJSComplex* owner, jsval Value ) = 0; - - // Copies the data directly out of a parent property - // Warning: Don't use if you're not certain the properties are not of the same type. - virtual void ImmediateCopy( IJSComplex* CopyTo, IJSComplex* CopyFrom, IJSComplexProperty* CopyProperty ) = 0; - - jsval Get( IJSComplex* owner ) { return( Get( g_ScriptingHost.GetContext(), owner ) ); } - void Set( IJSComplex* owner, jsval Value ) { return( Set( g_ScriptingHost.GetContext(), owner, Value ) ); } - - virtual ~IJSComplexProperty() {} -}; - -class IJSComplex -{ - // Make copy constructor and assignment operator private - since copying of - // these objects is unsafe unless done specially. - // These will never be implemented (they are, after all, here to *prevent* - // copying) - IJSComplex(const IJSComplex &other); - IJSComplex& operator=(const IJSComplex &other); - -public: - typedef STL_HASH_MAP PropertyTable; - typedef std::vector InheritorsList; - typedef std::set StringTable; - typedef std::pair IteratorState; - - // Used for freshen/update - typedef void (IJSComplex::*NotifyFn)(); - - // Property getters and setters - typedef jsval (IJSComplex::*GetFn)(); - typedef void (IJSComplex::*SetFn)( jsval ); - - // Properties of this object - PropertyTable m_Properties; - - // Parent object - IJSComplex* m_Parent; - - // Objects that inherit from this - InheritorsList m_Inheritors; - - // Destructor - virtual ~IJSComplex() { } - - // Set the base, and rebuild - void SetBase( IJSComplex* m_Parent ); - - // Rebuild any intrinsic (mapped-to-C++-variable) properties - virtual void Rebuild() = 0; - - // HACK: Doesn't belong here. - virtual void RebuildClassSet() = 0; - - // Check for a property - virtual IJSComplexProperty* HasProperty( const CStrW& PropertyName ) = 0; - - // Get all properties of an object - virtual void FillEnumerateSet( IteratorState* it, CStrW* PropertyRoot = NULL ) = 0; - - // Retrieve the value of a property (returning false if that property is not defined) - virtual bool GetProperty( JSContext* cx, const CStrW& PropertyName, jsval* vp ) = 0; - - // Add a property (with immediate value) - virtual void AddProperty( const CStrW& PropertyName, jsval Value ) = 0; - virtual void AddProperty( const CStrW& PropertyName, const CStrW& Value ) = 0; - - inline IJSComplex() {} -}; - - -class CJSReflector; - -template -void AddMethodImpl( const char* Name, uintN MinArgs ); - -template -void AddClassPropertyImpl( const CStrW& PropertyName, PropType T::*Native, bool PropAllowInheritance = true, IJSComplex::NotifyFn Update = NULL, IJSComplex::NotifyFn Refresh = NULL ); - -template -void AddReadOnlyClassPropertyImpl( const CStrW& PropertyName, PropType T::*Native, bool PropAllowInheritance = true, IJSComplex::NotifyFn Update = NULL, IJSComplex::NotifyFn Refresh = NULL ); - -template -void MemberAddPropertyImpl( IJSComplex* obj, const CStrW& PropertyName, PropType* Native, bool PropAllowInheritance = true, IJSComplex::NotifyFn Update = NULL, IJSComplex::NotifyFn Refresh = NULL ); - -template -void MemberAddReadOnlyPropertyImpl( IJSComplex* obj, const CStrW& PropertyName, PropType* Native, bool PropAllowInheritance = true, IJSComplex::NotifyFn Update = NULL, IJSComplex::NotifyFn Refresh = NULL ); - -template class CJSComplex : public IJSComplex -{ -public: - typedef STL_HASH_MAP ReflectorTable; - template friend class CJSComplexPropertyAccessor; - JSObject* m_JS; - - - std::vector m_Watches; - - ReflectorTable m_Reflectors; - - static JSPropertySpec JSI_props[]; - static std::vector m_Methods; - static PropertyTable m_IntrinsicProperties; - - -public: - static JSClass JSI_class; - - // Whether native code is responsible for managing this object. - // Script constructors should clear this *BEFORE* creating a JS - // mirror (otherwise it'll be rooted). - - bool m_EngineOwned; - - // JS Property access - bool GetProperty( JSContext* cx, const CStrW& PropertyName, jsval* vp ); - void SetProperty( JSContext* cx, const CStrW& PropertyName, jsval* vp ); - void WatchNotify( JSContext* cx, const CStrW& PropertyName, jsval* newval ); - - // - // Functions that must be provided to JavaScript - // - static JSBool JSGetProperty( JSContext* cx, JSObject* obj, jsval id, jsval* vp ); - static JSBool JSSetProperty( JSContext* cx, JSObject* obj, jsval id, jsval* vp ); - static JSBool JSEnumerate( JSContext* cx, JSObject* obj, JSIterateOp enum_op, jsval* statep, jsid *idp ); - static JSBool SetWatchAll( JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval ); - static JSBool UnWatchAll( JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval ); - static void ScriptingInit( const char* ClassName, JSNative Constructor = NULL, uintN ConstructorMinArgs = 0 ); - static void ScriptingShutdown(); - static void DefaultFinalize( JSContext *cx, JSObject *obj ); - -public: - - // Creating and releasing script objects is done automatically most of the time, but you - // can do it explicitly. - void CreateScriptObject(); - void ReleaseScriptObject(); - - JSObject* GetScript() - { - if( !m_JS ) - CreateScriptObject(); - return( m_JS ); - } - - CJSComplex(); - virtual ~CJSComplex(); - void Shutdown(); - void SetBase( IJSComplex* Parent ); - void Rebuild(); - - - IJSComplexProperty* HasProperty( const CStrW& PropertyName ); - - void FillEnumerateSet( IteratorState* it, CStrW* PropertyRoot = NULL ); - - - void AddProperty( const CStrW& PropertyName, jsval Value ); - void AddProperty( const CStrW& PropertyName, const CStrW& Value ); - - static void AddClassProperty( const CStrW& PropertyName, GetFn Getter, SetFn Setter = NULL ); - - // these functions are themselves templatized. we don't want to implement - // them in the header because that would drag in many dependencies. - // - // therefore, the publicly visible functions actually only call out to - // external friend functions implemented in the .inl file. - // these receive the template parameters from the class as well as the - // ones added for the member function. - // - // for non-static members, the friends additionally take a "this" pointer. - template - static void AddMethod( const char* Name, uintN MinArgs ) - { - AddMethodImpl(Name, MinArgs); - } - - template - static void AddClassProperty( const CStrW& PropertyName, PropType T::*Native, bool PropAllowInheritance = true, NotifyFn Update = NULL, NotifyFn Refresh = NULL ) - { - AddClassPropertyImpl(PropertyName, Native, PropAllowInheritance, Update, Refresh); - } - - template - static void AddReadOnlyClassProperty( const CStrW& PropertyName, PropType T::*Native, bool PropAllowInheritance = true, NotifyFn Update = NULL, NotifyFn Refresh = NULL ) - { - AddReadOnlyClassPropertyImpl(PropertyName, Native, PropAllowInheritance, Update, Refresh); - } - - // PropertyName must not already exist! (verified in debug build) - template - void AddProperty( const CStrW& PropertyName, PropType* Native, bool PropAllowInheritance = true, NotifyFn Update = NULL, NotifyFn Refresh = NULL ) - { - MemberAddPropertyImpl(this, PropertyName, Native, PropAllowInheritance, Update, Refresh); - } - - // PropertyName must not already exist! (verified in debug build) - template - void AddReadOnlyProperty( const CStrW& PropertyName, PropType* Native, bool PropAllowInheritance = true, NotifyFn Update = NULL, NotifyFn Refresh = NULL ) - { - MemberAddReadOnlyPropertyImpl(this, PropertyName, Native, PropAllowInheritance, Update, Refresh); - } - - // helper routine for Add*Property. Their interface requires the - // property not already exist; we check for this (in debug builds) - // and if so, warn and free the previously new-ed memory in - // m_Properties[PropertyName] (avoids mem leak). - void DeletePreviouslyAssignedProperty( const CStrW& PropertyName ); -}; - - -// -// static members -// - -template JSClass CJSComplex::JSI_class = { - NULL, JSCLASS_HAS_PRIVATE | JSCLASS_NEW_ENUMERATE, - JS_PropertyStub, JS_PropertyStub, - JSGetProperty, JSSetProperty, - (JSEnumerateOp)JSEnumerate, JS_ResolveStub, - JS_ConvertStub, DefaultFinalize, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL -}; - -template JSPropertySpec CJSComplex::JSI_props[] = { - { NULL, 0, 0, NULL, NULL }, -}; - -template std::vector CJSComplex::m_Methods; -template typename CJSComplex::PropertyTable CJSComplex::m_IntrinsicProperties; - - - -template -void ScriptableComplex_InitComplexPropertyAccessor(); - - - -// -// suballocator for CJSComplex.m_Properties elements -// (referenced from implementation in .inl) -// -extern void jscomplexproperty_suballoc_attach(); -extern void jscomplexproperty_suballoc_detach(); -extern void* jscomplexproperty_suballoc(); -extern void jscomplexproperty_suballoc_free(IJSComplexProperty* p); - -#endif Property changes on: ps/trunk/source/scripting/ScriptableComplex.h ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Index: ps/trunk/source/scripting/SynchedJSObject.cpp =================================================================== --- ps/trunk/source/scripting/SynchedJSObject.cpp (revision 7838) +++ ps/trunk/source/scripting/SynchedJSObject.cpp (nonexistent) @@ -1,122 +0,0 @@ -/* Copyright (C) 2009 Wildfire Games. - * This file is part of 0 A.D. - * - * 0 A.D. 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 2 of the License, or - * (at your option) any later version. - * - * 0 A.D. 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 0 A.D. If not, see . - */ - -#include "precompiled.h" - -#include "SynchedJSObject.h" -#include "ps/Parser.h" -#include "ScriptCustomTypes.h" - -template <> -CStrW ToNetString(const size_t &val) -{ - return CStrW((unsigned long)val); -} - -template <> -void SetFromNetString(size_t &val, const CStrW& string) -{ - val=string.ToUInt(); -} - -template <> -CStrW ToNetString(const int &val) -{ - return CStrW(val); -} - -template <> -void SetFromNetString(int &val, const CStrW& string) -{ - val=string.ToInt(); -} - -template <> -CStrW ToNetString(const bool &val) -{ - return val ? L"true" : L"false"; -} - -template <> -void SetFromNetString(bool &val, const CStrW& string) -{ - val = (string == L"true"); -} - -template <> -CStrW ToNetString(const CStrW& data) -{ - return data; -} - -template <> void SetFromNetString(CStrW& data, const CStrW& string) -{ - data=string; -} - -template <> -CStrW ToNetString(const SColour &data) -{ - wchar_t buf[256]; - swprintf_s(buf, ARRAY_SIZE(buf), L"%f %f %f %f", data.r, data.g, data.b, data.a); - - return buf; -} - -template <> -void SetFromNetString(SColour &data, const CStrW& wstring) -{ - CParser &parser(CParserCache::Get("$value_$value_$value_$value")); - CParserLine line; - - line.ParseString(parser, CStr(wstring)); - - float values[4]; - if (line.GetArgCount() != 4) return; - for (size_t i=0; i<4; ++i) - { - if (!line.GetArgFloat(i, values[i])) - { - return; - } - } - - data.r = values[0]; - data.g = values[1]; - data.b = values[2]; - data.a = values[3]; -} - -void CSynchedJSObjectBase::IterateSynchedProperties(IterateCB *cb, void *userdata) -{ - SynchedPropertyIterator it=m_SynchedProperties.begin(); - while (it != m_SynchedProperties.end()) - { - cb(it->first, it->second, userdata); - ++it; - } -} - -ISynchedJSProperty *CSynchedJSObjectBase::GetSynchedProperty(const CStrW& name) -{ - SynchedPropertyIterator prop=m_SynchedProperties.find(name); - if (prop != m_SynchedProperties.end()) - return prop->second; - else - return NULL; -} - Property changes on: ps/trunk/source/scripting/SynchedJSObject.cpp ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Index: ps/trunk/source/scripting/DOMEvent.h =================================================================== --- ps/trunk/source/scripting/DOMEvent.h (revision 7838) +++ ps/trunk/source/scripting/DOMEvent.h (nonexistent) @@ -1,152 +0,0 @@ -/* Copyright (C) 2009 Wildfire Games. - * This file is part of 0 A.D. - * - * 0 A.D. 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 2 of the License, or - * (at your option) any later version. - * - * 0 A.D. 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 0 A.D. If not, see . - */ - -// DOM-style event object - -// Note: Cancellable [UK]? Cancelable [US]? DOM says one l, OED says 2. -// JS interface uses 1. - -// Entity and e.g. projectile classes derive from this and use it for -// sending/receiving events. - -#ifndef INCLUDED_DOMEVENT -#define INCLUDED_DOMEVENT - -#include "ScriptableObject.h" -#include "EventTypes.h" // for EVENT_LAST - -class CScriptObject; -class CScriptEvent; - -typedef CScriptObject* DOMEventHandler; - -class IEventTarget -{ - // Return 'true' if we should stop propagating. - bool _DispatchEvent( CScriptEvent* evt, IEventTarget* target ); - - // Events dispatched to this object are sent here before being processed. - IEventTarget* before; - // Events dispatched to this object are sent here after being processed. - IEventTarget* after; - - typedef std::vector HandlerList; - HandlerList m_Handlers_id[EVENT_LAST]; - typedef STL_HASH_MULTIMAP HandlerMap; - HandlerMap m_Handlers_name; - typedef std::pair HandlerRange; -public: - IEventTarget() - { - before = NULL; - after = NULL; - } - virtual ~IEventTarget(); - // Set target that will receive each event after it is processed. - // unused - inline void SetPriorObject( IEventTarget* obj ) - { - before = obj; - } - // Set target that will receive each event after it is processed. - // used by Entity and EntityTemplate. - inline void SetNextObject( IEventTarget* obj ) - { - after = obj; - } - - // Register a handler for the given event type. - // Returns false if the handler was already present - bool AddHandler( size_t TypeCode, DOMEventHandler handler ); - bool AddHandler( const CStrW& TypeString, DOMEventHandler handler ); - // Remove a previously registered handler for the specified event. - // Returns false if the handler was not present - bool RemoveHandler( size_t TypeCode, DOMEventHandler handler ); - bool RemoveHandler( const CStrW& TypeString, DOMEventHandler handler ); - - // called by ScriptGlue.cpp for add|RemoveGlobalHandler - bool AddHandlerJS( JSContext* cx, uintN argc, jsval* argv ); - bool RemoveHandlerJS( JSContext* cx, uintN argc, jsval* argv ); - - // Return the JSObject* we'd like to be the 'this' object - // when executing the handler. The argument is the object - // to which the event is targeted. - // It is passed to CScriptObject::DispatchEvent. - virtual JSObject* GetScriptExecContext( IEventTarget* target ) = 0; - - // Dispatch an event to its handler. - // returns: whether the event arrived (i.e. wasn't cancelled) [bool] - bool DispatchEvent( CScriptEvent* evt ); -}; - -class CScriptEvent : public CJSObject -{ -public: - enum EPhaseType - { - CAPTURING_PHASE = 1, - AT_TARGET = 2, - BUBBLING_PHASE = 3 - }; - - // Target (currently unused) - IEventTarget* m_Target; - - // Listening object currently being processed (currently unused) - IEventTarget* m_CurrentTarget; - - // Phase type (currently unused) - // EPhaseType m_EventPhase; - - // Can bubble? (currently unused) - // bool m_Bubbles; - - // Can be cancelled (default actions prevented) - bool m_Cancelable; - - // Can be blocked (prevented from propogating along the handler chain) - bool m_Blockable; - - // Timestamp (milliseconds since epoch (start of game?)) - size_t m_Timestamp; - - // Event type string - CStrW m_Type; - - // Type code (to speed lookups) - size_t m_TypeCode; - - // Has been cancelled? - bool m_Cancelled; - - // Has it been blocked (won't be sent to any more handlers) - bool m_Blocked; - -// -- - - CStr ToString( JSContext* cx, uintN argc, jsval* argv ); - void PreventDefault( JSContext* cx, uintN argc, jsval* argv ); - void StopPropagation( JSContext* cx, uintN argc, jsval* argv ); - -public: - CScriptEvent( const CStrW& Type, size_t TypeCode = ~size_t(0), bool Cancelable = true, bool Blockable = true ); - static void ScriptingInit(); -}; - -#endif - - Property changes on: ps/trunk/source/scripting/DOMEvent.h ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Index: ps/trunk/source/scripting/SynchedJSObject.h =================================================================== --- ps/trunk/source/scripting/SynchedJSObject.h (revision 7838) +++ ps/trunk/source/scripting/SynchedJSObject.h (nonexistent) @@ -1,167 +0,0 @@ -/* Copyright (C) 2009 Wildfire Games. - * This file is part of 0 A.D. - * - * 0 A.D. 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 2 of the License, or - * (at your option) any later version. - * - * 0 A.D. 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 0 A.D. If not, see . - */ - -/* - CSynchedJSObject - - DESCRIPTION: - A helper class for CJSObject that enables a callback to be called - whenever an attribute of the class changes and enables all (synched) - properties to be set and retrieved as strings for network sync. - - All string conversions are performed by specific functions that use a - strictly (hrm) defined format - or at least a format that is specific - for the type in question (which is why JSParseString can't be used - - the JS interface's ToString function is also not usable since it often - produces a human-readable format that doesn't parse well and might - change outside the control of the network protocol). - - This replaces CAttributeMap for both player and game attributes. - - USAGE: - First you must create your subclass, make it inherit from - CSynchedJSObject and implement the pure virtual method Update (see - prototype below) - - Then you may use it just like CJSObject (see ScriptableObject.h) - with - one exception: Any property you want to be synchronized (i.e. have the - new property functionality including the update callback) is added using - the AddSynchedProperty method instead: AddSynchedProperty(name, &m_Property) - - The extra arguments that exist in the AddProperty method haven't been - implemented (if you by any chance would need to, just do it ;-) - -*/ - -#ifndef INCLUDED_SYNCHEDJSOBJECT -#define INCLUDED_SYNCHEDJSOBJECT - -#include "ps/CStr.h" -#include "ScriptableObject.h" - -template -void SetFromNetString(T &data, const CStrW& string); - -template -CStrW ToNetString(const T &data); - -#define TYPE(type) \ - template <> CStrW ToNetString(const type &data); \ - template <> void SetFromNetString(type &data, const CStrW& string); - -TYPE(size_t) -TYPE(CStrW) - -#undef TYPE - -class ISynchedJSProperty: public IJSProperty -{ -public: - virtual void FromString(const CStrW& value)=0; - virtual CStrW ToString()=0; -}; - -// non-templated base class -struct CSynchedJSObjectBase -{ - typedef void (*UpdateFn)(CSynchedJSObjectBase *owner); - - template - class CSynchedJSProperty: public ISynchedJSProperty - { - PropType *m_Data; - CStrW m_Name; - CSynchedJSObjectBase *m_Owner; - UpdateFn m_Update; - - virtual void Set(JSContext* cx, IJSObject* UNUSED(owner), jsval value) - { - if (!ReadOnly) - { - if (ToPrimitive(cx, value, *m_Data)) - { - m_Owner->Update(m_Name, this); - if (m_Update) - m_Update(m_Owner); - } - } - } - virtual jsval Get(JSContext* UNUSED(cx), IJSObject* UNUSED(owner)) - { - return ToJSVal(*m_Data); - } - - virtual void ImmediateCopy(IJSObject* UNUSED(CopyFrom), IJSObject* UNUSED(CopyTo), IJSProperty* other) - { - *m_Data = *( ((CSynchedJSProperty*)other)->m_Data ); - } - - virtual void FromString(const CStrW& value) - { - SetFromNetString(*m_Data, value); - if (m_Update) - m_Update(m_Owner); - } - - virtual CStrW ToString() - { - return ToNetString(*m_Data); - } - - public: - inline CSynchedJSProperty(const CStrW& name, PropType* native, CSynchedJSObjectBase *owner, UpdateFn update=NULL): - m_Data(native), - m_Name(name), - m_Owner(owner), - m_Update(update) - { - } - }; - - typedef STL_HASH_MAP SynchedPropertyTable; - typedef SynchedPropertyTable::iterator SynchedPropertyIterator; - SynchedPropertyTable m_SynchedProperties; - -protected: - virtual ~CSynchedJSObjectBase() { } - - // Called every time a property changes. - // This is where the individual callbacks are dispatched from. - virtual void Update(const CStrW& name, ISynchedJSProperty *prop)=0; - -public: - ISynchedJSProperty *GetSynchedProperty(const CStrW& name); - - typedef void (IterateCB)(const CStrW& name, ISynchedJSProperty *prop, void *userdata); - void IterateSynchedProperties(IterateCB *cb, void *userdata); -}; - -template -class CSynchedJSObject: public CJSObject, public CSynchedJSObjectBase -{ -protected: - // Add a property to the object; if desired, a callback is called every time it changes. - // Replaces CJSObject's AddProperty. - template void AddSynchedProperty(const CStrW& name, T *native, UpdateFn update=NULL) - { - ISynchedJSProperty *prop=new CSynchedJSProperty(name, native, this, update); - this->m_NonsharedProperties[name]=prop; - this->m_SynchedProperties[name]=prop; - } -}; - -#endif Property changes on: ps/trunk/source/scripting/SynchedJSObject.h ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Index: ps/trunk/source/scripting/ScriptObject.h =================================================================== --- ps/trunk/source/scripting/ScriptObject.h (revision 7838) +++ ps/trunk/source/scripting/ScriptObject.h (nonexistent) @@ -1,73 +0,0 @@ -/* Copyright (C) 2009 Wildfire Games. - * This file is part of 0 A.D. - * - * 0 A.D. 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 2 of the License, or - * (at your option) any later version. - * - * 0 A.D. 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 0 A.D. If not, see . - */ - -// A generic type and some helper functions -// for scripts - -#ifndef INCLUDED_SCRIPTOBJECT -#define INCLUDED_SCRIPTOBJECT - -#include "scripting/SpiderMonkey.h" - -class CStrW; -class CScriptEvent; - -class CScriptObject -{ - JSFunction* Function; - JSObject* FunctionObject; - void Root(); - void Uproot(); - -public: - - CScriptObject(); - CScriptObject( JSFunction* _Function ); - CScriptObject( jsval v ); - CScriptObject( const CScriptObject& copy ); - - ~CScriptObject(); - - // Initialize in various ways: from a JS function, a string to be compiled, or a jsval containing either. - void SetFunction( JSFunction* _Function ); - void SetJSVal( jsval v ); - void Compile( const CStrW& FileNameTag, const CStrW& FunctionBody ); - - inline bool Defined() - { - return( Function != NULL ); - } - - inline operator bool() { return( Function != NULL ); } - inline bool operator!() { return( !Function ); } - inline bool operator==( const CScriptObject& compare ) { return( Function == compare.Function ); } - - // JSObject wrapping the function if it's defined, NULL if it isn't. - JSObject* GetFunctionObject(); - - // Executes a script attached to a JS object. - // Returns false if the script isn't defined, if the script can't be executed, - // otherwise true. Script return value is in rval. - bool Run( JSObject* Context, jsval* rval, uintN argc = 0, jsval* argv = NULL ); - // This variant casts script return value to a boolean, and passes it back. - bool Run( JSObject* Context, uintN argc = 0, jsval* argv = NULL ); - - // Treat this as an event handler and dispatch an event to it. - bool DispatchEvent( JSObject* Context, CScriptEvent* evt ); -}; - -#endif Property changes on: ps/trunk/source/scripting/ScriptObject.h ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Index: ps/trunk/source/scripting/ScriptableComplex.inl =================================================================== --- ps/trunk/source/scripting/ScriptableComplex.inl (revision 7838) +++ ps/trunk/source/scripting/ScriptableComplex.inl (nonexistent) @@ -1,1105 +0,0 @@ -/* -Implementation of CJSComplex's functions and related helper functions and classes. -This file must be #included in any CPP file that accesses these functions directly, -but may be omitted in those that don't. - -rationale: -we are changing CJSComplex often for purposes of optimization. this triggers -close to a full rebuild, which is unacceptable. -ideally, we would move the method implementations into a cpp file, and -then only that need be recompiled. -unfortunately that is exactly what the export keyword enables, -which is not likely to be implemented in VC. - -several workarounds have been investigated. -1) reduce symptoms of the problem by reducing #includes of entity.h, which is - what pulls CJSComplex in. done; it's been removed entirely from headers and - is currently only left in ~30 cpp files (could probably be - further reduced) - -2) de-templatize CJSComplex. result would be enabling the normal split of - interface vs. implementation in cpp file. - - this would be mostly feasible because it's really only used by - CEntity and CEntityTemplate. however, since there are 2 users, the - static class data (notably m_Methods) would have to be duplicated for each. - one possibility would be an 'array' of each, indexed via class id. - - also, static data could be reduced by having property lookup be done - via per-property hash tables, instead of the root object holding one - large table of the entire property names (e.g. traits.health.max). - in retrospect, this would probably have been better, but is probably - too much work to change now. - -3) instead of CEntity IS-A CJSComplex, use composition and pImpl. this would - decouple entity.h from changes in scriptablecomplex.h. - however, we'd have to dynamically allocate the CJSComplex in each entity - (required by pImpl and less efficient/more annoying). also, there would - potentially be trouble with ToJSVal, since we no longer derive from - CJSComplex. - this is not deemed worth the effort due to steps taken in #1. - -We decided to split off the implementation of CJSComplex as well as many of its -helper classes into a separate header, ScriptableComplex.inl. This way, -ScriptableComplex.h does not need to be modified unless we change the API, -but the implementations of CJSComplex's methods can be changed. However, this -also means that this header (ScriptableComplex.inl) must be #included in any -CPP file that directly accesses ScriptableComplex's methods - otherwise, the -linker won't find the definitions of these functions. Right now this is only -5 files, which results in much faster rebuilds after modifying this code. -*/ - -#ifndef SCRIPTABLE_COMPLEX_INL_INCLUDED -#define SCRIPTABLE_COMPLEX_INL_INCLUDED - -#include "ScriptableComplex.h" - -//----------------------------------------------------------------------------- -// CJSComplexPropertyAccessor -//----------------------------------------------------------------------------- - - -template class CJSComplex; - -template class CJSComplexPropertyAccessor -{ - T* m_Owner; - CStrW m_PropertyRoot; - template friend class CJSComplex; - -public: - CJSComplexPropertyAccessor( T* Owner, const CStrW& PropertyRoot ) - { - m_Owner = Owner; - m_PropertyRoot = PropertyRoot; - } - - static JSObject* CreateAccessor( JSContext* cx, T* Owner, const CStrW& PropertyRoot ) - { - JSObject* Accessor = JS_NewObject( cx, &JSI_Class, NULL, NULL ); - JS_SetPrivate( cx, Accessor, new CJSComplexPropertyAccessor( Owner, PropertyRoot ) ); - - return( Accessor ); - } - - // JW: ugly, but more efficient than previous approach - // (string = root + "." + id;). saves 50ms during init. - // made macro to ensure there is no extra overhead. -#define BUILD_PROPNAME(root, id)\ - PropName.reserve(50);\ - PropName += root;\ - PropName += '.';\ - PropName += id; - - static JSBool JSGetProperty( JSContext* cx, JSObject* obj, jsval id, jsval* vp ) - { - CJSComplexPropertyAccessor* Instance = (CJSComplexPropertyAccessor*)JS_GetPrivate( cx, obj ); - if( !Instance ) return( JS_TRUE ); - - CStrW PropName; - BUILD_PROPNAME(Instance->m_PropertyRoot, g_ScriptingHost.ValueToUCString(id)); - - Instance->m_Owner->GetProperty( cx, PropName, vp ); - - return( JS_TRUE ); - } - - static JSBool JSSetProperty( JSContext* cx, JSObject* obj, jsval id, jsval* vp ) - { - CJSComplexPropertyAccessor* Instance = (CJSComplexPropertyAccessor*)JS_GetPrivate( cx, obj ); - if( !Instance ) return( JS_TRUE ); - - CStrW PropName; - BUILD_PROPNAME(Instance->m_PropertyRoot, g_ScriptingHost.ValueToUCString(id)); - - Instance->m_Owner->SetProperty( cx, PropName, vp ); - - return( JS_TRUE ); - } - - static JSBool JSEnumerate( JSContext* cx, JSObject* obj, JSIterateOp enum_op, jsval* statep, jsid *idp ) - { - IJSComplex::IteratorState* it; - - switch( enum_op ) - { - case JSENUMERATE_INIT: - { - CJSComplexPropertyAccessor* Instance = (CJSComplexPropertyAccessor*)JS_GetPrivate( cx, obj ); - - it = new IJSComplex::IteratorState; - - if( Instance ) - { - size_t rlen = Instance->m_PropertyRoot.length(); - - IJSComplex::PropertyTable::iterator iit; - for( iit = T::m_IntrinsicProperties.begin(); iit != T::m_IntrinsicProperties.end(); iit++ ) - if( ( iit->first.length() > rlen ) && ( iit->first.Left( rlen ) == Instance->m_PropertyRoot ) && ( iit->first[rlen] == '.' ) ) - it->first.insert( CStrW( iit->first.substr( rlen + 1 ) ).BeforeFirst( L"." ) ); - - - Instance->m_Owner->FillEnumerateSet( it, &( Instance->m_PropertyRoot ) ); - } - it->second = it->first.begin(); - - *statep = PRIVATE_TO_JSVAL( it ); - - if( idp ) - *idp = INT_TO_JSVAL( it->first.size() ); - - return( JS_TRUE ); - } - case JSENUMERATE_NEXT: - it = (IJSComplex::IteratorState*)JSVAL_TO_PRIVATE( *statep ); - if( it->second == it->first.end() ) - { - delete( it ); - *statep = JSVAL_NULL; - return( JS_TRUE ); - } - - // I think this is what I'm supposed to do... (cheers, Philip) - if( !JS_ValueToId( cx, ToJSVal( *( it->second ) ), idp ) ) - return( JS_FALSE ); - - // EVIL HACK: since https://bugzilla.mozilla.org/show_bug.cgi?id=261887 (which is in - // the SpiderMonkey 1.6 release, and not in 1.5), you can't enumerate properties that - // don't actually exist on the object. This should probably be fixed by defining a custom - // Resolve function to make them look like they exist, but for now we just define the - // property on the object here so that it will exist by the time the JS iteration code - // does its checks. - JS_DefineProperty(cx, obj, CStr(*it->second).c_str(), JSVAL_VOID, NULL, NULL, 0); - - (it->second)++; - - *statep = PRIVATE_TO_JSVAL( it ); - return( JS_TRUE ); - case JSENUMERATE_DESTROY: - it = (IJSComplex::IteratorState*)JSVAL_TO_PRIVATE( *statep ); - delete( it ); - *statep = JSVAL_NULL; - return( JS_TRUE ); - } - return( JS_FALSE ); - } - - static JSBool JSPrimitive( JSContext* cx, JSObject* obj, uintN UNUSED(argc), jsval* UNUSED(argv), jsval* rval ) - { - CJSComplexPropertyAccessor* Instance = (CJSComplexPropertyAccessor*)JS_GetPrivate( cx, obj ); - if( !Instance ) return( JS_TRUE ); - - // Check all along the inheritance tree - // Possible optimization: Store the hashed value over the lookups - IJSComplex* Target = Instance->m_Owner; - IJSComplexProperty* Property; - - while( Target ) - { - Property = Target->HasProperty( Instance->m_PropertyRoot ); - if( Property ) - { - *rval = Property->Get( cx, Target ); - break; - } - Target = Target->m_Parent; - } - - return( JS_TRUE ); - } - - static JSBool JSToString( JSContext* cx, JSObject* obj, uintN UNUSED(argc), jsval* UNUSED(argv), jsval* rval ) - { - CJSComplexPropertyAccessor* Instance = (CJSComplexPropertyAccessor*)JS_GetPrivate( cx, obj ); - if( !Instance ) return( JS_TRUE ); - - // Check all along the inheritance tree - // TODO: Optimization: Store the hashed value over the lookups - IJSComplex* Target = Instance->m_Owner; - IJSComplexProperty* Property; - JSString* str = NULL; - - while( Target ) - { - Property = Target->HasProperty( Instance->m_PropertyRoot ); - if( Property ) - { - str = JS_ValueToString( cx, Property->Get( cx, Target ) ); - break; - } - Target = Target->m_Parent; - } - - *rval = STRING_TO_JSVAL( str ); - - return( JS_TRUE ); - } - - static JSClass JSI_Class; - - static void ScriptingInit() - { - JSFunctionSpec JSI_methods[] = { { "valueOf", JSPrimitive, 0, 0, 0 }, { "toString", JSToString, 0, 0, 0 }, { 0 } }; - JSPropertySpec JSI_props[] = { { 0 } }; - - g_ScriptingHost.DefineCustomObjectType( &JSI_Class, NULL, 0, JSI_props, JSI_methods, NULL, NULL ); - } -}; - -template JSClass CJSComplexPropertyAccessor::JSI_Class = { - "Property", JSCLASS_HAS_PRIVATE | JSCLASS_NEW_ENUMERATE, - JS_PropertyStub, JS_PropertyStub, - JSGetProperty, JSSetProperty, - (JSEnumerateOp)JSEnumerate, JS_ResolveStub, - JS_ConvertStub, JS_FinalizeStub, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL -}; - - -template -void ScriptableComplex_InitComplexPropertyAccessor() -{ - CJSComplexPropertyAccessor::ScriptingInit(); -} - - -//----------------------------------------------------------------------------- -// various property types -//----------------------------------------------------------------------------- - -template class CJSSharedProperty : public IJSComplexProperty -{ - T IJSComplex::*m_Data; - - // Function on Owner to call after value is changed - IJSComplex::NotifyFn m_Update; - - // Function on Owner to call before reading or writing the value - IJSComplex::NotifyFn m_Freshen; - -public: - CJSSharedProperty( T IJSComplex::*Data, bool AllowsInheritance = false, IJSComplex::NotifyFn Update = NULL, IJSComplex::NotifyFn Freshen = NULL ) - { - m_Data = Data; - m_AllowsInheritance = AllowsInheritance; - m_Update = Update; - m_Freshen = Freshen; - m_Intrinsic = true; - m_Inherited = true; - } - jsval Get( JSContext* UNUSED(cx), IJSComplex* owner ) - { - if( m_Freshen ) (owner->*m_Freshen)(); - return( ToJSVal( owner->*m_Data ) ); - } - void ImmediateCopy( IJSComplex* CopyTo, IJSComplex* CopyFrom, IJSComplexProperty* CopyProperty ) - { - CJSSharedProperty* otherProp = (CJSSharedProperty*) CopyProperty; - T IJSComplex::*otherData = otherProp->m_Data; - CopyTo->*m_Data = CopyFrom->*otherData; - } - void Set( JSContext* cx, IJSComplex* owner, jsval Value ) - { - if( !ReadOnly ) - { - if( m_Freshen ) (owner->*m_Freshen)(); - if( ToPrimitive( cx, Value, owner->*m_Data ) ) - if( m_Update ) (owner->*m_Update)(); - } - } - -}; - -template class CJSComplexProperty : public IJSComplexProperty -{ - T* m_Data; - - // Function on Owner to call after value is changed - IJSComplex::NotifyFn m_Update; - - // Function on Owner to call before reading or writing the value - IJSComplex::NotifyFn m_Freshen; - -public: - CJSComplexProperty( T* Data, bool AllowsInheritance = false, IJSComplex::NotifyFn Update = NULL, IJSComplex::NotifyFn Freshen = NULL ) - { - m_Data = Data; - m_AllowsInheritance = AllowsInheritance; - m_Update = Update; - m_Freshen = Freshen; - m_Intrinsic = true; - } - jsval Get( JSContext* UNUSED(cx), IJSComplex* owner ) - { - if( m_Freshen ) (owner->*m_Freshen)(); - return( ToJSVal( *m_Data ) ); - } - void ImmediateCopy( IJSComplex* UNUSED(CopyTo), IJSComplex* UNUSED(CopyFrom), IJSComplexProperty* CopyProperty ) - { - *m_Data = *( ( (CJSComplexProperty*)CopyProperty )->m_Data ); - } - void Set( JSContext* cx, IJSComplex* owner, jsval Value ) - { - if( !ReadOnly ) - { - if( m_Freshen ) (owner->*m_Freshen)(); - if( ToPrimitive( cx, Value, *m_Data ) ) - if( m_Update ) (owner->*m_Update)(); - } - } - -}; - - -class CJSReflector -{ - template friend class CJSComplex; - JSObject* m_JSAccessor; -}; - -class CJSDynamicComplexProperty : public IJSComplexProperty -{ - template friend class CJSComplex; - - JSObject* m_JSAccessor; -public: - CJSDynamicComplexProperty() - { - m_JSAccessor = NULL; - m_Intrinsic = false; - m_Inherited = false; - } -}; - -class CJSValComplexProperty : public CJSDynamicComplexProperty -{ - template friend class CJSComplex; - - jsval m_Data; - -public: - CJSValComplexProperty( jsval Data, bool Inherited ) - { - m_Inherited = Inherited; - m_Data = Data; - Root(); - } - ~CJSValComplexProperty() - { - Uproot(); - } - void Root() - { - if( JSVAL_IS_GCTHING( m_Data ) ) -#ifndef NDEBUG - JS_AddNamedRoot( g_ScriptingHost.GetContext(), (void*)&m_Data, "jsval property" ); -#else - JS_AddRoot( g_ScriptingHost.GetContext(), (void*)&m_Data ); -#endif - } - void Uproot() - { - if( JSVAL_IS_GCTHING( m_Data ) ) - JS_RemoveRoot( g_ScriptingHost.GetContext(), (void*)&m_Data ); - } - jsval Get( JSContext* UNUSED(cx), IJSComplex* UNUSED(owner) ) - { - return( m_Data ); - } - void Set( JSContext* UNUSED(cx), IJSComplex* UNUSED(owner), jsval Value ) - { - Uproot(); - m_Data = Value; - Root(); - } - void ImmediateCopy( IJSComplex* UNUSED(CopyTo), IJSComplex* UNUSED(CopyFrom), - IJSComplexProperty* UNUSED(CopyProperty) ) - { - debug_warn(L"ImmediateCopy called on a CJSValComplexProperty (something's gone wrong with the inheritance on this object)" ); - } -}; - -class CJSFunctionComplexProperty : public IJSComplexProperty -{ - // Function on Owner to get the value - IJSComplex::GetFn m_Getter; - - // Function on Owner to set the value - IJSComplex::SetFn m_Setter; - -public: - CJSFunctionComplexProperty( IJSComplex::GetFn Getter, IJSComplex::SetFn Setter ) - { - m_Inherited = false; - m_Intrinsic = true; - m_Getter = Getter; - m_Setter = Setter; - // Must at least be able to read - debug_assert( m_Getter ); - } - jsval Get( JSContext* UNUSED(cx), IJSComplex* owner ) - { - return( (owner->*m_Getter)() ); - } - void Set( JSContext* UNUSED(cx), IJSComplex* owner, jsval Value ) - { - if( m_Setter ) - (owner->*m_Setter)( Value ); - } - void ImmediateCopy( IJSComplex* UNUSED(CopyTo), IJSComplex* UNUSED(CopyFrom), IJSComplexProperty* UNUSED(CopyProperty) ) - { - debug_warn(L"ImmediateCopy called on a property wrapping getter/setter functions (something's gone wrong with the inheritance for this object)" ); - } -}; - - -// Wrapper around native functions that are attached to CJSComplexs - -template class CNativeComplexFunction -{ -public: - static JSBool JSFunction( JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval ) - { - T* Native = ToNative( cx, obj ); - if( !Native ) - return( JS_TRUE ); - - *rval = ToJSVal( (Native->*NativeFunction)( cx, argc, argv ) ); - - return( JS_TRUE ); - } -}; - -//----------------------------------------------------------------------------- -// CJSComplex implementation -//----------------------------------------------------------------------------- - -template -void CJSComplex::SetProperty( JSContext* cx, const CStrW& PropertyName, jsval* vp ) -{ - if( !ReadOnly ) - { - IJSComplexProperty* prop = HasProperty( PropertyName ); - - if( prop ) - { - // Already exists - WatchNotify( cx, PropertyName, vp ); - prop->Set( cx, this, *vp ); - - if(!prop->m_Intrinsic) - prop->m_Inherited = false; - - // If it's a C++ property, reflect this change in objects that inherit this. - if( prop->m_AllowsInheritance && prop->m_Intrinsic ) - { - InheritorsList UpdateSet( m_Inheritors ); - - while( !UpdateSet.empty() ) - { - IJSComplex* UpdateObj = UpdateSet.back(); - UpdateSet.pop_back(); - IJSComplexProperty* UpdateProp = UpdateObj->HasProperty( PropertyName ); - // Property must exist, also be a C++ property, and not have its value specified. - if( UpdateProp && UpdateProp->m_Intrinsic && UpdateProp->m_Inherited ) - { - UpdateProp->Set( cx, this, *vp ); - InheritorsList::iterator it2; - for( it2 = UpdateObj->m_Inheritors.begin(); it2 != UpdateObj->m_Inheritors.end(); it2++ ) - UpdateSet.push_back( *it2 ); - } - } - } - } - else - { - // Need to add it - WatchNotify( cx, PropertyName, vp ); - AddProperty( PropertyName, *vp ); - } - } -} - - -template -void CJSComplex::WatchNotify( JSContext* cx, const CStrW& PropertyName, jsval* newval ) -{ - if( m_Watches.empty() ) return; - - jsval oldval = JSVAL_VOID; - GetProperty( cx, PropertyName, &oldval ); - - jsval args[3] = { ToJSVal( PropertyName ), oldval, *newval }; - - std::vector::iterator it; - for( it = m_Watches.begin(); it != m_Watches.end(); it++ ) - if( it->Run( GetScript(), newval, 3, args ) ) - args[2] = *newval; -} - - -// -// Functions that must be provided to JavaScript -// - -template -JSBool CJSComplex::JSGetProperty( JSContext* cx, JSObject* obj, jsval id, jsval* vp ) -{ - T* Instance = ToNative( cx, obj ); - if( !Instance ) - return( JS_TRUE ); - - CStrW PropName = g_ScriptingHost.ValueToUCString( id ); - - if( !Instance->GetProperty( cx, PropName, vp ) ) - return( JS_TRUE ); - - return( JS_TRUE ); -} - - -template -JSBool CJSComplex::JSSetProperty( JSContext* cx, JSObject* obj, jsval id, jsval* vp ) -{ - T* Instance = ToNative( cx, obj ); - if( !Instance ) - return( JS_TRUE ); - - CStrW PropName = g_ScriptingHost.ValueToUCString( id ); - - Instance->SetProperty( cx, PropName, vp ); - - return( JS_TRUE ); -} - - -template -JSBool CJSComplex::JSEnumerate( JSContext* cx, JSObject* obj, JSIterateOp enum_op, jsval* statep, jsid *idp ) -{ - IteratorState* it; - - switch( enum_op ) - { - case JSENUMERATE_INIT: - { - T* Instance = ToNative( cx, obj ); - - it = new IteratorState; - if( Instance ) - { - PropertyTable::iterator iit; - for( iit = T::m_IntrinsicProperties.begin(); iit != T::m_IntrinsicProperties.end(); iit++ ) - it->first.insert( iit->first ); - - Instance->FillEnumerateSet( it ); - } - - it->second = it->first.begin(); - *statep = PRIVATE_TO_JSVAL( it ); - - if( idp ) - *idp = INT_TO_JSVAL( it->first.size() ); - - return( JS_TRUE ); - } - case JSENUMERATE_NEXT: - it = (IteratorState*)JSVAL_TO_PRIVATE( *statep ); - if( it->second == it->first.end() ) - { - delete( it ); - *statep = JSVAL_NULL; - return( JS_TRUE ); - } - - // I think this is what I'm supposed to do... (cheers, Philip) - if( !JS_ValueToId( cx, ToJSVal( *( it->second ) ), idp ) ) - return( JS_FALSE ); - - // EVIL HACK: see the comment in the other JSEnumerate - JS_DefineProperty(cx, obj, CStr(*it->second).c_str(), JSVAL_VOID, NULL, NULL, 0); - - (it->second)++; - - *statep = PRIVATE_TO_JSVAL( it ); - return( JS_TRUE ); - case JSENUMERATE_DESTROY: - it = (IteratorState*)JSVAL_TO_PRIVATE( *statep ); - delete( it ); - *statep = JSVAL_NULL; - return( JS_TRUE ); - } - return( JS_FALSE ); -} - - -template -JSBool CJSComplex::SetWatchAll( JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval ) -{ - T* Native = ToNative( cx, obj ); - if( !Native ) - return( JS_TRUE ); - - debug_assert( argc >= 1 ); - - CScriptObject watch( argv[0] ); - std::vector::iterator it; - for( it = Native->m_Watches.begin(); it != Native->m_Watches.end(); it++ ) - if( *it == watch ) - { - *rval = JSVAL_FALSE; - return( JS_TRUE ); - } - - Native->m_Watches.push_back( watch ); - *rval = JSVAL_TRUE; - return( JS_TRUE ); -} - - -template -JSBool CJSComplex::UnWatchAll( JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval ) -{ - T* Native = ToNative( cx, obj ); - if( !Native ) - return( JS_TRUE ); - - if( argc >= 1 ) - { - CScriptObject watch( argv[0] ); - std::vector::iterator it; - for( it = Native->m_Watches.begin(); it != Native->m_Watches.end(); it++ ) - if( *it == watch ) - { - Native->m_Watches.erase( it ); - *rval = JSVAL_TRUE; - return( JS_TRUE ); - } - - *rval = JSVAL_FALSE; - } - else - { - Native->m_Watches.clear(); - *rval = JSVAL_TRUE; - } - return( JS_TRUE ); -} - - -template -void CJSComplex::ScriptingInit( const char* ClassName, JSNative Constructor, uintN ConstructorMinArgs ) -{ - JSFunctionSpec* JSI_methods = new JSFunctionSpec[ m_Methods.size() + 3 ]; - size_t MethodID; - for( MethodID = 0; MethodID < m_Methods.size(); MethodID++ ) - JSI_methods[MethodID] = m_Methods[MethodID]; - - JSFunctionSpec watchAll = { "watchAll", SetWatchAll, 1, 0, 0 }; - JSI_methods[MethodID + 0] = watchAll; - - JSFunctionSpec unwatchAll = { "unwatchAll", UnWatchAll, 1, 0, 0 }; - JSI_methods[MethodID + 1] = unwatchAll; - - JSI_methods[MethodID + 2].name = 0; - - JSI_class.name = ClassName; - - g_ScriptingHost.DefineCustomObjectType( &JSI_class, Constructor, ConstructorMinArgs, JSI_props, JSI_methods, NULL, NULL ); - - delete[]( JSI_methods ); - - atexit( ScriptingShutdown ); -} - -template -void CJSComplex::ScriptingShutdown() -{ - PropertyTable::iterator it; - for( it = m_IntrinsicProperties.begin(); it != m_IntrinsicProperties.end(); it++ ) - delete( it->second ); -} - - -template -void CJSComplex::DefaultFinalize( JSContext *cx, JSObject *obj ) -{ - T* Instance = ToNative( cx, obj ); - if( !Instance || Instance->m_EngineOwned ) - return; - - delete( Instance ); - JS_SetPrivate( cx, obj, NULL ); -} - -// Creating and releasing script objects is done automatically most of the time, but you -// can do it explicitly. -template -void CJSComplex::CreateScriptObject() -{ - if( !m_JS ) - { - m_JS = JS_NewObject( g_ScriptingHost.GetContext(), &JSI_class, NULL, NULL ); - if( m_EngineOwned ) - { -#ifndef NDEBUG // Name the GC roots something more useful than 'ScriptableObject.h' - JS_AddNamedRoot( g_ScriptingHost.GetContext(), (void*)&m_JS, JSI_class.name ); -#else - JS_AddRoot( g_ScriptingHost.GetContext(), (void*)&m_JS ); -#endif - } - JS_SetPrivate( g_ScriptingHost.GetContext(), m_JS, (T*)this ); - } -} - - -template -void CJSComplex::ReleaseScriptObject() -{ - if( m_JS ) - { - JS_SetPrivate( g_ScriptingHost.GetContext(), m_JS, NULL ); - if( m_EngineOwned ) - JS_RemoveRoot( g_ScriptingHost.GetContext(), &m_JS ); - m_JS = NULL; - } -} - - -template -CJSComplex::CJSComplex() -{ - jscomplexproperty_suballoc_attach(); - - m_Parent = NULL; - m_JS = NULL; - m_EngineOwned = true; -} - - -template -CJSComplex::~CJSComplex() -{ - Shutdown(); - - jscomplexproperty_suballoc_detach(); -} - - -template -void CJSComplex::Shutdown() -{ - PropertyTable::iterator it; - for( it = m_Properties.begin(); it != m_Properties.end(); it++ ) - { - if( !it->second->m_Intrinsic ) - { - CJSDynamicComplexProperty* extProp = (CJSValComplexProperty*)it->second; - if( extProp->m_JSAccessor ) - { - CJSComplexPropertyAccessor< CJSComplex >* accessor = (CJSComplexPropertyAccessor< CJSComplex >*)JS_GetPrivate( g_ScriptingHost.GetContext(), extProp->m_JSAccessor ); - debug_assert( accessor ); - delete( accessor ); - JS_SetPrivate( g_ScriptingHost.GetContext(), extProp->m_JSAccessor, NULL ); - JS_RemoveRoot( g_ScriptingHost.GetContext(), &( extProp->m_JSAccessor ) ); - } - } - - jscomplexproperty_suballoc_free(it->second); - } - - - ReflectorTable::iterator it_a; - for( it_a = m_Reflectors.begin(); it_a != m_Reflectors.end(); it_a++ ) - { - CJSComplexPropertyAccessor< CJSComplex >* accessor = (CJSComplexPropertyAccessor< CJSComplex >*)JS_GetPrivate( g_ScriptingHost.GetContext(), it_a->second->m_JSAccessor ); - debug_assert( accessor ); - delete( accessor ); - JS_SetPrivate( g_ScriptingHost.GetContext(), it_a->second->m_JSAccessor, NULL ); - JS_RemoveRoot( g_ScriptingHost.GetContext(), &( it_a->second->m_JSAccessor ) ); - delete( it_a->second ); - } - ReleaseScriptObject(); -} - - -template -void CJSComplex::SetBase( IJSComplex* Parent ) -{ - if( m_Parent ) - { - // Remove this from the list of our parent's inheritors - InheritorsList::iterator it; - for( it = m_Parent->m_Inheritors.begin(); it != m_Parent->m_Inheritors.end(); it++ ) - if( (*it) == this ) - { - m_Parent->m_Inheritors.erase( it ); - break; - } - } - m_Parent = Parent; - if( m_Parent ) - { - // Place this in the list of our parent's inheritors - m_Parent->m_Inheritors.push_back( this ); - Rebuild(); - } -} - -template -void CJSComplex::Rebuild() -{ - PropertyTable::iterator it; - // For each intrinsic property we have, - for( it = m_Properties.begin(); it != m_Properties.end(); it++ ) - { - const CStrW& prop_name = it->first; - IJSComplexProperty* prop = it->second; - - if( !prop->m_Intrinsic || !prop->m_Inherited ) - continue; - - // Attempt to locate it in the parent - IJSComplexProperty* parent_prop = m_Parent->HasProperty( prop_name ); - - // If it doesn't have it, we've inherited from an object of a different type - // This isn't allowed at the moment; but I don't have an totally convincing - // reason for forbidding it entirely. Mind, I can't think of any use for it, - // either. - // If it can be inherited, inherit it. - if( parent_prop && parent_prop->m_AllowsInheritance ) - { - debug_assert( parent_prop->m_Intrinsic ); - prop->ImmediateCopy( this, m_Parent, parent_prop ); - } - } - // Do the same for the shared properties table, too - for( it = m_IntrinsicProperties.begin(); it != m_IntrinsicProperties.end(); it++ ) - { - const CStrW& prop_name = it->first; - IJSComplexProperty* prop = it->second; - - if( !prop->m_Inherited ) - continue; - - IJSComplexProperty* parent_prop = m_Parent->HasProperty( prop_name ); - - if( parent_prop && parent_prop->m_AllowsInheritance ) - { - debug_assert( parent_prop->m_Intrinsic ); - prop->ImmediateCopy( this, m_Parent, parent_prop ); - } - } - - // Now recurse. - InheritorsList::iterator c; - for( c = m_Inheritors.begin(); c != m_Inheritors.end(); c++ ) - (*c)->Rebuild(); - -} - - -template -IJSComplexProperty* CJSComplex::HasProperty( const CStrW& PropertyName ) -{ - PropertyTable::iterator it; - it = T::m_IntrinsicProperties.find( PropertyName ); - if( it != T::m_IntrinsicProperties.end() ) - return( it->second ); - - it = m_Properties.find( PropertyName ); - if( it != m_Properties.end() ) - return( it->second ); - - return( NULL ); -} - - -template -void CJSComplex::FillEnumerateSet( IteratorState* it, CStrW* PropertyRoot) -{ - PropertyTable::iterator iit; - if( PropertyRoot ) - { - size_t rlen = PropertyRoot->length(); - for( iit = m_Properties.begin(); iit != m_Properties.end(); iit++ ) - if( ( iit->first.length() > rlen ) && ( iit->first.Left( rlen ) == *PropertyRoot ) && ( iit->first[rlen] == '.' ) ) - it->first.insert( CStrW( iit->first.substr( rlen + 1 ) ).BeforeFirst( L"." ) ); - } - else - { - for( iit = m_Properties.begin(); iit != m_Properties.end(); iit++ ) - it->first.insert( iit->first.BeforeFirst( L"." ) ); - } - if( m_Parent ) - m_Parent->FillEnumerateSet( it, PropertyRoot ); -} - - -template -void CJSComplex::AddProperty( const CStrW& PropertyName, jsval Value ) -{ - DeletePreviouslyAssignedProperty( PropertyName ); - void* mem = jscomplexproperty_suballoc(); - CJSDynamicComplexProperty* newProp = new(mem) CJSValComplexProperty( Value, false ); - m_Properties[PropertyName] = newProp; - - ReflectorTable::iterator it; - it = m_Reflectors.find( PropertyName ); - if( it != m_Reflectors.end() ) - { - // We had an accessor pointing to this property before it was defined. - newProp->m_JSAccessor = it->second->m_JSAccessor; - JS_RemoveRoot( g_ScriptingHost.GetContext(), &( it->second->m_JSAccessor ) ); - JS_AddRoot( g_ScriptingHost.GetContext(), &( newProp->m_JSAccessor ) ); - delete( it->second ); - m_Reflectors.erase( it ); - } -} - -template -void CJSComplex::AddProperty( const CStrW& PropertyName, const CStrW& Value ) -{ - AddProperty( PropertyName, JSParseString( Value ) ); -} - -template -void CJSComplex::AddClassProperty( const CStrW& PropertyName, GetFn Getter, SetFn Setter ) -{ - T::m_IntrinsicProperties[PropertyName] = new CJSFunctionComplexProperty( Getter, Setter ); -} - -// helper routine for Add*Property. Their interface requires the -// property not already exist; we check for this (in debug builds) -// and if so, warn and free the previously new-ed memory in -// m_Properties[PropertyName] (avoids mem leak). -template -void CJSComplex::DeletePreviouslyAssignedProperty( const CStrW& PropertyName ) -{ -#ifdef NDEBUG - UNUSED2(PropertyName); -#else - PropertyTable::iterator it; - it = m_Properties.find( PropertyName ); - if( it != m_Properties.end() ) - { - debug_warn(L"BUG: CJSComplexProperty added but already existed!"); - jscomplexproperty_suballoc_free(it->second); - } -#endif -} - - -template -bool CJSComplex::GetProperty( JSContext* cx, const CStrW& PropertyName, jsval* vp ) -{ - IJSComplexProperty* Property = HasProperty( PropertyName ); - if( Property && Property->m_Intrinsic ) - { - *vp = Property->Get( cx, this ); - } - else - { - CJSValComplexProperty* extProp; - - if( Property ) - { - extProp = (CJSValComplexProperty*)Property; - - // If it's already a JS object, there's no point in creating - // a PropertyAccessor for it; it can manage far better on its - // own (this was why valueOf() was necessary) - - if( !JSVAL_IS_OBJECT( extProp->m_Data ) ) - { - if( !extProp->m_JSAccessor ) - { - extProp->m_JSAccessor = CJSComplexPropertyAccessor< CJSComplex >::CreateAccessor( cx, this, PropertyName ); - JS_AddNamedRoot( cx, &extProp->m_JSAccessor, "property accessor" ); - } - - *vp = OBJECT_TO_JSVAL( extProp->m_JSAccessor ); - } - else - *vp = extProp->m_Data; - } - else - { - // Check to see if it exists on a parent - IJSComplex* check = m_Parent; - while( check ) - { - if( check->HasProperty( PropertyName ) ) break; - check = check->m_Parent; - } - - if( !check ) - return( false ); - - // FIXME: Fiddle a way so this /doesn't/ require multiple kilobytes - // of memory. Can't think of any better way to do it yet. Problem is - // that script may access a property that isn't defined locally, but - // is defined by an ancestor. We can't return an accessor to the - // ancestor's property, because then if it's altered it affects that - // object, not this. At the moment, creating a 'reflector' property - // accessor that references /this/ object to be returned to script. - - // (N.B. Can't just put JSComplexs* in the table -> table entries can - // move -> root no longer refers to the JSObject.) - - ReflectorTable::iterator it; - it = m_Reflectors.find( PropertyName ); - - if( it == m_Reflectors.end() ) - { - CJSReflector* reflector = new CJSReflector(); - reflector->m_JSAccessor = CJSComplexPropertyAccessor< CJSComplex >::CreateAccessor( cx, this, PropertyName ); - JS_AddRoot( cx, &reflector->m_JSAccessor ); - m_Reflectors.insert( std::pair( PropertyName, reflector ) ); - *vp = OBJECT_TO_JSVAL( reflector->m_JSAccessor ); - } - else - *vp = OBJECT_TO_JSVAL( it->second->m_JSAccessor ); - } - } - return( true ); -} - -template -void AddMethodImpl( const char* Name, uintN MinArgs ) -{ - JSFunctionSpec FnInfo = { Name, CNativeComplexFunction::JSFunction, MinArgs, 0, 0 }; - T::m_Methods.push_back( FnInfo ); -} - -template -void AddClassPropertyImpl( const CStrW& PropertyName, PropType T::*Native, bool PropAllowInheritance, IJSComplex::NotifyFn Update, IJSComplex::NotifyFn Refresh ) -{ - T::m_IntrinsicProperties[PropertyName] = new CJSSharedProperty( (PropType IJSComplex::*)Native, PropAllowInheritance, Update, Refresh ); -} - -template -void AddReadOnlyClassPropertyImpl( const CStrW& PropertyName, PropType T::*Native, bool PropAllowInheritance, IJSComplex::NotifyFn Update, IJSComplex::NotifyFn Refresh ) -{ - T::m_IntrinsicProperties[PropertyName] = new CJSSharedProperty( (PropType IJSComplex::*)Native, PropAllowInheritance, Update, Refresh ); -} - -// PropertyName must not already exist! (verified in debug build) -template -void MemberAddPropertyImpl( IJSComplex* obj, const CStrW& PropertyName, PropType* Native, bool PropAllowInheritance, IJSComplex::NotifyFn Update, IJSComplex::NotifyFn Refresh ) -{ - ((T*)obj)->DeletePreviouslyAssignedProperty( PropertyName ); - void* mem = jscomplexproperty_suballoc(); - obj->m_Properties[PropertyName] = new(mem) CJSComplexProperty( Native, PropAllowInheritance, Update, Refresh ); -} - -// PropertyName must not already exist! (verified in debug build) -template -void MemberAddReadOnlyPropertyImpl( IJSComplex* obj, const CStrW& PropertyName, PropType* Native, bool PropAllowInheritance, IJSComplex::NotifyFn Update, IJSComplex::NotifyFn Refresh ) -{ - ((T*)obj)->DeletePreviouslyAssignedProperty( PropertyName ); - void* mem = jscomplexproperty_suballoc(); - obj->m_Properties[PropertyName] = new(mem) CJSComplexProperty( Native, PropAllowInheritance, Update, Refresh ); -} - -#endif Index: ps/trunk/source/scripting/ScriptableComplex.cpp =================================================================== --- ps/trunk/source/scripting/ScriptableComplex.cpp (revision 7838) +++ ps/trunk/source/scripting/ScriptableComplex.cpp (nonexistent) @@ -1,62 +0,0 @@ -/* Copyright (C) 2009 Wildfire Games. - * This file is part of 0 A.D. - * - * 0 A.D. 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 2 of the License, or - * (at your option) any later version. - * - * 0 A.D. 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 0 A.D. If not, see . - */ - -#include "precompiled.h" -#include "ScriptableComplex.h" -#include "ScriptableComplex.inl" - -#include "lib/allocators/bucket.h" - -//----------------------------------------------------------------------------- -// suballocator for CJSComplex.m_Properties elements -// (must come after property defs, which are currently in the header) -//----------------------------------------------------------------------------- - -static Bucket bucket; -// HACK: it needs to be created/destroyed; since there is no -// global init/shutdown call here, we keep a refcnt. this assumes that -// going to 0 <==> shutdown! if that proves wrong, bucket_alloc will warn. -static size_t suballoc_refs; // initialized in suballoc_attach - -void jscomplexproperty_suballoc_attach() -{ - ONCE(\ - size_t el_size = std::max(sizeof(CJSValComplexProperty), sizeof(CJSComplexProperty));\ - (void)bucket_create(&bucket, el_size);\ - suballoc_refs = 0;\ - ); - suballoc_refs++; -} - -void jscomplexproperty_suballoc_detach() -{ - suballoc_refs--; - if(suballoc_refs == 0) - bucket_destroy(&bucket); -} - -void* jscomplexproperty_suballoc() -{ - return bucket_alloc(&bucket, 0); -} - -void jscomplexproperty_suballoc_free(IJSComplexProperty* p) -{ - // explicit dtor since caller uses placement new - p->~IJSComplexProperty(); - bucket_free(&bucket, p); -} Property changes on: ps/trunk/source/scripting/ScriptableComplex.cpp ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Index: ps/trunk/source/scripting/JSSerialization.h =================================================================== --- ps/trunk/source/scripting/JSSerialization.h (revision 7838) +++ ps/trunk/source/scripting/JSSerialization.h (nonexistent) @@ -1,158 +0,0 @@ -/* Copyright (C) 2009 Wildfire Games. - * This file is part of 0 A.D. - * - * 0 A.D. 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 2 of the License, or - * (at your option) any later version. - * - * 0 A.D. 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 0 A.D. If not, see . - */ - -// Functions for (de)serialization of jsvals - -// WIP, not yet functional - -#include "network/Serialization.h" -#include "JSConversions.h" -#include "ps/CStr.h" - -class jsval_ser : public ISerializable -{ - enum - { - TAG_BOOLEAN_FALSE, - TAG_BOOLEAN_TRUE, - TAG_INT, - TAG_DOUBLE, - TAG_STRING, - TAG_NOT_SERIALIZABLE = -1 - } m_tag; - jsval m_data; -public: - jsval_ser() : m_tag( TAG_NOT_SERIALIZABLE ) - { - } - jsval_ser( jsval data ) : m_data( data ) - { - if( m_data == JSVAL_FALSE ) - m_tag = TAG_BOOLEAN_FALSE; - if( m_data == JSVAL_TRUE ) - m_tag = TAG_BOOLEAN_TRUE; - if( JSVAL_IS_INT( m_data ) ) - m_tag = TAG_INT; - if( JSVAL_IS_DOUBLE( m_data ) ) - m_tag = TAG_DOUBLE; - if( JSVAL_IS_STRING( m_data ) ) - m_tag = TAG_STRING; - m_tag = TAG_NOT_SERIALIZABLE; - } - operator jsval() const - { - return( m_data ); - } - operator CStr() const - { - return( ToPrimitive( m_data ) ); - } - size_t GetSerializedLength() const - { - switch( m_tag ) - { - case TAG_BOOLEAN_FALSE: - case TAG_BOOLEAN_TRUE: - return( 1 ); - case TAG_INT: - return( 5 ); - case TAG_DOUBLE: - return( 9 ); - case TAG_STRING: - return( 1 + (ToPrimitive(m_data)).GetSerializedLength() ); - default: - debug_warn(L"An attempt was made to serialize a jsval other than a number, boolean or string." ); - return( 1 ); - } - } - u8* Serialize( u8* buffer ) const - { - Serialize_int_1( buffer, m_tag ); - switch( m_tag ) - { - case TAG_BOOLEAN_FALSE: - case TAG_BOOLEAN_TRUE: - break; - case TAG_INT: - { - u32 ival = JSVAL_TO_INT( m_data ); - Serialize_int_4( buffer, ival ); - } - break; - case TAG_DOUBLE: - { - union { - u64 ival; - double dval; - } val; - cassert(sizeof(u64) == sizeof(double)); - val.dval = *JSVAL_TO_DOUBLE( m_data ); - Serialize_int_8( buffer, val.ival ); - } - break; - case TAG_STRING: - buffer = ( ToPrimitive( m_data ) ).Serialize( buffer ); - break; - default: - debug_warn(L"An attempt was made to serialize a jsval other than a number, boolean or string." ); - break; - } - return( buffer ); - } - const u8* Deserialize( const u8* buffer, const u8* end ) - { - Deserialize_int_1( buffer, (u8&)m_tag ); - switch( m_tag ) - { - case TAG_BOOLEAN_FALSE: - m_data = JSVAL_FALSE; - break; - case TAG_BOOLEAN_TRUE: - m_data = JSVAL_TRUE; - break; - case TAG_INT: - { - u32 ival; - Deserialize_int_4( buffer, ival ); - m_data = INT_TO_JSVAL( ival ); - } - break; - case TAG_DOUBLE: - { - union { - u64 ival; - double dval; - } val; - cassert(sizeof(u64) == sizeof(double)); - Deserialize_int_8( buffer, val.ival ); - JS_NewDoubleValue( g_ScriptingHost.GetContext(), val.dval, &m_data ); - } - break; - case TAG_STRING: - { - CStrW ival; - buffer = ival.Deserialize( buffer, end ); - m_data = ToJSVal( ival ); - } - break; - default: - debug_warn(L"An attempt was made to deserialize a jsval other than a number, boolean or string." ); - break; - } - return( buffer ); - } -}; Property changes on: ps/trunk/source/scripting/JSSerialization.h ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Index: ps/trunk/source/scripting/ScriptCustomTypes.h =================================================================== --- ps/trunk/source/scripting/ScriptCustomTypes.h (revision 7838) +++ ps/trunk/source/scripting/ScriptCustomTypes.h (nonexistent) @@ -1,51 +0,0 @@ -/* Copyright (C) 2009 Wildfire Games. - * This file is part of 0 A.D. - * - * 0 A.D. 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 2 of the License, or - * (at your option) any later version. - * - * 0 A.D. 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 0 A.D. If not, see . - */ - -#include "scripting/ScriptableObject.h" - -#ifndef INCLUDED_SCRIPTCUSTOMTYPES -#define INCLUDED_SCRIPTCUSTOMTYPES - -// Custom object types - -// Whilst Point2d is fully coded, it is never registered so is not available in script -// This is mostly as a demonstration of what you need to code to add a new type - -// VECTOR2D -extern JSClass Point2dClass; -extern JSPropertySpec Point2dProperties[]; -JSBool Point2d_Constructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); - -// Colour -struct SColour : public CJSObject -{ -public: - float r, g, b, a; /* 0...1 */ - SColour() { SColourInit( 0.0f, 0.0f, 0.0f, 0.0f ); } - SColour( float r_, float g_, float b_ ) { SColourInit( r_, g_, b_, 1.0f ); } - SColour( float r_, float g_, float b_, float a_ ) { SColourInit( r_, g_, b_, a_ ); } - SColour( const SColour& other ) : CJSObject() { SColourInit( other.r, other.g, other.b, other.a ); } - void SColourInit( float r, float g, float b, float a ); - - SColour &operator = (const SColour &o); - - CStr ToString( JSContext* cx, uintN argc, jsval* argv ); - static void ScriptingInit(); - static JSBool Construct( JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval ); -}; - -#endif Property changes on: ps/trunk/source/scripting/ScriptCustomTypes.h ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Index: ps/trunk/source/scripting/DOMEvent.cpp =================================================================== --- ps/trunk/source/scripting/DOMEvent.cpp (revision 7838) +++ ps/trunk/source/scripting/DOMEvent.cpp (nonexistent) @@ -1,211 +0,0 @@ -/* Copyright (C) 2009 Wildfire Games. - * This file is part of 0 A.D. - * - * 0 A.D. 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 2 of the License, or - * (at your option) any later version. - * - * 0 A.D. 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 0 A.D. If not, see . - */ - -#include "precompiled.h" -#include "DOMEvent.h" -#include "lib/timer.h" -#include "ps/Profile.h" -#include "ScriptObject.h" - -IEventTarget::~IEventTarget() -{ - HandlerMap::iterator it; - for( it = m_Handlers_name.begin(); it != m_Handlers_name.end(); it++ ) - delete( it->second ); -} - -bool IEventTarget::_DispatchEvent( CScriptEvent* evt, IEventTarget* target ) -{ - PROFILE_START( "_DispatchEvent" ); - - // TODO: Deal correctly with multiple handlers - - if( before && before->_DispatchEvent( evt, target ) ) - { - return( true ); // Stop propagation. - } - - evt->m_CurrentTarget = this; - - HandlerList::const_iterator it; - const HandlerList &handlers=m_Handlers_id[evt->m_TypeCode]; - for( it = handlers.begin(); it != handlers.end(); it++ ) - { - DOMEventHandler id = *it; - if( id && id->DispatchEvent( GetScriptExecContext( target ), evt ) ) - { - return( true ); - } - } - - HandlerRange range = m_Handlers_name.equal_range( evt->m_Type ); - HandlerMap::iterator itm; - for( itm = range.first; itm != range.second; itm++ ) - { - DOMEventHandler id = itm->second; - if( id && id->DispatchEvent( GetScriptExecContext( target ), evt ) ) - { - return( true ); - } - } - - if( after && after->_DispatchEvent( evt, target ) ) - { - return( true ); // Stop propagation. - } - - return( false ); - - PROFILE_END( "_DispatchEvent" ); -} - -// Dispatch an event to its handler. -// returns: whether the event arrived (i.e. wasn't cancelled) [bool] -bool IEventTarget::DispatchEvent( CScriptEvent* evt ) -{ - const char* data; - PROFILE_START( "intern string" ); - data = g_Profiler.InternString( "script: " + (CStr8)evt->m_Type ); - PROFILE_END( "intern string" ); - g_Profiler.StartScript( data ); - evt->m_Target = this; - _DispatchEvent( evt, this ); - g_Profiler.Stop(); - return( !evt->m_Cancelled ); -} - -bool IEventTarget::AddHandler( size_t TypeCode, DOMEventHandler handler ) -{ - HandlerList::iterator it; - for( it = m_Handlers_id[TypeCode].begin(); it != m_Handlers_id[TypeCode].end(); it++ ) - if( **it == *handler ) return( false ); - m_Handlers_id[TypeCode].push_back( handler ); - return( true ); -} - -bool IEventTarget::AddHandler( const CStrW& TypeString, DOMEventHandler handler ) -{ - HandlerMap::iterator it; - HandlerRange range = m_Handlers_name.equal_range( TypeString ); - for( it = range.first; it != range.second; it++ ) - if( *( it->second ) == *handler ) return( false ); - m_Handlers_name.insert( HandlerMap::value_type( TypeString, handler ) ); - return( true ); -} - -bool IEventTarget::RemoveHandler( size_t TypeCode, DOMEventHandler handler ) -{ - HandlerList::iterator it; - for( it = m_Handlers_id[TypeCode].begin(); it != m_Handlers_id[TypeCode].end(); it++ ) - if( **it == *handler ) - { - m_Handlers_id[TypeCode].erase( it ); - return( true ); - } - - return( false ); -} - -bool IEventTarget::RemoveHandler( const CStrW& TypeString, DOMEventHandler handler ) -{ - HandlerMap::iterator it; - HandlerRange range = m_Handlers_name.equal_range( TypeString ); - for( it = range.first; it != range.second; it++ ) - if( *( it->second ) == *handler ) - { - delete( it->second ); - m_Handlers_name.erase( it ); - return( true ); - } - - return( false ); -} - -bool IEventTarget::AddHandlerJS( JSContext* UNUSED(cx), uintN argc, jsval* argv ) -{ - debug_assert( argc >= 2 ); - DOMEventHandler handler = new CScriptObject( argv[1] ); - if( !handler->Defined() ) - { - delete( handler ); - return( false ); - } - if( !AddHandler( ToPrimitive( argv[0] ), handler ) ) - { - delete( handler ); - return( false ); - } - return( true ); -} - -bool IEventTarget::RemoveHandlerJS( JSContext* UNUSED(cx), uintN argc, jsval* argv ) -{ - debug_assert( argc >= 2 ); - DOMEventHandler handler = new CScriptObject( argv[1] ); - if( !handler->Defined() ) - { - delete( handler ); - return( false ); - } - if( !RemoveHandler( ToPrimitive( argv[0] ), handler ) ) - { - delete( handler ); - return( false ); - } - delete( handler ); - return( true ); -} - -CScriptEvent::CScriptEvent( const CStrW& Type, size_t TypeCode, bool Cancelable, bool Blockable ) -{ - m_Type = Type; m_TypeCode = TypeCode; - m_Cancelable = Cancelable; m_Cancelled = false; - m_Blockable = Blockable; m_Blocked = false; - m_Timestamp = (size_t)( timer_Time() * 1000.0 ); -} - -void CScriptEvent::ScriptingInit() -{ - AddMethod( "toString", 0 ); - AddMethod( "preventDefault", 0 ); - AddMethod( "cancel", 0 ); - AddMethod( "stopPropagation", 0 ); - - AddProperty( L"type", &CScriptEvent::m_Type, true ); - AddProperty( L"cancelable", &CScriptEvent::m_Cancelable, true ); - AddProperty( L"blockable", &CScriptEvent::m_Blockable, true ); - AddProperty( L"timestamp", &CScriptEvent::m_Timestamp, true ); - - CJSObject::ScriptingInit( "Event" ); -} - -void CScriptEvent::PreventDefault( JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv) ) -{ - if( m_Cancelable ) - m_Cancelled = true; -} - -void CScriptEvent::StopPropagation( JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv) ) -{ - if( m_Blockable ) - m_Blocked = true; -} - -CStr CScriptEvent::ToString( JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv) ) -{ - return "[object Event: " + CStr(m_Type) + "]"; -} Property changes on: ps/trunk/source/scripting/DOMEvent.cpp ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Index: ps/trunk/source/ps/GameSetup/GameSetup.cpp =================================================================== --- ps/trunk/source/ps/GameSetup/GameSetup.cpp (revision 7838) +++ ps/trunk/source/ps/GameSetup/GameSetup.cpp (revision 7839) @@ -1,941 +1,935 @@ /* Copyright (C) 2010 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. 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 2 of the License, or * (at your option) any later version. * * 0 A.D. 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 0 A.D. If not, see . */ #include "precompiled.h" #include "lib/app_hooks.h" #include "lib/input.h" #include "lib/lockfree.h" #include "lib/ogl.h" #include "lib/timer.h" #include "lib/utf8.h" #include "lib/external_libraries/sdl.h" #include "lib/res/h_mgr.h" #include "lib/res/graphics/cursor.h" #include "lib/res/sound/snd_mgr.h" #include "lib/sysdep/cursor.h" #include "lib/sysdep/cpu.h" #include "lib/sysdep/gfx.h" #include "lib/tex/tex.h" #include "ps/CConsole.h" #include "ps/CLogger.h" #include "ps/ConfigDB.h" #include "ps/Filesystem.h" #include "ps/Font.h" #include "ps/Game.h" #include "ps/Globals.h" #include "ps/Hotkey.h" #include "ps/Loader.h" #include "ps/Overlay.h" #include "ps/Profile.h" #include "ps/ProfileViewer.h" #include "ps/StringConvert.h" #include "ps/Util.h" #include "ps/VideoMode.h" #include "ps/World.h" #include "ps/i18n.h" #include "graphics/CinemaTrack.h" #include "graphics/GameView.h" #include "graphics/LightEnv.h" #include "graphics/MapReader.h" #include "graphics/MaterialManager.h" #include "graphics/ParticleEngine.h" #include "graphics/TextureManager.h" #include "renderer/Renderer.h" #include "renderer/VertexBufferManager.h" #include "maths/MathUtil.h" #include "simulation2/Simulation2.h" -#include "scripting/ScriptableComplex.inl" #include "scripting/ScriptingHost.h" #include "scripting/ScriptGlue.h" -#include "scripting/DOMEvent.h" -#include "scripting/ScriptableComplex.h" #include "scriptinterface/ScriptInterface.h" #include "maths/scripting/JSInterface_Vector3D.h" #include "graphics/scripting/JSInterface_Camera.h" #include "graphics/scripting/JSInterface_LightEnv.h" #include "ps/scripting/JSInterface_Console.h" #include "gui/GUI.h" #include "gui/GUIManager.h" #include "gui/scripting/JSInterface_IGUIObject.h" #include "gui/scripting/JSInterface_GUITypes.h" #include "gui/scripting/ScriptFunctions.h" #include "sound/JSI_Sound.h" #include "network/NetServer.h" #include "network/NetClient.h" #include "ps/Pyrogenesis.h" // psSetLogDir #include "ps/GameSetup/Atlas.h" #include "ps/GameSetup/GameSetup.h" #include "ps/GameSetup/Paths.h" #include "ps/GameSetup/Config.h" #include "ps/GameSetup/CmdLineArgs.h" #if !(OS_WIN || OS_MACOSX) // assume all other platforms use X11 for wxWidgets #define MUST_INIT_X11 1 #include #else #define MUST_INIT_X11 0 #endif #include ERROR_GROUP(System); ERROR_TYPE(System, SDLInitFailed); ERROR_TYPE(System, VmodeFailed); ERROR_TYPE(System, RequiredExtensionsMissing); #define LOG_CATEGORY L"gamesetup" bool g_DoRenderGui = true; static const int SANE_TEX_QUALITY_DEFAULT = 5; // keep in sync with code static void SetTextureQuality(int quality) { int q_flags; GLint filter; retry: // keep this in sync with SANE_TEX_QUALITY_DEFAULT switch(quality) { // worst quality case 0: q_flags = OGL_TEX_HALF_RES|OGL_TEX_HALF_BPP; filter = GL_NEAREST; break; // [perf] add bilinear filtering case 1: q_flags = OGL_TEX_HALF_RES|OGL_TEX_HALF_BPP; filter = GL_LINEAR; break; // [vmem] no longer reduce resolution case 2: q_flags = OGL_TEX_HALF_BPP; filter = GL_LINEAR; break; // [vmem] add mipmaps case 3: q_flags = OGL_TEX_HALF_BPP; filter = GL_NEAREST_MIPMAP_LINEAR; break; // [perf] better filtering case 4: q_flags = OGL_TEX_HALF_BPP; filter = GL_LINEAR_MIPMAP_LINEAR; break; // [vmem] no longer reduce bpp case SANE_TEX_QUALITY_DEFAULT: q_flags = OGL_TEX_FULL_QUALITY; filter = GL_LINEAR_MIPMAP_LINEAR; break; // [perf] add anisotropy case 6: // TODO: add anisotropic filtering q_flags = OGL_TEX_FULL_QUALITY; filter = GL_LINEAR_MIPMAP_LINEAR; break; // invalid default: debug_warn(L"SetTextureQuality: invalid quality"); quality = SANE_TEX_QUALITY_DEFAULT; // careful: recursion doesn't work and we don't want to duplicate // the "sane" default values. goto retry; } ogl_tex_set_defaults(q_flags, filter); } //---------------------------------------------------------------------------- // GUI integration //---------------------------------------------------------------------------- // display progress / description in loading screen void GUI_DisplayLoadProgress(int percent, const wchar_t* pending_task) { CStrW i18n_description = I18n::translate(pending_task); JSString* js_desc = StringConvert::wstring_to_jsstring(g_ScriptingHost.getContext(), i18n_description); g_ScriptingHost.SetGlobal("g_Progress", INT_TO_JSVAL(percent)); g_ScriptingHost.SetGlobal("g_LoadDescription", STRING_TO_JSVAL(js_desc)); g_GUI->SendEventToAll("progress"); } void Render() { MICROLOG(L"begin frame"); ogl_WarnIfError(); CStr skystring = "61 193 255"; CFG_GET_USER_VAL("skycolor", String, skystring); CColor skycol; GUI::ParseString(skystring, skycol); g_Renderer.SetClearColor(skycol.AsSColor4ub()); // start new frame g_Renderer.BeginFrame(); ogl_WarnIfError(); if (g_Game && g_Game->IsGameStarted()) { g_Game->GetView()->Render(); } ogl_WarnIfError(); // set up overlay mode glPushAttrib(GL_ENABLE_BIT); glEnable(GL_TEXTURE_2D); glDisable(GL_CULL_FACE); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_BLEND); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glOrtho(0.f, (float)g_xres, 0.f, (float)g_yres, -1.f, 1000.f); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); ogl_WarnIfError(); // Temp GUI message GeeTODO PROFILE_START("render gui"); if(g_DoRenderGui) g_GUI->Draw(); PROFILE_END("render gui"); ogl_WarnIfError(); // Particle Engine Updating CParticleEngine::GetInstance()->UpdateEmitters(); ogl_WarnIfError(); // Text: // Use the GL_ALPHA texture as the alpha channel with a flat colouring glDisable(GL_ALPHA_TEST); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); // Added -- glEnable(GL_TEXTURE_2D); // -- GL glLoadIdentity(); PROFILE_START("render console"); g_Console->Render(); PROFILE_END("render console"); ogl_WarnIfError(); PROFILE_START("render logger"); g_Logger->Render(); PROFILE_END("render logger"); ogl_WarnIfError(); // Profile information PROFILE_START("render profiling"); g_ProfileViewer.RenderProfile(); PROFILE_END("render profiling"); ogl_WarnIfError(); // Draw the cursor (or set the Windows cursor, on Windows) CStrW cursorName = g_CursorName; if (cursorName.empty()) cursor_draw(g_VFS, NULL, g_mouse_x, g_yres-g_mouse_y); else cursor_draw(g_VFS, cursorName.c_str(), g_mouse_x, g_yres-g_mouse_y); // restore glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glPopMatrix(); glPopAttrib(); MICROLOG(L"end frame"); g_Renderer.EndFrame(); ogl_WarnIfError(); } static void RegisterJavascriptInterfaces() { // maths JSI_Vector3D::init(); // graphics JSI_Camera::init(); JSI_LightEnv::init(); CGameView::ScriptingInit(); // renderer CRenderer::ScriptingInit(); // sound JSI_Sound::ScriptingInit(); - // scripting - CScriptEvent::ScriptingInit(); - // ps JSI_Console::init(); // GUI CGUI::ScriptingInit(); GuiScriptingInit(g_ScriptingHost.GetScriptInterface()); } static void InitScripting() { TIMER(L"InitScripting"); // Create the scripting host. This needs to be done before the GUI is created. // [7ms] new ScriptingHost; RegisterJavascriptInterfaces(); } static size_t ChooseCacheSize() { #if OS_WIN //const size_t overheadKiB = (wutil_WindowsVersion() >= WUTIL_VERSION_VISTA)? 1024 : 512; #endif return 96*MiB; } static void InitVfs(const CmdLineArgs& args) { TIMER(L"InitVfs"); const Paths paths(args); fs::wpath logs(paths.Logs()); CreateDirectories(logs, 0700); psSetLogDir(logs); // desired location for crashlog is now known. update AppHooks ASAP // (particularly before the following error-prone operations): AppHooks hooks = {0}; hooks.bundle_logs = psBundleLogs; hooks.get_log_dir = psLogDir; app_hooks_update(&hooks); const size_t cacheSize = ChooseCacheSize(); g_VFS = CreateVfs(cacheSize); g_VFS->Mount(L"screenshots/", paths.Data()/L"screenshots/"); const fs::wpath readonlyConfig = paths.RData()/L"config/"; g_VFS->Mount(L"config/", readonlyConfig); if(readonlyConfig != paths.Config()) g_VFS->Mount(L"config/", paths.Config()); g_VFS->Mount(L"cache/", paths.Cache(), VFS_MOUNT_ARCHIVABLE); // (adding XMBs to archive speeds up subsequent reads) std::vector mods = args.GetMultiple("mod"); mods.push_back("public"); if(!args.Has("onlyPublicFiles")) mods.push_back("internal"); fs::wpath modArchivePath(paths.Cache()/L"mods"); fs::wpath modLoosePath(paths.RData()/L"mods"); for (size_t i = 0; i < mods.size(); ++i) { size_t priority = i; size_t flags = VFS_MOUNT_WATCH|VFS_MOUNT_ARCHIVABLE|VFS_MOUNT_MUST_EXIST; std::wstring modName (wstring_from_utf8(mods[i])); g_VFS->Mount(L"", AddSlash(modLoosePath/modName), flags, priority); g_VFS->Mount(L"", AddSlash(modArchivePath/modName), flags, priority); } // note: don't bother with g_VFS->TextRepresentation - directories // haven't yet been populated and are empty. } static void InitPs(bool setup_gui, const CStrW& gui_page) { { // console TIMER(L"ps_console"); g_Console->UpdateScreenSize(g_xres, g_yres); // Calculate and store the line spacing CFont font(CONSOLE_FONT); g_Console->m_iFontHeight = font.GetLineSpacing(); g_Console->m_iFontWidth = font.GetCharacterWidth(L'C'); g_Console->m_charsPerPage = (size_t)(g_xres / g_Console->m_iFontWidth); // Offset by an arbitrary amount, to make it fit more nicely g_Console->m_iFontOffset = 7; } // language and hotkeys { TIMER(L"ps_lang_hotkeys"); std::string lang = "english"; CFG_GET_SYS_VAL("language", String, lang); I18n::LoadLanguage(lang.c_str()); LoadHotkeys(); } if (!setup_gui) { // We do actually need *some* kind of GUI loaded, so use the // (currently empty) Atlas one g_GUI->SwitchPage(L"page_atlas.xml", JSVAL_VOID); return; } // GUI uses VFS, so this must come after VFS init. g_GUI->SwitchPage(gui_page, JSVAL_VOID); // Warn nicely about missing S3TC support if (!ogl_tex_has_s3tc()) { g_GUI->DisplayMessageBox(600, 350, L"Warning", L"Performance warning:\n\n" L"Your graphics drivers do not support S3TC compressed textures. This will significantly reduce performance and increase memory usage.\n\n" #if !(OS_WIN || OS_MACOSX) L"See http://dri.freedesktop.org/wiki/S3TC for details. " L"Installing the libtxc_dxtn library will fix these problems. " L"Alternatively, running 'driconf' and setting force_s3tc_enable will fix the performance but may cause rendering bugs." #else L"Please try updating your graphics drivers to ensure you have full hardware acceleration." #endif ); } } static void InitInput() { SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL); // register input handlers // This stack is constructed so the first added, will be the last // one called. This is important, because each of the handlers // has the potential to block events to go further down // in the chain. I.e. the last one in the list added, is the // only handler that can block all messages before they are // processed. in_add_handler(game_view_handler); in_add_handler(conInputHandler); in_add_handler(CProfileViewer::InputThunk); in_add_handler(HotkeyInputHandler); // gui_handler needs to be registered after (i.e. called before!) the // hotkey handler so that input boxes can be typed in without // setting off hotkeys. in_add_handler(gui_handler); // must be registered after (called before) the GUI which relies on these globals in_add_handler(GlobalsInputHandler); } static void ShutdownPs() { SAFE_DELETE(g_GUI); SAFE_DELETE(g_Console); // disable the special Windows cursor, or free textures for OGL cursors cursor_draw(g_VFS, 0, g_mouse_x, g_yres-g_mouse_y); // Unload the real language (since it depends on the scripting engine, // which is going to be killed later) and use the English fallback messages I18n::LoadLanguage(NULL); } static void InitRenderer() { TIMER(L"InitRenderer"); if(g_NoGLS3TC) ogl_tex_override(OGL_TEX_S3TC, OGL_TEX_DISABLE); if(g_NoGLAutoMipmap) ogl_tex_override(OGL_TEX_AUTO_MIPMAP_GEN, OGL_TEX_DISABLE); // create renderer new CRenderer; // set renderer options from command line options - NOVBO must be set before opening the renderer g_Renderer.SetOptionBool(CRenderer::OPT_NOVBO,g_NoGLVBO); g_Renderer.SetOptionBool(CRenderer::OPT_NOFRAMEBUFFEROBJECT,g_NoGLFramebufferObject); g_Renderer.SetOptionBool(CRenderer::OPT_SHADOWS,g_Shadows); g_Renderer.SetOptionBool(CRenderer::OPT_FANCYWATER,g_FancyWater); g_Renderer.SetRenderPath(CRenderer::GetRenderPathByName(g_RenderPath)); g_Renderer.SetOptionFloat(CRenderer::OPT_LODBIAS, g_LodBias); // create terrain related stuff new CTextureManager; // create the material manager new CMaterialManager; MICROLOG(L"init renderer"); g_Renderer.Open(g_xres,g_yres); // Setup lighting environment. Since the Renderer accesses the // lighting environment through a pointer, this has to be done before // the first Frame. g_Renderer.SetLightEnv(&g_LightEnv); // I haven't seen the camera affecting GUI rendering and such, but the // viewport has to be updated according to the video mode SViewPort vp; vp.m_X=0; vp.m_Y=0; vp.m_Width=g_xres; vp.m_Height=g_yres; g_Renderer.SetViewport(vp); ColorActivateFastImpl(); } static void InitSDL() { MICROLOG(L"init sdl"); if(SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER|SDL_INIT_NOPARACHUTE) < 0) { LOG(CLogger::Error, LOG_CATEGORY, L"SDL library initialization failed: %hs", SDL_GetError()); throw PSERROR_System_SDLInitFailed(); } atexit(SDL_Quit); SDL_EnableUNICODE(1); } static void ShutdownSDL() { SDL_Quit(); sys_cursor_reset(); } void EndGame() { SAFE_DELETE(g_NetServer); SAFE_DELETE(g_NetClient); SAFE_DELETE(g_Game); } void Shutdown(int UNUSED(flags)) { MICROLOG(L"Shutdown"); EndGame(); ShutdownPs(); // Must delete g_GUI before g_ScriptingHost in_reset_handlers(); // destroy actor related stuff TIMER_BEGIN(L"shutdown actor stuff"); delete &g_MaterialManager; TIMER_END(L"shutdown actor stuff"); // destroy terrain related stuff TIMER_BEGIN(L"shutdown TexMan"); delete &g_TexMan; TIMER_END(L"shutdown TexMan"); // destroy renderer TIMER_BEGIN(L"shutdown Renderer"); delete &g_Renderer; g_VBMan.Shutdown(); TIMER_END(L"shutdown Renderer"); tex_codec_unregister_all(); TIMER_BEGIN(L"shutdown SDL"); ShutdownSDL(); TIMER_END(L"shutdown SDL"); TIMER_BEGIN(L"shutdown ScriptingHost"); delete &g_ScriptingHost; TIMER_END(L"shutdown ScriptingHost"); TIMER_BEGIN(L"shutdown ConfigDB"); delete &g_ConfigDB; TIMER_END(L"shutdown ConfigDB"); // Really shut down the i18n system. Any future calls // to translate() will crash. TIMER_BEGIN(L"shutdown I18N"); I18n::Shutdown(); TIMER_END(L"shutdown I18N"); // resource // first shut down all resource owners, and then the handle manager. TIMER_BEGIN(L"resource modules"); snd_shutdown(); g_VFS.reset(); // this forcibly frees all open handles (thus preventing real leaks), // and makes further access to h_mgr impossible. h_mgr_shutdown(); TIMER_END(L"resource modules"); TIMER_BEGIN(L"shutdown misc"); timer_DisplayClientTotals(); CNetHost::Deinitialize(); // should be last, since the above use them SAFE_DELETE(g_Logger); delete &g_Profiler; delete &g_ProfileViewer; TIMER_END(L"shutdown misc"); } void EarlyInit() { MICROLOG(L"EarlyInit"); // If you ever want to catch a particular allocation: //_CrtSetBreakAlloc(232647); debug_SetThreadName("main"); // add all debug_printf "tags" that we are interested in: debug_filter_add(L"TIMER"); debug_filter_add(L"HRT"); cpu_ConfigureFloatingPoint(); timer_LatchStartTime(); // Because we do GL calls from a secondary thread, Xlib needs to // be told to support multiple threads safely. // This is needed for Atlas, but we have to call it before any other // Xlib functions (e.g. the ones used when drawing the main menu // before launching Atlas) #if MUST_INIT_X11 int status = XInitThreads(); if (status == 0) debug_printf(L"Error enabling thread-safety via XInitThreads\n"); #endif // Initialise the low-quality rand function srand(time(NULL)); // NOTE: this rand should *not* be used for simulation! } static bool Autostart(const CmdLineArgs& args); void Init(const CmdLineArgs& args, int flags) { const bool setup_vmode = (flags & INIT_HAVE_VMODE) == 0; MICROLOG(L"Init"); h_mgr_init(); // Do this as soon as possible, because it chdirs // and will mess up the error reporting if anything // crashes before the working directory is set. MICROLOG(L"init vfs"); InitVfs(args); // This must come after VFS init, which sets the current directory // (required for finding our output log files). g_Logger = new CLogger; // Special command-line mode to dump the entity schemas instead of running the game. // (This must be done after loading VFS etc, but should be done before wasting time // on anything else.) if (args.Has("dumpSchema")) { CSimulation2 sim(NULL, NULL); sim.LoadDefaultScripts(); std::ofstream f("entity.rng", std::ios_base::out | std::ios_base::trunc); f << sim.GenerateSchema(); std::cout << "Generated entity.rng\n"; exit(0); } // Call LoadLanguage(NULL) to initialize the I18n system, but // without loading an actual language file - translate() will // just show the English key text, which is better than crashing // from a null pointer when attempting to translate e.g. error messages. // Real languages can only be loaded when the scripting system has // been initialised. // // this uses LOG and must therefore come after CLogger init. MICROLOG(L"init i18n"); I18n::LoadLanguage(NULL); // override ah_translate with our i18n code. AppHooks hooks = {0}; hooks.translate = psTranslate; hooks.translate_free = psTranslateFree; app_hooks_update(&hooks); // Set up the console early, so that debugging // messages can be logged to it. (The console's size // and fonts are set later in InitPs()) g_Console = new CConsole(); CNetHost::Initialize(); if(setup_vmode) InitSDL(); new CProfileViewer; new CProfileManager; // before any script code MICROLOG(L"init scripting"); InitScripting(); // before GUI // g_ConfigDB, command line args, globals CONFIG_Init(args); if (setup_vmode) { if (!g_VideoMode.InitSDL()) throw PSERROR_System_VmodeFailed(); // abort startup SDL_WM_SetCaption("0 A.D.", "0 A.D."); } tex_codec_register_all(); const int quality = SANE_TEX_QUALITY_DEFAULT; // TODO: set value from config file SetTextureQuality(quality); // needed by ogl_tex to detect broken gfx card/driver combos, // but takes a while due to WMI startup, so make it optional. if(!g_Quickstart) gfx_detect(); ogl_WarnIfError(); if(!g_Quickstart) { WriteSystemInfo(); // note: no longer vfs_display here. it's dog-slow due to unbuffered // file output and very rarely needed. } if(g_DisableAudio) { // speed up startup by disabling all sound // (OpenAL init will be skipped). // must be called before first snd_open. snd_disable(true); } g_GUI = new CGUIManager(g_ScriptingHost.GetScriptInterface()); // (must come after SetVideoMode, since it calls ogl_Init) const char* missing = ogl_HaveExtensions(0, "GL_ARB_multitexture", "GL_EXT_draw_range_elements", "GL_ARB_texture_env_combine", "GL_ARB_texture_env_dot3", NULL); if(missing) { wchar_t buf[500]; swprintf_s(buf, ARRAY_SIZE(buf), L"The %hs extension doesn't appear to be available on your computer." L" The game may still work, though - you are welcome to try at your own risk." L" If not or it doesn't look right, upgrade your graphics card.", missing ); DEBUG_DISPLAY_ERROR(buf); // TODO: i18n } if (!ogl_HaveExtension("GL_ARB_texture_env_crossbar")) { DEBUG_DISPLAY_ERROR( L"The GL_ARB_texture_env_crossbar extension doesn't appear to be available on your computer." L" Shadows are not available and overall graphics quality might suffer." L" You are advised to try installing newer drivers and/or upgrade your graphics card."); g_Shadows = false; } // enable/disable VSync // note: "GL_EXT_SWAP_CONTROL" is "historical" according to dox. #if OS_WIN if(ogl_HaveExtension("WGL_EXT_swap_control")) pwglSwapIntervalEXT(g_VSync? 1 : 0); #endif ogl_WarnIfError(); InitRenderer(); InitInput(); ogl_WarnIfError(); if (!Autostart(args)) { const bool setup_gui = ((flags & INIT_NO_GUI) == 0); InitPs(setup_gui, L"page_pregame.xml"); } } void RenderGui(bool RenderingState) { g_DoRenderGui = RenderingState; } // Network autostart: class AutostartNetServer : public CNetServer { public: AutostartNetServer(const CStr& map, int maxPlayers) : CNetServer(), m_NeedsStart(false), m_NumPlayers(0), m_MaxPlayers(maxPlayers) { CScriptValRooted attrs; GetScriptInterface().Eval("({})", attrs); GetScriptInterface().SetProperty(attrs.get(), "map", std::string(map), false); UpdateGameAttributes(attrs); } protected: virtual void OnAddPlayer() { m_NumPlayers++; debug_printf(L"# player joined (got %d, need %d)\n", (int)m_NumPlayers, (int)m_MaxPlayers); if (m_NumPlayers >= m_MaxPlayers) m_NeedsStart = true; // delay until next Poll, so the new player has been fully processed } virtual void OnRemovePlayer() { debug_warn(L"client left?!"); m_NumPlayers--; } virtual void Poll() { if (m_NeedsStart) { StartGame(); m_NeedsStart = false; } CNetServer::Poll(); } private: bool m_NeedsStart; size_t m_NumPlayers; size_t m_MaxPlayers; }; static bool Autostart(const CmdLineArgs& args) { /* * Handle various command-line options, for quick testing of various features: * -autostart=mapname -- single-player * -autostart=mapname -autostart-playername=Player -autostart-host -autostart-players=2 -- multiplayer host, wait for 2 players * -autostart=mapname -autostart-playername=Player -autostart-client -autostart-ip=127.0.0.1 -- multiplayer client, connect to 127.0.0.1 */ CStr autostartMap = args.Get("autostart"); if (autostartMap.empty()) return false; g_Game = new CGame(); if (args.Has("autostart-host")) { InitPs(true, L"page_loading.xml"); size_t maxPlayers = 2; if (args.Has("autostart-players")) maxPlayers = args.Get("autostart-players").ToUInt(); g_NetServer = new AutostartNetServer(autostartMap, maxPlayers); bool ok = g_NetServer->SetupConnection(); debug_assert(ok); g_NetClient = new CNetClient(g_Game); // TODO: player name, etc g_NetClient->SetupConnection("127.0.0.1"); } else if (args.Has("autostart-client")) { InitPs(true, L"page_loading.xml"); g_NetClient = new CNetClient(g_Game); // TODO: player name, etc bool ok = g_NetClient->SetupConnection(args.Get("autostart-ip")); debug_assert(ok); } else { CScriptValRooted attrs; g_Game->GetSimulation2()->GetScriptInterface().Eval("({})", attrs); g_Game->GetSimulation2()->GetScriptInterface().SetProperty(attrs.get(), "map", std::string(autostartMap), false); g_Game->SetPlayerID(1); g_Game->StartGame(attrs); LDR_NonprogressiveLoad(); PSRETURN ret = g_Game->ReallyStartGame(); debug_assert(ret == PSRETURN_OK); InitPs(true, L"page_session_new.xml"); } return true; } Index: ps/trunk/source/ps/Game.h =================================================================== --- ps/trunk/source/ps/Game.h (revision 7838) +++ ps/trunk/source/ps/Game.h (revision 7839) @@ -1,181 +1,161 @@ /* Copyright (C) 2010 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. 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 2 of the License, or * (at your option) any later version. * * 0 A.D. 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 0 A.D. If not, see . */ /** * File : Game.h * Project : engine * Description : Contains the CGame Class which is a representation of the game itself. * **/ #ifndef INCLUDED_GAME #define INCLUDED_GAME #include "ps/Errors.h" #include class CWorld; class CSimulation2; class CGameView; class CNetTurnManager; class CScriptValRooted; struct CColor; /** * The container that holds the rules, resources and attributes of the game. * The CGame object is responsible for creating a game that is defined by * a set of attributes provided. The CGame object is also responsible for * maintaining the relations between CPlayer and CWorld, CSimulation and CWorld. **/ class CGame { NONCOPYABLE(CGame); /** * pointer to the CWorld object representing the game world. **/ CWorld *m_World; /** * pointer to the CSimulation2 object operating on the game world. **/ CSimulation2 *m_Simulation2; /** * pointer to the CGameView object representing the view into the game world. **/ CGameView *m_GameView; /** * the game has been initialized and ready for use if true. **/ bool m_GameStarted; /** * scale multiplier for simulation rate. **/ float m_SimRate; int m_PlayerID; - /** - * enumerated values for game status. - **/ - enum EOG - { - EOG_NEUTRAL, /// Game is in progress - EOG_DRAW, /// Game is over as a Draw by means of agreement of civilizations - EOG_SPECIAL_DRAW, /// Game is over by players dying at the same time...? - EOG_LOSE, /// Game is over, local player loses - EOG_WIN /// Game is over, local player wins - } GameStatus; - CNetTurnManager* m_TurnManager; public: enum ENetStatus { NET_WAITING_FOR_CONNECT, /// we have loaded the game; waiting for other players to finish loading NET_NORMAL /// running the game }; CGame(bool disableGraphics = false); ~CGame(); /** * the game is paused and no updates will be performed if true. **/ bool m_Paused; void StartGame(const CScriptValRooted& attribs); PSRETURN ReallyStartGame(); /* Perform all per-frame updates */ bool Update(double deltaTime, bool doInterpolate = true); void Interpolate(float frameLength); - void UpdateGameStatus(); - void EndGame(); - int GetPlayerID(); void SetPlayerID(int playerID); CColor GetPlayerColour(int player) const; /** * Get m_GameStarted. * * @return bool the value of m_GameStarted. **/ inline bool IsGameStarted() const { return m_GameStarted; } /** * Get the pointer to the game world object. * * @return CWorld * the value of m_World. **/ inline CWorld *GetWorld() { return m_World; } + /** * Get the pointer to the game view object. * * @return CGameView * the value of m_GameView. **/ inline CGameView *GetView() { return m_GameView; } + /** * Get the pointer to the simulation2 object. * * @return CSimulation2 * the value of m_Simulation2. **/ inline CSimulation2 *GetSimulation2() { return m_Simulation2; } /** * Set the simulation scale multiplier. * * @param simRate Float value to set m_SimRate to. * Because m_SimRate is also used to * scale TimeSinceLastFrame it must be * clamped to 0.0f. **/ inline void SetSimRate(float simRate) { m_SimRate = std::max(simRate, 0.0f); } - /** - * Get the simulation scale multiplier. - * - * @return float value of m_SimRate. - **/ - inline float GetSimRate() const - { return m_SimRate; } /** * Replace the current turn manager. * This class will take ownership of the pointer. */ void SetTurnManager(CNetTurnManager* turnManager); CNetTurnManager* GetTurnManager() const { return m_TurnManager; } private: void RegisterInit(const CScriptValRooted& attribs); }; extern CGame *g_Game; #endif Index: ps/trunk/source/ps/World.cpp =================================================================== --- ps/trunk/source/ps/World.cpp (revision 7838) +++ ps/trunk/source/ps/World.cpp (revision 7839) @@ -1,123 +1,123 @@ /* Copyright (C) 2010 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. 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 2 of the License, or * (at your option) any later version. * * 0 A.D. 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 0 A.D. If not, see . */ /** * File : World.cpp * Project : engine * Description : Contains the CWorld Class implementation. * **/ #include "precompiled.h" #include "graphics/GameView.h" #include "graphics/LightEnv.h" #include "graphics/MapReader.h" #include "graphics/MapWriter.h" #include "graphics/Terrain.h" #include "graphics/Terrain.h" #include "graphics/UnitManager.h" #include "lib/timer.h" #include "ps/CLogger.h" #include "ps/CStr.h" #include "ps/Errors.h" #include "ps/Game.h" #include "ps/Loader.h" #include "ps/LoaderThunks.h" #include "ps/World.h" #include "renderer/Renderer.h" #include "simulation2/Simulation2.h" /** * Global light settings. * It is not a member of CWorld because it is passed * to the renderer before CWorld exists. **/ CLightEnv g_LightEnv; /** * Constructor. * * @param pGame CGame * pGame pointer to the container game object. **/ CWorld::CWorld(CGame *pGame): m_pGame(pGame), m_Terrain(new CTerrain()), m_UnitManager(new CUnitManager()), m_LOSManager(NULL), m_TerritoryManager(NULL) { } /** * Initializes the game world with the attributes provided. **/ void CWorld::RegisterInit(const CStrW& mapFile, int playerID) { // Load the map, if one was specified if (mapFile.length()) { VfsPath mapfilename(VfsPath(L"maps/scenarios/")/(std::wstring)mapFile); CMapReader* reader = 0; try { reader = new CMapReader; CTriggerManager* pTriggerManager = NULL; reader->LoadMap(mapfilename, m_Terrain, CRenderer::IsInitialised() ? g_Renderer.GetWaterManager() : NULL, CRenderer::IsInitialised() ? g_Renderer.GetSkyManager() : NULL, &g_LightEnv, m_pGame->GetView() ? m_pGame->GetView()->GetCamera() : NULL, m_pGame->GetView() ? m_pGame->GetView()->GetCinema() : NULL, pTriggerManager, m_pGame->GetSimulation2(), playerID); // fails immediately, or registers for delay loading } catch (PSERROR_File& err) { delete reader; LOGERROR(L"Failed to load map %ls: %hs", mapfilename.string().c_str(), err.what()); throw PSERROR_Game_World_MapLoadFailed(); } } } /** * Destructor. * **/ CWorld::~CWorld() { delete m_Terrain; delete m_UnitManager; } /** * Redraw the world. * Provided for JS _rewritemaps function. * **/ void CWorld::RewriteMap() { - CMapWriter::RewriteAllMaps(m_Terrain, m_UnitManager, + CMapWriter::RewriteAllMaps(m_Terrain, g_Renderer.GetWaterManager(), g_Renderer.GetSkyManager(), &g_LightEnv, m_pGame->GetView()->GetCamera(), m_pGame->GetView()->GetCinema(), NULL, - m_pGame->GetSimulation2(), NULL); + m_pGame->GetSimulation2()); } Index: ps/trunk/source/ps/CConsole.cpp =================================================================== --- ps/trunk/source/ps/CConsole.cpp (revision 7838) +++ ps/trunk/source/ps/CConsole.cpp (revision 7839) @@ -1,802 +1,801 @@ /* Copyright (C) 2010 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. 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 2 of the License, or * (at your option) any later version. * * 0 A.D. 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 0 A.D. If not, see . */ /* * Implements the in-game console with scripting support. */ #include "precompiled.h" #include #include "CConsole.h" #include "lib/ogl.h" #include "lib/res/graphics/unifont.h" #include "lib/sysdep/clipboard.h" #include "maths/MathUtil.h" #include "network/NetClient.h" #include "network/NetServer.h" #include "ps/CLogger.h" #include "ps/Filesystem.h" #include "ps/Font.h" #include "ps/Globals.h" #include "ps/Hotkey.h" #include "ps/Pyrogenesis.h" #include "scripting/ScriptingHost.h" -#include "scripting/ScriptableComplex.inl" #define LOG_CATEGORY L"Console" CConsole* g_Console = 0; CConsole::CConsole() { m_bToggle = false; m_bVisible = false; m_fVisibleFrac = 0.0f; m_szBuffer = new wchar_t[CONSOLE_BUFFER_SIZE]; FlushBuffer(); m_iMsgHistPos = 1; m_charsPerPage=0; InsertMessage(L"[ 0 A.D. Console v0.12 ] type \"\\info\" for help"); InsertMessage(L""); if (FileExists(L"gui/text/help.txt")) { shared_ptr buf; size_t size; if ( g_VFS->LoadFile(L"gui/text/help.txt", buf, size) < 0 ) { LOG(CLogger::Error, LOG_CATEGORY, L"Help file not found for console"); return; } // TODO: read in text mode, or at least get rid of the \r\n somehow // TODO: maybe the help file should be UTF-8 - we assume it's iso-8859-1 here m_helpText = CStrW((const char*)buf.get()); } else { InsertMessage(L"No help file found."); } } CConsole::~CConsole() { m_deqMsgHistory.clear(); m_deqBufHistory.clear(); delete[] m_szBuffer; } void CConsole::SetSize(float X, float Y, float W, float H) { m_fX = X; m_fY = Y; m_fWidth = W; m_fHeight = H; } void CConsole::UpdateScreenSize(int w, int h) { float height = h * 0.6f; SetSize(0, h-height, (float)w, height); } void CConsole::ToggleVisible() { m_bToggle = true; m_bVisible = !m_bVisible; } void CConsole::SetVisible( bool visible ) { if( visible != m_bVisible ) m_bToggle = true; m_bVisible = visible; } void CConsole::FlushBuffer(void) { // Clear the buffer and set the cursor and length to 0 memset(m_szBuffer, '\0', sizeof(wchar_t) * CONSOLE_BUFFER_SIZE); m_iBufferPos = m_iBufferLength = 0; } void CConsole::ToLower(wchar_t* szMessage, size_t iSize) { size_t L = (size_t)wcslen(szMessage); if (L <= 0) return; if (iSize && iSize < L) L = iSize; for(size_t i = 0; i < L; i++) szMessage[i] = towlower(szMessage[i]); } void CConsole::Trim(wchar_t* szMessage, const wchar_t cChar, size_t iSize) { size_t L = wcslen(szMessage); if(!L) return; if (iSize && iSize < L) L = iSize; wchar_t szChar[2] = { cChar, 0 }; // Find the first point at which szChar does not // exist in the message size_t ofs = wcsspn(szMessage, szChar); if(ofs == 0) // no leading chars - we're done return; // move everything chars left, replacing leading cChar chars L -= ofs; memmove(szMessage, szMessage+ofs, L*sizeof(wchar_t)); for(ssize_t i = (ssize_t)L; i >= 0; i--) { szMessage[i] = '\0'; if (szMessage[i - 1] != cChar) break; } } void CConsole::Update(const float DeltaTime) { if(m_bToggle) { const float AnimateTime = .30f; const float Delta = DeltaTime / AnimateTime; if(m_bVisible) { m_fVisibleFrac += Delta; if(m_fVisibleFrac > 1.0f) { m_fVisibleFrac = 1.0f; m_bToggle = false; } } else { m_fVisibleFrac -= Delta; if(m_fVisibleFrac < 0.0f) { m_fVisibleFrac = 0.0f; m_bToggle = false; } } } } //Render Manager. void CConsole::Render() { if (! (m_bVisible || m_bToggle) ) return; CFont font(CONSOLE_FONT); font.Bind(); // animation: slide in from top of screen const float MaxY = m_fHeight; const float DeltaY = (1.0f - m_fVisibleFrac) * MaxY; glPushMatrix(); glTranslatef(m_fX, m_fY + DeltaY, 0.0f); //Move to window position glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); DrawWindow(); DrawHistory(); DrawBuffer(); glDisable(GL_BLEND); glPopMatrix(); } void CConsole::DrawWindow(void) { // TODO: Add texturing glDisable(GL_TEXTURE_2D); // Draw Background // Set the color to a translucent blue glColor4f(0.0f, 0.0f, 0.5f, 0.6f); glBegin(GL_QUADS); glVertex2f(0.0f, 0.0f); glVertex2f(m_fWidth-1.0f, 0.0f); glVertex2f(m_fWidth-1.0f, m_fHeight-1.0f); glVertex2f(0.0f, m_fHeight-1.0f); glEnd(); // Draw Border // Set the color to a translucent yellow glColor4f(0.5f, 0.5f, 0.0f, 0.6f); glBegin(GL_LINE_LOOP); glVertex2f(0.0f, 0.0f); glVertex2f(m_fWidth-1.0f, 0.0f); glVertex2f(m_fWidth-1.0f, m_fHeight-1.0f); glVertex2f(0.0f, m_fHeight-1.0f); glEnd(); if (m_fHeight > m_iFontHeight + 4) { glBegin(GL_LINES); glVertex2f(0.0f, (GLfloat)(m_iFontHeight + 4)); glVertex2f(m_fWidth, (GLfloat)(m_iFontHeight + 4)); glEnd(); } glEnable(GL_TEXTURE_2D); } void CConsole::DrawHistory(void) { int i = 1; std::deque::iterator Iter; //History iterator glPushMatrix(); glColor3f(1.0f, 1.0f, 1.0f); //Set color of text glTranslatef(9.0f, (float)m_iFontOffset, 0.0f); //move away from the border // Draw the text upside-down, because it's aligned with // the GUI (which uses the top-left as (0,0)) glScalef(1.0f, -1.0f, 1.0f); for (Iter = m_deqMsgHistory.begin(); Iter != m_deqMsgHistory.end() && (((i - m_iMsgHistPos + 1) * m_iFontHeight) < m_fHeight); Iter++) { if (i >= m_iMsgHistPos){ glTranslatef(0.0f, -(float)m_iFontHeight, 0.0f); glPushMatrix(); glwprintf(L"%ls", Iter->c_str()); glPopMatrix(); } i++; } glPopMatrix(); } //Renders the buffer to the screen. void CConsole::DrawBuffer(void) { if (m_fHeight < m_iFontHeight) return; glPushMatrix(); glColor3f(1.0f, 1.0f, 0.0f); glTranslatef(2.0f, (float)m_iFontOffset, 0); glScalef(1.0f, -1.0f, 1.0f); glwprintf(L"]"); glColor3f(1.0f, 1.0f, 1.0f); if (m_iBufferPos==0) DrawCursor(); for (int i = 0; i < m_iBufferLength; i++){ glwprintf(L"%lc", m_szBuffer[i]); if (m_iBufferPos-1==i) DrawCursor(); } glPopMatrix(); } void CConsole::DrawCursor(void) { // (glPushMatrix is necessary because glwprintf does glTranslatef) glPushMatrix(); // Slightly translucent yellow glColor4f(1.0f, 1.0f, 0.0f, 0.8f); // Cursor character is chosen to be an underscore glwprintf(L"_"); // Revert to the standard text colour glColor3f(1.0f, 1.0f, 1.0f); glPopMatrix(); } //Inserts a character into the buffer. void CConsole::InsertChar(const int szChar, const wchar_t cooked ) { static int iHistoryPos = -1; if (!m_bVisible) return; switch (szChar){ case SDLK_RETURN: iHistoryPos = -1; m_iMsgHistPos = 1; ProcessBuffer(m_szBuffer); FlushBuffer(); return; case SDLK_TAB: // Auto Complete return; case SDLK_BACKSPACE: if (IsEmpty() || IsBOB()) return; if (m_iBufferPos == m_iBufferLength) m_szBuffer[m_iBufferPos - 1] = '\0'; else{ for(int j=m_iBufferPos-1; j= m_iBufferPos) { bool bad = false; for(int i=0; i 0 ) { int oldHistoryPos = iHistoryPos; while( iHistoryPos != 0) { iHistoryPos--; std::wstring& histString = m_deqBufHistory.at(iHistoryPos); if((int)histString.length() >= m_iBufferPos) { bool bad = false; for(int i=0; im_iBufferPos; i--) m_szBuffer[i] = m_szBuffer[i-1]; // move chars to right m_szBuffer[i] = cooked; } m_iBufferPos++; m_iBufferLength++; return; } } void CConsole::InsertMessage(const wchar_t* szMessage, ...) { va_list args; wchar_t szBuffer[CONSOLE_MESSAGE_SIZE]; va_start(args, szMessage); if (vswprintf(szBuffer, CONSOLE_MESSAGE_SIZE, szMessage, args) == -1) { debug_printf(L"Error printfing console message (buffer size exceeded?)\n"); // Make it obvious that the text was trimmed (assuming it was) wcscpy(szBuffer+CONSOLE_MESSAGE_SIZE-4, L"..."); } va_end(args); InsertMessageRaw(szBuffer); } void CConsole::InsertMessageRaw(const CStrW& message) { // (TODO: this text-wrapping is rubbish since we now use variable-width fonts) //Insert newlines to wraparound text where needed CStrW wrapAround(message); CStrW newline(L'\n'); size_t oldNewline=0; size_t distance; //make sure everything has been initialized if ( m_charsPerPage != 0 ) { while ( oldNewline+m_charsPerPage < wrapAround.length() ) { distance = wrapAround.find(newline, oldNewline) - oldNewline; if ( distance > m_charsPerPage ) { oldNewline += m_charsPerPage; wrapAround.insert( oldNewline++, newline ); } else oldNewline += distance+1; } } // Split into lines and add each one individually oldNewline = 0; while ( (distance = wrapAround.find(newline, oldNewline)) != wrapAround.npos) { distance -= oldNewline; m_deqMsgHistory.push_front(wrapAround.substr(oldNewline, distance)); oldNewline += distance+1; } m_deqMsgHistory.push_front(wrapAround.substr(oldNewline)); } const wchar_t* CConsole::GetBuffer() { m_szBuffer[m_iBufferLength] = 0; return( m_szBuffer ); } void CConsole::SetBuffer(const wchar_t* szMessage) { int oldBufferPos = m_iBufferPos; // remember since FlushBuffer will set it to 0 FlushBuffer(); wcsncpy(m_szBuffer, szMessage, CONSOLE_BUFFER_SIZE); m_iBufferLength = (int)wcslen(m_szBuffer); m_iBufferPos = std::min(oldBufferPos, m_iBufferLength); } void CConsole::UseHistoryFile(const VfsPath& filename, int max_history_lines) { m_MaxHistoryLines = max_history_lines; m_sHistoryFile = filename; LoadHistory(); } void CConsole::ProcessBuffer(const wchar_t* szLine) { if (szLine == NULL) return; if (wcslen(szLine) <= 0) return; debug_assert(wcslen(szLine) < CONSOLE_BUFFER_SIZE); m_deqBufHistory.push_front(szLine); SaveHistory(); // Do this each line for the moment; if a script causes // a crash it's a useful record. wchar_t szCommand[CONSOLE_BUFFER_SIZE] = { 0 }; if (szLine[0] == '\\') { if (swscanf(szLine, L"\\%ls", szCommand) != 1) return; Trim(szCommand); ToLower(szCommand); if (!wcscmp(szCommand, L"info")) { InsertMessage(L""); InsertMessage(L"[Information]"); InsertMessage(L" -View commands \"\\commands\""); InsertMessage(L" -Call command \"\\\""); InsertMessage(L" -Say \"\""); InsertMessage(L" -Help - Lists functions usable from console"); InsertMessage(L""); } else if (!wcscmp(szCommand, L"commands")) { InsertMessage(L""); InsertMessage(L"[Commands]"); InsertMessage(L" (none registered)"); InsertMessage(L""); } else if (! (wcscmp(szCommand, L"Help") && wcscmp(szCommand, L"help")) ) { InsertMessage(L""); InsertMessage(L"[Help]"); InsertMessageRaw(m_helpText); } else { InsertMessage(L"unknown command <%ls>", szCommand); } } else if (szLine[0] == ':' || szLine[0] == '?') { // Process it as JavaScript jsval rval = g_ScriptingHost.ExecuteScript( szLine+1, L"Console" ); if (szLine[0] == '?' && rval) { try { InsertMessage( L"%ls", g_ScriptingHost.ValueToUCString( rval ).c_str() ); } catch (PSERROR_Scripting_ConversionFailed) { InsertMessage( L"%hs", "" ); } } } else { SendChatMessage(szLine); } } void CConsole::LoadHistory() { // note: we don't care if this file doesn't exist or can't be read; // just don't load anything in that case. // do this before LoadFile to avoid an error message if file not found. if (!FileExists(m_sHistoryFile)) return; shared_ptr buf; size_t buflen; if (g_VFS->LoadFile(m_sHistoryFile, buf, buflen) < 0) return; CStr bytes ((char*)buf.get(), buflen); CStrW str (bytes.FromUTF8()); size_t pos = 0; while (pos != CStrW::npos) { pos = str.find('\n'); if (pos != CStrW::npos) { if (pos > 0) m_deqBufHistory.push_front(str.Left(str[pos-1] == '\r' ? pos - 1 : pos)); str = str.substr(pos + 1); } else if (str.length() > 0) m_deqBufHistory.push_front(str); } } void CConsole::SaveHistory() { WriteBuffer buffer; const int linesToSkip = (int)m_deqBufHistory.size() - m_MaxHistoryLines; std::deque::reverse_iterator it = m_deqBufHistory.rbegin(); if(linesToSkip > 0) std::advance(it, linesToSkip); for (; it != m_deqBufHistory.rend(); ++it) { CStr8 line = CStrW(*it).ToUTF8(); buffer.Append(line.data(), line.length()); static const char newline = '\n'; buffer.Append(&newline, 1); } g_VFS->CreateFile(m_sHistoryFile, buffer.Data(), buffer.Size()); } void CConsole::SendChatMessage(const wchar_t *pText) { if (g_NetClient) { // TODO // g_NetClient3->SendChatMessage(pText); } } void CConsole::ReceivedChatMessage(const wchar_t *szSender, const wchar_t *szMessage) { InsertMessage(L"%ls: %ls", szSender, szMessage); } static bool isUnprintableChar(SDL_keysym key) { // U+0000 to U+001F are control characters if (key.unicode < 0x20) { switch (key.sym) { // We want to allow some, which are handled specially case SDLK_RETURN: case SDLK_TAB: case SDLK_BACKSPACE: case SDLK_DELETE: case SDLK_HOME: case SDLK_END: case SDLK_LEFT: case SDLK_RIGHT: case SDLK_UP: case SDLK_DOWN: case SDLK_PAGEUP: case SDLK_PAGEDOWN: return false; // Ignore the others default: return true; } } return false; } InReaction conInputHandler(const SDL_Event_* ev) { if( ev->ev.type == SDL_HOTKEYDOWN ) { if( ev->ev.user.code == HOTKEY_CONSOLE_TOGGLE ) { g_Console->ToggleVisible(); return IN_HANDLED; } else if( ev->ev.user.code == HOTKEY_CONSOLE_COPY ) { sys_clipboard_set( g_Console->GetBuffer() ); return IN_HANDLED; } else if( ev->ev.user.code == HOTKEY_CONSOLE_PASTE ) { wchar_t* text = sys_clipboard_get(); if(text) { for(wchar_t* c = text; *c; c++) g_Console->InsertChar(0, *c); sys_clipboard_free(text); } return IN_HANDLED; } } if( ev->ev.type != SDL_KEYDOWN) return IN_PASS; SDLKey sym = ev->ev.key.keysym.sym; if(!g_Console->IsActive()) return IN_PASS; // Stop unprintable characters (ctrl+, alt+ and escape), // also prevent ` and/or ~ appearing in console every time it's toggled. if( !isUnprintableChar(ev->ev.key.keysym) && !hotkeys[HOTKEY_CONSOLE_TOGGLE] ) g_Console->InsertChar(sym, (wchar_t)ev->ev.key.keysym.unicode ); return IN_PASS; } Index: ps/trunk/source/ps/Game.cpp =================================================================== --- ps/trunk/source/ps/Game.cpp (revision 7838) +++ ps/trunk/source/ps/Game.cpp (revision 7839) @@ -1,313 +1,243 @@ /* Copyright (C) 2010 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. 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 2 of the License, or * (at your option) any later version. * * 0 A.D. 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 0 A.D. If not, see . */ /** * File : Game.cpp * Project : engine * Description : Contains the CGame Class implementation. * **/ #include "precompiled.h" #include "Game.h" #include "graphics/GameView.h" #include "graphics/UnitManager.h" #include "lib/timer.h" #include "network/NetClient.h" #include "network/NetServer.h" #include "network/NetTurnManager.h" #include "ps/CConsole.h" #include "ps/CLogger.h" #include "ps/CStr.h" #include "ps/Loader.h" #include "ps/Overlay.h" #include "ps/Profile.h" #include "ps/World.h" #include "scripting/ScriptingHost.h" #include "scriptinterface/ScriptInterface.h" #include "simulation2/Simulation2.h" #include "simulation2/components/ICmpPlayer.h" #include "simulation2/components/ICmpPlayerManager.h" #include "gui/GUIManager.h" extern bool g_GameRestarted; /** * Globally accessible pointer to the CGame object. **/ CGame *g_Game=NULL; /** * Constructor * **/ CGame::CGame(bool disableGraphics): m_World(new CWorld(this)), m_Simulation2(new CSimulation2(&m_World->GetUnitManager(), m_World->GetTerrain())), m_GameView(disableGraphics ? NULL : new CGameView(this)), m_GameStarted(false), m_Paused(false), m_SimRate(1.0f), m_PlayerID(-1) { // Need to set the CObjectManager references after various objects have // been initialised, so do it here rather than via the initialisers above. if (m_GameView) m_World->GetUnitManager().SetObjectManager(m_GameView->GetObjectManager()); m_TurnManager = new CNetLocalTurnManager(*m_Simulation2); // this will get replaced if we're a net server/client m_Simulation2->LoadDefaultScripts(); m_Simulation2->ResetState(); CScriptVal initData; // TODO: ought to get this from the GUI, somehow m_Simulation2->InitGame(initData); } /** * Destructor * **/ CGame::~CGame() { // Again, the in-game call tree is going to be different to the main menu one. if (CProfileManager::IsInitialised()) g_Profiler.StructuralReset(); delete m_TurnManager; delete m_GameView; delete m_Simulation2; delete m_World; } void CGame::SetTurnManager(CNetTurnManager* turnManager) { if (m_TurnManager) delete m_TurnManager; m_TurnManager = turnManager; if (m_TurnManager) m_TurnManager->SetPlayerID(m_PlayerID); } /** * Initializes the game with the set of attributes provided. * Makes calls to initialize the game view, world, and simulation objects. * Calls are made to facilitate progress reporting of the initialization. **/ void CGame::RegisterInit(const CScriptValRooted& attribs) { std::wstring mapFile; m_Simulation2->GetScriptInterface().GetProperty(attribs.get(), "map", mapFile); mapFile += L".pmp"; LDR_BeginRegistering(); // RC, 040804 - GameView needs to be initialized before World, otherwise GameView initialization // overwrites anything stored in the map file that gets loaded by CWorld::Initialize with default // values. At the minute, it's just lighting settings, but could be extended to store camera position. // Storing lighting settings in the game view seems a little odd, but it's no big deal; maybe move it at // some point to be stored in the world object? if (m_GameView) m_GameView->RegisterInit(); m_World->RegisterInit(mapFile, m_PlayerID); LDR_EndRegistering(); } /** * Game initialization has been completed. Set game started flag and start the session. * * @return PSRETURN 0 **/ PSRETURN CGame::ReallyStartGame() { // Call the reallyStartGame GUI function, but only if it exists if (g_GUI && g_GUI->HasPages()) { jsval fval, rval; JSBool ok = JS_GetProperty(g_ScriptingHost.getContext(), g_GUI->GetScriptObject(), "reallyStartGame", &fval); debug_assert(ok); if (ok && !JSVAL_IS_VOID(fval)) ok = JS_CallFunctionValue(g_ScriptingHost.getContext(), g_GUI->GetScriptObject(), fval, 0, NULL, &rval); } if (g_NetClient) g_NetClient->LoadFinished(); debug_printf(L"GAME STARTED, ALL INIT COMPLETE\n"); m_GameStarted=true; // The call tree we've built for pregame probably isn't useful in-game. if (CProfileManager::IsInitialised()) g_Profiler.StructuralReset(); // Mark terrain as modified so the minimap can repaint (is there a cleaner way of handling this?) g_GameRestarted = true; return 0; } int CGame::GetPlayerID() { return m_PlayerID; } void CGame::SetPlayerID(int playerID) { m_PlayerID = playerID; if (m_TurnManager) m_TurnManager->SetPlayerID(m_PlayerID); } void CGame::StartGame(const CScriptValRooted& attribs) { RegisterInit(attribs); } // TODO: doInterpolate is optional because Atlas interpolates explicitly, // so that it has more control over the update rate. The game might want to // do the same, and then doInterpolate should be redundant and removed. /** * Periodic heartbeat that controls the process. * Simulation update is called and game status update is called. * * @param deltaTime Double. Elapsed time since last beat in seconds. * @param doInterpolate Bool. Perform interpolation if true. * @return bool false if it can't keep up with the desired simulation rate * indicating that you might want to render less frequently. **/ bool CGame::Update(double deltaTime, bool doInterpolate) { if (m_Paused) return true; if (!m_TurnManager) return true; deltaTime *= m_SimRate; bool ok = true; if (deltaTime) { PROFILE("update"); if (m_TurnManager->Update(deltaTime)) g_GUI->SendEventToAll("SimulationUpdate"); } if (doInterpolate) { PROFILE("interpolate"); m_TurnManager->Interpolate(deltaTime); } return ok; } void CGame::Interpolate(float frameLength) { if (!m_TurnManager) return; m_TurnManager->Interpolate(frameLength); } - -/** - * Test player statistics and update game status as required. - * - **/ -/* -void CGame::UpdateGameStatus() -{ - bool EOG_lose = true; - bool EOG_win = true; - CPlayer *local = GetLocalPlayer(); - - for (int i=0; iGetEntityManager().GetHandle(i); - if ( !handle ) - continue; - CPlayer *tmpPlayer = handle->m_entity->GetPlayer(); - - //Are we still alive? - if ( local == tmpPlayer && handle->m_entity->m_extant ) - { - EOG_lose = false; - if (EOG_win == false) - break; - } - //Are they still alive? - else if ( handle->m_entity->m_extant ) - { - EOG_win = false; - if (EOG_lose == false) - break; - } - } - if (EOG_lose && EOG_win) - GameStatus = EOG_SPECIAL_DRAW; - else if (EOG_win) - GameStatus = EOG_WIN; - else if (EOG_lose) - GameStatus = EOG_LOSE; - else - GameStatus = EOG_NEUTRAL; -}*/ - -/** - * End of game console message creation. - * - **/ -void CGame::EndGame() -{ - g_Console->InsertMessage( L"It's the end of the game as we know it!"); - switch (GameStatus) - { - case EOG_DRAW: - g_Console->InsertMessage( L"A diplomatic draw ain't so bad, eh?"); - break; - case EOG_SPECIAL_DRAW: - g_Console->InsertMessage( L"Amazingly, you managed to draw from dieing at the same time as your opponent...you have my respect."); - break; - case EOG_LOSE: - g_Console->InsertMessage( L"My condolences on your loss."); - break; - case EOG_WIN: - g_Console->InsertMessage( L"Thou art victorious!"); - break; - default: - break; - } -} - static CColor BrokenColor(0.3f, 0.3f, 0.3f, 1.0f); CColor CGame::GetPlayerColour(int player) const { CmpPtr cmpPlayerManager(*m_Simulation2, SYSTEM_ENTITY); if (cmpPlayerManager.null()) return BrokenColor; CmpPtr cmpPlayer(*m_Simulation2, cmpPlayerManager->GetPlayerByID(player)); if (cmpPlayer.null()) return BrokenColor; return cmpPlayer->GetColour(); } Index: ps/trunk/source/ps/World.h =================================================================== --- ps/trunk/source/ps/World.h (revision 7838) +++ ps/trunk/source/ps/World.h (revision 7839) @@ -1,122 +1,120 @@ /* Copyright (C) 2009 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. 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 2 of the License, or * (at your option) any later version. * * 0 A.D. 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 0 A.D. If not, see . */ /** * File : World.h * Project : engine * Description : Contains the CWorld Class which contains all the entities and represents them at a specific moment in time. * **/ #ifndef INCLUDED_WORLD #define INCLUDED_WORLD #include "ps/Errors.h" #ifndef ERROR_GROUP_GAME_DEFINED #define ERROR_GROUP_GAME_DEFINED ERROR_GROUP(Game); #endif ERROR_SUBGROUP(Game, World); ERROR_TYPE(Game_World, MapLoadFailed); class CGame; class CUnitManager; -class CEntityManager; -class CProjectileManager; class CLOSManager; class CTerritoryManager; class CTerrain; class CStrW; /** * CWorld is a general data class containing whatever is needed to accurately represent the world. * This includes the map, entities, influence maps, tiles, heightmap, etc. **/ class CWorld { NONCOPYABLE(CWorld); /** * pointer to the CGame object representing the game. **/ CGame *m_pGame; /** * pointer to the CTerrain object representing the height map. **/ CTerrain *m_Terrain; /** * pointer to the CUnitManager that holds all the units in the world. **/ CUnitManager *m_UnitManager; /** * pointer to the CLOSManager that holds the visibility matrix for the world. **/ CLOSManager *m_LOSManager; /** * pointer to the CTerritoryManager that holds territory matrix for the world. **/ CTerritoryManager *m_TerritoryManager; public: CWorld(CGame *pGame); ~CWorld(); /* Initialize the World - load the map and all objects */ void RegisterInit(const CStrW& mapFile, int playerID); // provided for JS _rewritemaps function void RewriteMap(); /** * Get the pointer to the terrain object. * * @return CTerrain * the value of m_Terrain. **/ inline CTerrain *GetTerrain() { return m_Terrain; } /** * Get a reference to the unit manager object. * * @return CUnitManager & dereferenced m_UnitManager. **/ inline CUnitManager &GetUnitManager() { return *m_UnitManager; } /** * Get the pointer to the LOS manager object. * * @return CLOSManager * the value of m_LOSManager. **/ inline CLOSManager *GetLOSManager() { return m_LOSManager; } /** * Get the pointer to the territory manager object. * * @return CTerritoryManager * the value of m_TerritoryManager. **/ inline CTerritoryManager *GetTerritoryManager() { return m_TerritoryManager; } }; // rationale: see definition. class CLightEnv; extern CLightEnv g_LightEnv; #endif Index: ps/trunk/source/gui/MiniMap.h =================================================================== --- ps/trunk/source/gui/MiniMap.h (revision 7838) +++ ps/trunk/source/gui/MiniMap.h (revision 7839) @@ -1,93 +1,83 @@ -/* Copyright (C) 2009 Wildfire Games. +/* Copyright (C) 2010 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. 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 2 of the License, or * (at your option) any later version. * * 0 A.D. 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 0 A.D. If not, see . */ #ifndef INCLUDED_MINIMAP #define INCLUDED_MINIMAP #include "gui/GUI.h" -class CVector2D; -class CVector3D; class CCamera; class CTerrain; -class CUnitManager; extern bool g_TerrainModified; class CMiniMap : public IGUIObject { GUI_OBJECT(CMiniMap) public: CMiniMap(); virtual ~CMiniMap(); protected: virtual void Draw(); virtual void HandleMessage(const SGUIMessage &Message); // create the minimap textures void CreateTextures(); // rebuild the terrain texture map void RebuildTerrainTexture(); // rebuild the LOS map void RebuildLOSTexture(); // destroy and free any memory and textures void Destroy(); void SetCameraPos(); void FireWorldClickEvent(int button, int clicks); - // calculate the relative heightmap space coordinates - // for a units world position - CVector2D GetMapSpaceCoords(CVector3D worldPos); - // the terrain we are mini-mapping const CTerrain* m_Terrain; - // the unit manager with unit positions - const CUnitManager* m_UnitManager; - // not const: camera is moved by clicking on minimap CCamera* m_Camera; //Whether or not the mouse is currently down bool m_Clicking; // minimap texture handles GLuint m_TerrainTexture; GLuint m_LOSTexture; // texture data u32* m_TerrainData; u8* m_LOSData; ssize_t m_Width, m_Height; // map size ssize_t m_MapSize; // texture size GLsizei m_TextureSize; void DrawViewRect(); // split out of Draw }; #endif Index: ps/trunk/source/gui/MiniMap.cpp =================================================================== --- ps/trunk/source/gui/MiniMap.cpp (revision 7838) +++ ps/trunk/source/gui/MiniMap.cpp (revision 7839) @@ -1,581 +1,558 @@ -/* Copyright (C) 2009 Wildfire Games. +/* Copyright (C) 2010 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. 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 2 of the License, or * (at your option) any later version. * * 0 A.D. 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 0 A.D. If not, see . */ #include "precompiled.h" #include #include "MiniMap.h" #include "graphics/GameView.h" #include "graphics/MiniPatch.h" #include "graphics/Terrain.h" #include "graphics/TextureEntry.h" #include "graphics/TextureManager.h" -#include "graphics/Unit.h" -#include "graphics/UnitManager.h" #include "lib/ogl.h" #include "lib/external_libraries/sdl.h" #include "lib/bits.h" #include "lib/timer.h" #include "lib/sysdep/cpu.h" #include "ps/Game.h" #include "ps/Profile.h" #include "ps/World.h" #include "renderer/Renderer.h" #include "renderer/WaterManager.h" #include "simulation2/Simulation2.h" #include "simulation2/components/ICmpMinimap.h" bool g_TerrainModified = false; bool g_GameRestarted = false; -// used by GetMapSpaceCoords (precalculated as an optimization). -// this was formerly access via inline asm, which required it to be -// static data instead of a class member. that is no longer the case, -// but we leave it because this is slightly more efficient. -static float m_scaleX, m_scaleY; - - static unsigned int ScaleColor(unsigned int color, float x) { unsigned int r = unsigned(float(color & 0xff) * x); unsigned int g = unsigned(float((color>>8) & 0xff) * x); unsigned int b = unsigned(float((color>>16) & 0xff) * x); return (0xff000000 | r | g<<8 | b<<16); } CMiniMap::CMiniMap() : m_TerrainTexture(0), m_TerrainData(0), m_MapSize(0), m_Terrain(0), - m_LOSTexture(0), m_LOSData(0), m_UnitManager(0) + m_LOSTexture(0), m_LOSData(0) { AddSetting(GUIST_CColor, "fov_wedge_color"); AddSetting(GUIST_CStr, "tooltip"); AddSetting(GUIST_CStr, "tooltip_style"); m_Clicking = false; } CMiniMap::~CMiniMap() { Destroy(); } void CMiniMap::HandleMessage(const SGUIMessage &Message) { switch(Message.type) { case GUIM_MOUSE_PRESS_LEFT: { SetCameraPos(); m_Clicking = true; break; } case GUIM_MOUSE_RELEASE_LEFT: { if(m_Clicking) SetCameraPos(); m_Clicking = false; break; } case GUIM_MOUSE_DBLCLICK_LEFT: { if(m_Clicking) SetCameraPos(); m_Clicking = false; break; } case GUIM_MOUSE_ENTER: { // g_Selection.m_mouseOverMM = true; break; } case GUIM_MOUSE_LEAVE: { // g_Selection.m_mouseOverMM = false; m_Clicking = false; break; } case GUIM_MOUSE_RELEASE_RIGHT: { CMiniMap::FireWorldClickEvent(SDL_BUTTON_RIGHT, 1); break; } case GUIM_MOUSE_DBLCLICK_RIGHT: { CMiniMap::FireWorldClickEvent(SDL_BUTTON_RIGHT, 2); break; } case GUIM_MOUSE_MOTION: { if (m_Clicking) SetCameraPos(); break; } default: break; } // switch } void CMiniMap::SetCameraPos() { CTerrain *MMTerrain=g_Game->GetWorld()->GetTerrain(); CVector3D CamOrient=m_Camera->m_Orientation.GetTranslation(); //get center point of screen int x = g_Renderer.GetWidth()/2; int y = g_Renderer.GetHeight()/2; CVector3D ScreenMiddle=m_Camera->GetWorldCoordinates(x,y); //Get Vector required to go from camera position to ScreenMiddle CVector3D TransVector; TransVector.X=CamOrient.X-ScreenMiddle.X; TransVector.Z=CamOrient.Z-ScreenMiddle.Z; //world position of where mouse clicked CVector3D Destination; CPos MousePos = GetMousePos(); //X and Z according to proportion of mouse position and minimap Destination.X = CELL_SIZE * m_MapSize * ( (MousePos.x - m_CachedActualSize.left) / m_CachedActualSize.GetWidth() ); Destination.Z = CELL_SIZE * m_MapSize * ( (m_CachedActualSize.bottom - MousePos.y) / m_CachedActualSize.GetHeight() ); m_Camera->m_Orientation._14=Destination.X; m_Camera->m_Orientation._34=Destination.Z; m_Camera->m_Orientation._14+=TransVector.X; m_Camera->m_Orientation._34+=TransVector.Z; //Lock Y coord. No risk of zoom exceeding limit-Y does not increase float Height=MMTerrain->GetExactGroundLevel( m_Camera->m_Orientation._14, m_Camera->m_Orientation._34) + g_YMinOffset; if (m_Camera->m_Orientation._24 < Height) { m_Camera->m_Orientation._24=Height; } m_Camera->UpdateFrustum(); } void CMiniMap::FireWorldClickEvent(int button, int clicks) { // TODO: we ought to pass this through to the GUI system //debug_printf(L"FireWorldClickEvent: button %d, clicks %d\n", button, clicks); /* CPos MousePos = GetMousePos(); CVector2D Destination; //X and Z according to proportion of mouse position and minimap Destination.x = CELL_SIZE * m_MapSize * ( (MousePos.x - m_CachedActualSize.left) / m_CachedActualSize.GetWidth() ); Destination.y = CELL_SIZE * m_MapSize * ( (m_CachedActualSize.bottom - MousePos.y) / m_CachedActualSize.GetHeight() ); */ UNUSED2(button); UNUSED2(clicks); } // render view rect : John M. Mena // This sets up and draws the rectangle on the mini-map // which represents the view of the camera in the world. void CMiniMap::DrawViewRect() { // Get correct world coordinates based off corner of screen start // at Bottom Left and going CW CVector3D hitPt[4]; hitPt[0]=m_Camera->GetWorldCoordinates(0,g_Renderer.GetHeight()); hitPt[1]=m_Camera->GetWorldCoordinates(g_Renderer.GetWidth(),g_Renderer.GetHeight()); hitPt[2]=m_Camera->GetWorldCoordinates(g_Renderer.GetWidth(),0); hitPt[3]=m_Camera->GetWorldCoordinates(0,0); float ViewRect[4][2]; for (int i=0;i<4;i++) { // convert to minimap space float px=hitPt[i].X; float pz=hitPt[i].Z; ViewRect[i][0]=(m_CachedActualSize.GetWidth()*px/float(CELL_SIZE*m_MapSize)); ViewRect[i][1]=(m_CachedActualSize.GetHeight()*pz/float(CELL_SIZE*m_MapSize)); } // Enable Scissoring as to restrict the rectangle // to only the mini-map below by retrieving the mini-maps // screen coords. glScissor((int)m_CachedActualSize.left, 0, (int)m_CachedActualSize.right, (int)m_CachedActualSize.GetHeight()); glEnable(GL_SCISSOR_TEST); glEnable(GL_LINE_SMOOTH); glLineWidth(2.0f); glColor3f(1.0f, 0.3f, 0.3f); // Draw the viewing rectangle with the ScEd's conversion algorithm const float x = m_CachedActualSize.left, y = m_CachedActualSize.bottom; glBegin(GL_LINE_LOOP); glVertex2f(x+ViewRect[0][0], y-ViewRect[0][1]); glVertex2f(x+ViewRect[1][0], y-ViewRect[1][1]); glVertex2f(x+ViewRect[2][0], y-ViewRect[2][1]); glVertex2f(x+ViewRect[3][0], y-ViewRect[3][1]); glEnd(); // restore state glDisable(GL_SCISSOR_TEST); glDisable(GL_LINE_SMOOTH); glLineWidth(1.0f); } struct MinimapUnitVertex { u8 r, g, b, a; float x, y; }; void CMiniMap::Draw() { PROFILE("minimap"); // The terrain isn't actually initialized until the map is loaded, which // happens when the game is started, so abort until then. if(!(GetGUI() && g_Game && g_Game->IsGameStarted())) return; glDisable(GL_DEPTH_TEST); // Set our globals in case they hadn't been set before m_Camera = g_Game->GetView()->GetCamera(); m_Terrain = g_Game->GetWorld()->GetTerrain(); - m_UnitManager = &g_Game->GetWorld()->GetUnitManager(); m_Width = (u32)(m_CachedActualSize.right - m_CachedActualSize.left); m_Height = (u32)(m_CachedActualSize.bottom - m_CachedActualSize.top); m_MapSize = m_Terrain->GetVerticesPerSide(); m_TextureSize = (GLsizei)round_up_to_pow2((size_t)m_MapSize); - m_scaleX = float(m_Width) / float(m_MapSize - 1); - m_scaleY = float(m_Height) / float(m_MapSize - 1); - if(!m_TerrainTexture || g_GameRestarted) CreateTextures(); // do not limit this as with LOS updates below - we must update // immediately after changes are reported because this flag will be // reset at the end of the frame. if(g_TerrainModified) RebuildTerrainTexture(); // only update 10x / second // (note: since units only move a few pixels per second on the minimap, // we can get away with infrequent updates; this is slow, ~20ms) static double last_time; const double cur_time = timer_Time(); if(cur_time - last_time > 100e-3) // 10 updates/sec { last_time = cur_time; // CLOSManager* losMgr = g_Game->GetWorld()->GetLOSManager(); // if(losMgr->m_LOSSetting != LOS_SETTING_ALL_VISIBLE) // RebuildLOSTexture(); } const float texCoordMax = ((float)m_MapSize - 1) / ((float)m_TextureSize); const float x = m_CachedActualSize.left, y = m_CachedActualSize.bottom; const float x2 = m_CachedActualSize.right, y2 = m_CachedActualSize.top; const float z = GetBufferedZ(); // Draw the main textured quad g_Renderer.BindTexture(0, m_TerrainTexture); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glBegin(GL_QUADS); glTexCoord2f(0.0f, 0.0f); glVertex3f(x, y, z); glTexCoord2f(texCoordMax, 0.0f); glVertex3f(x2, y, z); glTexCoord2f(texCoordMax, texCoordMax); glVertex3f(x2, y2, z); glTexCoord2f(0.0f, texCoordMax); glVertex3f(x, y2, z); glEnd(); /* // TODO: reimplement with new sim system // Shade territories by player CTerritoryManager* territoryMgr = g_Game->GetWorld()->GetTerritoryManager(); std::vector& territories = territoryMgr->GetTerritories(); PROFILE_START("minimap territory shade"); glDisable(GL_TEXTURE_2D); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); for( size_t i=0; iowner->GetPlayerID() == 0 ) continue; std::vector& boundary = territories[i]->boundary; SPlayerColour col = territories[i]->owner->GetColour(); glColor4f(col.r, col.g, col.b, 0.25f); glBegin(GL_POLYGON); for( size_t j=0; jGetTilesPerSide() * CELL_SIZE); float fy = boundary[j].y / (m_Terrain->GetTilesPerSide() * CELL_SIZE); glVertex3f( x*(1-fx) + x2*fx, y*(1-fy) + y2*fy, z ); } glEnd(); } glDisable(GL_BLEND); PROFILE_END("minimap territory shade"); // Draw territory boundaries glEnable(GL_LINE_SMOOTH); glLineWidth(1.0f); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glColor4f(0.8f, 0.8f, 0.8f, 0.8f); for( size_t i=0; i& boundary = territories[i]->boundary; glBegin(GL_LINE_LOOP); for( size_t j=0; jGetTilesPerSide() * CELL_SIZE); float fy = boundary[j].y / (m_Terrain->GetTilesPerSide() * CELL_SIZE); glVertex3f( x*(1-fx) + x2*fx, y*(1-fy) + y2*fy, z ); } glEnd(); } glLineWidth(1.0f); glDisable(GL_LINE_SMOOTH); glDisable(GL_BLEND); */ // Draw the LOS quad in black, using alpha values from the LOS texture g_Renderer.BindTexture(0, m_LOSTexture); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PRIMARY_COLOR_ARB); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glBegin(GL_QUADS); glColor3f(0.0f, 0.0f, 0.0f); glTexCoord2f(0.0f, 0.0f); glVertex3f(x, y, z); glTexCoord2f(texCoordMax, 0.0f); glVertex3f(x2, y, z); glTexCoord2f(texCoordMax, texCoordMax); glVertex3f(x2, y2, z); glTexCoord2f(0.0f, texCoordMax); glVertex3f(x, y2, z); glEnd(); glDisable(GL_BLEND); PROFILE_START("minimap units"); // Don't enable GL_POINT_SMOOTH because it's far too slow // (~70msec/frame on a GF4 rendering a thousand points) glPointSize(3.f); - float sx = m_scaleX / CELL_SIZE; - float sy = m_scaleY / CELL_SIZE; + float sx = (float)m_Width / ((m_MapSize - 1) * CELL_SIZE); + float sy = (float)m_Height / ((m_MapSize - 1) * CELL_SIZE); CSimulation2* sim = g_Game->GetSimulation2(); const CSimulation2::InterfaceList& ents = sim->GetEntitiesWithInterface(IID_Minimap); std::vector vertexArray; vertexArray.reserve(ents.size()); for (CSimulation2::InterfaceList::const_iterator it = ents.begin(); it != ents.end(); ++it) { MinimapUnitVertex v; ICmpMinimap* cmpMinimap = static_cast(it->second); if (cmpMinimap->GetRenderData(v.r, v.g, v.b, v.x, v.y)) { v.a = 255; v.x = x + v.x*sx; v.y = y - v.y*sy; vertexArray.push_back(v); } } if (!vertexArray.empty()) { glPushMatrix(); glTranslatef(0, 0, z); glInterleavedArrays(GL_C4UB_V2F, sizeof(MinimapUnitVertex), &vertexArray[0]); glDrawArrays(GL_POINTS, 0, (GLsizei)vertexArray.size()); glDisableClientState(GL_COLOR_ARRAY); glDisableClientState(GL_VERTEX_ARRAY); glPopMatrix(); } PROFILE_END("minimap units"); DrawViewRect(); // Reset everything back to normal glPointSize(1.0f); glEnable(GL_TEXTURE_2D); glEnable(GL_DEPTH_TEST); } void CMiniMap::CreateTextures() { Destroy(); // Create terrain texture glGenTextures(1, &m_TerrainTexture); g_Renderer.BindTexture(0, m_TerrainTexture); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, m_TextureSize, m_TextureSize, 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, 0); m_TerrainData = new u32[(m_MapSize - 1) * (m_MapSize - 1)]; glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP); // Create LOS texture glGenTextures(1, &m_LOSTexture); g_Renderer.BindTexture(0, m_LOSTexture); glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA8, m_TextureSize, m_TextureSize, 0, GL_ALPHA, GL_UNSIGNED_BYTE, 0); m_LOSData = new u8[(m_MapSize - 1) * (m_MapSize - 1)]; glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP); // Rebuild and upload both of them RebuildTerrainTexture(); RebuildLOSTexture(); } void CMiniMap::RebuildTerrainTexture() { u32 x = 0; u32 y = 0; u32 w = m_MapSize - 1; u32 h = m_MapSize - 1; float waterHeight = g_Renderer.GetWaterManager()->m_WaterHeight; for(u32 j = 0; j < h; j++) { u32 *dataPtr = m_TerrainData + ((y + j) * (m_MapSize - 1)) + x; for(u32 i = 0; i < w; i++) { float avgHeight = ( m_Terrain->GetVertexGroundLevel((int)i, (int)j) + m_Terrain->GetVertexGroundLevel((int)i+1, (int)j) + m_Terrain->GetVertexGroundLevel((int)i, (int)j+1) + m_Terrain->GetVertexGroundLevel((int)i+1, (int)j+1) ) / 4.0f; if(avgHeight < waterHeight) { *dataPtr++ = 0xff304080; // TODO: perhaps use the renderer's water color? } else { int hmap = ((int)m_Terrain->GetHeightMap()[(y + j) * m_MapSize + x + i]) >> 8; int val = (hmap / 3) + 170; u32 color = 0xFFFFFFFF; CMiniPatch *mp = m_Terrain->GetTile(x + i, y + j); if(mp) { CTextureEntry *tex = mp->GetTextureEntry(); if(tex) color = tex->GetBaseColor(); } *dataPtr++ = ScaleColor(color, float(val) / 255.0f); } } } // Upload the texture g_Renderer.BindTexture(0, m_TerrainTexture); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_MapSize - 1, m_MapSize - 1, GL_BGRA_EXT, GL_UNSIGNED_BYTE, m_TerrainData); } void CMiniMap::RebuildLOSTexture() { PROFILE_START("rebuild minimap: los"); ssize_t x = 0; ssize_t y = 0; ssize_t w = m_MapSize - 1; ssize_t h = m_MapSize - 1; memset(m_LOSData, 0, w*h); // TODO: ought to do something like: /* CLOSManager* losMgr = g_Game->GetWorld()->GetLOSManager(); CPlayer* player = g_Game->GetLocalPlayer(); for(ssize_t j = 0; j < h; j++) { u8 *dataPtr = m_LOSData + ((y + j) * (m_MapSize - 1)) + x; for(ssize_t i = 0; i < w; i++) { ELOSStatus status = losMgr->GetStatus(i, j, player); if(status == LOS_UNEXPLORED) *dataPtr++ = 0xff; else if(status == LOS_EXPLORED) *dataPtr++ = (u8) (0xff * 0.3f); else *dataPtr++ = 0; } } */ // Upload the texture g_Renderer.BindTexture(0, m_LOSTexture); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_MapSize - 1, m_MapSize - 1, GL_ALPHA, GL_UNSIGNED_BYTE, m_LOSData); PROFILE_END("rebuild minimap: los"); } void CMiniMap::Destroy() { if(m_TerrainTexture) glDeleteTextures(1, &m_TerrainTexture); if(m_LOSTexture) glDeleteTextures(1, &m_LOSTexture); delete[] m_TerrainData; m_TerrainData = 0; delete[] m_LOSData; m_LOSData = 0; } - -CVector2D CMiniMap::GetMapSpaceCoords(CVector3D worldPos) -{ - float x = rintf(worldPos.X / CELL_SIZE); - float y = rintf(worldPos.Z / CELL_SIZE); - // Entity's Z coordinate is really its longitudinal coordinate on the terrain - - // Calculate map space scale - return CVector2D(x * m_scaleX, y * m_scaleY); -} Index: ps/trunk/source/tools/atlas/GameInterface/Handlers/MapHandlers.cpp =================================================================== --- ps/trunk/source/tools/atlas/GameInterface/Handlers/MapHandlers.cpp (revision 7838) +++ ps/trunk/source/tools/atlas/GameInterface/Handlers/MapHandlers.cpp (revision 7839) @@ -1,103 +1,103 @@ /* Copyright (C) 2010 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. 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 2 of the License, or * (at your option) any later version. * * 0 A.D. 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 0 A.D. If not, see . */ #include "precompiled.h" #include "MessageHandler.h" #include "../GameLoop.h" #include "graphics/GameView.h" #include "graphics/MapWriter.h" #include "graphics/Patch.h" #include "graphics/Terrain.h" #include "graphics/TextureEntry.h" #include "graphics/TextureManager.h" #include "ps/Game.h" #include "ps/Loader.h" #include "ps/World.h" #include "renderer/Renderer.h" #include "scriptinterface/ScriptInterface.h" #include "simulation/LOSManager.h" #include "simulation/Simulation.h" #include "simulation2/Simulation2.h" #include "simulation2/components/ICmpPlayer.h" #include "simulation2/components/ICmpPlayerManager.h" #include "simulation2/components/ICmpPosition.h" namespace { void InitGame() { if (g_Game) { delete g_Game; g_Game = NULL; } g_Game = new CGame(); // Default to player 1 for playtesting g_Game->SetPlayerID(1); } void StartGame(const CStrW& map) { CStrW mapBase = map.BeforeLast(L".pmp"); // strip the file extension, if any CScriptValRooted attrs; g_Game->GetSimulation2()->GetScriptInterface().Eval("({})", attrs); g_Game->GetSimulation2()->GetScriptInterface().SetProperty(attrs.get(), "map", std::wstring(mapBase), false); g_Game->StartGame(attrs); LDR_NonprogressiveLoad(); PSRETURN ret = g_Game->ReallyStartGame(); debug_assert(ret == PSRETURN_OK); } } namespace AtlasMessage { MESSAGEHANDLER(GenerateMap) { InitGame(); // Load the empty default map StartGame(L"_default"); // TODO: use msg->size somehow // (e.g. load the map then resize the terrain to match it) UNUSED2(msg); } MESSAGEHANDLER(LoadMap) { InitGame(); StartGame(*msg->filename); } MESSAGEHANDLER(SaveMap) { CMapWriter writer; const VfsPath pathname = VfsPath(L"maps/scenarios/") / *msg->filename; writer.SaveMap(pathname, - g_Game->GetWorld()->GetTerrain(), &g_Game->GetWorld()->GetUnitManager(), + g_Game->GetWorld()->GetTerrain(), g_Renderer.GetWaterManager(), g_Renderer.GetSkyManager(), &g_LightEnv, g_Game->GetView()->GetCamera(), g_Game->GetView()->GetCinema(), g_Game->GetSimulation2()); } } Index: ps/trunk/source/graphics/MapReader.cpp =================================================================== --- ps/trunk/source/graphics/MapReader.cpp (revision 7838) +++ ps/trunk/source/graphics/MapReader.cpp (revision 7839) @@ -1,1299 +1,1293 @@ /* Copyright (C) 2010 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. 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 2 of the License, or * (at your option) any later version. * * 0 A.D. 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 0 A.D. If not, see . */ #include "precompiled.h" #include "MapReader.h" #include "graphics/Camera.h" #include "graphics/CinemaTrack.h" -#include "graphics/GameView.h" -#include "graphics/Model.h" -#include "graphics/ObjectManager.h" #include "graphics/Patch.h" #include "graphics/Terrain.h" #include "graphics/TextureEntry.h" #include "graphics/TextureManager.h" -#include "graphics/Unit.h" -#include "graphics/UnitManager.h" #include "lib/timer.h" #include "maths/MathUtil.h" #include "ps/CLogger.h" -#include "ps/Game.h" #include "ps/Loader.h" #include "ps/LoaderThunks.h" #include "ps/XML/Xeromyces.h" #include "renderer/SkyManager.h" #include "renderer/WaterManager.h" #include "simulation2/Simulation2.h" #include "simulation2/components/ICmpOwnership.h" #include "simulation2/components/ICmpPlayer.h" #include "simulation2/components/ICmpPlayerManager.h" #include "simulation2/components/ICmpPosition.h" #include "simulation2/components/ICmpTerrain.h" #include "simulation2/components/ICmpWaterManager.h" #include #define LOG_CATEGORY L"graphics" CMapReader::CMapReader() : xml_reader(0), m_PatchesPerSide(0) { cur_terrain_tex = 0; // important - resets generator state } // LoadMap: try to load the map from given file; reinitialise the scene to new data if successful void CMapReader::LoadMap(const VfsPath& pathname, CTerrain *pTerrain_, WaterManager* pWaterMan_, SkyManager* pSkyMan_, CLightEnv *pLightEnv_, CCamera *pCamera_, CCinemaManager* pCinema_, CTriggerManager* pTrigMan_, CSimulation2 *pSimulation2_, int playerID_) { // latch parameters (held until DelayedLoadFinished) pTerrain = pTerrain_; pLightEnv = pLightEnv_; pCamera = pCamera_; pWaterMan = pWaterMan_; pSkyMan = pSkyMan_; pCinema = pCinema_; pTrigMan = pTrigMan_; pSimulation2 = pSimulation2_; m_PlayerID = playerID_; m_CameraStartupTarget = INVALID_ENTITY; filename_xml = fs::change_extension(pathname, L".xml"); // In some cases (particularly tests) we don't want to bother storing a large // mostly-empty .pmp file, so we let the XML file specify basic terrain instead. // If there's an .xml file and no .pmp, then we're probably in this XML-only mode only_xml = false; if (!FileExists(pathname) && FileExists(filename_xml)) { only_xml = true; } file_format_version = CMapIO::FILE_VERSION; // default if there's no .pmp if (!only_xml) { // [25ms] unpacker.Read(pathname, "PSMP"); file_format_version = unpacker.GetVersion(); } // check oldest supported version if (file_format_version < FILE_READ_VERSION) throw PSERROR_File_InvalidVersion(); // delete all existing entities if (pSimulation2) pSimulation2->ResetState(); // unpack the data if (!only_xml) RegMemFun(this, &CMapReader::UnpackMap, L"CMapReader::UnpackMap", 1200); if (file_format_version >= 3) { // read the corresponding XML file RegMemFun(this, &CMapReader::ReadXML, L"CMapReader::ReadXML", 5800); } // apply data to the world RegMemFun(this, &CMapReader::ApplyData, L"CMapReader::ApplyData", 5); RegMemFun(this, &CMapReader::DelayLoadFinished, L"CMapReader::DelayLoadFinished", 5); } // UnpackMap: unpack the given data from the raw data stream into local variables int CMapReader::UnpackMap() { // now unpack everything into local data int ret = UnpackTerrain(); if(ret != 0) // failed or timed out return ret; if (unpacker.GetVersion() < 4) debug_warn(L"Old unsupported map version - objects and lighting will be lost"); return 0; } // UnpackTerrain: unpack the terrain from the end of the input data stream // - data: map size, heightmap, list of textures used by map, texture tile assignments int CMapReader::UnpackTerrain() { // yield after this time is reached. balances increased progress bar // smoothness vs. slowing down loading. const double end_time = timer_Time() + 200e-3; // first call to generator (this is skipped after first call, // i.e. when the loop below was interrupted) if (cur_terrain_tex == 0) { m_PatchesPerSide = (ssize_t)unpacker.UnpackSize(); // unpack heightmap [600us] size_t verticesPerSide = m_PatchesPerSide*PATCH_SIZE+1; m_Heightmap.resize(SQR(verticesPerSide)); unpacker.UnpackRaw(&m_Heightmap[0], SQR(verticesPerSide)*sizeof(u16)); // unpack # textures num_terrain_tex = unpacker.UnpackSize(); m_TerrainTextures.reserve(num_terrain_tex); } // unpack texture names; find handle for each texture. // interruptible. while (cur_terrain_tex < num_terrain_tex) { CStr texturename; unpacker.UnpackString(texturename); CTextureEntry* texentry = NULL; if (CTextureManager::IsInitialised()) texentry = g_TexMan.FindTexture(texturename); m_TerrainTextures.push_back(texentry); cur_terrain_tex++; LDR_CHECK_TIMEOUT(cur_terrain_tex, num_terrain_tex); } // unpack tile data [3ms] ssize_t tilesPerSide = m_PatchesPerSide*PATCH_SIZE; m_Tiles.resize(size_t(SQR(tilesPerSide))); unpacker.UnpackRaw(&m_Tiles[0], sizeof(STileDesc)*m_Tiles.size()); // reset generator state. cur_terrain_tex = 0; return 0; } // ApplyData: take all the input data, and rebuild the scene from it int CMapReader::ApplyData() { if (m_PatchesPerSide == 0) { debug_warn(L"Map has no terrain data"); return -1; // we'll probably crash when trying to use this map later } if (!only_xml) { // initialise the terrain pTerrain->Initialize(m_PatchesPerSide, &m_Heightmap[0]); // setup the textures on the minipatches STileDesc* tileptr = &m_Tiles[0]; for (ssize_t j=0; jGetPatch(i,j)->m_MiniPatches[m][k]; // can't fail mp.Tex = m_TerrainTextures[tileptr->m_Tex1Index]; mp.Priority = tileptr->m_Priority; tileptr++; } } } } } if (file_format_version >= 4) { // copy over the lighting parameters if (pLightEnv) *pLightEnv = m_LightEnv; } if (m_CameraStartupTarget != INVALID_ENTITY && pCamera) { CmpPtr cmpPosition(*pSimulation2, m_CameraStartupTarget); if (!cmpPosition.null()) { pCamera->m_Orientation.SetIdentity(); pCamera->m_Orientation.Translate(CVector3D(0.f, 0.f, -200.f)); // move backwards from the target entity pCamera->m_Orientation.RotateX(DEGTORAD(30)); pCamera->m_Orientation.RotateY(DEGTORAD(0)); CFixedVector3D pos = cmpPosition->GetPosition(); pCamera->m_Orientation.Translate(CVector3D(pos.X.ToFloat(), pos.Y.ToFloat(), pos.Z.ToFloat())); pCamera->UpdateFrustum(); } } CmpPtr cmpTerrain(*pSimulation2, SYSTEM_ENTITY); if (!cmpTerrain.null()) cmpTerrain->ReloadTerrain(); return 0; } // Holds various state data while reading maps, so that loading can be // interrupted (e.g. to update the progress display) then later resumed. class CXMLReader { NONCOPYABLE(CXMLReader); public: CXMLReader(const VfsPath& xml_filename, CMapReader& mapReader) : m_MapReader(mapReader) { Init(xml_filename); } // return semantics: see Loader.cpp!LoadFunc. int ProgressiveRead(); private: CXeromyces xmb_file; CMapReader& m_MapReader; int el_entity; int el_tracks; int el_template, el_player; int el_position, el_orientation; int el_nonentity; int el_actor; int at_x, at_y, at_z; int at_id; int at_angle; int at_uid; XMBElementList nodes; // children of root // loop counters int node_idx; int entity_idx, nonentity_idx; // # entities+nonentities processed and total (for progress calc) int completed_jobs, total_jobs; // maximum used entity ID, so we can safely allocate new ones entity_id_t max_uid; void Init(const VfsPath& xml_filename); void ReadPlayers(); void ReadTerrain(XMBElement parent); void ReadEnvironment(XMBElement parent); void ReadCamera(XMBElement parent); void ReadCinema(XMBElement parent); void ReadTriggers(XMBElement parent); // void ReadTriggerGroup(XMBElement parent, MapTriggerGroup& group); int ReadEntities(XMBElement parent, double end_time); int ReadOldEntities(XMBElement parent, double end_time); int ReadNonEntities(XMBElement parent, double end_time); }; void CXMLReader::Init(const VfsPath& xml_filename) { // must only assign once, so do it here node_idx = entity_idx = nonentity_idx = 0; if (xmb_file.Load(g_VFS, xml_filename) != PSRETURN_OK) throw PSERROR_File_ReadFailed(); // define the elements and attributes that are frequently used in the XML file, // so we don't need to do lots of string construction and comparison when // reading the data. // (Needs to be synchronised with the list in CXMLReader - ugh) #define EL(x) el_##x = xmb_file.GetElementID(#x) #define AT(x) at_##x = xmb_file.GetAttributeID(#x) EL(entity); EL(tracks); EL(template); EL(player); EL(position); EL(orientation); EL(nonentity); EL(actor); AT(x); AT(y); AT(z); AT(angle); AT(uid); #undef AT #undef EL XMBElement root = xmb_file.GetRoot(); debug_assert(xmb_file.GetElementString(root.GetNodeName()) == "Scenario"); nodes = root.GetChildNodes(); // find out total number of entities+nonentities // (used when calculating progress) completed_jobs = 0; total_jobs = 0; for (int i = 0; i < nodes.Count; i++) total_jobs += nodes.Item(i).GetChildNodes().Count; // Find the maximum entity ID, so we can safely allocate new IDs without conflicts max_uid = SYSTEM_ENTITY; XMBElement ents = nodes.GetFirstNamedItem(xmb_file.GetElementID("Entities")); XERO_ITER_EL(ents, ent) { utf16string uid = ent.GetAttributes().GetNamedItem(at_uid); max_uid = std::max(max_uid, (entity_id_t)CStr(uid).ToInt()); } // Initialise player data ReadPlayers(); } void CXMLReader::ReadPlayers() { CmpPtr cmpPlayerMan(*m_MapReader.pSimulation2, SYSTEM_ENTITY); debug_assert(!cmpPlayerMan.null()); // TODO: we ought to read at least some of this data from the map file. // For now, just always use the defaults. std::map playerDefaultNames; std::map playerDefaultCivs; std::map playerDefaultColours; CXeromyces playerDefaultFile; if (playerDefaultFile.Load(g_VFS, L"simulation/data/players.xml") != PSRETURN_OK) throw PSERROR_File_ReadFailed(); #define AT(x) int at_##x = playerDefaultFile.GetAttributeID(#x) AT(id); AT(name); AT(civ); AT(r); AT(g); AT(b); #undef AT XERO_ITER_EL(playerDefaultFile.GetRoot(), player) { XMBAttributeList attrs = player.GetAttributes(); int id = CStr(attrs.GetNamedItem(at_id)).ToInt(); playerDefaultNames[id] = attrs.GetNamedItem(at_name); playerDefaultCivs[id] = attrs.GetNamedItem(at_civ); SColor3ub colour; colour.R = (u8)CStr(attrs.GetNamedItem(at_r)).ToInt(); colour.G = (u8)CStr(attrs.GetNamedItem(at_g)).ToInt(); colour.B = (u8)CStr(attrs.GetNamedItem(at_b)).ToInt(); playerDefaultColours[id] = colour; } size_t numPlayers = 9; // including Gaia for (size_t i = 0; i < numPlayers; ++i) { int uid = ++max_uid; entity_id_t ent = m_MapReader.pSimulation2->AddEntity(L"special/player", uid); CmpPtr cmpPlayer(*m_MapReader.pSimulation2, ent); debug_assert(!cmpPlayer.null()); cmpPlayer->SetName(playerDefaultNames[i]); cmpPlayer->SetCiv(playerDefaultCivs[i]); SColor3ub colour = playerDefaultColours[i]; cmpPlayer->SetColour(colour.R, colour.G, colour.B); cmpPlayerMan->AddPlayer(ent); } } void CXMLReader::ReadTerrain(XMBElement parent) { #define AT(x) int at_##x = xmb_file.GetAttributeID(#x) AT(patches); AT(texture); AT(priority); AT(height); #undef AT ssize_t patches = 9; CStr texture = "grass1_spring"; int priority = 0; u16 height = 16384; XERO_ITER_ATTR(parent, attr) { if (attr.Name == at_patches) patches = CStr(attr.Value).ToInt(); else if (attr.Name == at_texture) texture = CStr(attr.Value); else if (attr.Name == at_priority) priority = CStr(attr.Value).ToInt(); else if (attr.Name == at_height) height = (u16)CStr(attr.Value).ToInt(); } m_MapReader.m_PatchesPerSide = patches; // Load the texture CTextureEntry* texentry = NULL; if (CTextureManager::IsInitialised()) texentry = g_TexMan.FindTexture(texture); m_MapReader.pTerrain->Initialize(patches, NULL); // Fill the heightmap u16* heightmap = m_MapReader.pTerrain->GetHeightMap(); ssize_t verticesPerSide = m_MapReader.pTerrain->GetVerticesPerSide(); for (ssize_t i = 0; i < SQR(verticesPerSide); ++i) heightmap[i] = height; // Fill the texture map for (ssize_t pz = 0; pz < patches; ++pz) { for (ssize_t px = 0; px < patches; ++px) { CPatch* patch = m_MapReader.pTerrain->GetPatch(px, pz); // can't fail for (ssize_t z = 0; z < PATCH_SIZE; ++z) { for (ssize_t x = 0; x < PATCH_SIZE; ++x) { patch->m_MiniPatches[z][x].Tex = texentry; patch->m_MiniPatches[z][x].Priority = priority; } } } } } void CXMLReader::ReadEnvironment(XMBElement parent) { #define EL(x) int el_##x = xmb_file.GetElementID(#x) #define AT(x) int at_##x = xmb_file.GetAttributeID(#x) EL(skyset); EL(suncolour); EL(sunelevation); EL(sunrotation); EL(terrainambientcolour); EL(unitsambientcolour); EL(terrainshadowtransparency); EL(water); EL(waterbody); EL(type); EL(colour); EL(height); EL(shininess); EL(waviness); EL(murkiness); EL(tint); EL(reflectiontint); EL(reflectiontintstrength); AT(r); AT(g); AT(b); #undef AT #undef EL XERO_ITER_EL(parent, element) { int element_name = element.GetNodeName(); XMBAttributeList attrs = element.GetAttributes(); if (element_name == el_skyset) { if (m_MapReader.pSkyMan) m_MapReader.pSkyMan->SetSkySet(element.GetText()); } else if (element_name == el_suncolour) { m_MapReader.m_LightEnv.m_SunColor = RGBColor( CStr(attrs.GetNamedItem(at_r)).ToFloat(), CStr(attrs.GetNamedItem(at_g)).ToFloat(), CStr(attrs.GetNamedItem(at_b)).ToFloat()); } else if (element_name == el_sunelevation) { m_MapReader.m_LightEnv.m_Elevation = CStr(attrs.GetNamedItem(at_angle)).ToFloat(); } else if (element_name == el_sunrotation) { m_MapReader.m_LightEnv.m_Rotation = CStr(attrs.GetNamedItem(at_angle)).ToFloat(); } else if (element_name == el_terrainambientcolour) { m_MapReader.m_LightEnv.m_TerrainAmbientColor = RGBColor( CStr(attrs.GetNamedItem(at_r)).ToFloat(), CStr(attrs.GetNamedItem(at_g)).ToFloat(), CStr(attrs.GetNamedItem(at_b)).ToFloat()); } else if (element_name == el_unitsambientcolour) { m_MapReader.m_LightEnv.m_UnitsAmbientColor = RGBColor( CStr(attrs.GetNamedItem(at_r)).ToFloat(), CStr(attrs.GetNamedItem(at_g)).ToFloat(), CStr(attrs.GetNamedItem(at_b)).ToFloat()); } else if (element_name == el_terrainshadowtransparency) { m_MapReader.m_LightEnv.SetTerrainShadowTransparency(CStr(element.GetText()).ToFloat()); } else if (element_name == el_water) { XERO_ITER_EL(element, waterbody) { debug_assert(waterbody.GetNodeName() == el_waterbody); XERO_ITER_EL(waterbody, waterelement) { if (!m_MapReader.pWaterMan) continue; int element_name = waterelement.GetNodeName(); if (element_name == el_type) { // TODO: implement this, when WaterManager supports it } else if (element_name == el_height) { CmpPtr cmpWaterMan(*m_MapReader.pSimulation2, SYSTEM_ENTITY); debug_assert(!cmpWaterMan.null()); cmpWaterMan->SetWaterLevel(entity_pos_t::FromString(CStr(waterelement.GetText()))); } #define READ_COLOUR(el, out) \ else if (element_name == el) \ { \ XMBAttributeList attrs = waterelement.GetAttributes(); \ out = CColor( \ CStr(attrs.GetNamedItem(at_r)).ToFloat(), \ CStr(attrs.GetNamedItem(at_g)).ToFloat(), \ CStr(attrs.GetNamedItem(at_b)).ToFloat(), \ 1.f); \ } #define READ_FLOAT(el, out) \ else if (element_name == el) \ { \ out = CStr(waterelement.GetText()).ToFloat(); \ } \ READ_COLOUR(el_colour, m_MapReader.pWaterMan->m_WaterColor) READ_FLOAT(el_shininess, m_MapReader.pWaterMan->m_Shininess) READ_FLOAT(el_waviness, m_MapReader.pWaterMan->m_Waviness) READ_FLOAT(el_murkiness, m_MapReader.pWaterMan->m_Murkiness) READ_COLOUR(el_tint, m_MapReader.pWaterMan->m_WaterTint) READ_COLOUR(el_reflectiontint, m_MapReader.pWaterMan->m_ReflectionTint) READ_FLOAT(el_reflectiontintstrength, m_MapReader.pWaterMan->m_ReflectionTintStrength) #undef READ_FLOAT #undef READ_COLOUR else debug_warn(L"Invalid map XML data"); } } } else debug_warn(L"Invalid map XML data"); } m_MapReader.m_LightEnv.CalculateSunDirection(); } void CXMLReader::ReadCamera(XMBElement parent) { #define EL(x) int el_##x = xmb_file.GetElementID(#x) #define AT(x) int at_##x = xmb_file.GetAttributeID(#x) EL(declination); EL(rotation); EL(position); AT(angle); AT(x); AT(y); AT(z); #undef AT #undef EL float declination = DEGTORAD(30.f), rotation = DEGTORAD(-45.f); CVector3D translation = CVector3D(100, 150, -100); XERO_ITER_EL(parent, element) { int element_name = element.GetNodeName(); XMBAttributeList attrs = element.GetAttributes(); if (element_name == el_declination) { declination = CStr(attrs.GetNamedItem(at_angle)).ToFloat(); } else if (element_name == el_rotation) { rotation = CStr(attrs.GetNamedItem(at_angle)).ToFloat(); } else if (element_name == el_position) { translation = CVector3D( CStr(attrs.GetNamedItem(at_x)).ToFloat(), CStr(attrs.GetNamedItem(at_y)).ToFloat(), CStr(attrs.GetNamedItem(at_z)).ToFloat()); } else debug_warn(L"Invalid map XML data"); } if (m_MapReader.pCamera) { m_MapReader.pCamera->m_Orientation.SetXRotation(declination); m_MapReader.pCamera->m_Orientation.RotateY(rotation); m_MapReader.pCamera->m_Orientation.Translate(translation); m_MapReader.pCamera->UpdateFrustum(); } } void CXMLReader::ReadCinema(XMBElement parent) { #define EL(x) int el_##x = xmb_file.GetElementID(#x) #define AT(x) int at_##x = xmb_file.GetAttributeID(#x) EL(path); EL(rotation); EL(distortion); EL(node); EL(position); EL(time); AT(name); AT(timescale); AT(mode); AT(style); AT(growth); AT(switch); AT(x); AT(y); AT(z); #undef EL #undef AT std::map pathList; XERO_ITER_EL(parent, element) { int elementName = element.GetNodeName(); if ( elementName == el_path ) { XMBAttributeList attrs = element.GetAttributes(); CStrW name( CStr(attrs.GetNamedItem(at_name)) ); float timescale = CStr(attrs.GetNamedItem(at_timescale)).ToFloat(); CCinemaData pathData; pathData.m_Timescale = timescale; TNSpline spline, backwardSpline; XERO_ITER_EL(element, pathChild) { elementName = pathChild.GetNodeName(); attrs = pathChild.GetAttributes(); //Load distortion attributes if ( elementName == el_distortion ) { pathData.m_Mode = CStr(attrs.GetNamedItem(at_mode)).ToInt(); pathData.m_Style = CStr(attrs.GetNamedItem(at_style)).ToInt(); pathData.m_Growth = CStr(attrs.GetNamedItem(at_growth)).ToInt(); pathData.m_Switch = CStr(attrs.GetNamedItem(at_switch)).ToInt(); } //Load node data used for spline else if ( elementName == el_node ) { SplineData data; XERO_ITER_EL(pathChild, nodeChild) { elementName = nodeChild.GetNodeName(); attrs = nodeChild.GetAttributes(); //Fix?: assumes that time is last element if ( elementName == el_position ) { data.Position.X = CStr(attrs.GetNamedItem(at_x)).ToFloat(); data.Position.Y = CStr(attrs.GetNamedItem(at_y)).ToFloat(); data.Position.Z = CStr(attrs.GetNamedItem(at_z)).ToFloat(); continue; } else if ( elementName == el_rotation ) { data.Rotation.X = CStr(attrs.GetNamedItem(at_x)).ToFloat(); data.Rotation.Y = CStr(attrs.GetNamedItem(at_y)).ToFloat(); data.Rotation.Z = CStr(attrs.GetNamedItem(at_z)).ToFloat(); continue; } else if ( elementName == el_time ) data.Distance = CStr( nodeChild.GetText() ).ToFloat(); else debug_warn(L"Invalid cinematic element for node child"); backwardSpline.AddNode(data.Position, data.Rotation, data.Distance); } } else debug_warn(L"Invalid cinematic element for path child"); } //Construct cinema path with data gathered CCinemaPath temp(pathData, backwardSpline); const std::vector& nodes = temp.GetAllNodes(); if ( nodes.empty() ) { debug_warn(L"Failure loading cinematics"); return; } for ( std::vector::const_reverse_iterator it = nodes.rbegin(); it != nodes.rend(); ++it ) { spline.AddNode(it->Position, it->Rotation, it->Distance); } CCinemaPath path(pathData, spline); pathList[name] = path; } else debug_assert("Invalid cinema child"); } if (m_MapReader.pCinema) m_MapReader.pCinema->SetAllPaths(pathList); } -void CXMLReader::ReadTriggers(XMBElement parent) +void CXMLReader::ReadTriggers(XMBElement UNUSED(parent)) { // MapTriggerGroup rootGroup( L"Triggers", L"" ); // if (m_MapReader.pTrigMan) // m_MapReader.pTrigMan->DestroyEngineTriggers(); // ReadTriggerGroup(parent, rootGroup); } /* void CXMLReader::ReadTriggerGroup(XMBElement parent, MapTriggerGroup& group) { #define EL(x) int el_##x = xmb_file.GetElementID(#x) #define AT(x) int at_##x = xmb_file.GetAttributeID(#x) EL(group); EL(trigger); EL(active); EL(delay); EL(maxruncount); EL(conditions); EL(logicblock); EL(logicblockend); EL(condition); EL(parameter); EL(linklogic); EL(effects); EL(effect); EL(function); EL(display); AT(name); AT(function); AT(display); AT(not); #undef EL #undef AT CStrW name = parent.GetAttributes().GetNamedItem(at_name), parentName = group.parentName; if ( group.name == L"Triggers" ) name = group.name; MapTriggerGroup mapGroup(name, parentName); XERO_ITER_EL(parent, groupChild) { int elementName = groupChild.GetNodeName(); if ( elementName == el_group ) ReadTriggerGroup(groupChild, mapGroup); else if ( elementName == el_trigger ) { MapTrigger mapTrigger; mapTrigger.name = CStrW( groupChild.GetAttributes().GetNamedItem(at_name) ); //Read everything in this trigger XERO_ITER_EL(groupChild, triggerChild) { elementName = triggerChild.GetNodeName(); if ( elementName == el_active ) { if ( CStr("false") == CStr( triggerChild.GetText() ) ) mapTrigger.active = false; else mapTrigger.active = true; } else if ( elementName == el_maxruncount ) mapTrigger.maxRunCount = CStr( triggerChild.GetText() ).ToInt(); else if ( elementName == el_delay ) mapTrigger.timeValue = CStr( triggerChild.GetText() ).ToFloat(); else if ( elementName == el_conditions ) { //Read in all conditions for this trigger XERO_ITER_EL(triggerChild, condition) { elementName = condition.GetNodeName(); if ( elementName == el_condition ) { MapTriggerCondition mapCondition; mapCondition.name = condition.GetAttributes().GetNamedItem(at_name); mapCondition.functionName = condition.GetAttributes().GetNamedItem(at_function); mapCondition.displayName = condition.GetAttributes().GetNamedItem(at_display); CStr notAtt(condition.GetAttributes().GetNamedItem(at_not)); if ( notAtt == CStr("true") ) mapCondition.negated = true; //Read in each condition child XERO_ITER_EL(condition, conditionChild) { elementName = conditionChild.GetNodeName(); if ( elementName == el_function ) mapCondition.functionName = CStrW(conditionChild.GetText()); else if ( elementName == el_display ) mapCondition.displayName = CStrW(conditionChild.GetText()); else if ( elementName == el_parameter ) mapCondition.parameters.push_back( conditionChild.GetText() ); else if ( elementName == el_linklogic ) { CStr logic = conditionChild.GetText(); if ( logic == CStr("AND") ) mapCondition.linkLogic = 1; else mapCondition.linkLogic = 2; } } mapTrigger.conditions.push_back(mapCondition); } //Read all conditions else if ( elementName == el_logicblock) { if ( CStr(condition.GetAttributes().GetNamedItem(at_not)) == CStr("true") ) mapTrigger.AddLogicBlock(true); else mapTrigger.AddLogicBlock(false); } else if ( elementName == el_logicblockend) mapTrigger.AddLogicBlockEnd(); } //Read all conditions } else if ( elementName == el_effects ) { //Read all effects XERO_ITER_EL(triggerChild, effect) { if ( effect.GetNodeName() != el_effect ) { debug_warn(L"Invalid effect tag in trigger XML file"); return; } MapTriggerEffect mapEffect; mapEffect.name = effect.GetAttributes().GetNamedItem(at_name); //Read parameters XERO_ITER_EL(effect, effectChild) { elementName = effectChild.GetNodeName(); if ( elementName == el_function ) mapEffect.functionName = effectChild.GetText(); else if ( elementName == el_display ) mapEffect.displayName = effectChild.GetText(); else if ( elementName == el_parameter ) mapEffect.parameters.push_back( effectChild.GetText() ); else { debug_warn(L"Invalid parameter tag in trigger XML file"); return; } } mapTrigger.effects.push_back(mapEffect); } } else debug_warn(L"Invalid trigger node child in trigger XML file"); } //Read trigger children m_MapReader.pTrigMan->AddTrigger(mapGroup, mapTrigger); } else debug_warn(L"Invalid group node child in XML file"); } //Read group children if (m_MapReader.pTrigMan) m_MapReader.pTrigMan->AddGroup(mapGroup); } */ int CXMLReader::ReadEntities(XMBElement parent, double end_time) { XMBElementList entities = parent.GetChildNodes(); while (entity_idx < entities.Count) { // all new state at this scope and below doesn't need to be // wrapped, since we only yield after a complete iteration. XMBElement entity = entities.Item(entity_idx++); debug_assert(entity.GetNodeName() == el_entity); XMBAttributeList attrs = entity.GetAttributes(); utf16string uid = attrs.GetNamedItem(at_uid); debug_assert(!uid.empty()); int EntityUid = CStr(uid).ToInt(); CStrW TemplateName; int PlayerID = 0; CFixedVector3D Position; CFixedVector3D Orientation; XERO_ITER_EL(entity, setting) { int element_name = setting.GetNodeName(); //