From abdeee4b81d16344407ff88f0cf783fa71b8d315 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Sun, 16 Jun 2013 20:48:17 +0200 Subject: [PATCH 001/313] Add info structure for multipass camera rendering effects #43 --- code/nel/include/nel/3d/driver.h | 13 +++ .../nel/3d/multipass_camera_effect_info.h | 89 +++++++++++++++++++ .../3d/driver/direct3d/driver_direct3d.cpp | 20 +++++ .../src/3d/driver/direct3d/driver_direct3d.h | 4 + .../src/3d/driver/opengl/driver_opengl.cpp | 29 ++++++ code/nel/src/3d/driver/opengl/driver_opengl.h | 7 ++ .../src/3d/multipass_camera_effect_info.cpp | 65 ++++++++++++++ 7 files changed, 227 insertions(+) create mode 100644 code/nel/include/nel/3d/multipass_camera_effect_info.h create mode 100644 code/nel/src/3d/multipass_camera_effect_info.cpp diff --git a/code/nel/include/nel/3d/driver.h b/code/nel/include/nel/3d/driver.h index d090d77da..6630be4ca 100644 --- a/code/nel/include/nel/3d/driver.h +++ b/code/nel/include/nel/3d/driver.h @@ -66,6 +66,9 @@ class CScissor; class CViewport; struct CMonitorColorProperties; struct IOcclusionQuery; +class CMultipassCameraEffectInfo; +class IMultipassCameraEffectInfoPriv; +class IMultipassCameraEffect; @@ -807,6 +810,16 @@ public: // return true if driver support non-power of two textures virtual bool supportNonPowerOfTwoTextures() const =0; + /// \name Multipass Camera Effects. Prefer to use CMultipassCameraEffectManager instead of calling these directly. + // @{ + /// Return the number of installed multipass camera effects. + virtual int getMultipassCameraEffectNb() =0; + /// Return information about a specified multipass camera effect. + virtual const CMultipassCameraEffectInfo *getMultipassCameraEffectInfo(int idx) const =0; + /// Create a multipass camera effect with specified id. + virtual IMultipassCameraEffect *createMultipassCameraEffect(int idx) const =0; + // @} + /** get a part of the ZBuffer (back buffer). * NB: 0,0 is the bottom left corner of the screen. * diff --git a/code/nel/include/nel/3d/multipass_camera_effect_info.h b/code/nel/include/nel/3d/multipass_camera_effect_info.h new file mode 100644 index 000000000..bdc8a2bc7 --- /dev/null +++ b/code/nel/include/nel/3d/multipass_camera_effect_info.h @@ -0,0 +1,89 @@ +/** + * \file multipass_camera_effect_info.h + * \brief CMultipassCameraEffectInfo + * \date 2013-06-16 17:27GMT + * \author Jan Boon (Kaetemi) + * CMultipassCameraEffectInfo + */ + +/* + * Copyright (C) 2013 by authors + * + * This file is part of NEVRAX NEL. + * NEVRAX NEL is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * NEVRAX NEL 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with NEVRAX NEL. If not, see + * . + */ + +#ifndef NL3D_MULTIPASS_CAMERA_EFFECT_INFO_H +#define NL3D_MULTIPASS_CAMERA_EFFECT_INFO_H +#include + +// STL includes + +// NeL includes + +// Project includes + +namespace NL3D { + class IMultipassCameraEffect; + +enum TMultipassCameraType +{ + MULTIPASS_CAMERA_UNKNOWN, + /// A multi pass effect used to support stereo displays. + MULTIPASS_CAMERA_STEREO, + /// A multi pass effect which renders at different animation deltas to create a motion blur (experimental). + MULTIPASS_CAMERA_MOTION_BLUR, + /// A multi pass effect which renders with modified camera settings to create depth of field (experimental). + MULTIPASS_CAMERA_DEPTH_OF_FIELD, + // etc. +}; + +/** + * \brief CMultipassCameraEffectInfo + * \date 2013-06-16 17:27GMT + * \author Jan Boon (Kaetemi) + * Information on a multi pass camera effect. + */ +struct CMultipassCameraEffectInfo +{ +public: + CMultipassCameraEffectInfo(); + virtual ~CMultipassCameraEffectInfo(); + + /// Name used in configs etc. Use i18n for storing display name and descriptions. + std::string Name; + + /// Type of multipass. Useful for filtering which ones the user can pick. + TMultipassCameraType Type; + +}; /* class CMultipassCameraEffectInfo */ + +/// Inherit from this class with a class which fills the public +/// information fields and that overrides the create() method. +/// Driver has list of installed IMultipassCameraEffectInfoPriv. +struct IMultipassCameraEffectInfoPriv : public CMultipassCameraEffectInfo +{ +public: + IMultipassCameraEffectInfoPriv(); + virtual ~IMultipassCameraEffectInfoPriv(); + + virtual IMultipassCameraEffect *create() const = 0; +}; + +} /* namespace NL3D */ + +#endif /* #ifndef NL3D_MULTIPASS_CAMERA_EFFECT_INFO_H */ + +/* end of file */ diff --git a/code/nel/src/3d/driver/direct3d/driver_direct3d.cpp b/code/nel/src/3d/driver/direct3d/driver_direct3d.cpp index 412cb52da..d92cd1ffd 100644 --- a/code/nel/src/3d/driver/direct3d/driver_direct3d.cpp +++ b/code/nel/src/3d/driver/direct3d/driver_direct3d.cpp @@ -2999,6 +2999,26 @@ bool CDriverD3D::supportNonPowerOfTwoTextures() const // *************************************************************************** +int CDriverD3D::getMultipassCameraEffectNb() +{ + // Screw D3D. + return 0; +} + +const NL3D::CMultipassCameraEffectInfo *CDriverD3D::getMultipassCameraEffectInfo(int idx) const +{ + // Screw D3D. + return NULL; +} + +NL3D::IMultipassCameraEffect *CDriverD3D::createMultipassCameraEffect(int idx) const +{ + // Screw D3D. + return NULL; +} + +// *************************************************************************** + bool CDriverD3D::fillBuffer (NLMISC::CBitmap &bitmap) { H_AUTO_D3D(CDriverD3D_fillBuffer); diff --git a/code/nel/src/3d/driver/direct3d/driver_direct3d.h b/code/nel/src/3d/driver/direct3d/driver_direct3d.h index cff7cb804..992a0efdd 100644 --- a/code/nel/src/3d/driver/direct3d/driver_direct3d.h +++ b/code/nel/src/3d/driver/direct3d/driver_direct3d.h @@ -832,6 +832,10 @@ public: // return true if driver support non-power of two textures virtual bool supportNonPowerOfTwoTextures() const; + virtual int getMultipassCameraEffectNb(); + virtual const CMultipassCameraEffectInfo *getMultipassCameraEffectInfo(int idx) const; + virtual IMultipassCameraEffect *createMultipassCameraEffect(int idx) const; + // copy the first texture in a second one of different dimensions virtual bool stretchRect (ITexture * srcText, NLMISC::CRect &srcRect, ITexture * destText, NLMISC::CRect &destRect); // Only 32 bits back buffer supported virtual bool isTextureRectangle(ITexture * /* tex */) const {return false;} diff --git a/code/nel/src/3d/driver/opengl/driver_opengl.cpp b/code/nel/src/3d/driver/opengl/driver_opengl.cpp index 29e14a1a0..a8a59aae0 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl.cpp +++ b/code/nel/src/3d/driver/opengl/driver_opengl.cpp @@ -27,6 +27,7 @@ #include "nel/3d/vertex_buffer.h" #include "nel/3d/light.h" #include "nel/3d/index_buffer.h" +#include "nel/3d/multipass_camera_effect_info.h" #include "nel/misc/rect.h" #include "nel/misc/di_event_emitter.h" #include "nel/misc/mouse_device.h" @@ -699,6 +700,34 @@ bool CDriverGL::supportNonPowerOfTwoTextures() const return _Extensions.ARBTextureNonPowerOfTwo; } +// *************************************************************************** + +void CDriverGL::initMultipassCameraEffectInfos() +{ + if (m_MultipassCameraEffectInfos.size() == 0) + { + // Add pointers to static class instances to the list. + } +} + +int CDriverGL::getMultipassCameraEffectNb() +{ + initMultipassCameraEffectInfos(); + return m_MultipassCameraEffectInfos.size(); +} + +const NL3D::CMultipassCameraEffectInfo *CDriverGL::getMultipassCameraEffectInfo(int idx) const +{ + nlassert(idx < m_MultipassCameraEffectInfos.size()); + return static_cast(m_MultipassCameraEffectInfos[idx]); +} + +NL3D::IMultipassCameraEffect *CDriverGL::createMultipassCameraEffect(int idx) const +{ + nlassert(idx < m_MultipassCameraEffectInfos.size()); + return m_MultipassCameraEffectInfos[idx]->create(); +} + // *************************************************************************** bool CDriverGL::isTextureRectangle(ITexture * tex) const { diff --git a/code/nel/src/3d/driver/opengl/driver_opengl.h b/code/nel/src/3d/driver/opengl/driver_opengl.h index bfe73492d..24c9b6688 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl.h +++ b/code/nel/src/3d/driver/opengl/driver_opengl.h @@ -556,6 +556,10 @@ public: // return true if driver support non-power of two textures virtual bool supportNonPowerOfTwoTextures() const; + virtual int getMultipassCameraEffectNb(); + virtual const CMultipassCameraEffectInfo *getMultipassCameraEffectInfo(int idx) const; + virtual IMultipassCameraEffect *createMultipassCameraEffect(int idx) const; + virtual bool activeFrameBufferObject(ITexture * tex); virtual void getZBufferPart (std::vector &zbuffer, NLMISC::CRect &rect); @@ -1491,6 +1495,9 @@ private: */ inline void setupTextureBasicParameters(ITexture &tex); + /// Multipass Camera Effects + void initMultipassCameraEffectInfos(); + std::vector m_MultipassCameraEffectInfos; }; // *************************************************************************** diff --git a/code/nel/src/3d/multipass_camera_effect_info.cpp b/code/nel/src/3d/multipass_camera_effect_info.cpp new file mode 100644 index 000000000..971dca52b --- /dev/null +++ b/code/nel/src/3d/multipass_camera_effect_info.cpp @@ -0,0 +1,65 @@ +/** + * \file multipass_camera_effect_info.cpp + * \brief CMultipassCameraEffectInfo + * \date 2013-06-16 17:27GMT + * \author Jan Boon (Kaetemi) + * CMultipassCameraEffectInfo + */ + +/* + * Copyright (C) 2013 by authors + * + * This file is part of NEVRAX NEL. + * NEVRAX NEL is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * NEVRAX NEL 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with NEVRAX NEL. If not, see + * . + */ + +#include +#include + +// STL includes + +// NeL includes +// #include + +// Project includes + +using namespace std; +// using namespace NLMISC; + +namespace NL3D { + +CMultipassCameraEffectInfo::CMultipassCameraEffectInfo() +{ + +} + +CMultipassCameraEffectInfo::~CMultipassCameraEffectInfo() +{ + +} + +IMultipassCameraEffectInfoPriv::IMultipassCameraEffectInfoPriv() +{ + +} + +IMultipassCameraEffectInfoPriv::~IMultipassCameraEffectInfoPriv() +{ + +} + +} /* namespace NL3D */ + +/* end of file */ From f1f06da75f026308ae1f6bb11da43d4d8fc76baf Mon Sep 17 00:00:00 2001 From: kaetemi Date: Sun, 16 Jun 2013 23:48:40 +0200 Subject: [PATCH 002/313] Allow configuration of bloom settings from config files in snowballs --- code/snowballs2/client/src/snowballs_client.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/code/snowballs2/client/src/snowballs_client.cpp b/code/snowballs2/client/src/snowballs_client.cpp index c5cb73072..76338cfa5 100644 --- a/code/snowballs2/client/src/snowballs_client.cpp +++ b/code/snowballs2/client/src/snowballs_client.cpp @@ -179,6 +179,8 @@ void releaseIngame(); void releaseOnline(); void releaseOffline(); void cbGraphicsDriver(CConfigFile::CVar &var); +void cbSquareBloom(CConfigFile::CVar &var); +void cbDensityBloom(CConfigFile::CVar &var); // // Functions @@ -362,6 +364,8 @@ void initIngame() CBloomEffect::instance().setDriver(Driver); CBloomEffect::instance().setScene(Scene); CBloomEffect::instance().init(ConfigFile->getVar("OpenGL").asInt() == 1); + CConfiguration::setAndCallback("SquareBloom", cbSquareBloom); + CConfiguration::setAndCallback("DensityBloom", cbDensityBloom); // Init the landscape using the previously created UScene displayLoadingState("Initialize Landscape"); @@ -562,6 +566,8 @@ void releaseIngame() MouseListener = NULL; // release bloom effect + CConfiguration::dropCallback("SquareBloom"); + CConfiguration::dropCallback("DensityBloom"); CBloomEffect::instance().releaseInstance(); Driver->deleteScene(Scene); @@ -884,6 +890,16 @@ void cbGraphicsDriver(CConfigFile::CVar &var) NextGameState = GameStateReset; } +void cbSquareBloom(CConfigFile::CVar &var) +{ + CBloomEffect::instance().setSquareBloom(var.asBool()); +} + +void cbDensityBloom(CConfigFile::CVar &var) +{ + CBloomEffect::instance().setDensityBloom((uint8)(var.asInt() & 0xFF)); +} + // // Loading state procedure // From 09df2d8befd10503aa84b87ba0d399294c8a234d Mon Sep 17 00:00:00 2001 From: kaetemi Date: Sun, 16 Jun 2013 23:49:45 +0200 Subject: [PATCH 003/313] Remove silly heap allocation of a null pointer --- code/nel/src/3d/bloom_effect.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/code/nel/src/3d/bloom_effect.cpp b/code/nel/src/3d/bloom_effect.cpp index 7cbb48310..f3a13946a 100644 --- a/code/nel/src/3d/bloom_effect.cpp +++ b/code/nel/src/3d/bloom_effect.cpp @@ -475,9 +475,8 @@ void CBloomEffect::endInterfacesDisplayBloom() // clientcfg return; NL3D::IDriver *drvInternal = ((CDriverUser *) _Driver)->getDriver(); - CTextureUser *txt = new CTextureUser(); - ((CDriverUser *)_Driver)->setRenderTarget(*txt, 0, 0, 0, 0); - delete txt; + CTextureUser txtNull; + ((CDriverUser *)_Driver)->setRenderTarget(txtNull, 0, 0, 0, 0); // initialize texture coordinates float newU = drvInternal->isTextureRectangle(_InitText) ? (float)_WndWidth : 1.f; From 968e0579feca299507f7a25c1ebcf1d5f81cad2f Mon Sep 17 00:00:00 2001 From: kaetemi Date: Sun, 16 Jun 2013 23:51:33 +0200 Subject: [PATCH 004/313] Allow stepping through the time delta in multiple passes in snowballs --- code/snowballs2/client/src/game_time.cpp | 31 ++++++++++++++----- code/snowballs2/client/src/game_time.h | 3 ++ .../client/src/snowballs_client.cpp | 1 + 3 files changed, 28 insertions(+), 7 deletions(-) diff --git a/code/snowballs2/client/src/game_time.cpp b/code/snowballs2/client/src/game_time.cpp index 62bfc2b2b..c6b1ff62c 100644 --- a/code/snowballs2/client/src/game_time.cpp +++ b/code/snowballs2/client/src/game_time.cpp @@ -42,6 +42,14 @@ static bool _SkipAnimationOnce; static NLMISC::TTime _TimeMs; static CValueSmootherTemplate _FpsSmoother; +namespace +{ + +NLMISC::TLocalTime a_LocalTimeDelta; +NL3D::TGlobalAnimationTime a_AnimationTimeDelta; + +} /* anonymous namespace */ + static void cbFpsSmoothing(CConfigFile::CVar &var) { _FpsSmoother.init((uint)var.asInt()); @@ -53,8 +61,10 @@ void CGameTime::init() _TimeMs = NLMISC::CTime::getLocalTime(); LocalTime = ((TLocalTime)_TimeMs) / 1000.0; LocalTimeDelta = 0.0; + a_LocalTimeDelta = 0.0; AnimationTime = 0.0; AnimationTimeDelta = 0.f; + a_AnimationTimeDelta = 0.0; FramesPerSecond = 0.f; FramesPerSecondSmooth = 0.f; CConfiguration::setAndCallback("FpsSmoothing", cbFpsSmoothing); @@ -78,26 +88,24 @@ void CGameTime::updateTime() // average of previous fps and this fps should be ok FramesPerSecond *= 3; - LocalTimeDelta = 0.f; - AnimationTimeDelta = 0.f; + a_LocalTimeDelta = 0.f; + a_AnimationTimeDelta = 0.f; } else { FramesPerSecond = 1000.0f / (float)deltams; TLocalTime localTime = ((TLocalTime)timems) / 1000.0; - LocalTimeDelta = localTime - LocalTime; - LocalTime = localTime; + a_LocalTimeDelta = localTime - LocalTime; if (_SkipAnimationOnce) { - AnimationTimeDelta = 0.f; + a_AnimationTimeDelta = 0.f; _SkipAnimationOnce = false; } else { - AnimationTimeDelta = (TAnimationTime)LocalTimeDelta; - AnimationTime += (TGlobalAnimationTime)LocalTimeDelta; + a_AnimationTimeDelta = (TGlobalAnimationTime)a_LocalTimeDelta; } } @@ -105,6 +113,15 @@ void CGameTime::updateTime() FramesPerSecondSmooth = _FpsSmoother.getSmoothValue(); } +void CGameTime::advanceTime(double f) +{ + LocalTimeDelta = a_LocalTimeDelta * f; + LocalTime += LocalTimeDelta; + TGlobalAnimationTime atd = a_AnimationTimeDelta * f; + AnimationTimeDelta = (NL3D::TAnimationTime)atd; + AnimationTime += atd; +} + void CGameTime::skipAnimationOnce() { _SkipAnimationOnce = true; diff --git a/code/snowballs2/client/src/game_time.h b/code/snowballs2/client/src/game_time.h index 72a671d43..7cf4ec827 100644 --- a/code/snowballs2/client/src/game_time.h +++ b/code/snowballs2/client/src/game_time.h @@ -40,6 +40,9 @@ public: static void updateTime(); + /// Advance time to target time by factor f. + static void advanceTime(double f); + /// Used when loading, this will skip changing animation time on the next update /// (updates aren't called during loading) static void skipAnimationOnce(); diff --git a/code/snowballs2/client/src/snowballs_client.cpp b/code/snowballs2/client/src/snowballs_client.cpp index 76338cfa5..1d7fefd60 100644 --- a/code/snowballs2/client/src/snowballs_client.cpp +++ b/code/snowballs2/client/src/snowballs_client.cpp @@ -679,6 +679,7 @@ void loopIngame() // 02. Update Time (deltas) CGameTime::updateTime(); + CGameTime::advanceTime(1.0); // 03. Update Input (keyboard controls, etc) Driver->EventServer.pump(); // Pump user input messages From 23e3393d2ef34431bc91739577db8836f823a621 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Mon, 17 Jun 2013 00:45:17 +0200 Subject: [PATCH 005/313] Allow rendering with bloom to a user provided render target (untested) (#43) --- code/nel/include/nel/3d/bloom_effect.h | 5 +- code/nel/src/3d/bloom_effect.cpp | 71 +++++++++++++++++--------- 2 files changed, 51 insertions(+), 25 deletions(-) diff --git a/code/nel/include/nel/3d/bloom_effect.h b/code/nel/include/nel/3d/bloom_effect.h index 77673741c..f5da7a4dd 100644 --- a/code/nel/include/nel/3d/bloom_effect.h +++ b/code/nel/include/nel/3d/bloom_effect.h @@ -83,6 +83,7 @@ public: // If window size exceeds 256*256 the textures used to apply blur are reinitialized with // 256*256 size. If a dimension is less than 256, the texture is initialized with the nearer // power of 2, lower than this window dimension. + void initBloom(UTexture &renderTarget); void initBloom(); // Called at the end of renderAll method in the main loop, recover stretched texture, apply @@ -131,13 +132,15 @@ private: uint8 _DensityBloom; // render target textures - // used to display scene + // used to display scene (FIXME: redundant when user render target provided...) NLMISC::CSmartPtr _InitText; // used as stretched texture from _InitText, as displayed texture in first blur pass, // and as render target in second blur pass. NLMISC::CSmartPtr _BlurFinalTex; // used as render target in first blur pass, and as displayed texture on second blur pass. NLMISC::CSmartPtr _BlurHorizontalTex; + /// User provided render target. + NLMISC::CSmartPtr m_UserRenderTarget; // materials diff --git a/code/nel/src/3d/bloom_effect.cpp b/code/nel/src/3d/bloom_effect.cpp index f3a13946a..0bc9c1e41 100644 --- a/code/nel/src/3d/bloom_effect.cpp +++ b/code/nel/src/3d/bloom_effect.cpp @@ -271,11 +271,19 @@ void CBloomEffect::initTexture(CSmartPtr & tex, bool isMode2D, uint32 //----------------------------------------------------------------------------------------------------------- -void CBloomEffect::initBloom() // clientcfg +void CBloomEffect::initBloom() +{ + CTextureUser cu; + initBloom(cu); +} + +void CBloomEffect::initBloom(UTexture &renderTarget) // clientcfg { if(!((CDriverUser *)_Driver)->getDriver()->supportBloomEffect()) return; + m_UserRenderTarget = dynamic_cast(renderTarget).getITexture(); + // don't activate bloom when PolygonMode is different from Filled if (_Driver->getPolygonMode() != UDriver::Filled) return; @@ -348,14 +356,25 @@ void CBloomEffect::initBloom() // clientcfg _DisplaySquareBlurMat.getObjectPtr()->setTexture(1, _BlurFinalTex); } } + + // For now the user target must be the window size + // to be compatible with the existing code. + // TODO: Instead, if user render target is provided, + // assume the size of the user render target as + // the screen size to be used. + if (m_UserRenderTarget.getPtr()) + { + nlassert(_WndWidth == m_UserRenderTarget->getWidth()); + nlassert(_WndHeight == m_UserRenderTarget->getHeight()); + _DisplayInitMat.getObjectPtr()->setTexture(0, m_UserRenderTarget); + } - NL3D::CTextureUser *txt = (_InitBloomEffect) ? (new CTextureUser(_InitText)) : (new CTextureUser()); - if(!((CDriverUser *) _Driver)->setRenderTarget(*txt, 0, 0, _WndWidth, _WndHeight)) + NL3D::CTextureUser txt = (_InitBloomEffect) ? (CTextureUser(m_UserRenderTarget.getPtr() ? m_UserRenderTarget : _InitText)) : (CTextureUser()); + if(!((CDriverUser *) _Driver)->setRenderTarget(txt, 0, 0, _WndWidth, _WndHeight)) { nlwarning("setRenderTarget return false with initial texture for bloom effect\n"); return; } - delete txt; } //----------------------------------------------------------------------------------------------------------- @@ -371,13 +390,13 @@ void CBloomEffect::endBloom() // clientcfg if(_Driver->getWindowWidth()==0 || _Driver->getWindowHeight()==0) return; - CTextureUser *txt1 = (_InitBloomEffect) ? (new CTextureUser(_InitText)) : (new CTextureUser()); - CTextureUser *txt2 = new CTextureUser(_BlurFinalTex); - CRect *rect1 = new CRect(0, 0, _WndWidth, _WndHeight); - CRect *rect2 = new CRect(0, 0, _BlurWidth, _BlurHeight); + CTextureUser txt1 = (_InitBloomEffect) ? (CTextureUser(m_UserRenderTarget.getPtr() ? m_UserRenderTarget : _InitText)) : (CTextureUser()); + CTextureUser txt2(_BlurFinalTex); + CRect rect1(0, 0, _WndWidth, _WndHeight); + CRect rect2(0, 0, _BlurWidth, _BlurHeight); // stretch rect - ((CDriverUser *) _Driver)->stretchRect(_Scene, *txt1 , *rect1, - *txt2, *rect2); + ((CDriverUser *) _Driver)->stretchRect(_Scene, txt1 , rect1, + txt2, rect2); // horizontal blur pass doBlur(true); @@ -387,10 +406,6 @@ void CBloomEffect::endBloom() // clientcfg // apply blur with a blend operation applyBlur(); - delete txt1; - delete txt2; - delete rect1; - delete rect2; } //----------------------------------------------------------------------------------------------------------- @@ -402,13 +417,12 @@ void CBloomEffect::applyBlur() // in opengl, display in init texture if(_InitBloomEffect) { - CTextureUser *txt = new CTextureUser(_InitText); - if(!((CDriverUser *) _Driver)->setRenderTarget(*txt, 0, 0, _WndWidth, _WndHeight)) + CTextureUser txt(m_UserRenderTarget.getPtr() ? m_UserRenderTarget : _InitText); + if(!((CDriverUser *) _Driver)->setRenderTarget(txt, 0, 0, _WndWidth, _WndHeight)) { nlwarning("setRenderTarget return false with initial texture for bloom effect\n"); return; } - delete txt; } // display blur texture @@ -463,7 +477,9 @@ void CBloomEffect::applyBlur() void CBloomEffect::endInterfacesDisplayBloom() // clientcfg { - if(_InitBloomEffect) + // Render from render target to screen if necessary. + // Don't do this when the blend was done to the screen or when rendering to a user provided rendertarget. + if (_InitBloomEffect && m_UserRenderTarget.isNull()) { if(!_Driver->supportBloomEffect() || !_Init) return; @@ -496,6 +512,15 @@ void CBloomEffect::endInterfacesDisplayBloom() // clientcfg _Driver->drawQuad(_DisplayQuad, _DisplayInitMat); _Driver->setMatrixMode3D(pCam); } + + if (m_UserRenderTarget.getPtr()) + { + if (_InitBloomEffect) + { + _DisplayInitMat.getObjectPtr()->setTexture(0, _InitText); + } + m_UserRenderTarget = NULL; + } } @@ -522,14 +547,13 @@ void CBloomEffect::doBlur(bool horizontalBlur) } NL3D::IDriver *drvInternal = ((CDriverUser *) _Driver)->getDriver(); - CTextureUser *txt = new CTextureUser(endTexture); + CTextureUser txt(endTexture); // initialize render target - if(!((CDriverUser *) _Driver)->setRenderTarget(*txt, 0, 0, _BlurWidth, _BlurHeight)) + if(!((CDriverUser *) _Driver)->setRenderTarget(txt, 0, 0, _BlurWidth, _BlurHeight)) { nlwarning("setRenderTarget return false with blur texture for bloom effect\n"); return; } - delete txt; // initialize vertex program drvInternal->activeVertexProgram(&TextureOffsetVertexProgram); @@ -578,10 +602,9 @@ void CBloomEffect::doBlur(bool horizontalBlur) // disable render target and vertex program drvInternal->activeVertexProgram(NULL); - txt = new CTextureUser(); - ((CDriverUser *)_Driver)->setRenderTarget(*txt, 0, 0, 0, 0); + CTextureUser cu; + ((CDriverUser *)_Driver)->setRenderTarget(cu, 0, 0, 0, 0); _Driver->setMatrixMode3D(pCam); - delete txt; } }; // NL3D From ad6cc747b96b1810414adba36085c6a27e8bbb66 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 19 Jun 2013 01:03:20 +0200 Subject: [PATCH 006/313] Backed out changeset ea4b76b7213a, bad approach --- code/nel/include/nel/3d/driver.h | 13 --- .../nel/3d/multipass_camera_effect_info.h | 89 ------------------- .../3d/driver/direct3d/driver_direct3d.cpp | 20 ----- .../src/3d/driver/direct3d/driver_direct3d.h | 4 - .../src/3d/driver/opengl/driver_opengl.cpp | 29 ------ code/nel/src/3d/driver/opengl/driver_opengl.h | 7 -- .../src/3d/multipass_camera_effect_info.cpp | 65 -------------- 7 files changed, 227 deletions(-) delete mode 100644 code/nel/include/nel/3d/multipass_camera_effect_info.h delete mode 100644 code/nel/src/3d/multipass_camera_effect_info.cpp diff --git a/code/nel/include/nel/3d/driver.h b/code/nel/include/nel/3d/driver.h index 6630be4ca..d090d77da 100644 --- a/code/nel/include/nel/3d/driver.h +++ b/code/nel/include/nel/3d/driver.h @@ -66,9 +66,6 @@ class CScissor; class CViewport; struct CMonitorColorProperties; struct IOcclusionQuery; -class CMultipassCameraEffectInfo; -class IMultipassCameraEffectInfoPriv; -class IMultipassCameraEffect; @@ -810,16 +807,6 @@ public: // return true if driver support non-power of two textures virtual bool supportNonPowerOfTwoTextures() const =0; - /// \name Multipass Camera Effects. Prefer to use CMultipassCameraEffectManager instead of calling these directly. - // @{ - /// Return the number of installed multipass camera effects. - virtual int getMultipassCameraEffectNb() =0; - /// Return information about a specified multipass camera effect. - virtual const CMultipassCameraEffectInfo *getMultipassCameraEffectInfo(int idx) const =0; - /// Create a multipass camera effect with specified id. - virtual IMultipassCameraEffect *createMultipassCameraEffect(int idx) const =0; - // @} - /** get a part of the ZBuffer (back buffer). * NB: 0,0 is the bottom left corner of the screen. * diff --git a/code/nel/include/nel/3d/multipass_camera_effect_info.h b/code/nel/include/nel/3d/multipass_camera_effect_info.h deleted file mode 100644 index bdc8a2bc7..000000000 --- a/code/nel/include/nel/3d/multipass_camera_effect_info.h +++ /dev/null @@ -1,89 +0,0 @@ -/** - * \file multipass_camera_effect_info.h - * \brief CMultipassCameraEffectInfo - * \date 2013-06-16 17:27GMT - * \author Jan Boon (Kaetemi) - * CMultipassCameraEffectInfo - */ - -/* - * Copyright (C) 2013 by authors - * - * This file is part of NEVRAX NEL. - * NEVRAX NEL is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * NEVRAX NEL 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 - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public - * License along with NEVRAX NEL. If not, see - * . - */ - -#ifndef NL3D_MULTIPASS_CAMERA_EFFECT_INFO_H -#define NL3D_MULTIPASS_CAMERA_EFFECT_INFO_H -#include - -// STL includes - -// NeL includes - -// Project includes - -namespace NL3D { - class IMultipassCameraEffect; - -enum TMultipassCameraType -{ - MULTIPASS_CAMERA_UNKNOWN, - /// A multi pass effect used to support stereo displays. - MULTIPASS_CAMERA_STEREO, - /// A multi pass effect which renders at different animation deltas to create a motion blur (experimental). - MULTIPASS_CAMERA_MOTION_BLUR, - /// A multi pass effect which renders with modified camera settings to create depth of field (experimental). - MULTIPASS_CAMERA_DEPTH_OF_FIELD, - // etc. -}; - -/** - * \brief CMultipassCameraEffectInfo - * \date 2013-06-16 17:27GMT - * \author Jan Boon (Kaetemi) - * Information on a multi pass camera effect. - */ -struct CMultipassCameraEffectInfo -{ -public: - CMultipassCameraEffectInfo(); - virtual ~CMultipassCameraEffectInfo(); - - /// Name used in configs etc. Use i18n for storing display name and descriptions. - std::string Name; - - /// Type of multipass. Useful for filtering which ones the user can pick. - TMultipassCameraType Type; - -}; /* class CMultipassCameraEffectInfo */ - -/// Inherit from this class with a class which fills the public -/// information fields and that overrides the create() method. -/// Driver has list of installed IMultipassCameraEffectInfoPriv. -struct IMultipassCameraEffectInfoPriv : public CMultipassCameraEffectInfo -{ -public: - IMultipassCameraEffectInfoPriv(); - virtual ~IMultipassCameraEffectInfoPriv(); - - virtual IMultipassCameraEffect *create() const = 0; -}; - -} /* namespace NL3D */ - -#endif /* #ifndef NL3D_MULTIPASS_CAMERA_EFFECT_INFO_H */ - -/* end of file */ diff --git a/code/nel/src/3d/driver/direct3d/driver_direct3d.cpp b/code/nel/src/3d/driver/direct3d/driver_direct3d.cpp index d92cd1ffd..412cb52da 100644 --- a/code/nel/src/3d/driver/direct3d/driver_direct3d.cpp +++ b/code/nel/src/3d/driver/direct3d/driver_direct3d.cpp @@ -2999,26 +2999,6 @@ bool CDriverD3D::supportNonPowerOfTwoTextures() const // *************************************************************************** -int CDriverD3D::getMultipassCameraEffectNb() -{ - // Screw D3D. - return 0; -} - -const NL3D::CMultipassCameraEffectInfo *CDriverD3D::getMultipassCameraEffectInfo(int idx) const -{ - // Screw D3D. - return NULL; -} - -NL3D::IMultipassCameraEffect *CDriverD3D::createMultipassCameraEffect(int idx) const -{ - // Screw D3D. - return NULL; -} - -// *************************************************************************** - bool CDriverD3D::fillBuffer (NLMISC::CBitmap &bitmap) { H_AUTO_D3D(CDriverD3D_fillBuffer); diff --git a/code/nel/src/3d/driver/direct3d/driver_direct3d.h b/code/nel/src/3d/driver/direct3d/driver_direct3d.h index 992a0efdd..cff7cb804 100644 --- a/code/nel/src/3d/driver/direct3d/driver_direct3d.h +++ b/code/nel/src/3d/driver/direct3d/driver_direct3d.h @@ -832,10 +832,6 @@ public: // return true if driver support non-power of two textures virtual bool supportNonPowerOfTwoTextures() const; - virtual int getMultipassCameraEffectNb(); - virtual const CMultipassCameraEffectInfo *getMultipassCameraEffectInfo(int idx) const; - virtual IMultipassCameraEffect *createMultipassCameraEffect(int idx) const; - // copy the first texture in a second one of different dimensions virtual bool stretchRect (ITexture * srcText, NLMISC::CRect &srcRect, ITexture * destText, NLMISC::CRect &destRect); // Only 32 bits back buffer supported virtual bool isTextureRectangle(ITexture * /* tex */) const {return false;} diff --git a/code/nel/src/3d/driver/opengl/driver_opengl.cpp b/code/nel/src/3d/driver/opengl/driver_opengl.cpp index a8a59aae0..29e14a1a0 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl.cpp +++ b/code/nel/src/3d/driver/opengl/driver_opengl.cpp @@ -27,7 +27,6 @@ #include "nel/3d/vertex_buffer.h" #include "nel/3d/light.h" #include "nel/3d/index_buffer.h" -#include "nel/3d/multipass_camera_effect_info.h" #include "nel/misc/rect.h" #include "nel/misc/di_event_emitter.h" #include "nel/misc/mouse_device.h" @@ -700,34 +699,6 @@ bool CDriverGL::supportNonPowerOfTwoTextures() const return _Extensions.ARBTextureNonPowerOfTwo; } -// *************************************************************************** - -void CDriverGL::initMultipassCameraEffectInfos() -{ - if (m_MultipassCameraEffectInfos.size() == 0) - { - // Add pointers to static class instances to the list. - } -} - -int CDriverGL::getMultipassCameraEffectNb() -{ - initMultipassCameraEffectInfos(); - return m_MultipassCameraEffectInfos.size(); -} - -const NL3D::CMultipassCameraEffectInfo *CDriverGL::getMultipassCameraEffectInfo(int idx) const -{ - nlassert(idx < m_MultipassCameraEffectInfos.size()); - return static_cast(m_MultipassCameraEffectInfos[idx]); -} - -NL3D::IMultipassCameraEffect *CDriverGL::createMultipassCameraEffect(int idx) const -{ - nlassert(idx < m_MultipassCameraEffectInfos.size()); - return m_MultipassCameraEffectInfos[idx]->create(); -} - // *************************************************************************** bool CDriverGL::isTextureRectangle(ITexture * tex) const { diff --git a/code/nel/src/3d/driver/opengl/driver_opengl.h b/code/nel/src/3d/driver/opengl/driver_opengl.h index 24c9b6688..bfe73492d 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl.h +++ b/code/nel/src/3d/driver/opengl/driver_opengl.h @@ -556,10 +556,6 @@ public: // return true if driver support non-power of two textures virtual bool supportNonPowerOfTwoTextures() const; - virtual int getMultipassCameraEffectNb(); - virtual const CMultipassCameraEffectInfo *getMultipassCameraEffectInfo(int idx) const; - virtual IMultipassCameraEffect *createMultipassCameraEffect(int idx) const; - virtual bool activeFrameBufferObject(ITexture * tex); virtual void getZBufferPart (std::vector &zbuffer, NLMISC::CRect &rect); @@ -1495,9 +1491,6 @@ private: */ inline void setupTextureBasicParameters(ITexture &tex); - /// Multipass Camera Effects - void initMultipassCameraEffectInfos(); - std::vector m_MultipassCameraEffectInfos; }; // *************************************************************************** diff --git a/code/nel/src/3d/multipass_camera_effect_info.cpp b/code/nel/src/3d/multipass_camera_effect_info.cpp deleted file mode 100644 index 971dca52b..000000000 --- a/code/nel/src/3d/multipass_camera_effect_info.cpp +++ /dev/null @@ -1,65 +0,0 @@ -/** - * \file multipass_camera_effect_info.cpp - * \brief CMultipassCameraEffectInfo - * \date 2013-06-16 17:27GMT - * \author Jan Boon (Kaetemi) - * CMultipassCameraEffectInfo - */ - -/* - * Copyright (C) 2013 by authors - * - * This file is part of NEVRAX NEL. - * NEVRAX NEL is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * NEVRAX NEL 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 - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public - * License along with NEVRAX NEL. If not, see - * . - */ - -#include -#include - -// STL includes - -// NeL includes -// #include - -// Project includes - -using namespace std; -// using namespace NLMISC; - -namespace NL3D { - -CMultipassCameraEffectInfo::CMultipassCameraEffectInfo() -{ - -} - -CMultipassCameraEffectInfo::~CMultipassCameraEffectInfo() -{ - -} - -IMultipassCameraEffectInfoPriv::IMultipassCameraEffectInfoPriv() -{ - -} - -IMultipassCameraEffectInfoPriv::~IMultipassCameraEffectInfoPriv() -{ - -} - -} /* namespace NL3D */ - -/* end of file */ From fd42d61af990e01b3f28b2ec769f96d436be7f69 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 19 Jun 2013 01:14:30 +0200 Subject: [PATCH 007/313] Add some files from old nevrax shader code for exposing pixel programs in the drivers --- code/nel/include/nel/3d/pixel_program.h | 86 +++ .../driver_direct3d_pixel_program.cpp | 305 ++++++++++ .../opengl/driver_opengl_pixel_program.cpp | 553 ++++++++++++++++++ code/nel/src/3d/pixel_program.cpp | 62 ++ 4 files changed, 1006 insertions(+) create mode 100644 code/nel/include/nel/3d/pixel_program.h create mode 100644 code/nel/src/3d/driver/direct3d/driver_direct3d_pixel_program.cpp create mode 100644 code/nel/src/3d/driver/opengl/driver_opengl_pixel_program.cpp create mode 100644 code/nel/src/3d/pixel_program.cpp diff --git a/code/nel/include/nel/3d/pixel_program.h b/code/nel/include/nel/3d/pixel_program.h new file mode 100644 index 000000000..35731ca6d --- /dev/null +++ b/code/nel/include/nel/3d/pixel_program.h @@ -0,0 +1,86 @@ +/** \file pixel_program.h + * Pixel program definition + * + * $Id: pixel_program.h,v 1.1.2.3 2007/07/06 15:58:45 legallo Exp $ + */ + +/* Copyright, 2000, 2001 Nevrax Ltd. + * + * This file is part of NEVRAX NEL. + * NEVRAX NEL 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, or (at your option) + * any later version. + + * NEVRAX NEL 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 NEVRAX NEL; see the file COPYING. If not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#ifndef NL_PIXEL_PROGRAM_H +#define NL_PIXEL_PROGRAM_H + +#include "program.h" + +#include "nel/misc/types_nl.h" +#include "nel/misc/smart_ptr.h" + +#include + +#define PIXEL_SHADER_PROFILE "ps_2_0" + +namespace NL3D { + +// List typedef. +class IDriver; +class IPixelProgramDrvInfos; +typedef std::list TPixelPrgDrvInfoPtrList; +typedef TPixelPrgDrvInfoPtrList::iterator ItPixelPrgDrvInfoPtrList; + +// Class for interaction of pixel program with Driver. +// IPixelProgramDrvInfos represent the real data of the pixel program, stored into the driver. +class IPixelProgramDrvInfos : public NLMISC::CRefCount +{ +private: + IDriver *_Driver; + ItPixelPrgDrvInfoPtrList _DriverIterator; + +public: + IPixelProgramDrvInfos (IDriver *drv, ItPixelPrgDrvInfoPtrList it); + // The virtual dtor is important. + virtual ~IPixelProgramDrvInfos(void); +}; + + +//------------------------------------------------------------------------------- +class CPixelProgram : public IProgram +{ +public: + + /// Constructor + CPixelProgram (const char* program, bool isEffectPrg=false); + + /// Destructor + virtual ~CPixelProgram (); + + /// The driver informations. For the driver implementation only. + NLMISC::CRefPtr _DrvInfo; + + const char * getASMProfile() { return PIXEL_SHADER_PROFILE; } ; + + static const char * getPixelASMProfile() { return PIXEL_SHADER_PROFILE; } ; +}; + + +} // NL3D + + +#endif // NL_PIXEL_PROGRAM_H + +/* End of vertex_program.h */ diff --git a/code/nel/src/3d/driver/direct3d/driver_direct3d_pixel_program.cpp b/code/nel/src/3d/driver/direct3d/driver_direct3d_pixel_program.cpp new file mode 100644 index 000000000..755592043 --- /dev/null +++ b/code/nel/src/3d/driver/direct3d/driver_direct3d_pixel_program.cpp @@ -0,0 +1,305 @@ +/** \file driver_direct3d_pixel_program.cpp + * Direct 3d driver implementation + * + * $Id: driver_direct3d_pixel_program.cpp,v 1.1.2.4 2007/07/09 15:26:35 legallo Exp $ + * + * \todo manage better the init/release system (if a throw occurs in the init, we must release correctly the driver) + */ + +/* Copyright, 2000 Nevrax Ltd. + * + * This file is part of NEVRAX NEL. + * NEVRAX NEL 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, or (at your option) + * any later version. + + * NEVRAX NEL 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 NEVRAX NEL; see the file COPYING. If not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include "stddirect3d.h" + +#include "driver_direct3d.h" + +using namespace std; +using namespace NLMISC; + +namespace NL3D +{ + +// *************************************************************************** + +CPixelProgramDrvInfosD3D::CPixelProgramDrvInfosD3D(IDriver *drv, ItPixelPrgDrvInfoPtrList it) : IPixelProgramDrvInfos (drv, it) +{ + H_AUTO_D3D(CPixelProgramDrvInfosD3D_CPixelProgamDrvInfosD3D) + Shader = NULL; +} + +// *************************************************************************** + +CPixelProgramDrvInfosD3D::~CPixelProgramDrvInfosD3D() +{ + H_AUTO_D3D(CPixelProgramDrvInfosD3D_CPixelProgramDrvInfosD3DDtor) + if (Shader) + Shader->Release(); +} + +// *************************************************************************** + +bool CDriverD3D::isPixelProgramSupported () const +{ + H_AUTO_D3D(CDriverD3D_isPixelProgramSupported ) + return _PixelProgram; +} + +// *************************************************************************** + +bool CDriverD3D::activePixelProgram(CPixelProgram *program) +{ + H_AUTO_D3D(CDriverD3D_activePixelProgram ) + if (_DisableHardwarePixelProgram) + return false; + + // Setup or unsetup ? + if (program) + { + // Program setuped ? + if (program->_DrvInfo==NULL) + { + _PixelPrgDrvInfos.push_front (NULL); + ItPixelPrgDrvInfoPtrList itPix = _PixelPrgDrvInfos.begin(); + *itPix = new CPixelProgramDrvInfosD3D(this, itPix); + + // Create a driver info structure + program->_DrvInfo = *itPix; + + std::string dest; + if(program->isEffectProgram()) + { + dest = program->getProgram(); + } + + LPD3DXBUFFER pShader; + LPD3DXBUFFER pErrorMsgs; + if (D3DXAssembleShader (dest.c_str(), dest.size(), NULL, NULL, 0, &pShader, &pErrorMsgs) == D3D_OK) + { + if (_DeviceInterface->CreatePixelShader((DWORD*)pShader->GetBufferPointer(), &(getPixelProgramD3D(*program)->Shader)) != D3D_OK) + return false; + } + else + { + nlwarning ("Can't assemble pixel program:"); + nlwarning ((const char*)pErrorMsgs->GetBufferPointer()); + return false; + } + } + } + + // Set the pixel program + if (program) + { + CPixelProgramDrvInfosD3D *info = static_cast((IPixelProgramDrvInfos*)program->_DrvInfo); + setPixelShader (info->Shader); + + float z = 0; + float o = 1; + setRenderState (D3DRS_FOGSTART, *((DWORD*) (&o))); + setRenderState (D3DRS_FOGEND, *((DWORD*) (&z))); + } + else + { + setPixelShader (NULL); + + // Set the old fog range + setRenderState (D3DRS_FOGSTART, *((DWORD*) (&_FogStart))); + setRenderState (D3DRS_FOGEND, *((DWORD*) (&_FogEnd))); + } + + return true; +} + +// *************************************************************************** + +void CDriverD3D::setPixelProgramConstant (uint index, float f0, float f1, float f2, float f3) +{ + H_AUTO_D3D(CDriverD3D_setPixelProgramConstant) + if (!_PixelProgram) + { + #ifdef NL_DEBUG + nlwarning("No pixel programs available!!"); + #endif + return; + } + const float tabl[4] = {f0, f1, f2, f3}; + setPixelShaderConstant (index, tabl); +} + +// *************************************************************************** + +void CDriverD3D::setPixelProgramConstant (uint index, double d0, double d1, double d2, double d3) +{ + H_AUTO_D3D(CDriverD3D_setPixelProgramConstant ) + if (!_PixelProgram) + { + #ifdef NL_DEBUG + nlwarning("No pixel programs available!!"); + #endif + return; + } + const float tabl[4] = {(float)d0, (float)d1, (float)d2, (float)d3}; + setPixelShaderConstant (index, tabl); +} + +// *************************************************************************** + +void CDriverD3D::setPixelProgramConstant (uint index, const NLMISC::CVector& value) +{ + H_AUTO_D3D(CDriverD3D_setPixelProgramConstant ) + if (!_PixelProgram) + { + #ifdef NL_DEBUG + nlwarning("No pixel programs available!!"); + #endif + return; + } + const float tabl[4] = {value.x, value.y, value.z, 0}; + setPixelShaderConstant (index, tabl); +} + +// *************************************************************************** + +void CDriverD3D::setPixelProgramConstant (uint index, const NLMISC::CVectorD& value) +{ + H_AUTO_D3D(CDriverD3D_setPixelProgramConstant ) + if (!_PixelProgram) + { + #ifdef NL_DEBUG + nlwarning("No pixel programs available!!"); + #endif + return; + } + const float tabl[4] = {(float)value.x, (float)value.y, (float)value.z, 0}; + setPixelShaderConstant (index, tabl); +} + +// *************************************************************************** + +void CDriverD3D::setPixelProgramConstant (uint index, uint num, const float *src) +{ + H_AUTO_D3D(CDriverD3D_setPixelProgramConstant ) + if (!_PixelProgram) + { + #ifdef NL_DEBUG + nlwarning("No pixel programs available!!"); + #endif + return; + } + uint i; + for (i=0; i_11, matPtr->_21, matPtr->_31, matPtr->_41); + setPixelProgramConstant (index+1, matPtr->_12, matPtr->_22, matPtr->_32, matPtr->_42); + setPixelProgramConstant (index+2, matPtr->_13, matPtr->_23, matPtr->_33, matPtr->_43); + setPixelProgramConstant (index+3, matPtr->_14, matPtr->_24, matPtr->_34, matPtr->_44); +} + +// *************************************************************************** + +void CDriverD3D::disableHardwarePixelProgram() +{ + H_AUTO_D3D(CDriverD3D_disableHardwarePixelProgram) + _DisableHardwarePixelProgram = true; + _PixelProgram = false; +} + +// *************************************************************************** + +uint CDriverD3D::getMaxTexturesForEffects() const +{ + H_AUTO_D3D(CDriverD3D_getMaxTexturesForEffects) + + // we use ps_2_0 profile for direct3D ASM pixel program, then 16 texture samplers are available + if(!strcmp(CPixelProgram::getPixelASMProfile(), "ps_2_0")) + return 16; + + return 0; +} + +} // NL3D diff --git a/code/nel/src/3d/driver/opengl/driver_opengl_pixel_program.cpp b/code/nel/src/3d/driver/opengl/driver_opengl_pixel_program.cpp new file mode 100644 index 000000000..7677e6151 --- /dev/null +++ b/code/nel/src/3d/driver/opengl/driver_opengl_pixel_program.cpp @@ -0,0 +1,553 @@ +/** \file driver_opengl_pixel_program.cpp + * OpenGL driver implementation for pixel program manipulation. + * + * $Id: driver_opengl_pixel_program.cpp,v 1.1.2.4 2007/07/09 15:29:00 legallo Exp $ + * + * \todo manage better the init/release system (if a throw occurs in the init, we must release correctly the driver) + */ + +/* Copyright, 2000 Nevrax Ltd. + * + * This file is part of NEVRAX NEL. + * NEVRAX NEL 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, or (at your option) + * any later version. + + * NEVRAX NEL 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 NEVRAX NEL; see the file COPYING. If not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include "stdopengl.h" + +#include "driver_opengl.h" +#include "../../index_buffer.h" +#include "../../vertex_program.h" +//#include "../../vertex_program_parse.h" +#include "../../program_parse_D3D.h" +#include + +// tmp +#include "nel/misc/file.h" + +using namespace std; +using namespace NLMISC; + +//#define DEBUG_SETUP_EXT_VERTEX_SHADER + +namespace NL3D +{ + +// *************************************************************************** +CPixelProgamDrvInfosGL::CPixelProgamDrvInfosGL (CDriverGL *drv, ItPixelPrgDrvInfoPtrList it) : IPixelProgramDrvInfos (drv, it) +{ + H_AUTO_OGL(CPixelProgamDrvInfosGL_CPixelProgamDrvInfosGL) + // Extension must exist + nlassert(drv->_Extensions.ARBFragmentProgram); + + if (drv->_Extensions.ARBFragmentProgram) // ARB implementation + { + nglGenProgramsARB(1, &ID); + } +} + +// *************************************************************************** +bool CDriverGL::isPixelProgramSupported () const +{ + H_AUTO_OGL(CPixelProgamDrvInfosGL_isPixelProgramSupported) + return _Extensions.ARBFragmentProgram; +} + +// *************************************************************************** +bool CDriverGL::activePixelProgram(CPixelProgram *program) +{ + H_AUTO_OGL(CDriverGL_activePixelProgram) + + if (_Extensions.ARBFragmentProgram) + { + return activeARBPixelProgram(program); + } + + return false; +} + +// *************************************************************************** +bool CDriverGL::activeARBPixelProgram(CPixelProgram *program) +{ + H_AUTO_OGL(CDriverGL_activeARBPixelProgram) + + // Setup or unsetup ? + if (program) + { + // Driver info + CPixelProgamDrvInfosGL *drvInfo; + + // Program setuped ? + if (program->_DrvInfo==NULL) + { + // Insert into driver list. (so it is deleted when driver is deleted). + ItPixelPrgDrvInfoPtrList it= _PixelPrgDrvInfos.insert(_PixelPrgDrvInfos.end()); + + // Create a driver info + *it = drvInfo = new CPixelProgamDrvInfosGL (this, it); + // Set the pointer + program->_DrvInfo=drvInfo; + + std::string asmProgram; + CPixelProgramParser::CPProgram parsedProgram; + if(program->isEffectProgram()) + { + asmProgram = program->getProgram(); + + CPPParserD3D parser; + // try to parse the program + std::string errorOutput; + bool result = parser.parse(asmProgram.c_str(), parsedProgram, errorOutput); + if (!result) + { + nlwarning("Unable to parse a pixel program."); + #ifdef NL_DEBUG + nlerror(errorOutput.c_str()); + #endif + return false; + } + } + + if(!setupARBPixelProgram(parsedProgram, drvInfo->ID)) + { + delete drvInfo; + program->_DrvInfo = NULL; + _PixelPrgDrvInfos.erase(it); + return false; + } + } + else + { + // Cast the driver info pointer + drvInfo=safe_cast((IPixelProgramDrvInfos*)program->_DrvInfo); + } + glEnable( GL_FRAGMENT_PROGRAM_ARB ); + _PixelProgramEnabled = true; + nglBindProgramARB( GL_FRAGMENT_PROGRAM_ARB, drvInfo->ID ); + + glDisable( GL_COLOR_SUM_ARB ); // no specular written + + _LastSetuppedPP = program; + } + else + { + glDisable( GL_FRAGMENT_PROGRAM_ARB ); + glDisable( GL_COLOR_SUM_ARB ); + _PixelProgramEnabled = false; + } + + return true; +} + +// *************************************************************************** +bool CDriverGL::setupARBPixelProgram (const CPixelProgramParser::CPProgram &inParsedProgram, GLuint id/*, bool &specularWritten*/) +{ + H_AUTO_OGL(CDriverGL_setupARBPixelProgram) + + // convert from proprietary format to ARB_pixel_program code + CPixelProgramConversionARB vpConvertARB; + std::string code; + if(!vpConvertARB.convert(inParsedProgram, code)) return false; + + // + nglBindProgramARB( GL_FRAGMENT_PROGRAM_ARB, id); + glGetError(); + nglProgramStringARB( GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, code.size(), code.c_str() ); + GLenum err = glGetError(); + if (err != GL_NO_ERROR) + { + if (err == GL_INVALID_OPERATION) + { + GLint position; + glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &position); + nlassert(position != -1) // there was an error.. + nlassert(position < (GLint) code.size()); + uint line = 0; + const char *lineStart = code.c_str(); + for(uint k = 0; k < (uint) position; ++k) + { + if (code[k] == '\n') + { + lineStart = code.c_str() + k; + ++line; + } + } + nlwarning("ARB fragment program parse error at line %d.", (int) line); + // search end of line + const char *lineEnd = code.c_str() + code.size(); + for(uint k = position; k < code.size(); ++k) + { + if (code[k] == '\n') + { + lineEnd = code.c_str() + k; + break; + } + } + nlwarning(std::string(lineStart, lineEnd).c_str()); + // display the gl error msg + const GLubyte *errorMsg = glGetString(GL_PROGRAM_ERROR_STRING_ARB); + nlassert((const char *) errorMsg); + nlwarning((const char *) errorMsg); + } + nlassert(0); + return false; + } + return true; +} + +// *************************************************************************** + +void CDriverGL::setPixelProgramConstant (uint index, float f0, float f1, float f2, float f3) +{ + H_AUTO_OGL(CDriverGL_setPixelProgramConstant) + + if(_LastSetuppedVP && _LastSetuppedVP->isEffectProgram()) + { + if (_Extensions.ARBFragmentProgram) + nglProgramEnvParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, index, f0, f1, f2, f3); + } +} + + +// *************************************************************************** + +void CDriverGL::setPixelProgramConstant (uint index, double d0, double d1, double d2, double d3) +{ + H_AUTO_OGL(CDriverGL_setPixelProgramConstant) + + if(_LastSetuppedVP && _LastSetuppedVP->isEffectProgram()) + { + if (_Extensions.ARBFragmentProgram) + nglProgramEnvParameter4dARB(GL_FRAGMENT_PROGRAM_ARB, index, d0, d1, d2, d3); + } +} + + +// *************************************************************************** + +void CDriverGL::setPixelProgramConstant (uint index, const NLMISC::CVector& value) +{ + H_AUTO_OGL(CDriverGL_setPixelProgramConstant) + + if(_LastSetuppedVP && _LastSetuppedVP->isEffectProgram()) + { + if (_Extensions.ARBFragmentProgram) + nglProgramEnvParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, index, value.x, value.y, value.z, 0); + } +} + + +// *************************************************************************** + +void CDriverGL::setPixelProgramConstant (uint index, const NLMISC::CVectorD& value) +{ + H_AUTO_OGL(CDriverGL_setPixelProgramConstant) + + if(_LastSetuppedVP && _LastSetuppedVP->isEffectProgram()) + { + if (_Extensions.ARBFragmentProgram) + nglProgramEnvParameter4dARB(GL_FRAGMENT_PROGRAM_ARB, index, value.x, value.y, value.z, 0); + } +} + + +// *************************************************************************** +void CDriverGL::setPixelProgramConstant (uint index, uint num, const float *src) +{ + H_AUTO_OGL(CDriverGL_setPixelProgramConstant) + + if(_LastSetuppedVP && _LastSetuppedVP->isEffectProgram()) + { + if (_Extensions.ARBFragmentProgram) + { + for(uint k = 0; k < num; ++k) + { + nglProgramEnvParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, index + k, src + 4 * k); + } + } + } +} + +// *************************************************************************** +void CDriverGL::setPixelProgramConstant (uint index, uint num, const double *src) +{ + H_AUTO_OGL(CDriverGL_setPixelProgramConstant) + + if(_LastSetuppedVP && _LastSetuppedVP->isEffectProgram()) + { + if (_Extensions.ARBFragmentProgram) + { + for(uint k = 0; k < num; ++k) + { + nglProgramEnvParameter4dvARB(GL_FRAGMENT_PROGRAM_ARB, index + k, src + 4 * k); + } + } + } +} + +// *************************************************************************** + +void CDriverGL::setPixelProgramConstantMatrix (uint index, IDriver::TMatrix matrix, IDriver::TTransform transform) +{ + H_AUTO_OGL(CDriverGL_setPixelProgramConstantMatrix) + + if(_LastSetuppedVP && _LastSetuppedVP->isEffectProgram()) + { + if (_Extensions.ARBFragmentProgram) + { + + // First, ensure that the render setup is correctly setuped. + refreshRenderSetup(); + CMatrix mat; + switch (matrix) + { + case IDriver::ModelView: + mat = _ModelViewMatrix; + break; + case IDriver::Projection: + { + refreshProjMatrixFromGL(); + mat = _GLProjMat; + } + break; + case IDriver::ModelViewProjection: + refreshProjMatrixFromGL(); + mat = _GLProjMat * _ModelViewMatrix; + break; + default: + break; + } + + switch(transform) + { + case IDriver::Identity: break; + case IDriver::Inverse: + mat.invert(); + break; + case IDriver::Transpose: + mat.transpose(); + break; + case IDriver::InverseTranspose: + mat.invert(); + mat.transpose(); + break; + default: + break; + } + mat.transpose(); + float matDatas[16]; + mat.get(matDatas); + + nglProgramEnvParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, index, matDatas); + nglProgramEnvParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, index + 1, matDatas + 4); + nglProgramEnvParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, index + 2, matDatas + 8); + nglProgramEnvParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, index + 3, matDatas + 12); + } + } +} + +// *************************************************************************** +uint CDriverGL::getMaxTexturesForEffects() const +{ + H_AUTO_OGL(CDriverGL_getMaxTexturesForEffects) + + uint texSamplerNb = 0; + if (_Extensions.ARBFragmentProgram) // ARB implementation + glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS_ARB, (int*)(&texSamplerNb)); + + return texSamplerNb; +} + +// *************************************************************************** +// ***************** CPixelProgramConversionARB ***************************** +// *************************************************************************** + +const char * CPixelProgramConversionARB::ARBPixelProgramInputRegisterToName[CPPOperand::InputRegisterCount] = +{ + "color.primary", + "color.secondary", + "texcoord[0]", + "texcoord[1]", + "texcoord[2]", + "texcoord[3]", + "texcoord[4]", + "texcoord[5]", + "texcoord[6]", + "texcoord[7]", +}; + +// *************************************************************************** +const char * CPixelProgramConversionARB::ARBPixelProgramOutputRegisterToName[CPPOperand::OutputRegisterCount] = +{ + "color", + "depth" +}; + +// *************************************************************************** +bool CPixelProgramConversionARB::convert(const CPixelProgramParser::CPProgram &inParsedProgram, std::string & code) +{ + CPixelProgramParser::TPProgram parsedProgram = inParsedProgram._Program; + // + code = "!!ARBfp1.0\n"; + // declare temporary registers + GLint glMaxTempVar; + nglGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB, GL_MAX_PROGRAM_TEMPORARIES_ARB, &glMaxTempVar); + uint usedTempVar = inParsedProgram.getUsedVariablesNb(); + if(usedTempVar>glMaxTempVar) + { + nlwarning(" Used temporary registers number is superior to maximum ARB_FRAGMENT_PROGRAM temporaries registers."); + return false; + } + ARBProgramTemporaryRegisters(code, usedTempVar); + // declare constant register + if(!CProgramConversionARB::constantRegisters(inParsedProgram._Constants, code)) return false; + + for(uint k = 0; k < parsedProgram.size(); ++k) + { + std::string instr; + ARBPixelProgramDumpInstr(parsedProgram[k], instr); + code += instr + "\r\n"; + } + code += "END\n"; + + return true; +} + +// *************************************************************************** +// Dump an instruction in a string +void CPixelProgramConversionARB::ARBPixelProgramDumpInstr(const CPPInstruction &instr, std::string &out) +{ + nlassert(instr.Opcode.PPOp < CPPInstruction::OpcodeCount); + // Special case for EXP with a scalar output argument (y component) -> translate to FRC + + out = std::string(); + switch(instr.Opcode.PPOp) + { + case CPPInstruction::ADD: + out = "ADD"; + break; + case CPPInstruction::DP3: + out = "DP3"; + break; + case CPPInstruction::DP4: + out = "DP4"; + break; + case CPPInstruction::EXP: + out = "EXP"; + break; + case CPPInstruction::FRC: + out = "FRC"; + break; + case CPPInstruction::LOG: + out = "LOG"; + break; + case CPPInstruction::MAD: + out = "MAD"; + break; + case CPPInstruction::MAX: + out = "MAX"; + break; + case CPPInstruction::MIN: + out = "MIN"; + break; + case CPPInstruction::MOV: + out = "MOV"; + break; + case CPPInstruction::MUL: + out = "MUL"; + break; + case CPPInstruction::RCP: + out = "RCP"; + break; + case CPPInstruction::RSQ: + out = "RSQ"; + break; + case CPPInstruction::SUB: + out = "SUB"; + break; + case CPPInstruction::ABS: + out = "ABS"; + break; + case CPPInstruction::CMP: + out = "CMP"; + break; + case CPPInstruction::CRS: + out = "XPD"; + break; + case CPPInstruction::LRP: + out = "LRP"; + break; + case CPPInstruction::POW: + out = "POW"; + break; + case CPPInstruction::TEX: + out = "TEX"; + break; + case CPPInstruction::TEXB: + out = "TXB"; + break; + case CPPInstruction::TEXP: + out = "TXP"; + break; + default: + nlwarning("no match with a ARB Pixel Program Operator"); + break; + } + + if(instr.Sat) out += "_SAT"; + out += " "; + uint nbOp = instr.getNumUsedSrc(); + std::string destOperand; + ARBPixelProgramDumpOperand(instr.Dest, true, destOperand); + out += destOperand; + for(uint k = 0; k < nbOp; ++k) + { + out += ", "; + std::string srcOperand; + ARBPixelProgramDumpOperand(instr.getSrc(k), false, srcOperand); + out += srcOperand; + } + out +="; \n"; +} + +// *************************************************************************** +void CPixelProgramConversionARB::ARBPixelProgramDumpOperand(const CPPOperand &op, bool destOperand, std::string &out) +{ + out = op.Negate ? " -" : " "; + switch(op.Type) + { + case CPPOperand::Variable: out += "R" + NLMISC::toString(op.Value.VariableValue); break; + case CPPOperand::Constant: + out += "c["; + out += NLMISC::toString(op.Value.ConstantValue) + "]"; + break; + case CPPOperand::InputRegister: out += string("fragment.") + ARBPixelProgramInputRegisterToName[(uint) op.Value.InputRegisterValue]; break; + case CPPOperand::OutputRegister: + nlassert(op.Value.OutputRegisterValue < CVPOperand::OutputRegisterCount); + out += "result." + std::string(ARBPixelProgramOutputRegisterToName[op.Value.OutputRegisterValue]); + break; + case CPPOperand::Sampler2DRegister: + out += string("texture[") + op.Value.SamplerValue + "], 2D"; + break; + case CPPOperand::Sampler3DRegister: + out += string("texture[") + op.Value.SamplerValue + "], 3D"; + break; + default: + break; + } + ARBProgramSuffix(op, destOperand, out); +} + +} // NL3D diff --git a/code/nel/src/3d/pixel_program.cpp b/code/nel/src/3d/pixel_program.cpp new file mode 100644 index 000000000..bd0e4d709 --- /dev/null +++ b/code/nel/src/3d/pixel_program.cpp @@ -0,0 +1,62 @@ +/** \file pixel_program.cpp + * Pixel program definition + * + * $Id: pixel_program.cpp,v 1.1.2.1 2007/04/27 17:35:07 legallo Exp $ + */ + +/* Copyright, 2000, 2001 Nevrax Ltd. + * + * This file is part of NEVRAX NEL. + * NEVRAX NEL 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, or (at your option) + * any later version. + + * NEVRAX NEL 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 NEVRAX NEL; see the file COPYING. If not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include "std3d.h" + +#include "pixel_program.h" + +#include "driver.h" + +namespace NL3D +{ + +// *************************************************************************** +IPixelProgramDrvInfos::IPixelProgramDrvInfos (IDriver *drv, ItPixelPrgDrvInfoPtrList it) +{ + _Driver= drv; + _DriverIterator= it; +} + + +// *************************************************************************** +IPixelProgramDrvInfos::~IPixelProgramDrvInfos () +{ + _Driver->removePixelPrgDrvInfoPtr (_DriverIterator); +} + + +// *************************************************************************** +CPixelProgram::CPixelProgram(const char* program, bool isEffectPrg) +:IProgram(program, isEffectPrg) +{ +} + + +// *************************************************************************** +CPixelProgram::~CPixelProgram() +{ +} + +} // NL3D From b5dfdbdd55951a61953895a7d846b408fe27b1fc Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 19 Jun 2013 01:19:45 +0200 Subject: [PATCH 008/313] Simplify CPixelProgram --- code/nel/include/nel/3d/pixel_program.h | 16 ++++------------ code/nel/src/3d/pixel_program.cpp | 9 +++++---- 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/code/nel/include/nel/3d/pixel_program.h b/code/nel/include/nel/3d/pixel_program.h index 35731ca6d..fd39a76bc 100644 --- a/code/nel/include/nel/3d/pixel_program.h +++ b/code/nel/include/nel/3d/pixel_program.h @@ -26,15 +26,11 @@ #ifndef NL_PIXEL_PROGRAM_H #define NL_PIXEL_PROGRAM_H -#include "program.h" - -#include "nel/misc/types_nl.h" -#include "nel/misc/smart_ptr.h" +#include +#include #include -#define PIXEL_SHADER_PROFILE "ps_2_0" - namespace NL3D { // List typedef. @@ -59,22 +55,18 @@ public: //------------------------------------------------------------------------------- -class CPixelProgram : public IProgram +class CPixelProgram : public NLMISC::CRefCount { public: /// Constructor - CPixelProgram (const char* program, bool isEffectPrg=false); + CPixelProgram (const char* program); /// Destructor virtual ~CPixelProgram (); /// The driver informations. For the driver implementation only. NLMISC::CRefPtr _DrvInfo; - - const char * getASMProfile() { return PIXEL_SHADER_PROFILE; } ; - - static const char * getPixelASMProfile() { return PIXEL_SHADER_PROFILE; } ; }; diff --git a/code/nel/src/3d/pixel_program.cpp b/code/nel/src/3d/pixel_program.cpp index bd0e4d709..b7cba51d0 100644 --- a/code/nel/src/3d/pixel_program.cpp +++ b/code/nel/src/3d/pixel_program.cpp @@ -25,9 +25,9 @@ #include "std3d.h" -#include "pixel_program.h" +#include -#include "driver.h" +#include namespace NL3D { @@ -48,15 +48,16 @@ IPixelProgramDrvInfos::~IPixelProgramDrvInfos () // *************************************************************************** -CPixelProgram::CPixelProgram(const char* program, bool isEffectPrg) -:IProgram(program, isEffectPrg) +CPixelProgram::CPixelProgram(const char* program) { + } // *************************************************************************** CPixelProgram::~CPixelProgram() { + } } // NL3D From 9f6b60a920d5f062b653277b925d9f05c8dd19a5 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 19 Jun 2013 01:20:51 +0200 Subject: [PATCH 009/313] Add diff from old nevrax pixel program code to IDriver, CEffect related code not included --- code/nel/include/nel/3d/driver.h | 50 +++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/code/nel/include/nel/3d/driver.h b/code/nel/include/nel/3d/driver.h index d090d77da..b3b8a250f 100644 --- a/code/nel/include/nel/3d/driver.h +++ b/code/nel/include/nel/3d/driver.h @@ -30,6 +30,7 @@ #include "nel/3d/vertex_buffer.h" #include "nel/3d/index_buffer.h" #include "nel/3d/vertex_program.h" +#include "nel/3d/pixel_program.h" #include "nel/3d/material.h" #include "nel/misc/mutex.h" #include "nel/3d/primitive_profile.h" @@ -145,6 +146,7 @@ public: protected: CSynchronized _SyncTexDrvInfos; + CSynchronized _SyncEffectDrvInfos; TTexDrvSharePtrList _TexDrvShares; TMatDrvInfoPtrList _MatDrvInfos; @@ -152,6 +154,7 @@ protected: TIBDrvInfoPtrList _IBDrvInfos; TPolygonMode _PolygonMode; TVtxPrgDrvInfoPtrList _VtxPrgDrvInfos; + TPixelPrgDrvInfoPtrList _PixelPrgDrvInfos; TShaderDrvInfoPtrList _ShaderDrvInfos; uint _ResetCounter; @@ -172,6 +175,7 @@ public: */ // @{ virtual void disableHardwareVertexProgram()=0; + virtual void disableHardwarePixelProgram()=0; virtual void disableHardwareVertexArrayAGP()=0; virtual void disableHardwareTextureShader()=0; // @} @@ -1009,6 +1013,11 @@ public: */ virtual bool isVertexProgramEmulated () const =0; + /** + * Does the driver supports pixel programs ? + */ + virtual bool isPixelProgramSupported () const =0; + /** @@ -1021,7 +1030,16 @@ public: virtual bool activeVertexProgram (CVertexProgram *program) =0; /** - * Setup constant values. + * Activate / disactivate a pixel program + * + * \param program is a pointer on a pixel program. Can be NULL to disable the current pixel program. + * + * \return true if setup/unsetup successed, false else. + */ + virtual bool activePixelProgram (CPixelProgram *program) =0; + + /** + * Setup vertex program constant values. */ virtual void setConstant (uint index, float, float, float, float) =0; virtual void setConstant (uint index, double, double, double, double) =0; @@ -1031,7 +1049,32 @@ public: virtual void setConstant (uint index, uint num, const float *src) =0; /// setup several 4 double csts taken from the given tab virtual void setConstant (uint index, uint num, const double *src) =0; + + /** + * Setup pixel program constant values. + */ + virtual void setPixelProgramConstant (uint index, float, float, float, float) =0; + virtual void setPixelProgramConstant (uint index, double, double, double, double) =0; + virtual void setPixelProgramConstant (uint index, const NLMISC::CVector& value) =0; + virtual void setPixelProgramConstant (uint index, const NLMISC::CVectorD& value) =0; + /// setup several 4 float csts taken from the given tab + virtual void setPixelProgramConstant (uint index, uint num, const float *src) =0; + /// setup several 4 double csts taken from the given tab + virtual void setPixelProgramConstant (uint index, uint num, const double *src) =0; + /** + * Setup constants with a current matrix. + * + * This call must be done after setFrustum(), setupViewMatrix() or setupModelMatrix() to get correct + * results. + * + * \param index is the base constant index where to store the matrix. This index must be a multiple of 4. + * \param matrix is the matrix id to store in the constants + * \param transform is the transformation to apply to the matrix before store it in the constants. + * + */ + virtual void setPixelProgramConstantMatrix (uint index, TMatrix matrix, TTransform transform) =0; + /** * Setup constants with a current matrix. * @@ -1251,6 +1294,9 @@ public: virtual void stencilOp(TStencilOp fail, TStencilOp zfail, TStencilOp zpass) = 0; virtual void stencilMask(uint mask) = 0; + // get the number of texture samplers available for pû•el programs + virtual uint getMaxTexturesForEffects() const = 0; + protected: friend class IVBDrvInfos; friend class IIBDrvInfos; @@ -1258,6 +1304,7 @@ protected: friend class ITextureDrvInfos; friend class IMaterialDrvInfos; friend class IVertexProgramDrvInfos; + friend class IPixelProgramDrvInfos; friend class IShaderDrvInfos; /// remove ptr from the lists in the driver. @@ -1268,6 +1315,7 @@ protected: void removeMatDrvInfoPtr(ItMatDrvInfoPtrList shaderIt); void removeShaderDrvInfoPtr(ItShaderDrvInfoPtrList shaderIt); void removeVtxPrgDrvInfoPtr(ItVtxPrgDrvInfoPtrList vtxPrgDrvInfoIt); + void removePixelPrgDrvInfoPtr(ItPixelPrgDrvInfoPtrList pixelPrgDrvInfoIt); private: bool _StaticMemoryToVRAM; From 7da48d8866b8d4cbc9e75d57095f79bf9600017d Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 19 Jun 2013 01:22:26 +0200 Subject: [PATCH 010/313] Removed some CEffect related bit that slipped in --- code/nel/include/nel/3d/driver.h | 1 - 1 file changed, 1 deletion(-) diff --git a/code/nel/include/nel/3d/driver.h b/code/nel/include/nel/3d/driver.h index b3b8a250f..f9667cca9 100644 --- a/code/nel/include/nel/3d/driver.h +++ b/code/nel/include/nel/3d/driver.h @@ -146,7 +146,6 @@ public: protected: CSynchronized _SyncTexDrvInfos; - CSynchronized _SyncEffectDrvInfos; TTexDrvSharePtrList _TexDrvShares; TMatDrvInfoPtrList _MatDrvInfos; From cb966505ccf7bf492130e7c867a4efff175a73c1 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 19 Jun 2013 01:38:23 +0200 Subject: [PATCH 011/313] Add rest of the diff from the old nevrax code for pixel programs to NL3D --- code/nel/include/nel/3d/driver_user.h | 1 + code/nel/include/nel/3d/u_driver.h | 1 + code/nel/src/3d/driver.cpp | 5 +++++ code/nel/src/3d/driver_user.cpp | 6 ++++++ code/nel/src/3d/flare_model.cpp | 3 +++ code/nel/src/3d/scene.cpp | 4 ++++ 6 files changed, 20 insertions(+) diff --git a/code/nel/include/nel/3d/driver_user.h b/code/nel/include/nel/3d/driver_user.h index bfe4c1755..fff83d6d5 100644 --- a/code/nel/include/nel/3d/driver_user.h +++ b/code/nel/include/nel/3d/driver_user.h @@ -133,6 +133,7 @@ public: // @{ virtual void disableHardwareVertexProgram(); + virtual void disableHardwarePixelProgram(); virtual void disableHardwareVertexArrayAGP(); virtual void disableHardwareTextureShader(); diff --git a/code/nel/include/nel/3d/u_driver.h b/code/nel/include/nel/3d/u_driver.h index d66a72e47..1397c9c6e 100644 --- a/code/nel/include/nel/3d/u_driver.h +++ b/code/nel/include/nel/3d/u_driver.h @@ -168,6 +168,7 @@ public: */ // @{ virtual void disableHardwareVertexProgram()=0; + virtual void disableHardwarePixelProgram()=0; virtual void disableHardwareVertexArrayAGP()=0; virtual void disableHardwareTextureShader()=0; // @} diff --git a/code/nel/src/3d/driver.cpp b/code/nel/src/3d/driver.cpp index fa48649ae..d44079749 100644 --- a/code/nel/src/3d/driver.cpp +++ b/code/nel/src/3d/driver.cpp @@ -258,6 +258,11 @@ void IDriver::removeVtxPrgDrvInfoPtr(ItVtxPrgDrvInfoPtrList vtxPrgDrvInfoIt) { _VtxPrgDrvInfos.erase(vtxPrgDrvInfoIt); } +// *************************************************************************** +void IDriver::removePixelPrgDrvInfoPtr(ItPixelPrgDrvInfoPtrList pixelPrgDrvInfoIt) +{ + _PixelPrgDrvInfos.erase(pixelPrgDrvInfoIt); +} // *************************************************************************** bool IDriver::invalidateShareTexture (ITexture &texture) diff --git a/code/nel/src/3d/driver_user.cpp b/code/nel/src/3d/driver_user.cpp index b45ebbd37..83c7343ec 100644 --- a/code/nel/src/3d/driver_user.cpp +++ b/code/nel/src/3d/driver_user.cpp @@ -213,6 +213,12 @@ void CDriverUser::disableHardwareVertexProgram() _Driver->disableHardwareVertexProgram(); } +void CDriverUser::disableHardwarePixelProgram() +{ + NL3D_HAUTO_UI_DRIVER; + + _Driver->disableHardwarePixelProgram(); +} void CDriverUser::disableHardwareVertexArrayAGP() { NL3D_HAUTO_UI_DRIVER; diff --git a/code/nel/src/3d/flare_model.cpp b/code/nel/src/3d/flare_model.cpp index 47d9fdb43..ba5cc8098 100644 --- a/code/nel/src/3d/flare_model.cpp +++ b/code/nel/src/3d/flare_model.cpp @@ -363,6 +363,7 @@ void CFlareModel::traverseRender() } // setup driver drv->activeVertexProgram(NULL); + drv->activePixelProgram(NULL); drv->setupModelMatrix(fs->getLookAtMode() ? CMatrix::Identity : getWorldMatrix()); // we don't change the fustrum to draw 2d shapes : it is costly, and we need to restore it after the drawing has been done // we setup Z to be (near + far) / 2, and setup x and y to get the screen coordinates we want @@ -565,6 +566,7 @@ void CFlareModel::updateOcclusionQueryBegin(IDriver *drv) { nlassert(drv); drv->activeVertexProgram(NULL); + drv->activePixelProgram(NULL); drv->setupModelMatrix(CMatrix::Identity); initStatics(); drv->setColorMask(false, false, false, false); // don't write any pixel during the test @@ -661,6 +663,7 @@ void CFlareModel::occlusionTest(CMesh &mesh, IDriver &drv) } drv.setColorMask(false, false, false, false); // don't write any pixel during the test drv.activeVertexProgram(NULL); + drv.activePixelProgram(NULL); setupOcclusionMeshMatrix(drv, *_Scene); drv.activeVertexBuffer(const_cast(mesh.getVertexBuffer())); // query drawn count diff --git a/code/nel/src/3d/scene.cpp b/code/nel/src/3d/scene.cpp index df5297e2f..f257c7206 100644 --- a/code/nel/src/3d/scene.cpp +++ b/code/nel/src/3d/scene.cpp @@ -377,6 +377,9 @@ void CScene::endPartRender() // Reset profiling _NextRenderProfile= false; + IDriver *drv = getDriver(); + drv->activeVertexProgram(NULL); + drv->activePixelProgram(NULL); /* uint64 total = PSStatsRegisterPSModelObserver + @@ -1561,6 +1564,7 @@ void CScene::renderOcclusionTestMeshs() nlassert(RenderTrav.getDriver()); RenderTrav.getDriver()->setupViewport(RenderTrav.getViewport()); RenderTrav.getDriver()->activeVertexProgram(NULL); + RenderTrav.getDriver()->activePixelProgram(NULL); IDriver::TPolygonMode oldPolygonMode = RenderTrav.getDriver()->getPolygonMode(); CMaterial m; m.initUnlit(); From acf8ec653c94ab7473ac13eba50019ddbae5d140 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 19 Jun 2013 02:01:12 +0200 Subject: [PATCH 012/313] Added diff to opengl driver for old nevrax pixel program code, marked todos in comments --- code/nel/include/nel/3d/driver.h | 3 -- .../src/3d/driver/opengl/driver_opengl.cpp | 8 +++ code/nel/src/3d/driver/opengl/driver_opengl.h | 42 ++++++++++++++++ .../driver/opengl/driver_opengl_extension.cpp | 11 ++++ .../driver/opengl/driver_opengl_extension.h | 2 + .../opengl/driver_opengl_pixel_program.cpp | 50 ++++++++++--------- 6 files changed, 90 insertions(+), 26 deletions(-) diff --git a/code/nel/include/nel/3d/driver.h b/code/nel/include/nel/3d/driver.h index f9667cca9..9a079d95d 100644 --- a/code/nel/include/nel/3d/driver.h +++ b/code/nel/include/nel/3d/driver.h @@ -1293,9 +1293,6 @@ public: virtual void stencilOp(TStencilOp fail, TStencilOp zfail, TStencilOp zpass) = 0; virtual void stencilMask(uint mask) = 0; - // get the number of texture samplers available for pû•el programs - virtual uint getMaxTexturesForEffects() const = 0; - protected: friend class IVBDrvInfos; friend class IIBDrvInfos; diff --git a/code/nel/src/3d/driver/opengl/driver_opengl.cpp b/code/nel/src/3d/driver/opengl/driver_opengl.cpp index 29e14a1a0..b3567ed90 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl.cpp +++ b/code/nel/src/3d/driver/opengl/driver_opengl.cpp @@ -482,6 +482,7 @@ bool CDriverGL::setupDisplay() } _VertexProgramEnabled= false; + _PixelProgramEnabled= false; _LastSetupGLArrayVertexProgram= false; // Init VertexArrayRange according to supported extenstion. @@ -737,6 +738,12 @@ void CDriverGL::disableHardwareVertexProgram() _Extensions.DisableHardwareVertexProgram= true; } +void CDriverGL::disableHardwarePixelProgram() +{ + H_AUTO_OGL(CDriverGL_disableHardwarePixelProgram) + _Extensions.DisableHardwarePixelProgram= true; +} + // *************************************************************************** void CDriverGL::disableHardwareVertexArrayAGP() { @@ -854,6 +861,7 @@ bool CDriverGL::swapBuffers() // Reset texture shaders //resetTextureShaders(); activeVertexProgram(NULL); + activePixelProgram(NULL); #ifndef USE_OPENGLES /* Yoyo: must do this (GeForce bug ??) else weird results if end render with a VBHard. diff --git a/code/nel/src/3d/driver/opengl/driver_opengl.h b/code/nel/src/3d/driver/opengl/driver_opengl.h index bfe73492d..25503d9c6 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl.h +++ b/code/nel/src/3d/driver/opengl/driver_opengl.h @@ -306,6 +306,7 @@ public: virtual bool init (uint windowIcon = 0, emptyProc exitFunc = 0); virtual void disableHardwareVertexProgram(); + virtual void disableHardwarePixelProgram(); virtual void disableHardwareVertexArrayAGP(); virtual void disableHardwareTextureShader(); @@ -692,6 +693,7 @@ private: virtual class IVertexBufferHardGL *createVertexBufferHard(uint size, uint numVertices, CVertexBuffer::TPreferredMemory vbType, CVertexBuffer *vb); friend class CTextureDrvInfosGL; friend class CVertexProgamDrvInfosGL; + friend class CPixelProgamDrvInfosGL; private: // Version of the driver. Not the interface version!! Increment when implementation of the driver change. @@ -1302,8 +1304,10 @@ private: // @{ bool isVertexProgramSupported () const; + bool isPixelProgramSupported () const; bool isVertexProgramEmulated () const; bool activeVertexProgram (CVertexProgram *program); + bool activePixelProgram (CPixelProgram *program); void setConstant (uint index, float, float, float, float); void setConstant (uint index, double, double, double, double); void setConstant (uint indexStart, const NLMISC::CVector& value); @@ -1314,6 +1318,15 @@ private: void setConstantFog (uint index); void enableVertexProgramDoubleSidedColor(bool doubleSided); bool supportVertexProgramDoubleSidedColor() const; + + // Pixel program + virtual void setPixelProgramConstant (uint index, float, float, float, float); + virtual void setPixelProgramConstant (uint index, double, double, double, double); + virtual void setPixelProgramConstant (uint index, const NLMISC::CVector& value); + virtual void setPixelProgramConstant (uint index, const NLMISC::CVectorD& value); + virtual void setPixelProgramConstant (uint index, uint num, const float *src); + virtual void setPixelProgramConstant (uint index, uint num, const double *src); + virtual void setPixelProgramConstantMatrix (uint index, IDriver::TMatrix matrix, IDriver::TTransform transform); virtual bool supportMADOperator() const ; @@ -1328,6 +1341,12 @@ private: //@} + /// \name Pixel program implementation + // @{ + bool activeARBPixelProgram (CPixelProgram *program); + // TODO_REMOVE_PARSER bool setupARBPixelProgram (const CPixelProgramParser::CPProgram &parsedProgram, GLuint id/*, bool &specularWritten*/); + //@} + /// \fallback for material shaders // @{ @@ -1340,15 +1359,27 @@ private: // Don't use glIsEnabled, too slow. return _VertexProgramEnabled; } + + bool isPixelProgramEnabled () const + { + // Don't use glIsEnabled, too slow. + return _PixelProgramEnabled; + } // Track state of activeVertexProgram() bool _VertexProgramEnabled; + // Track state of activePixelProgram() + bool _PixelProgramEnabled; + // Say if last setupGlArrays() was a VertexProgram setup. bool _LastSetupGLArrayVertexProgram; // The last vertex program that was setupped NLMISC::CRefPtr _LastSetuppedVP; + // The last pixel program that was setupped + NLMISC::CRefPtr _LastSetuppedPP; + bool _ForceDXTCCompression; /// Divisor for textureResize (power). uint _ForceTextureResizePower; @@ -1518,6 +1549,17 @@ public: CVertexProgamDrvInfosGL (CDriverGL *drv, ItVtxPrgDrvInfoPtrList it); }; +// *************************************************************************** +class CPixelProgamDrvInfosGL : public IPixelProgramDrvInfos +{ +public: + // The GL Id. + GLuint ID; + + // The gl id is auto created here. + CPixelProgamDrvInfosGL (CDriverGL *drv, ItPixelPrgDrvInfoPtrList it); +}; + #ifdef NL_STATIC } // NLDRIVERGL/ES #endif diff --git a/code/nel/src/3d/driver/opengl/driver_opengl_extension.cpp b/code/nel/src/3d/driver/opengl/driver_opengl_extension.cpp index d38ff8f51..e777d2696 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl_extension.cpp +++ b/code/nel/src/3d/driver/opengl/driver_opengl_extension.cpp @@ -1560,6 +1560,17 @@ void registerGlExtensions(CGlExtensions &ext) ext.EXTVertexShader = false; ext.ARBVertexProgram = false; } + + // Check pixel program + // Disable feature ??? + if(!ext.DisableHardwarePixelProgram) + { + ext.ARBFragmentProgram= setupARBFragmentProgram(glext); + } + else + { + ext.ARBFragmentProgram = false; + } ext.OESDrawTexture = setupOESDrawTexture(glext); ext.OESMapBuffer = setupOESMapBuffer(glext); diff --git a/code/nel/src/3d/driver/opengl/driver_opengl_extension.h b/code/nel/src/3d/driver/opengl/driver_opengl_extension.h index 9d28a15ab..d6d4974a6 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl_extension.h +++ b/code/nel/src/3d/driver/opengl/driver_opengl_extension.h @@ -111,6 +111,7 @@ public: /// \name Disable Hardware feature. False by default. setuped by IDriver // @{ bool DisableHardwareVertexProgram; + bool DisableHardwarePixelProgram; bool DisableHardwareVertexArrayAGP; bool DisableHardwareTextureShader; // @} @@ -174,6 +175,7 @@ public: /// \name Disable Hardware feature. False by default. setuped by IDriver DisableHardwareVertexProgram= false; + DisableHardwarePixelProgram= false; DisableHardwareVertexArrayAGP= false; DisableHardwareTextureShader= false; } diff --git a/code/nel/src/3d/driver/opengl/driver_opengl_pixel_program.cpp b/code/nel/src/3d/driver/opengl/driver_opengl_pixel_program.cpp index 7677e6151..a0439f3b3 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl_pixel_program.cpp +++ b/code/nel/src/3d/driver/opengl/driver_opengl_pixel_program.cpp @@ -28,10 +28,8 @@ #include "stdopengl.h" #include "driver_opengl.h" -#include "../../index_buffer.h" -#include "../../vertex_program.h" -//#include "../../vertex_program_parse.h" -#include "../../program_parse_D3D.h" +#include +#include #include // tmp @@ -92,6 +90,8 @@ bool CDriverGL::activeARBPixelProgram(CPixelProgram *program) // Program setuped ? if (program->_DrvInfo==NULL) { + /* TODO_REMOVE_PARSER + // Insert into driver list. (so it is deleted when driver is deleted). ItPixelPrgDrvInfoPtrList it= _PixelPrgDrvInfos.insert(_PixelPrgDrvInfos.end()); @@ -127,6 +127,8 @@ bool CDriverGL::activeARBPixelProgram(CPixelProgram *program) _PixelPrgDrvInfos.erase(it); return false; } + + */ } else { @@ -150,7 +152,8 @@ bool CDriverGL::activeARBPixelProgram(CPixelProgram *program) return true; } - +// TODO_REMOVE_PARSER +#if 0 // *************************************************************************** bool CDriverGL::setupARBPixelProgram (const CPixelProgramParser::CPProgram &inParsedProgram, GLuint id/*, bool &specularWritten*/) { @@ -206,6 +209,7 @@ bool CDriverGL::setupARBPixelProgram (const CPixelProgramParser::CPProgram &inPa } return true; } +#endif // *************************************************************************** @@ -213,7 +217,8 @@ void CDriverGL::setPixelProgramConstant (uint index, float f0, float f1, float f { H_AUTO_OGL(CDriverGL_setPixelProgramConstant) - if(_LastSetuppedVP && _LastSetuppedVP->isEffectProgram()) + //if(_LastSetuppedVP && _LastSetuppedVP->isEffectProgram()) + if (_LastSetuppedPP) // TODO_REMOVE_EFFECTS { if (_Extensions.ARBFragmentProgram) nglProgramEnvParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, index, f0, f1, f2, f3); @@ -227,7 +232,8 @@ void CDriverGL::setPixelProgramConstant (uint index, double d0, double d1, doubl { H_AUTO_OGL(CDriverGL_setPixelProgramConstant) - if(_LastSetuppedVP && _LastSetuppedVP->isEffectProgram()) + // if(_LastSetuppedVP && _LastSetuppedVP->isEffectProgram()) + if (_LastSetuppedPP) // TODO_REMOVE_EFFECTS { if (_Extensions.ARBFragmentProgram) nglProgramEnvParameter4dARB(GL_FRAGMENT_PROGRAM_ARB, index, d0, d1, d2, d3); @@ -241,7 +247,8 @@ void CDriverGL::setPixelProgramConstant (uint index, const NLMISC::CVector& valu { H_AUTO_OGL(CDriverGL_setPixelProgramConstant) - if(_LastSetuppedVP && _LastSetuppedVP->isEffectProgram()) + // if(_LastSetuppedVP && _LastSetuppedVP->isEffectProgram()) + if (_LastSetuppedPP) // TODO_REMOVE_EFFECTS { if (_Extensions.ARBFragmentProgram) nglProgramEnvParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, index, value.x, value.y, value.z, 0); @@ -255,7 +262,8 @@ void CDriverGL::setPixelProgramConstant (uint index, const NLMISC::CVectorD& val { H_AUTO_OGL(CDriverGL_setPixelProgramConstant) - if(_LastSetuppedVP && _LastSetuppedVP->isEffectProgram()) + // if(_LastSetuppedVP && _LastSetuppedVP->isEffectProgram()) + if (_LastSetuppedPP) // TODO_REMOVE_EFFECTS { if (_Extensions.ARBFragmentProgram) nglProgramEnvParameter4dARB(GL_FRAGMENT_PROGRAM_ARB, index, value.x, value.y, value.z, 0); @@ -268,7 +276,8 @@ void CDriverGL::setPixelProgramConstant (uint index, uint num, const float *src) { H_AUTO_OGL(CDriverGL_setPixelProgramConstant) - if(_LastSetuppedVP && _LastSetuppedVP->isEffectProgram()) + // if(_LastSetuppedVP && _LastSetuppedVP->isEffectProgram()) + if (_LastSetuppedPP) // TODO_REMOVE_EFFECTS { if (_Extensions.ARBFragmentProgram) { @@ -285,7 +294,8 @@ void CDriverGL::setPixelProgramConstant (uint index, uint num, const double *src { H_AUTO_OGL(CDriverGL_setPixelProgramConstant) - if(_LastSetuppedVP && _LastSetuppedVP->isEffectProgram()) + // if(_LastSetuppedVP && _LastSetuppedVP->isEffectProgram()) + if (_LastSetuppedPP) // TODO_REMOVE_EFFECTS { if (_Extensions.ARBFragmentProgram) { @@ -303,7 +313,8 @@ void CDriverGL::setPixelProgramConstantMatrix (uint index, IDriver::TMatrix matr { H_AUTO_OGL(CDriverGL_setPixelProgramConstantMatrix) - if(_LastSetuppedVP && _LastSetuppedVP->isEffectProgram()) + // if(_LastSetuppedVP && _LastSetuppedVP->isEffectProgram()) + if (_LastSetuppedPP) // TODO_REMOVE_EFFECTS { if (_Extensions.ARBFragmentProgram) { @@ -358,17 +369,8 @@ void CDriverGL::setPixelProgramConstantMatrix (uint index, IDriver::TMatrix matr } } -// *************************************************************************** -uint CDriverGL::getMaxTexturesForEffects() const -{ - H_AUTO_OGL(CDriverGL_getMaxTexturesForEffects) - - uint texSamplerNb = 0; - if (_Extensions.ARBFragmentProgram) // ARB implementation - glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS_ARB, (int*)(&texSamplerNb)); - - return texSamplerNb; -} +// TODO_REMOVE_PARSER +#if 0 // *************************************************************************** // ***************** CPixelProgramConversionARB ***************************** @@ -550,4 +552,6 @@ void CPixelProgramConversionARB::ARBPixelProgramDumpOperand(const CPPOperand &op ARBProgramSuffix(op, destOperand, out); } +#endif + } // NL3D From 7dcc86c717ce566efd1cf1dd7f3dd442b6179d4d Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 19 Jun 2013 02:10:38 +0200 Subject: [PATCH 013/313] Add direct3d diff for old nevrax pixel program code, marked a todo in the comments --- .../3d/driver/direct3d/driver_direct3d.cpp | 8 +++++ .../src/3d/driver/direct3d/driver_direct3d.h | 36 +++++++++++++++++++ .../driver_direct3d_pixel_program.cpp | 16 ++------- 3 files changed, 46 insertions(+), 14 deletions(-) diff --git a/code/nel/src/3d/driver/direct3d/driver_direct3d.cpp b/code/nel/src/3d/driver/direct3d/driver_direct3d.cpp index 412cb52da..26cf628e0 100644 --- a/code/nel/src/3d/driver/direct3d/driver_direct3d.cpp +++ b/code/nel/src/3d/driver/direct3d/driver_direct3d.cpp @@ -193,6 +193,11 @@ CDriverD3D::CDriverD3D() #else // NL_DISABLE_HARDWARE_VERTEX_PROGAM _DisableHardwareVertexProgram = false; #endif // NL_DISABLE_HARDWARE_VERTEX_PROGAM +#ifdef NL_DISABLE_HARDWARE_PIXEL_PROGAM + _DisableHardwarePixelProgram = true; +#else // NL_DISABLE_HARDWARE_PIXEL_PROGAM + _DisableHardwarePixelProgram = false; +#endif // NL_DISABLE_HARDWARE_PIXEL_PROGAM #ifdef NL_DISABLE_HARDWARE_VERTEX_ARRAY_AGP _DisableHardwareVertexArrayAGP = true; #else // NL_DISABLE_HARDWARE_VERTEX_ARRAY_AGP @@ -1546,6 +1551,7 @@ bool CDriverD3D::setDisplay(nlWindow wnd, const GfxMode& mode, bool show, bool r #endif // NL_FORCE_TEXTURE_STAGE_COUNT _VertexProgram = !_DisableHardwareVertexProgram && ((caps.VertexShaderVersion&0xffff) >= 0x0100); + _PixelProgram = !_DisableHardwarePixelProgram && (caps.PixelShaderVersion&0xffff) >= 0x0101; _PixelShader = !_DisableHardwarePixelShader && (caps.PixelShaderVersion&0xffff) >= 0x0101; _MaxVerticesByVertexBufferHard = caps.MaxVertexIndex; _MaxLight = caps.MaxActiveLights; @@ -2016,6 +2022,8 @@ bool CDriverD3D::swapBuffers() // Reset vertex program setVertexProgram (NULL, NULL); + // Reset pixel program + setPixelShader (NULL); if (_VBHardProfiling) { diff --git a/code/nel/src/3d/driver/direct3d/driver_direct3d.h b/code/nel/src/3d/driver/direct3d/driver_direct3d.h index cff7cb804..7668bd63c 100644 --- a/code/nel/src/3d/driver/direct3d/driver_direct3d.h +++ b/code/nel/src/3d/driver/direct3d/driver_direct3d.h @@ -240,6 +240,19 @@ public: CVertexProgamDrvInfosD3D(IDriver *drv, ItVtxPrgDrvInfoPtrList it); ~CVertexProgamDrvInfosD3D(); }; + + +// *************************************************************************** +class CPixelProgramDrvInfosD3D : public IPixelProgramDrvInfos +{ +public: + + // The shader + IDirect3DPixelShader9 *Shader; + + CPixelProgramDrvInfosD3D(IDriver *drv, ItPixelPrgDrvInfoPtrList it); + ~CPixelProgramDrvInfosD3D(); +}; // *************************************************************************** @@ -773,6 +786,7 @@ public: // Driver parameters virtual void disableHardwareVertexProgram(); + virtual void disableHardwarePixelProgram(); virtual void disableHardwareIndexArrayAGP(); virtual void disableHardwareVertexArrayAGP(); virtual void disableHardwareTextureShader(); @@ -993,8 +1007,10 @@ public: // Vertex program virtual bool isVertexProgramSupported () const; + virtual bool isPixelProgramSupported () const; virtual bool isVertexProgramEmulated () const; virtual bool activeVertexProgram (CVertexProgram *program); + virtual bool activePixelProgram (CPixelProgram *program); virtual void setConstant (uint index, float, float, float, float); virtual void setConstant (uint index, double, double, double, double); virtual void setConstant (uint index, const NLMISC::CVector& value); @@ -1006,6 +1022,15 @@ public: virtual void enableVertexProgramDoubleSidedColor(bool doubleSided); virtual bool supportVertexProgramDoubleSidedColor() const; + // Pixel program + virtual void setPixelProgramConstant (uint index, float, float, float, float); + virtual void setPixelProgramConstant (uint index, double, double, double, double); + virtual void setPixelProgramConstant (uint index, const NLMISC::CVector& value); + virtual void setPixelProgramConstant (uint index, const NLMISC::CVectorD& value); + virtual void setPixelProgramConstant (uint index, uint num, const float *src); + virtual void setPixelProgramConstant (uint index, uint num, const double *src); + virtual void setPixelProgramConstantMatrix (uint index, IDriver::TMatrix matrix, IDriver::TTransform transform); + // Occlusion query virtual bool supportOcclusionQuery() const; virtual IOcclusionQuery *createOcclusionQuery(); @@ -1892,6 +1917,15 @@ public: return d3dtex; } + // Get the d3dtext mirror of an existing setuped pixel program. + static inline CPixelProgramDrvInfosD3D* getPixelProgramD3D(CPixelProgram& pixelProgram) + { + H_AUTO_D3D(CDriverD3D_getPixelProgramD3D); + CPixelProgramDrvInfosD3D* d3dPixelProgram; + d3dPixelProgram = (CPixelProgramDrvInfosD3D*)(IPixelProgramDrvInfos*)(pixelProgram._DrvInfo); + return d3dPixelProgram; + } + // Get the d3dtext mirror of an existing setuped vertex program. static inline CVertexProgamDrvInfosD3D* getVertexProgramD3D(CVertexProgram& vertexProgram) { @@ -2197,8 +2231,10 @@ private: bool _ForceDXTCCompression:1; bool _TextureCubeSupported; bool _VertexProgram; + bool _PixelProgram; bool _PixelShader; bool _DisableHardwareVertexProgram; + bool _DisableHardwarePixelProgram; bool _DisableHardwareVertexArrayAGP; bool _DisableHardwareIndexArrayAGP; bool _DisableHardwarePixelShader; diff --git a/code/nel/src/3d/driver/direct3d/driver_direct3d_pixel_program.cpp b/code/nel/src/3d/driver/direct3d/driver_direct3d_pixel_program.cpp index 755592043..f2255f88b 100644 --- a/code/nel/src/3d/driver/direct3d/driver_direct3d_pixel_program.cpp +++ b/code/nel/src/3d/driver/direct3d/driver_direct3d_pixel_program.cpp @@ -82,10 +82,11 @@ bool CDriverD3D::activePixelProgram(CPixelProgram *program) program->_DrvInfo = *itPix; std::string dest; + /* TODO_REMOVE if(program->isEffectProgram()) { dest = program->getProgram(); - } + }*/ LPD3DXBUFFER pShader; LPD3DXBUFFER pErrorMsgs; @@ -289,17 +290,4 @@ void CDriverD3D::disableHardwarePixelProgram() _PixelProgram = false; } -// *************************************************************************** - -uint CDriverD3D::getMaxTexturesForEffects() const -{ - H_AUTO_D3D(CDriverD3D_getMaxTexturesForEffects) - - // we use ps_2_0 profile for direct3D ASM pixel program, then 16 texture samplers are available - if(!strcmp(CPixelProgram::getPixelASMProfile(), "ps_2_0")) - return 16; - - return 0; -} - } // NL3D From 72035e936afa62d9af9651b84f7867a2216826ef Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 19 Jun 2013 02:33:19 +0200 Subject: [PATCH 014/313] Removed unneeded parser related code from the opengl pixel program implementation --- code/nel/include/nel/3d/pixel_program.h | 8 + code/nel/src/3d/driver/opengl/driver_opengl.h | 2 +- .../opengl/driver_opengl_pixel_program.cpp | 194 ++++++------------ code/nel/src/3d/pixel_program.cpp | 2 +- 4 files changed, 78 insertions(+), 128 deletions(-) diff --git a/code/nel/include/nel/3d/pixel_program.h b/code/nel/include/nel/3d/pixel_program.h index fd39a76bc..811dbe0e1 100644 --- a/code/nel/include/nel/3d/pixel_program.h +++ b/code/nel/include/nel/3d/pixel_program.h @@ -65,8 +65,16 @@ public: /// Destructor virtual ~CPixelProgram (); + /// Get the program + inline const std::string& getProgram() const { return _Program; }; + /// The driver informations. For the driver implementation only. NLMISC::CRefPtr _DrvInfo; + +protected: + + /// The progam + std::string _Program; }; diff --git a/code/nel/src/3d/driver/opengl/driver_opengl.h b/code/nel/src/3d/driver/opengl/driver_opengl.h index 25503d9c6..f53f6f535 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl.h +++ b/code/nel/src/3d/driver/opengl/driver_opengl.h @@ -1344,7 +1344,7 @@ private: /// \name Pixel program implementation // @{ bool activeARBPixelProgram (CPixelProgram *program); - // TODO_REMOVE_PARSER bool setupARBPixelProgram (const CPixelProgramParser::CPProgram &parsedProgram, GLuint id/*, bool &specularWritten*/); + bool setupARBPixelProgram (const CPixelProgram *program, GLuint id/*, bool &specularWritten*/); //@} diff --git a/code/nel/src/3d/driver/opengl/driver_opengl_pixel_program.cpp b/code/nel/src/3d/driver/opengl/driver_opengl_pixel_program.cpp index a0439f3b3..eff54736f 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl_pixel_program.cpp +++ b/code/nel/src/3d/driver/opengl/driver_opengl_pixel_program.cpp @@ -90,45 +90,21 @@ bool CDriverGL::activeARBPixelProgram(CPixelProgram *program) // Program setuped ? if (program->_DrvInfo==NULL) { - /* TODO_REMOVE_PARSER - // Insert into driver list. (so it is deleted when driver is deleted). - ItPixelPrgDrvInfoPtrList it= _PixelPrgDrvInfos.insert(_PixelPrgDrvInfos.end()); + ItPixelPrgDrvInfoPtrList it= _PixelPrgDrvInfos.insert(_PixelPrgDrvInfos.end(), (NL3D::IPixelProgramDrvInfos*)NULL); // Create a driver info *it = drvInfo = new CPixelProgamDrvInfosGL (this, it); // Set the pointer program->_DrvInfo=drvInfo; - - std::string asmProgram; - CPixelProgramParser::CPProgram parsedProgram; - if(program->isEffectProgram()) - { - asmProgram = program->getProgram(); - - CPPParserD3D parser; - // try to parse the program - std::string errorOutput; - bool result = parser.parse(asmProgram.c_str(), parsedProgram, errorOutput); - if (!result) - { - nlwarning("Unable to parse a pixel program."); - #ifdef NL_DEBUG - nlerror(errorOutput.c_str()); - #endif - return false; - } - } - if(!setupARBPixelProgram(parsedProgram, drvInfo->ID)) + if(!setupARBPixelProgram(program, drvInfo->ID)) { delete drvInfo; program->_DrvInfo = NULL; _PixelPrgDrvInfos.erase(it); return false; } - - */ } else { @@ -152,19 +128,14 @@ bool CDriverGL::activeARBPixelProgram(CPixelProgram *program) return true; } -// TODO_REMOVE_PARSER -#if 0 + // *************************************************************************** -bool CDriverGL::setupARBPixelProgram (const CPixelProgramParser::CPProgram &inParsedProgram, GLuint id/*, bool &specularWritten*/) +bool CDriverGL::setupARBPixelProgram (const CPixelProgram *program, GLuint id/*, bool &specularWritten*/) { H_AUTO_OGL(CDriverGL_setupARBPixelProgram) - // convert from proprietary format to ARB_pixel_program code - CPixelProgramConversionARB vpConvertARB; - std::string code; - if(!vpConvertARB.convert(inParsedProgram, code)) return false; + const std::string &code = program->getProgram(); - // nglBindProgramARB( GL_FRAGMENT_PROGRAM_ARB, id); glGetError(); nglProgramStringARB( GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, code.size(), code.c_str() ); @@ -175,10 +146,10 @@ bool CDriverGL::setupARBPixelProgram (const CPixelProgramParser::CPProgram &inPa { GLint position; glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &position); - nlassert(position != -1) // there was an error.. + nlassert(position != -1); // there was an error.. nlassert(position < (GLint) code.size()); uint line = 0; - const char *lineStart = code.c_str(); + const char *lineStart = program->getProgram().c_str(); for(uint k = 0; k < (uint) position; ++k) { if (code[k] == '\n') @@ -209,7 +180,6 @@ bool CDriverGL::setupARBPixelProgram (const CPixelProgramParser::CPProgram &inPa } return true; } -#endif // *************************************************************************** @@ -217,12 +187,8 @@ void CDriverGL::setPixelProgramConstant (uint index, float f0, float f1, float f { H_AUTO_OGL(CDriverGL_setPixelProgramConstant) - //if(_LastSetuppedVP && _LastSetuppedVP->isEffectProgram()) - if (_LastSetuppedPP) // TODO_REMOVE_EFFECTS - { - if (_Extensions.ARBFragmentProgram) - nglProgramEnvParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, index, f0, f1, f2, f3); - } + if (_Extensions.ARBFragmentProgram) + nglProgramEnvParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, index, f0, f1, f2, f3); } @@ -232,12 +198,8 @@ void CDriverGL::setPixelProgramConstant (uint index, double d0, double d1, doubl { H_AUTO_OGL(CDriverGL_setPixelProgramConstant) - // if(_LastSetuppedVP && _LastSetuppedVP->isEffectProgram()) - if (_LastSetuppedPP) // TODO_REMOVE_EFFECTS - { - if (_Extensions.ARBFragmentProgram) - nglProgramEnvParameter4dARB(GL_FRAGMENT_PROGRAM_ARB, index, d0, d1, d2, d3); - } + if (_Extensions.ARBFragmentProgram) + nglProgramEnvParameter4dARB(GL_FRAGMENT_PROGRAM_ARB, index, d0, d1, d2, d3); } @@ -247,12 +209,8 @@ void CDriverGL::setPixelProgramConstant (uint index, const NLMISC::CVector& valu { H_AUTO_OGL(CDriverGL_setPixelProgramConstant) - // if(_LastSetuppedVP && _LastSetuppedVP->isEffectProgram()) - if (_LastSetuppedPP) // TODO_REMOVE_EFFECTS - { - if (_Extensions.ARBFragmentProgram) - nglProgramEnvParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, index, value.x, value.y, value.z, 0); - } + if (_Extensions.ARBFragmentProgram) + nglProgramEnvParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, index, value.x, value.y, value.z, 0); } @@ -262,12 +220,8 @@ void CDriverGL::setPixelProgramConstant (uint index, const NLMISC::CVectorD& val { H_AUTO_OGL(CDriverGL_setPixelProgramConstant) - // if(_LastSetuppedVP && _LastSetuppedVP->isEffectProgram()) - if (_LastSetuppedPP) // TODO_REMOVE_EFFECTS - { - if (_Extensions.ARBFragmentProgram) - nglProgramEnvParameter4dARB(GL_FRAGMENT_PROGRAM_ARB, index, value.x, value.y, value.z, 0); - } + if (_Extensions.ARBFragmentProgram) + nglProgramEnvParameter4dARB(GL_FRAGMENT_PROGRAM_ARB, index, value.x, value.y, value.z, 0); } @@ -276,15 +230,11 @@ void CDriverGL::setPixelProgramConstant (uint index, uint num, const float *src) { H_AUTO_OGL(CDriverGL_setPixelProgramConstant) - // if(_LastSetuppedVP && _LastSetuppedVP->isEffectProgram()) - if (_LastSetuppedPP) // TODO_REMOVE_EFFECTS + if (_Extensions.ARBFragmentProgram) { - if (_Extensions.ARBFragmentProgram) - { - for(uint k = 0; k < num; ++k) - { - nglProgramEnvParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, index + k, src + 4 * k); - } + for(uint k = 0; k < num; ++k) + { + nglProgramEnvParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, index + k, src + 4 * k); } } } @@ -294,15 +244,11 @@ void CDriverGL::setPixelProgramConstant (uint index, uint num, const double *src { H_AUTO_OGL(CDriverGL_setPixelProgramConstant) - // if(_LastSetuppedVP && _LastSetuppedVP->isEffectProgram()) - if (_LastSetuppedPP) // TODO_REMOVE_EFFECTS + if (_Extensions.ARBFragmentProgram) { - if (_Extensions.ARBFragmentProgram) - { - for(uint k = 0; k < num; ++k) - { - nglProgramEnvParameter4dvARB(GL_FRAGMENT_PROGRAM_ARB, index + k, src + 4 * k); - } + for(uint k = 0; k < num; ++k) + { + nglProgramEnvParameter4dvARB(GL_FRAGMENT_PROGRAM_ARB, index + k, src + 4 * k); } } } @@ -313,59 +259,55 @@ void CDriverGL::setPixelProgramConstantMatrix (uint index, IDriver::TMatrix matr { H_AUTO_OGL(CDriverGL_setPixelProgramConstantMatrix) - // if(_LastSetuppedVP && _LastSetuppedVP->isEffectProgram()) - if (_LastSetuppedPP) // TODO_REMOVE_EFFECTS + if (_Extensions.ARBFragmentProgram) { - if (_Extensions.ARBFragmentProgram) + + // First, ensure that the render setup is correctly setuped. + refreshRenderSetup(); + CMatrix mat; + switch (matrix) { - - // First, ensure that the render setup is correctly setuped. - refreshRenderSetup(); - CMatrix mat; - switch (matrix) - { - case IDriver::ModelView: - mat = _ModelViewMatrix; + case IDriver::ModelView: + mat = _ModelViewMatrix; + break; + case IDriver::Projection: + { + refreshProjMatrixFromGL(); + mat = _GLProjMat; + } + break; + case IDriver::ModelViewProjection: + refreshProjMatrixFromGL(); + mat = _GLProjMat * _ModelViewMatrix; + break; + default: break; - case IDriver::Projection: - { - refreshProjMatrixFromGL(); - mat = _GLProjMat; - } - break; - case IDriver::ModelViewProjection: - refreshProjMatrixFromGL(); - mat = _GLProjMat * _ModelViewMatrix; - break; - default: - break; - } - - switch(transform) - { - case IDriver::Identity: break; - case IDriver::Inverse: - mat.invert(); - break; - case IDriver::Transpose: - mat.transpose(); - break; - case IDriver::InverseTranspose: - mat.invert(); - mat.transpose(); - break; - default: - break; - } - mat.transpose(); - float matDatas[16]; - mat.get(matDatas); - - nglProgramEnvParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, index, matDatas); - nglProgramEnvParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, index + 1, matDatas + 4); - nglProgramEnvParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, index + 2, matDatas + 8); - nglProgramEnvParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, index + 3, matDatas + 12); } + + switch(transform) + { + case IDriver::Identity: break; + case IDriver::Inverse: + mat.invert(); + break; + case IDriver::Transpose: + mat.transpose(); + break; + case IDriver::InverseTranspose: + mat.invert(); + mat.transpose(); + break; + default: + break; + } + mat.transpose(); + float matDatas[16]; + mat.get(matDatas); + + nglProgramEnvParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, index, matDatas); + nglProgramEnvParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, index + 1, matDatas + 4); + nglProgramEnvParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, index + 2, matDatas + 8); + nglProgramEnvParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, index + 3, matDatas + 12); } } diff --git a/code/nel/src/3d/pixel_program.cpp b/code/nel/src/3d/pixel_program.cpp index b7cba51d0..320bfa541 100644 --- a/code/nel/src/3d/pixel_program.cpp +++ b/code/nel/src/3d/pixel_program.cpp @@ -48,7 +48,7 @@ IPixelProgramDrvInfos::~IPixelProgramDrvInfos () // *************************************************************************** -CPixelProgram::CPixelProgram(const char* program) +CPixelProgram::CPixelProgram(const char* program) : _Program(program) { } From 0c1bfe0f638e85006ac4f0f2cfb4188d5d5d41b3 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 19 Jun 2013 02:36:57 +0200 Subject: [PATCH 015/313] Cleanup more unneeded code --- .../driver_direct3d_pixel_program.cpp | 7 +- .../opengl/driver_opengl_pixel_program.cpp | 185 ------------------ 2 files changed, 1 insertion(+), 191 deletions(-) diff --git a/code/nel/src/3d/driver/direct3d/driver_direct3d_pixel_program.cpp b/code/nel/src/3d/driver/direct3d/driver_direct3d_pixel_program.cpp index f2255f88b..08ac5d74b 100644 --- a/code/nel/src/3d/driver/direct3d/driver_direct3d_pixel_program.cpp +++ b/code/nel/src/3d/driver/direct3d/driver_direct3d_pixel_program.cpp @@ -81,12 +81,7 @@ bool CDriverD3D::activePixelProgram(CPixelProgram *program) // Create a driver info structure program->_DrvInfo = *itPix; - std::string dest; - /* TODO_REMOVE - if(program->isEffectProgram()) - { - dest = program->getProgram(); - }*/ + const std::string &dest = program->getProgram(); LPD3DXBUFFER pShader; LPD3DXBUFFER pErrorMsgs; diff --git a/code/nel/src/3d/driver/opengl/driver_opengl_pixel_program.cpp b/code/nel/src/3d/driver/opengl/driver_opengl_pixel_program.cpp index eff54736f..18ce82846 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl_pixel_program.cpp +++ b/code/nel/src/3d/driver/opengl/driver_opengl_pixel_program.cpp @@ -311,189 +311,4 @@ void CDriverGL::setPixelProgramConstantMatrix (uint index, IDriver::TMatrix matr } } -// TODO_REMOVE_PARSER -#if 0 - -// *************************************************************************** -// ***************** CPixelProgramConversionARB ***************************** -// *************************************************************************** - -const char * CPixelProgramConversionARB::ARBPixelProgramInputRegisterToName[CPPOperand::InputRegisterCount] = -{ - "color.primary", - "color.secondary", - "texcoord[0]", - "texcoord[1]", - "texcoord[2]", - "texcoord[3]", - "texcoord[4]", - "texcoord[5]", - "texcoord[6]", - "texcoord[7]", -}; - -// *************************************************************************** -const char * CPixelProgramConversionARB::ARBPixelProgramOutputRegisterToName[CPPOperand::OutputRegisterCount] = -{ - "color", - "depth" -}; - -// *************************************************************************** -bool CPixelProgramConversionARB::convert(const CPixelProgramParser::CPProgram &inParsedProgram, std::string & code) -{ - CPixelProgramParser::TPProgram parsedProgram = inParsedProgram._Program; - // - code = "!!ARBfp1.0\n"; - // declare temporary registers - GLint glMaxTempVar; - nglGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB, GL_MAX_PROGRAM_TEMPORARIES_ARB, &glMaxTempVar); - uint usedTempVar = inParsedProgram.getUsedVariablesNb(); - if(usedTempVar>glMaxTempVar) - { - nlwarning(" Used temporary registers number is superior to maximum ARB_FRAGMENT_PROGRAM temporaries registers."); - return false; - } - ARBProgramTemporaryRegisters(code, usedTempVar); - // declare constant register - if(!CProgramConversionARB::constantRegisters(inParsedProgram._Constants, code)) return false; - - for(uint k = 0; k < parsedProgram.size(); ++k) - { - std::string instr; - ARBPixelProgramDumpInstr(parsedProgram[k], instr); - code += instr + "\r\n"; - } - code += "END\n"; - - return true; -} - -// *************************************************************************** -// Dump an instruction in a string -void CPixelProgramConversionARB::ARBPixelProgramDumpInstr(const CPPInstruction &instr, std::string &out) -{ - nlassert(instr.Opcode.PPOp < CPPInstruction::OpcodeCount); - // Special case for EXP with a scalar output argument (y component) -> translate to FRC - - out = std::string(); - switch(instr.Opcode.PPOp) - { - case CPPInstruction::ADD: - out = "ADD"; - break; - case CPPInstruction::DP3: - out = "DP3"; - break; - case CPPInstruction::DP4: - out = "DP4"; - break; - case CPPInstruction::EXP: - out = "EXP"; - break; - case CPPInstruction::FRC: - out = "FRC"; - break; - case CPPInstruction::LOG: - out = "LOG"; - break; - case CPPInstruction::MAD: - out = "MAD"; - break; - case CPPInstruction::MAX: - out = "MAX"; - break; - case CPPInstruction::MIN: - out = "MIN"; - break; - case CPPInstruction::MOV: - out = "MOV"; - break; - case CPPInstruction::MUL: - out = "MUL"; - break; - case CPPInstruction::RCP: - out = "RCP"; - break; - case CPPInstruction::RSQ: - out = "RSQ"; - break; - case CPPInstruction::SUB: - out = "SUB"; - break; - case CPPInstruction::ABS: - out = "ABS"; - break; - case CPPInstruction::CMP: - out = "CMP"; - break; - case CPPInstruction::CRS: - out = "XPD"; - break; - case CPPInstruction::LRP: - out = "LRP"; - break; - case CPPInstruction::POW: - out = "POW"; - break; - case CPPInstruction::TEX: - out = "TEX"; - break; - case CPPInstruction::TEXB: - out = "TXB"; - break; - case CPPInstruction::TEXP: - out = "TXP"; - break; - default: - nlwarning("no match with a ARB Pixel Program Operator"); - break; - } - - if(instr.Sat) out += "_SAT"; - out += " "; - uint nbOp = instr.getNumUsedSrc(); - std::string destOperand; - ARBPixelProgramDumpOperand(instr.Dest, true, destOperand); - out += destOperand; - for(uint k = 0; k < nbOp; ++k) - { - out += ", "; - std::string srcOperand; - ARBPixelProgramDumpOperand(instr.getSrc(k), false, srcOperand); - out += srcOperand; - } - out +="; \n"; -} - -// *************************************************************************** -void CPixelProgramConversionARB::ARBPixelProgramDumpOperand(const CPPOperand &op, bool destOperand, std::string &out) -{ - out = op.Negate ? " -" : " "; - switch(op.Type) - { - case CPPOperand::Variable: out += "R" + NLMISC::toString(op.Value.VariableValue); break; - case CPPOperand::Constant: - out += "c["; - out += NLMISC::toString(op.Value.ConstantValue) + "]"; - break; - case CPPOperand::InputRegister: out += string("fragment.") + ARBPixelProgramInputRegisterToName[(uint) op.Value.InputRegisterValue]; break; - case CPPOperand::OutputRegister: - nlassert(op.Value.OutputRegisterValue < CVPOperand::OutputRegisterCount); - out += "result." + std::string(ARBPixelProgramOutputRegisterToName[op.Value.OutputRegisterValue]); - break; - case CPPOperand::Sampler2DRegister: - out += string("texture[") + op.Value.SamplerValue + "], 2D"; - break; - case CPPOperand::Sampler3DRegister: - out += string("texture[") + op.Value.SamplerValue + "], 3D"; - break; - default: - break; - } - ARBProgramSuffix(op, destOperand, out); -} - -#endif - } // NL3D From f534bac6e1547567085482859657057740120a93 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 19 Jun 2013 02:59:32 +0200 Subject: [PATCH 016/313] Add test for ARBfp1.0 fragment program in snowballs (it works) --- code/snowballs2/client/src/commands.cpp | 35 +++++++++++++++++++ code/snowballs2/client/src/snowballs_config.h | 7 +++- 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/code/snowballs2/client/src/commands.cpp b/code/snowballs2/client/src/commands.cpp index 70dd3b247..f5acf0b80 100644 --- a/code/snowballs2/client/src/commands.cpp +++ b/code/snowballs2/client/src/commands.cpp @@ -40,6 +40,12 @@ #include "snowballs_client.h" #include "interface.h" +#if SBCLIENT_DEV_PIXEL_PROGRAM +#include +#include +#include +#endif + // // Namespaces // @@ -237,6 +243,12 @@ void cbUpdateCommands (CConfigFile::CVar &var) else nlwarning ("Unknown variable update %s", var.Name.c_str()); } +#if SBCLIENT_DEV_PIXEL_PROGRAM +namespace { +CPixelProgram *a_DevPixelProgram; +} +#endif + void initCommands() { // Add the keyboard listener in the event server @@ -278,6 +290,15 @@ void initCommands() CommandsMaterial.initUnlit(); CommandsMaterial.setBlendFunc(UMaterial::srcalpha, UMaterial::invsrcalpha); CommandsMaterial.setBlend(true); + +#if SBCLIENT_DEV_PIXEL_PROGRAM + static const char *program = + "!!ARBfp1.0\n" + "PARAM red = {1.0, 0.0, 0.0, 1.0};\n" + "MOV result.color, red;\n" + "END\n"; + a_DevPixelProgram = new CPixelProgram(program); +#endif } void updateCommands() @@ -301,8 +322,18 @@ void updateCommands() float y0 = CommandsBoxY - CommandsBoxBorderY; float x1 = CommandsBoxX + CommandsBoxWidth + CommandsBoxBorderX; float y1 = CommandsBoxY + CommandsBoxHeight + CommandsBoxBorderY; + +#if SBCLIENT_DEV_PIXEL_PROGRAM + NL3D::IDriver *d = dynamic_cast(Driver)->getDriver(); + d->activePixelProgram(a_DevPixelProgram); +#endif + Driver->drawQuad(CQuad(CVector(x0, y0, 0), CVector(x1, y0, 0), CVector(x1, y1, 0), CVector(x0, y1, 0)), CommandsMaterial); +#if SBCLIENT_DEV_PIXEL_PROGRAM + d->activePixelProgram(NULL); +#endif + // Set the text context TextContext->setHotSpot (UTextContext::BottomLeft); TextContext->setColor (CommandsFrontColor); @@ -334,6 +365,10 @@ void clearCommands () void releaseCommands() { +#if SBCLIENT_DEV_PIXEL_PROGRAM + delete a_DevPixelProgram; + a_DevPixelProgram = NULL; +#endif // Remove the displayers CommandsLog.removeDisplayer(&CommandsDisplayer); #ifndef NL_RELEASE diff --git a/code/snowballs2/client/src/snowballs_config.h b/code/snowballs2/client/src/snowballs_config.h index edd282718..9565fe4cf 100644 --- a/code/snowballs2/client/src/snowballs_config.h +++ b/code/snowballs2/client/src/snowballs_config.h @@ -37,7 +37,11 @@ #define SBCLIENT_ERASE_LOG true // version number -#define SBCLIENT_VERSION "2.1.551" +// 2.1 +// - Bloom +// 2.2 +// - OculusVR support +#define SBCLIENT_VERSION "2.2" @@ -45,6 +49,7 @@ #define SBCLIENT_DEV_SOUND 0 #define SBCLIENT_DEV_STEREO 0 #define SBCLIENT_DEV_MEMLEAK 0 +#define SBCLIENT_DEV_PIXEL_PROGRAM 1 From 9c5fabf615313a9bf2afbf0a86c106a0bc631c9c Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 19 Jun 2013 04:03:32 +0200 Subject: [PATCH 017/313] Add test for ps.1.1 pixel program in snowballs (it works too now) --- code/nel/include/nel/3d/material.h | 8 +++++++- code/snowballs2/client/src/commands.cpp | 13 +++++++++++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/code/nel/include/nel/3d/material.h b/code/nel/include/nel/3d/material.h index a7ca18bff..b9f063af1 100644 --- a/code/nel/include/nel/3d/material.h +++ b/code/nel/include/nel/3d/material.h @@ -171,7 +171,12 @@ public: * - Alpha of texture in stage 0 is blended with alpha of texture in stage 1. Blend done with the alpha color of each * stage and the whole is multiplied by the alpha in color vertex [AT0*ADiffuseCol+AT1*(1-ADiffuseCol)]*AStage * - RGB still unchanged - * + * Water : + * - Water + * PostProcessing : + * - For internal use only when a pixel program is set manually through activePixelProgram. + * - Only textures are set by CMaterial (probably does not work yet), the rest must be set manually. + * - May be replaced in the future by some generic shader system. */ enum TShader { Normal=0, Bump, @@ -183,6 +188,7 @@ public: PerPixelLightingNoSpec, Cloud, Water, + PostProcessing, shaderCount}; /// \name Texture Env Modes. diff --git a/code/snowballs2/client/src/commands.cpp b/code/snowballs2/client/src/commands.cpp index f5acf0b80..fe33e566a 100644 --- a/code/snowballs2/client/src/commands.cpp +++ b/code/snowballs2/client/src/commands.cpp @@ -44,6 +44,7 @@ #include #include #include +#include #endif // @@ -292,12 +293,17 @@ void initCommands() CommandsMaterial.setBlend(true); #if SBCLIENT_DEV_PIXEL_PROGRAM - static const char *program = + CommandsMaterial.getObjectPtr()->setShader(NL3D::CMaterial::PostProcessing); + static const char *program_arbfp10 = "!!ARBfp1.0\n" "PARAM red = {1.0, 0.0, 0.0, 1.0};\n" "MOV result.color, red;\n" "END\n"; - a_DevPixelProgram = new CPixelProgram(program); + static const char *program_ps10 = + "ps.1.1\n" + "def c0, 1.0, 0.0, 0.0, 1.0\n" + "mov r0, c0\n"; + a_DevPixelProgram = new CPixelProgram(program_ps10); #endif } @@ -326,11 +332,14 @@ void updateCommands() #if SBCLIENT_DEV_PIXEL_PROGRAM NL3D::IDriver *d = dynamic_cast(Driver)->getDriver(); d->activePixelProgram(a_DevPixelProgram); + bool fogEnabled = d->fogEnabled(); + d->enableFog(false); #endif Driver->drawQuad(CQuad(CVector(x0, y0, 0), CVector(x1, y0, 0), CVector(x1, y1, 0), CVector(x0, y1, 0)), CommandsMaterial); #if SBCLIENT_DEV_PIXEL_PROGRAM + d->enableFog(fogEnabled); d->activePixelProgram(NULL); #endif From 3865e6e56f52cc1074972317affe43e616f9cc90 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 19 Jun 2013 05:03:47 +0200 Subject: [PATCH 018/313] Add function to check which pixel program profiles are available on a driver --- code/nel/include/nel/3d/driver.h | 20 ++++++++++++++++++- code/nel/src/3d/driver.cpp | 2 +- .../3d/driver/direct3d/driver_direct3d.cpp | 9 +++++---- .../src/3d/driver/direct3d/driver_direct3d.h | 5 ++--- .../direct3d/driver_direct3d_material.cpp | 6 +++--- .../driver_direct3d_pixel_program.cpp | 9 ++++++++- code/nel/src/3d/driver/opengl/driver_opengl.h | 1 + .../opengl/driver_opengl_pixel_program.cpp | 7 ++++++- code/snowballs2/client/src/commands.cpp | 12 +++++++---- 9 files changed, 53 insertions(+), 18 deletions(-) diff --git a/code/nel/include/nel/3d/driver.h b/code/nel/include/nel/3d/driver.h index 9a079d95d..9e77e31d6 100644 --- a/code/nel/include/nel/3d/driver.h +++ b/code/nel/include/nel/3d/driver.h @@ -142,6 +142,23 @@ public: */ enum TMatrixCount { MaxModelMatrix= 16 }; + enum TPixelProgramProfile + { + // direct3d + ps_1_1 = 0xD3D00101, + ps_1_2 = 0xD3D00102, + ps_1_3 = 0xD3D00103, + ps_1_4 = 0xD3D00104, + ps_2_0 = 0xD3D00200, + ps_2_x = 0xD3D00201, // not sure... + ps_3_0 = 0xD3D00300, + + // opengl + arbfp1 = 0x061A0100, // made up values + fp20 = 0x06100200, + fp30 = 0x06100300, + fp40 = 0x06100400, + }; protected: @@ -1015,7 +1032,8 @@ public: /** * Does the driver supports pixel programs ? */ - virtual bool isPixelProgramSupported () const =0; + virtual bool isPixelProgramSupported() const =0; + virtual bool isPixelProgramSupported(TPixelProgramProfile profile) const =0; diff --git a/code/nel/src/3d/driver.cpp b/code/nel/src/3d/driver.cpp index d44079749..5a08da982 100644 --- a/code/nel/src/3d/driver.cpp +++ b/code/nel/src/3d/driver.cpp @@ -33,7 +33,7 @@ namespace NL3D { // *************************************************************************** -const uint32 IDriver::InterfaceVersion = 0x6b; // added anisotropic filter +const uint32 IDriver::InterfaceVersion = 0x6c; // pixel program interface // *************************************************************************** IDriver::IDriver() : _SyncTexDrvInfos( "IDriver::_SyncTexDrvInfos" ) diff --git a/code/nel/src/3d/driver/direct3d/driver_direct3d.cpp b/code/nel/src/3d/driver/direct3d/driver_direct3d.cpp index 26cf628e0..e33075b60 100644 --- a/code/nel/src/3d/driver/direct3d/driver_direct3d.cpp +++ b/code/nel/src/3d/driver/direct3d/driver_direct3d.cpp @@ -1551,14 +1551,15 @@ bool CDriverD3D::setDisplay(nlWindow wnd, const GfxMode& mode, bool show, bool r #endif // NL_FORCE_TEXTURE_STAGE_COUNT _VertexProgram = !_DisableHardwareVertexProgram && ((caps.VertexShaderVersion&0xffff) >= 0x0100); - _PixelProgram = !_DisableHardwarePixelProgram && (caps.PixelShaderVersion&0xffff) >= 0x0101; - _PixelShader = !_DisableHardwarePixelShader && (caps.PixelShaderVersion&0xffff) >= 0x0101; + _PixelProgramVersion = _DisableHardwareVertexProgram ? 0x0000 : caps.PixelShaderVersion & 0xffff; + nldebug("Pixel Program Version: %i.%i", (uint32)((_PixelProgramVersion & 0xFF00) >> 8), (uint32)(_PixelProgramVersion & 0xFF)); + _PixelProgram = _PixelProgramVersion >= 0x0101; _MaxVerticesByVertexBufferHard = caps.MaxVertexIndex; _MaxLight = caps.MaxActiveLights; if(_MaxLight > 0xFF) _MaxLight = 3; - if (_PixelShader) + if (_PixelProgram) { _MaxNumPerStageConstantLighted = _NbNeLTextureStages; _MaxNumPerStageConstantUnlighted = _NbNeLTextureStages; @@ -3626,7 +3627,7 @@ void CDriverD3D::CVertexProgramPtrState::apply(CDriverD3D *driver) void CDriverD3D::CPixelShaderPtrState::apply(CDriverD3D *driver) { H_AUTO_D3D(CDriverD3D_CPixelShaderPtrState); - if (!driver->supportPixelShaders()) return; + if (!driver->isPixelProgramSupported()) return; driver->_DeviceInterface->SetPixelShader(PixelShader); } diff --git a/code/nel/src/3d/driver/direct3d/driver_direct3d.h b/code/nel/src/3d/driver/direct3d/driver_direct3d.h index 7668bd63c..465689838 100644 --- a/code/nel/src/3d/driver/direct3d/driver_direct3d.h +++ b/code/nel/src/3d/driver/direct3d/driver_direct3d.h @@ -1008,6 +1008,7 @@ public: // Vertex program virtual bool isVertexProgramSupported () const; virtual bool isPixelProgramSupported () const; + virtual bool isPixelProgramSupported (TPixelProgramProfile profile) const; virtual bool isVertexProgramEmulated () const; virtual bool activeVertexProgram (CVertexProgram *program); virtual bool activePixelProgram (CPixelProgram *program); @@ -1065,8 +1066,6 @@ public: uint32 getMaxVertexIndex() const { return _MaxVertexIndex; } - bool supportPixelShaders() const { return _PixelShader; } - // *** Inline info uint inlGetNumTextStages() const { return _NbNeLTextureStages; } @@ -2232,7 +2231,7 @@ private: bool _TextureCubeSupported; bool _VertexProgram; bool _PixelProgram; - bool _PixelShader; + uint16 _PixelProgramVersion; bool _DisableHardwareVertexProgram; bool _DisableHardwarePixelProgram; bool _DisableHardwareVertexArrayAGP; diff --git a/code/nel/src/3d/driver/direct3d/driver_direct3d_material.cpp b/code/nel/src/3d/driver/direct3d/driver_direct3d_material.cpp index a7d218e79..eb550c2cc 100644 --- a/code/nel/src/3d/driver/direct3d/driver_direct3d_material.cpp +++ b/code/nel/src/3d/driver/direct3d/driver_direct3d_material.cpp @@ -567,7 +567,7 @@ bool CDriverD3D::setupMaterial(CMaterial &mat) normalShaderDesc.TexEnvMode[stage] = mat.getTexEnvMode(uint8(stage)); } - if (_PixelShader) + if (_PixelProgram) { #ifdef NL_DEBUG_D3D // Check, should not occured @@ -933,7 +933,7 @@ bool CDriverD3D::setupMaterial(CMaterial &mat) activeShader (NULL); /* If unlighted trick is needed, set the shader later */ - if (!pShader->NeedsConstantForDiffuse && _PixelShader) + if (!pShader->NeedsConstantForDiffuse && _PixelProgram) setPixelShader (pShader->PixelShader); } break; @@ -2019,7 +2019,7 @@ void CDriverD3D::endMaterialMultiPass() bool CDriverD3D::supportCloudRenderSinglePass () const { H_AUTO_D3D(CDriver3D_supportCloudRenderSinglePass); - return _PixelShader; + return _PixelProgram; } // *************************************************************************** diff --git a/code/nel/src/3d/driver/direct3d/driver_direct3d_pixel_program.cpp b/code/nel/src/3d/driver/direct3d/driver_direct3d_pixel_program.cpp index 08ac5d74b..2d7303589 100644 --- a/code/nel/src/3d/driver/direct3d/driver_direct3d_pixel_program.cpp +++ b/code/nel/src/3d/driver/direct3d/driver_direct3d_pixel_program.cpp @@ -56,10 +56,17 @@ CPixelProgramDrvInfosD3D::~CPixelProgramDrvInfosD3D() bool CDriverD3D::isPixelProgramSupported () const { - H_AUTO_D3D(CDriverD3D_isPixelProgramSupported ) + H_AUTO_D3D(CDriverD3D_isPixelProgramSupported) return _PixelProgram; } +bool CDriverD3D::isPixelProgramSupported (TPixelProgramProfile profile) const +{ + H_AUTO_D3D(CDriverD3D_isPixelProgramSupported_profile) + return ((profile & 0xFFFF0000) == 0xD3D00000) + && (_PixelProgramVersion >= (uint16)(profile & 0x0000FFFF)); +} + // *************************************************************************** bool CDriverD3D::activePixelProgram(CPixelProgram *program) diff --git a/code/nel/src/3d/driver/opengl/driver_opengl.h b/code/nel/src/3d/driver/opengl/driver_opengl.h index f53f6f535..3f7487937 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl.h +++ b/code/nel/src/3d/driver/opengl/driver_opengl.h @@ -1305,6 +1305,7 @@ private: bool isVertexProgramSupported () const; bool isPixelProgramSupported () const; + bool isPixelProgramSupported (TPixelProgramProfile profile) const; bool isVertexProgramEmulated () const; bool activeVertexProgram (CVertexProgram *program); bool activePixelProgram (CPixelProgram *program); diff --git a/code/nel/src/3d/driver/opengl/driver_opengl_pixel_program.cpp b/code/nel/src/3d/driver/opengl/driver_opengl_pixel_program.cpp index 18ce82846..5cc3316c1 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl_pixel_program.cpp +++ b/code/nel/src/3d/driver/opengl/driver_opengl_pixel_program.cpp @@ -57,11 +57,16 @@ CPixelProgamDrvInfosGL::CPixelProgamDrvInfosGL (CDriverGL *drv, ItPixelPrgDrvInf } // *************************************************************************** -bool CDriverGL::isPixelProgramSupported () const +bool CDriverGL::isPixelProgramSupported() const { H_AUTO_OGL(CPixelProgamDrvInfosGL_isPixelProgramSupported) return _Extensions.ARBFragmentProgram; } +bool CDriverGL::isPixelProgramSupported(TPixelProgramProfile profile) const +{ + H_AUTO_OGL(CPixelProgamDrvInfosGL_isPixelProgramSupported_profile) + return profile == arbfp1 && _Extensions.ARBFragmentProgram; +} // *************************************************************************** bool CDriverGL::activePixelProgram(CPixelProgram *program) diff --git a/code/snowballs2/client/src/commands.cpp b/code/snowballs2/client/src/commands.cpp index fe33e566a..a92f296c8 100644 --- a/code/snowballs2/client/src/commands.cpp +++ b/code/snowballs2/client/src/commands.cpp @@ -246,7 +246,7 @@ void cbUpdateCommands (CConfigFile::CVar &var) #if SBCLIENT_DEV_PIXEL_PROGRAM namespace { -CPixelProgram *a_DevPixelProgram; +CPixelProgram *a_DevPixelProgram = NULL; } #endif @@ -294,16 +294,20 @@ void initCommands() #if SBCLIENT_DEV_PIXEL_PROGRAM CommandsMaterial.getObjectPtr()->setShader(NL3D::CMaterial::PostProcessing); - static const char *program_arbfp10 = + static const char *program_arbfp1 = "!!ARBfp1.0\n" "PARAM red = {1.0, 0.0, 0.0, 1.0};\n" "MOV result.color, red;\n" "END\n"; - static const char *program_ps10 = + static const char *program_ps_1_1 = "ps.1.1\n" "def c0, 1.0, 0.0, 0.0, 1.0\n" "mov r0, c0\n"; - a_DevPixelProgram = new CPixelProgram(program_ps10); + NL3D::IDriver *d = dynamic_cast(Driver)->getDriver(); + if (d->isPixelProgramSupported(IDriver::arbfp1)) + a_DevPixelProgram = new CPixelProgram(program_arbfp1); + if (d->isPixelProgramSupported(IDriver::ps_1_1)) + a_DevPixelProgram = new CPixelProgram(program_ps_1_1); #endif } From 4cc4a84578e88cd664b8464100fcb94bf191cf49 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 19 Jun 2013 16:18:09 +0200 Subject: [PATCH 019/313] Fix linux compile --- .../opengl/driver_opengl_pixel_program.cpp | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/code/nel/src/3d/driver/opengl/driver_opengl_pixel_program.cpp b/code/nel/src/3d/driver/opengl/driver_opengl_pixel_program.cpp index 5cc3316c1..a70fbb42d 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl_pixel_program.cpp +++ b/code/nel/src/3d/driver/opengl/driver_opengl_pixel_program.cpp @@ -28,8 +28,8 @@ #include "stdopengl.h" #include "driver_opengl.h" -#include -#include +#include "nel/3d/index_buffer.h" +#include "nel/3d/pixel_program.h" #include // tmp @@ -37,11 +37,17 @@ using namespace std; using namespace NLMISC; - -//#define DEBUG_SETUP_EXT_VERTEX_SHADER namespace NL3D { + +#ifdef NL_STATIC +#ifdef USE_OPENGLES +namespace NLDRIVERGLES { +#else +namespace NLDRIVERGL { +#endif +#endif // *************************************************************************** CPixelProgamDrvInfosGL::CPixelProgamDrvInfosGL (CDriverGL *drv, ItPixelPrgDrvInfoPtrList it) : IPixelProgramDrvInfos (drv, it) @@ -316,4 +322,8 @@ void CDriverGL::setPixelProgramConstantMatrix (uint index, IDriver::TMatrix matr } } +#ifdef NL_STATIC +} // NLDRIVERGL/ES +#endif + } // NL3D From 1224d570372652b5e3a4542118438e263404142d Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 19 Jun 2013 16:22:48 +0200 Subject: [PATCH 020/313] Updated snowballs default config --- code/snowballs2/bin/snowballs_client_default.cfg | 8 ++++++++ 1 file changed, 8 insertions(+) mode change 100644 => 100755 code/snowballs2/bin/snowballs_client_default.cfg diff --git a/code/snowballs2/bin/snowballs_client_default.cfg b/code/snowballs2/bin/snowballs_client_default.cfg old mode 100644 new mode 100755 index 37b958820..ba9178e19 --- a/code/snowballs2/bin/snowballs_client_default.cfg +++ b/code/snowballs2/bin/snowballs_client_default.cfg @@ -256,6 +256,14 @@ RetrieverBankName = "snowballs.rbank"; GlobalRetrieverName = "snowballs.gr"; +////////////////////////////////////////////////////////////////////////////// +// Bloom Variables /////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +SquareBloom = 1; +DensityBloom = 128; + + ////////////////////////////////////////////////////////////////////////////// // Compass interface Variables /////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// From cfb9827b2c1fbc9be2d884cb1dd3a110d5eaea22 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 19 Jun 2013 20:47:23 +0200 Subject: [PATCH 021/313] Test texture with postprocessing material, seems to work with opengl --- code/nel/include/nel/3d/driver.h | 14 +++--- code/snowballs2/bin/pp_test.cg | 13 +++++ code/snowballs2/client/src/commands.cpp | 65 ++++++++++++++++++++----- 3 files changed, 75 insertions(+), 17 deletions(-) create mode 100644 code/snowballs2/bin/pp_test.cg diff --git a/code/nel/include/nel/3d/driver.h b/code/nel/include/nel/3d/driver.h index 9e77e31d6..58db119c0 100644 --- a/code/nel/include/nel/3d/driver.h +++ b/code/nel/include/nel/3d/driver.h @@ -144,7 +144,7 @@ public: enum TPixelProgramProfile { - // direct3d + // direct3d - 0xD3D0,major,minor ps_1_1 = 0xD3D00101, ps_1_2 = 0xD3D00102, ps_1_3 = 0xD3D00103, @@ -153,11 +153,13 @@ public: ps_2_x = 0xD3D00201, // not sure... ps_3_0 = 0xD3D00300, - // opengl - arbfp1 = 0x061A0100, // made up values - fp20 = 0x06100200, - fp30 = 0x06100300, - fp40 = 0x06100400, + // opengl - 0x0610,bitfield + arbfp1 = 0x06100001, // ARB_fragment_program + // fp20 = 0x061B0002, + fp30 = 0x06100004, // NV_fragment_program + fp40 = 0x06100008, // NV_fragment_program2 + gp4fp = 0x06100010, // NV_gpu_program4 + gp5fp = 0x06100020, // NV_gpu_program5 }; protected: diff --git a/code/snowballs2/bin/pp_test.cg b/code/snowballs2/bin/pp_test.cg new file mode 100644 index 000000000..f44c2d424 --- /dev/null +++ b/code/snowballs2/bin/pp_test.cg @@ -0,0 +1,13 @@ +void pp_test( + // Per fragment parameters + float2 texCoord : TEXCOORD0, + + // Fragment program constants + uniform sampler2D cTex0 : TEX0, + + // Output color + out float4 oCol : COLOR) +{ + oCol.rba = float3(1.0, 0.0, 1.0); + oCol.g = tex2D(cTex0, texCoord).g; +} \ No newline at end of file diff --git a/code/snowballs2/client/src/commands.cpp b/code/snowballs2/client/src/commands.cpp index a92f296c8..09f7b354c 100644 --- a/code/snowballs2/client/src/commands.cpp +++ b/code/snowballs2/client/src/commands.cpp @@ -45,6 +45,7 @@ #include #include #include +#include #endif // @@ -247,6 +248,7 @@ void cbUpdateCommands (CConfigFile::CVar &var) #if SBCLIENT_DEV_PIXEL_PROGRAM namespace { CPixelProgram *a_DevPixelProgram = NULL; +UTextureFile *a_NelLogo; } #endif @@ -294,20 +296,44 @@ void initCommands() #if SBCLIENT_DEV_PIXEL_PROGRAM CommandsMaterial.getObjectPtr()->setShader(NL3D::CMaterial::PostProcessing); - static const char *program_arbfp1 = + /*static const char *program_arbfp1 = "!!ARBfp1.0\n" - "PARAM red = {1.0, 0.0, 0.0, 1.0};\n" - "MOV result.color, red;\n" + "PARAM c[1] = { { 0, 1 } };\n" + "MOV result.color, c[0].xyxy;\n" "END\n"; - static const char *program_ps_1_1 = + static const char *program_ps_2_0 = "ps.1.1\n" "def c0, 1.0, 0.0, 0.0, 1.0\n" - "mov r0, c0\n"; + "mov r0, c0\n";*/ + a_NelLogo = Driver->createTextureFile("nel128.tga"); + CommandsMaterial.setTexture(dynamic_cast(a_NelLogo)); + /*CommandsMaterial.setBlend (false); + CommandsMaterial.setAlphaTest (false); + CommandsMaterial.setBlendFunc (UMaterial::one, UMaterial::zero); + CommandsMaterial.setZWrite(false); + CommandsMaterial.setZFunc(UMaterial::always); + CommandsMaterial.setDoubleSided(true);*/ + //CommandsMaterial.set + static const char *program_arbfp1 = + "!!ARBfp1.0\n" + "PARAM c[1] = { { 1, 0 } };\n" + "MOV result.color.xzw, c[0].xyyx;\n" + "TEX result.color.y, fragment.texcoord[0], texture[0], 2D;\n" + "END\n"; + static const char *program_ps_2_0 = + "ps_2_0\n" + "dcl_2d s0\n" + "def c0, 1.00000000, 0.00000000, 0, 0\n" + "dcl t0.xy\n" + "texld r0, t0, s0\n" + "mov r0.z, c0.y\n" + "mov r0.xw, c0.x\n" + "mov oC0, r0\n"; NL3D::IDriver *d = dynamic_cast(Driver)->getDriver(); if (d->isPixelProgramSupported(IDriver::arbfp1)) a_DevPixelProgram = new CPixelProgram(program_arbfp1); - if (d->isPixelProgramSupported(IDriver::ps_1_1)) - a_DevPixelProgram = new CPixelProgram(program_ps_1_1); + if (d->isPixelProgramSupported(IDriver::ps_2_0)) + a_DevPixelProgram = new CPixelProgram(program_ps_2_0); #endif } @@ -327,7 +353,11 @@ void updateCommands() // Display the background Driver->setMatrixMode2D11 (); +#if SBCLIENT_DEV_PIXEL_PROGRAM + CommandsMaterial.setColor(CRGBA::Blue); // Test to check which shader is displaying. +#else CommandsMaterial.setColor(CommandsBackColor); +#endif float x0 = CommandsBoxX - CommandsBoxBorderX; float y0 = CommandsBoxY - CommandsBoxBorderY; float x1 = CommandsBoxX + CommandsBoxWidth + CommandsBoxBorderX; @@ -338,13 +368,26 @@ void updateCommands() d->activePixelProgram(a_DevPixelProgram); bool fogEnabled = d->fogEnabled(); d->enableFog(false); -#endif + + // Driver->drawQuad(CQuad(CVector(x0, y0, 0), CVector(x1, y0, 0), CVector(x1, y1, 0), CVector(x0, y1, 0)), CommandsMaterial); + CQuadUV quadUV; + quadUV.V0 = CVector(x0, y0, 0); + quadUV.V1 = CVector(x1, y0, 0); + quadUV.V2 = CVector(x1, y1, 0); + quadUV.V3 = CVector(x0, y1, 0); + quadUV.Uv0 = CUV(0, 1); + quadUV.Uv1 = CUV(1, 1); + quadUV.Uv2 = CUV(1, 0); + quadUV.Uv3 = CUV(0, 0); + Driver->drawQuad(quadUV, CommandsMaterial); + //Driver->drawBitmap(x0, y0, x1 - x0, y1 - y0, *a_NelLogo); + + d->enableFog(fogEnabled); + d->activePixelProgram(NULL); +#else Driver->drawQuad(CQuad(CVector(x0, y0, 0), CVector(x1, y0, 0), CVector(x1, y1, 0), CVector(x0, y1, 0)), CommandsMaterial); -#if SBCLIENT_DEV_PIXEL_PROGRAM - d->enableFog(fogEnabled); - d->activePixelProgram(NULL); #endif // Set the text context From 0d1e405d3ea18bd83d6501e4fa1fbe0d725eadb5 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 19 Jun 2013 21:16:13 +0200 Subject: [PATCH 022/313] Activate textures for postprocessing material under direct3d --- code/nel/src/3d/driver/direct3d/driver_direct3d_material.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/nel/src/3d/driver/direct3d/driver_direct3d_material.cpp b/code/nel/src/3d/driver/direct3d/driver_direct3d_material.cpp index eb550c2cc..07ec5187b 100644 --- a/code/nel/src/3d/driver/direct3d/driver_direct3d_material.cpp +++ b/code/nel/src/3d/driver/direct3d/driver_direct3d_material.cpp @@ -648,7 +648,7 @@ bool CDriverD3D::setupMaterial(CMaterial &mat) // Must separate texture setup and texture activation in 2 "for"... // because setupTexture() may disable all stage. - if (matShader == CMaterial::Normal) + if (matShader == CMaterial::Normal || matShader == CMaterial::PostProcessing) { uint stage; for(stage=0 ; stage Date: Wed, 19 Jun 2013 21:31:29 +0200 Subject: [PATCH 023/313] Additional test, textures does not seem to work in ps_3_0 --- code/snowballs2/client/src/commands.cpp | 50 ++++++++++++++++--------- 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/code/snowballs2/client/src/commands.cpp b/code/snowballs2/client/src/commands.cpp index 09f7b354c..e1039a1ea 100644 --- a/code/snowballs2/client/src/commands.cpp +++ b/code/snowballs2/client/src/commands.cpp @@ -296,30 +296,22 @@ void initCommands() #if SBCLIENT_DEV_PIXEL_PROGRAM CommandsMaterial.getObjectPtr()->setShader(NL3D::CMaterial::PostProcessing); - /*static const char *program_arbfp1 = - "!!ARBfp1.0\n" - "PARAM c[1] = { { 0, 1 } };\n" - "MOV result.color, c[0].xyxy;\n" - "END\n"; - static const char *program_ps_2_0 = - "ps.1.1\n" - "def c0, 1.0, 0.0, 0.0, 1.0\n" - "mov r0, c0\n";*/ a_NelLogo = Driver->createTextureFile("nel128.tga"); CommandsMaterial.setTexture(dynamic_cast(a_NelLogo)); - /*CommandsMaterial.setBlend (false); - CommandsMaterial.setAlphaTest (false); - CommandsMaterial.setBlendFunc (UMaterial::one, UMaterial::zero); - CommandsMaterial.setZWrite(false); - CommandsMaterial.setZFunc(UMaterial::always); - CommandsMaterial.setDoubleSided(true);*/ - //CommandsMaterial.set static const char *program_arbfp1 = "!!ARBfp1.0\n" "PARAM c[1] = { { 1, 0 } };\n" "MOV result.color.xzw, c[0].xyyx;\n" "TEX result.color.y, fragment.texcoord[0], texture[0], 2D;\n" "END\n"; + static const char *program_ps_1_1 = + "ps.1.1\n" + "def c0, 0.000000, 0.000000, 1.000000, 0.000000\n" + "def c1, 1.000000, 0.000000, 0.000000, 0.000000\n" + "def c2, 0.000000, 1.000000, 0.000000, 0.000000\n" + "tex t0\n" + "mad r0.rgb, c2, t0, c1\n" + "mov r0.a, c0.b\n"; static const char *program_ps_2_0 = "ps_2_0\n" "dcl_2d s0\n" @@ -329,11 +321,35 @@ void initCommands() "mov r0.z, c0.y\n" "mov r0.xw, c0.x\n" "mov oC0, r0\n"; + static const char *program_ps_3_0 = + "ps_3_0\n" + "dcl_2d s0\n" + "def c0, 1.00000000, 0.00000000, 0, 0\n" + "dcl_texcoord0 v0.xy\n" + "mov oC0.xzw, c0.xyyx\n" + "texld oC0.y, v0, s0\n"; NL3D::IDriver *d = dynamic_cast(Driver)->getDriver(); if (d->isPixelProgramSupported(IDriver::arbfp1)) + { + nldebug("arbfp1"); a_DevPixelProgram = new CPixelProgram(program_arbfp1); - if (d->isPixelProgramSupported(IDriver::ps_2_0)) + } + /*else if (d->isPixelProgramSupported(IDriver::ps_3_0)) + { + nldebug("ps_3_0"); + a_DevPixelProgram = new CPixelProgram(program_ps_3_0); + // Textures do not seem to work with ps_3_0... + }*/ + else if (d->isPixelProgramSupported(IDriver::ps_2_0)) + { + nldebug("ps_2_0"); a_DevPixelProgram = new CPixelProgram(program_ps_2_0); + } + else if (d->isPixelProgramSupported(IDriver::ps_1_1)) + { + nldebug("ps_1_1"); + a_DevPixelProgram = new CPixelProgram(program_ps_1_1); + } #endif } From aa3462b7bc2c244950534055590f7ea19ea9b632 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 19 Jun 2013 22:41:03 +0200 Subject: [PATCH 024/313] Add support for fp40 with opengl --- code/nel/include/nel/3d/driver.h | 6 +++--- .../driver/opengl/driver_opengl_extension.cpp | 17 +++++++++++++---- .../3d/driver/opengl/driver_opengl_extension.h | 4 ++++ .../opengl/driver_opengl_pixel_program.cpp | 8 +++++++- code/snowballs2/client/src/commands.cpp | 17 ++++++++++++++++- 5 files changed, 43 insertions(+), 9 deletions(-) diff --git a/code/nel/include/nel/3d/driver.h b/code/nel/include/nel/3d/driver.h index 58db119c0..ee040df78 100644 --- a/code/nel/include/nel/3d/driver.h +++ b/code/nel/include/nel/3d/driver.h @@ -155,9 +155,9 @@ public: // opengl - 0x0610,bitfield arbfp1 = 0x06100001, // ARB_fragment_program - // fp20 = 0x061B0002, - fp30 = 0x06100004, // NV_fragment_program - fp40 = 0x06100008, // NV_fragment_program2 + // fp20 = 0x061B0002, // very limited and outdated, unnecessary + // fp30 = 0x06100004, // NV_fragment_program, now arbfp1, redundant + fp40 = 0x06100008, // NV_fragment_program2, arbfp1 with "OPTION NV_fragment_program2;\n" gp4fp = 0x06100010, // NV_gpu_program4 gp5fp = 0x06100020, // NV_gpu_program5 }; diff --git a/code/nel/src/3d/driver/opengl/driver_opengl_extension.cpp b/code/nel/src/3d/driver/opengl/driver_opengl_extension.cpp index e777d2696..3b771e1c1 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl_extension.cpp +++ b/code/nel/src/3d/driver/opengl/driver_opengl_extension.cpp @@ -1225,6 +1225,15 @@ static bool setupARBFragmentProgram(const char *glext) return true; } +// ********************************* +static bool setupNVFragmentProgram2(const char *glext) +{ + H_AUTO_OGL(setupNVFragmentProgram2); + CHECK_EXT("GL_NV_fragment_program2"); + + return true; +} + // *************************************************************************** static bool setupARBVertexBufferObject(const char *glext) { @@ -1563,13 +1572,15 @@ void registerGlExtensions(CGlExtensions &ext) // Check pixel program // Disable feature ??? - if(!ext.DisableHardwarePixelProgram) + if (!ext.DisableHardwarePixelProgram) { - ext.ARBFragmentProgram= setupARBFragmentProgram(glext); + ext.ARBFragmentProgram = setupARBFragmentProgram(glext); + ext.NVFragmentProgram2 = setupNVFragmentProgram2(glext); } else { ext.ARBFragmentProgram = false; + ext.NVFragmentProgram2 = false; } ext.OESDrawTexture = setupOESDrawTexture(glext); @@ -1582,14 +1593,12 @@ void registerGlExtensions(CGlExtensions &ext) ext.NVTextureShader = setupNVTextureShader(glext); ext.ATIEnvMapBumpMap = setupATIEnvMapBumpMap(glext); ext.ATIFragmentShader = setupATIFragmentShader(glext); - ext.ARBFragmentProgram = setupARBFragmentProgram(glext); } else { ext.ATIEnvMapBumpMap = false; ext.NVTextureShader = false; ext.ATIFragmentShader = false; - ext.ARBFragmentProgram = false; } // For now, the only way to know if emulation, is to test some extension which exist only on GeForce3. diff --git a/code/nel/src/3d/driver/opengl/driver_opengl_extension.h b/code/nel/src/3d/driver/opengl/driver_opengl_extension.h index d6d4974a6..fe7738fd8 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl_extension.h +++ b/code/nel/src/3d/driver/opengl/driver_opengl_extension.h @@ -103,6 +103,9 @@ struct CGlExtensions bool ARBTextureNonPowerOfTwo; bool ARBMultisample; + // NV Pixel Programs + bool NVFragmentProgram2; + bool OESDrawTexture; bool OESMapBuffer; @@ -208,6 +211,7 @@ public: result += NVTextureShader ? "NVTextureShader " : ""; result += ATIFragmentShader ? "ATIFragmentShader " : ""; result += ARBFragmentProgram ? "ARBFragmentProgram " : ""; + result += NVFragmentProgram2 ? "NVFragmentProgram2 " : ""; result += ARBVertexProgram ? "ARBVertexProgram " : ""; result += NVVertexProgram ? "NVVertexProgram " : ""; result += EXTVertexShader ? "EXTVertexShader " : ""; diff --git a/code/nel/src/3d/driver/opengl/driver_opengl_pixel_program.cpp b/code/nel/src/3d/driver/opengl/driver_opengl_pixel_program.cpp index a70fbb42d..4131c76b1 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl_pixel_program.cpp +++ b/code/nel/src/3d/driver/opengl/driver_opengl_pixel_program.cpp @@ -71,7 +71,13 @@ bool CDriverGL::isPixelProgramSupported() const bool CDriverGL::isPixelProgramSupported(TPixelProgramProfile profile) const { H_AUTO_OGL(CPixelProgamDrvInfosGL_isPixelProgramSupported_profile) - return profile == arbfp1 && _Extensions.ARBFragmentProgram; + switch (profile) + { + case arbfp1: + return _Extensions.ARBFragmentProgram; + case fp40: + return _Extensions.NVFragmentProgram2; + } } // *************************************************************************** diff --git a/code/snowballs2/client/src/commands.cpp b/code/snowballs2/client/src/commands.cpp index e1039a1ea..5c1274472 100644 --- a/code/snowballs2/client/src/commands.cpp +++ b/code/snowballs2/client/src/commands.cpp @@ -304,6 +304,16 @@ void initCommands() "MOV result.color.xzw, c[0].xyyx;\n" "TEX result.color.y, fragment.texcoord[0], texture[0], 2D;\n" "END\n"; + static const char *program_fp40 = + "!!ARBfp1.0\n" + "OPTION NV_fragment_program2;\n" + "PARAM c[1] = { { 1, 0 } };\n" + "TEMP RC;\n" + "TEMP HC;\n" + "OUTPUT oCol = result.color;\n" + "MOVR oCol.xzw, c[0].xyyx;\n" + "TEX oCol.y, fragment.texcoord[0], texture[0], 2D;\n" + "END\n"; static const char *program_ps_1_1 = "ps.1.1\n" "def c0, 0.000000, 0.000000, 1.000000, 0.000000\n" @@ -329,7 +339,12 @@ void initCommands() "mov oC0.xzw, c0.xyyx\n" "texld oC0.y, v0, s0\n"; NL3D::IDriver *d = dynamic_cast(Driver)->getDriver(); - if (d->isPixelProgramSupported(IDriver::arbfp1)) + if (d->isPixelProgramSupported(IDriver::fp40)) + { + nldebug("fp40"); + a_DevPixelProgram = new CPixelProgram(program_fp40); + } + else if (d->isPixelProgramSupported(IDriver::arbfp1)) { nldebug("arbfp1"); a_DevPixelProgram = new CPixelProgram(program_arbfp1); From 1ed0d46026dbb208c7c32f87e4ab4411686d8b4c Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 19 Jun 2013 23:34:40 +0200 Subject: [PATCH 025/313] Cleanup --- code/nel/include/nel/3d/driver.h | 22 +------------------ code/nel/include/nel/3d/material.h | 2 +- code/nel/include/nel/3d/pixel_program.h | 20 +++++++++++++++++ .../src/3d/driver/direct3d/driver_direct3d.h | 2 +- .../driver_direct3d_pixel_program.cpp | 2 +- code/nel/src/3d/driver/opengl/driver_opengl.h | 2 +- .../opengl/driver_opengl_pixel_program.cpp | 6 ++--- code/snowballs2/client/src/commands.cpp | 10 ++++----- 8 files changed, 33 insertions(+), 33 deletions(-) diff --git a/code/nel/include/nel/3d/driver.h b/code/nel/include/nel/3d/driver.h index ee040df78..3644fefca 100644 --- a/code/nel/include/nel/3d/driver.h +++ b/code/nel/include/nel/3d/driver.h @@ -142,26 +142,6 @@ public: */ enum TMatrixCount { MaxModelMatrix= 16 }; - enum TPixelProgramProfile - { - // direct3d - 0xD3D0,major,minor - ps_1_1 = 0xD3D00101, - ps_1_2 = 0xD3D00102, - ps_1_3 = 0xD3D00103, - ps_1_4 = 0xD3D00104, - ps_2_0 = 0xD3D00200, - ps_2_x = 0xD3D00201, // not sure... - ps_3_0 = 0xD3D00300, - - // opengl - 0x0610,bitfield - arbfp1 = 0x06100001, // ARB_fragment_program - // fp20 = 0x061B0002, // very limited and outdated, unnecessary - // fp30 = 0x06100004, // NV_fragment_program, now arbfp1, redundant - fp40 = 0x06100008, // NV_fragment_program2, arbfp1 with "OPTION NV_fragment_program2;\n" - gp4fp = 0x06100010, // NV_gpu_program4 - gp5fp = 0x06100020, // NV_gpu_program5 - }; - protected: CSynchronized _SyncTexDrvInfos; @@ -1035,7 +1015,7 @@ public: * Does the driver supports pixel programs ? */ virtual bool isPixelProgramSupported() const =0; - virtual bool isPixelProgramSupported(TPixelProgramProfile profile) const =0; + virtual bool isPixelProgramSupported(CPixelProgram::TProfile profile) const =0; diff --git a/code/nel/include/nel/3d/material.h b/code/nel/include/nel/3d/material.h index b9f063af1..bf00f9741 100644 --- a/code/nel/include/nel/3d/material.h +++ b/code/nel/include/nel/3d/material.h @@ -175,7 +175,7 @@ public: * - Water * PostProcessing : * - For internal use only when a pixel program is set manually through activePixelProgram. - * - Only textures are set by CMaterial (probably does not work yet), the rest must be set manually. + * - Only textures are set by CMaterial (does not work with ps_3_0 for some reason), the rest must be set manually. * - May be replaced in the future by some generic shader system. */ enum TShader { Normal=0, diff --git a/code/nel/include/nel/3d/pixel_program.h b/code/nel/include/nel/3d/pixel_program.h index 811dbe0e1..6e14f0f04 100644 --- a/code/nel/include/nel/3d/pixel_program.h +++ b/code/nel/include/nel/3d/pixel_program.h @@ -59,6 +59,26 @@ class CPixelProgram : public NLMISC::CRefCount { public: + enum TProfile + { + // direct3d - 0xD3D0,major,minor + ps_1_1 = 0xD3D00101, + ps_1_2 = 0xD3D00102, + ps_1_3 = 0xD3D00103, + ps_1_4 = 0xD3D00104, + ps_2_0 = 0xD3D00200, + ps_2_x = 0xD3D00201, // not sure... + ps_3_0 = 0xD3D00300, + + // opengl - 0x0610,bitfield + // fp20 = 0x061B0001, // very limited and outdated, unnecessary + // fp30 = 0x06100002, // NV_fragment_program, now arbfp1, redundant + arbfp1 = 0x06100004, // ARB_fragment_program + fp40 = 0x06100008, // NV_fragment_program2, arbfp1 with "OPTION NV_fragment_program2;\n" + gp4fp = 0x06100010, // NV_gpu_program4 + gp5fp = 0x06100020, // NV_gpu_program5 + }; + /// Constructor CPixelProgram (const char* program); diff --git a/code/nel/src/3d/driver/direct3d/driver_direct3d.h b/code/nel/src/3d/driver/direct3d/driver_direct3d.h index 465689838..cb6975725 100644 --- a/code/nel/src/3d/driver/direct3d/driver_direct3d.h +++ b/code/nel/src/3d/driver/direct3d/driver_direct3d.h @@ -1008,7 +1008,7 @@ public: // Vertex program virtual bool isVertexProgramSupported () const; virtual bool isPixelProgramSupported () const; - virtual bool isPixelProgramSupported (TPixelProgramProfile profile) const; + virtual bool isPixelProgramSupported (CPixelProgram::TProfile profile) const; virtual bool isVertexProgramEmulated () const; virtual bool activeVertexProgram (CVertexProgram *program); virtual bool activePixelProgram (CPixelProgram *program); diff --git a/code/nel/src/3d/driver/direct3d/driver_direct3d_pixel_program.cpp b/code/nel/src/3d/driver/direct3d/driver_direct3d_pixel_program.cpp index 2d7303589..d27019fff 100644 --- a/code/nel/src/3d/driver/direct3d/driver_direct3d_pixel_program.cpp +++ b/code/nel/src/3d/driver/direct3d/driver_direct3d_pixel_program.cpp @@ -60,7 +60,7 @@ bool CDriverD3D::isPixelProgramSupported () const return _PixelProgram; } -bool CDriverD3D::isPixelProgramSupported (TPixelProgramProfile profile) const +bool CDriverD3D::isPixelProgramSupported (CPixelProgram::TProfile profile) const { H_AUTO_D3D(CDriverD3D_isPixelProgramSupported_profile) return ((profile & 0xFFFF0000) == 0xD3D00000) diff --git a/code/nel/src/3d/driver/opengl/driver_opengl.h b/code/nel/src/3d/driver/opengl/driver_opengl.h index 3f7487937..a46d8c704 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl.h +++ b/code/nel/src/3d/driver/opengl/driver_opengl.h @@ -1305,7 +1305,7 @@ private: bool isVertexProgramSupported () const; bool isPixelProgramSupported () const; - bool isPixelProgramSupported (TPixelProgramProfile profile) const; + bool isPixelProgramSupported (CPixelProgram::TProfile profile) const; bool isVertexProgramEmulated () const; bool activeVertexProgram (CVertexProgram *program); bool activePixelProgram (CPixelProgram *program); diff --git a/code/nel/src/3d/driver/opengl/driver_opengl_pixel_program.cpp b/code/nel/src/3d/driver/opengl/driver_opengl_pixel_program.cpp index 4131c76b1..74a1127c6 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl_pixel_program.cpp +++ b/code/nel/src/3d/driver/opengl/driver_opengl_pixel_program.cpp @@ -68,14 +68,14 @@ bool CDriverGL::isPixelProgramSupported() const H_AUTO_OGL(CPixelProgamDrvInfosGL_isPixelProgramSupported) return _Extensions.ARBFragmentProgram; } -bool CDriverGL::isPixelProgramSupported(TPixelProgramProfile profile) const +bool CDriverGL::isPixelProgramSupported(CPixelProgram::TProfile profile) const { H_AUTO_OGL(CPixelProgamDrvInfosGL_isPixelProgramSupported_profile) switch (profile) { - case arbfp1: + case CPixelProgram::arbfp1: return _Extensions.ARBFragmentProgram; - case fp40: + case CPixelProgram::fp40: return _Extensions.NVFragmentProgram2; } } diff --git a/code/snowballs2/client/src/commands.cpp b/code/snowballs2/client/src/commands.cpp index 5c1274472..46c655a5b 100644 --- a/code/snowballs2/client/src/commands.cpp +++ b/code/snowballs2/client/src/commands.cpp @@ -339,28 +339,28 @@ void initCommands() "mov oC0.xzw, c0.xyyx\n" "texld oC0.y, v0, s0\n"; NL3D::IDriver *d = dynamic_cast(Driver)->getDriver(); - if (d->isPixelProgramSupported(IDriver::fp40)) + if (d->isPixelProgramSupported(CPixelProgram::fp40)) { nldebug("fp40"); a_DevPixelProgram = new CPixelProgram(program_fp40); } - else if (d->isPixelProgramSupported(IDriver::arbfp1)) + else if (d->isPixelProgramSupported(CPixelProgram::arbfp1)) { nldebug("arbfp1"); a_DevPixelProgram = new CPixelProgram(program_arbfp1); } - /*else if (d->isPixelProgramSupported(IDriver::ps_3_0)) + /*else if (d->isPixelProgramSupported(CPixelProgram::ps_3_0)) { nldebug("ps_3_0"); a_DevPixelProgram = new CPixelProgram(program_ps_3_0); // Textures do not seem to work with ps_3_0... }*/ - else if (d->isPixelProgramSupported(IDriver::ps_2_0)) + else if (d->isPixelProgramSupported(CPixelProgram::ps_2_0)) { nldebug("ps_2_0"); a_DevPixelProgram = new CPixelProgram(program_ps_2_0); } - else if (d->isPixelProgramSupported(IDriver::ps_1_1)) + else if (d->isPixelProgramSupported(CPixelProgram::ps_1_1)) { nldebug("ps_1_1"); a_DevPixelProgram = new CPixelProgram(program_ps_1_1); From 1f680259312b652fdaad14b8b7d88138797e03dd Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 19 Jun 2013 23:49:39 +0200 Subject: [PATCH 026/313] Rename some functions to follow the same naming convention of others --- code/nel/include/nel/3d/driver.h | 10 +++++----- code/nel/src/3d/driver/direct3d/driver_direct3d.cpp | 8 ++++---- code/nel/src/3d/driver/direct3d/driver_direct3d.h | 10 +++++----- .../driver/direct3d/driver_direct3d_pixel_program.cpp | 8 ++++---- .../driver/direct3d/driver_direct3d_vertex_program.cpp | 4 ++-- code/nel/src/3d/driver/opengl/driver_opengl.cpp | 10 +++++----- code/nel/src/3d/driver/opengl/driver_opengl.h | 10 +++++----- .../3d/driver/opengl/driver_opengl_pixel_program.cpp | 9 +++++---- .../3d/driver/opengl/driver_opengl_vertex_program.cpp | 4 ++-- code/nel/src/3d/landscape.cpp | 2 +- code/nel/src/3d/landscapevb_allocator.cpp | 2 +- code/nel/src/3d/meshvp_per_pixel_light.cpp | 2 +- code/nel/src/3d/meshvp_wind_tree.cpp | 4 ++-- code/nel/src/3d/ps_particle_basic.cpp | 10 +++++----- code/nel/src/3d/vegetablevb_allocator.cpp | 2 +- code/nel/src/3d/water_model.cpp | 8 ++++---- code/nel/src/3d/water_shape.cpp | 2 +- code/ryzom/client/src/decal.cpp | 2 +- code/snowballs2/client/src/commands.cpp | 10 +++++----- 19 files changed, 59 insertions(+), 58 deletions(-) diff --git a/code/nel/include/nel/3d/driver.h b/code/nel/include/nel/3d/driver.h index 3644fefca..32953e58b 100644 --- a/code/nel/include/nel/3d/driver.h +++ b/code/nel/include/nel/3d/driver.h @@ -1004,7 +1004,7 @@ public: /** * Does the driver supports vertex programs ? */ - virtual bool isVertexProgramSupported () const =0; + virtual bool supportVertexProgram () const =0; /** * Does the driver supports vertex program, but emulated by CPU ? @@ -1014,8 +1014,8 @@ public: /** * Does the driver supports pixel programs ? */ - virtual bool isPixelProgramSupported() const =0; - virtual bool isPixelProgramSupported(CPixelProgram::TProfile profile) const =0; + virtual bool supportPixelProgram() const =0; + virtual bool supportPixelProgram(CPixelProgram::TProfile profile) const =0; @@ -1122,10 +1122,10 @@ public: /// test whether the device supports some form of texture shader. (could be limited to DX6 EMBM for example) virtual bool supportTextureShaders() const = 0; // Is the shader water supported ? If not, the driver caller should implement its own version - virtual bool isWaterShaderSupported() const = 0; + virtual bool supportWaterShader() const = 0; // /// test whether a texture addressing mode is supported - virtual bool isTextureAddrModeSupported(CMaterial::TTexAddressingMode mode) const = 0; + virtual bool supportTextureAddrMode(CMaterial::TTexAddressingMode mode) const = 0; /** setup the 2D matrix for the OffsetTexture, OffsetTextureScale and OffsetTexture addressing mode * It should be stored as the following * [a0 a1] diff --git a/code/nel/src/3d/driver/direct3d/driver_direct3d.cpp b/code/nel/src/3d/driver/direct3d/driver_direct3d.cpp index e33075b60..8f8901f5c 100644 --- a/code/nel/src/3d/driver/direct3d/driver_direct3d.cpp +++ b/code/nel/src/3d/driver/direct3d/driver_direct3d.cpp @@ -2996,7 +2996,7 @@ bool CDriverD3D::stretchRect(ITexture * srcText, NLMISC::CRect &srcRect, ITextur bool CDriverD3D::supportBloomEffect() const { - return isVertexProgramSupported(); + return supportVertexProgram(); } // *************************************************************************** @@ -3339,9 +3339,9 @@ uint COcclusionQueryD3D::getVisibleCount() } // *************************************************************************** -bool CDriverD3D::isWaterShaderSupported() const +bool CDriverD3D::supportWaterShader() const { - H_AUTO_D3D(CDriverD3D_isWaterShaderSupported); + H_AUTO_D3D(CDriverD3D_supportWaterShader); return _PixelShaderVersion >= D3DPS_VERSION(1, 1); } @@ -3627,7 +3627,7 @@ void CDriverD3D::CVertexProgramPtrState::apply(CDriverD3D *driver) void CDriverD3D::CPixelShaderPtrState::apply(CDriverD3D *driver) { H_AUTO_D3D(CDriverD3D_CPixelShaderPtrState); - if (!driver->isPixelProgramSupported()) return; + if (!driver->supportPixelProgram()) return; driver->_DeviceInterface->SetPixelShader(PixelShader); } diff --git a/code/nel/src/3d/driver/direct3d/driver_direct3d.h b/code/nel/src/3d/driver/direct3d/driver_direct3d.h index cb6975725..779a62a5c 100644 --- a/code/nel/src/3d/driver/direct3d/driver_direct3d.h +++ b/code/nel/src/3d/driver/direct3d/driver_direct3d.h @@ -973,9 +973,9 @@ public: virtual bool supportTextureShaders() const {return false;}; virtual bool supportMADOperator() const; // todo hulud d3d adressing mode - virtual bool isWaterShaderSupported() const; + virtual bool supportWaterShader() const; // todo hulud d3d adressing mode - virtual bool isTextureAddrModeSupported(CMaterial::TTexAddressingMode /* mode */) const {return false;}; + virtual bool supportTextureAddrMode(CMaterial::TTexAddressingMode /* mode */) const {return false;}; // todo hulud d3d adressing mode virtual void setMatrix2DForTextureOffsetAddrMode(const uint /* stage */, const float /* mat */[4]) {} @@ -1006,9 +1006,9 @@ public: virtual void endMaterialMultiPass(); // Vertex program - virtual bool isVertexProgramSupported () const; - virtual bool isPixelProgramSupported () const; - virtual bool isPixelProgramSupported (CPixelProgram::TProfile profile) const; + virtual bool supportVertexProgram () const; + virtual bool supportPixelProgram () const; + virtual bool supportPixelProgram (CPixelProgram::TProfile profile) const; virtual bool isVertexProgramEmulated () const; virtual bool activeVertexProgram (CVertexProgram *program); virtual bool activePixelProgram (CPixelProgram *program); diff --git a/code/nel/src/3d/driver/direct3d/driver_direct3d_pixel_program.cpp b/code/nel/src/3d/driver/direct3d/driver_direct3d_pixel_program.cpp index d27019fff..8234fc31d 100644 --- a/code/nel/src/3d/driver/direct3d/driver_direct3d_pixel_program.cpp +++ b/code/nel/src/3d/driver/direct3d/driver_direct3d_pixel_program.cpp @@ -54,15 +54,15 @@ CPixelProgramDrvInfosD3D::~CPixelProgramDrvInfosD3D() // *************************************************************************** -bool CDriverD3D::isPixelProgramSupported () const +bool CDriverD3D::supportPixelProgram () const { - H_AUTO_D3D(CDriverD3D_isPixelProgramSupported) + H_AUTO_D3D(CDriverD3D_supportPixelProgram) return _PixelProgram; } -bool CDriverD3D::isPixelProgramSupported (CPixelProgram::TProfile profile) const +bool CDriverD3D::supportPixelProgram (CPixelProgram::TProfile profile) const { - H_AUTO_D3D(CDriverD3D_isPixelProgramSupported_profile) + H_AUTO_D3D(CDriverD3D_supportPixelProgram_profile) return ((profile & 0xFFFF0000) == 0xD3D00000) && (_PixelProgramVersion >= (uint16)(profile & 0x0000FFFF)); } diff --git a/code/nel/src/3d/driver/direct3d/driver_direct3d_vertex_program.cpp b/code/nel/src/3d/driver/direct3d/driver_direct3d_vertex_program.cpp index ce6bda220..a758aa7b3 100644 --- a/code/nel/src/3d/driver/direct3d/driver_direct3d_vertex_program.cpp +++ b/code/nel/src/3d/driver/direct3d/driver_direct3d_vertex_program.cpp @@ -43,9 +43,9 @@ CVertexProgamDrvInfosD3D::~CVertexProgamDrvInfosD3D() // *************************************************************************** -bool CDriverD3D::isVertexProgramSupported () const +bool CDriverD3D::supportVertexProgram () const { - H_AUTO_D3D(CDriverD3D_isVertexProgramSupported ) + H_AUTO_D3D(CDriverD3D_supportVertexProgram ) return _VertexProgram; } diff --git a/code/nel/src/3d/driver/opengl/driver_opengl.cpp b/code/nel/src/3d/driver/opengl/driver_opengl.cpp index b3567ed90..42d8b4834 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl.cpp +++ b/code/nel/src/3d/driver/opengl/driver_opengl.cpp @@ -691,7 +691,7 @@ bool CDriverGL::stretchRect(ITexture * /* srcText */, NLMISC::CRect &/* srcRect // *************************************************************************** bool CDriverGL::supportBloomEffect() const { - return (isVertexProgramSupported() && supportFrameBufferObject() && supportPackedDepthStencil() && supportTextureRectangle()); + return (supportVertexProgram() && supportFrameBufferObject() && supportPackedDepthStencil() && supportTextureRectangle()); } // *************************************************************************** @@ -1539,9 +1539,9 @@ bool CDriverGL::supportTextureShaders() const } // *************************************************************************** -bool CDriverGL::isWaterShaderSupported() const +bool CDriverGL::supportWaterShader() const { - H_AUTO_OGL(CDriverGL_isWaterShaderSupported); + H_AUTO_OGL(CDriverGL_supportWaterShader); if(_Extensions.ARBFragmentProgram && ARBWaterShader[0] != 0) return true; @@ -1551,9 +1551,9 @@ bool CDriverGL::isWaterShaderSupported() const } // *************************************************************************** -bool CDriverGL::isTextureAddrModeSupported(CMaterial::TTexAddressingMode /* mode */) const +bool CDriverGL::supportTextureAddrMode(CMaterial::TTexAddressingMode /* mode */) const { - H_AUTO_OGL(CDriverGL_isTextureAddrModeSupported) + H_AUTO_OGL(CDriverGL_supportTextureAddrMode) if (_Extensions.NVTextureShader) { diff --git a/code/nel/src/3d/driver/opengl/driver_opengl.h b/code/nel/src/3d/driver/opengl/driver_opengl.h index a46d8c704..f88936a8a 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl.h +++ b/code/nel/src/3d/driver/opengl/driver_opengl.h @@ -601,9 +601,9 @@ public: // @{ virtual bool supportTextureShaders() const; - virtual bool isWaterShaderSupported() const; + virtual bool supportWaterShader() const; - virtual bool isTextureAddrModeSupported(CMaterial::TTexAddressingMode mode) const; + virtual bool supportTextureAddrMode(CMaterial::TTexAddressingMode mode) const; virtual void setMatrix2DForTextureOffsetAddrMode(const uint stage, const float mat[4]); // @} @@ -1303,9 +1303,9 @@ private: /// \name Vertex program interface // @{ - bool isVertexProgramSupported () const; - bool isPixelProgramSupported () const; - bool isPixelProgramSupported (CPixelProgram::TProfile profile) const; + bool supportVertexProgram () const; + bool supportPixelProgram () const; + bool supportPixelProgram (CPixelProgram::TProfile profile) const; bool isVertexProgramEmulated () const; bool activeVertexProgram (CVertexProgram *program); bool activePixelProgram (CPixelProgram *program); diff --git a/code/nel/src/3d/driver/opengl/driver_opengl_pixel_program.cpp b/code/nel/src/3d/driver/opengl/driver_opengl_pixel_program.cpp index 74a1127c6..2bcfc185d 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl_pixel_program.cpp +++ b/code/nel/src/3d/driver/opengl/driver_opengl_pixel_program.cpp @@ -63,14 +63,14 @@ CPixelProgamDrvInfosGL::CPixelProgamDrvInfosGL (CDriverGL *drv, ItPixelPrgDrvInf } // *************************************************************************** -bool CDriverGL::isPixelProgramSupported() const +bool CDriverGL::supportPixelProgram() const { - H_AUTO_OGL(CPixelProgamDrvInfosGL_isPixelProgramSupported) + H_AUTO_OGL(CPixelProgamDrvInfosGL_supportPixelProgram) return _Extensions.ARBFragmentProgram; } -bool CDriverGL::isPixelProgramSupported(CPixelProgram::TProfile profile) const +bool CDriverGL::supportPixelProgram(CPixelProgram::TProfile profile) const { - H_AUTO_OGL(CPixelProgamDrvInfosGL_isPixelProgramSupported_profile) + H_AUTO_OGL(CPixelProgamDrvInfosGL_supportPixelProgram_profile) switch (profile) { case CPixelProgram::arbfp1: @@ -78,6 +78,7 @@ bool CDriverGL::isPixelProgramSupported(CPixelProgram::TProfile profile) const case CPixelProgram::fp40: return _Extensions.NVFragmentProgram2; } + return false; } // *************************************************************************** diff --git a/code/nel/src/3d/driver/opengl/driver_opengl_vertex_program.cpp b/code/nel/src/3d/driver/opengl/driver_opengl_vertex_program.cpp index 5392bcbdd..1ddb42a7d 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl_vertex_program.cpp +++ b/code/nel/src/3d/driver/opengl/driver_opengl_vertex_program.cpp @@ -70,9 +70,9 @@ CVertexProgamDrvInfosGL::CVertexProgamDrvInfosGL (CDriverGL *drv, ItVtxPrgDrvInf // *************************************************************************** -bool CDriverGL::isVertexProgramSupported () const +bool CDriverGL::supportVertexProgram () const { - H_AUTO_OGL(CVertexProgamDrvInfosGL_isVertexProgramSupported) + H_AUTO_OGL(CVertexProgamDrvInfosGL_supportVertexProgram) return _Extensions.NVVertexProgram || _Extensions.EXTVertexShader || _Extensions.ARBVertexProgram; } diff --git a/code/nel/src/3d/landscape.cpp b/code/nel/src/3d/landscape.cpp index 31f1bb051..e842ae369 100644 --- a/code/nel/src/3d/landscape.cpp +++ b/code/nel/src/3d/landscape.cpp @@ -574,7 +574,7 @@ void CLandscape::setDriver(IDriver *drv) // Does the driver support VertexShader??? // only if VP supported by GPU. - _VertexShaderOk= (_Driver->isVertexProgramSupported() && !_Driver->isVertexProgramEmulated()); + _VertexShaderOk= (_Driver->supportVertexProgram() && !_Driver->isVertexProgramEmulated()); // Does the driver has sufficient requirements for Vegetable??? diff --git a/code/nel/src/3d/landscapevb_allocator.cpp b/code/nel/src/3d/landscapevb_allocator.cpp index 00cf78f1f..f57d6ef8d 100644 --- a/code/nel/src/3d/landscapevb_allocator.cpp +++ b/code/nel/src/3d/landscapevb_allocator.cpp @@ -82,7 +82,7 @@ void CLandscapeVBAllocator::updateDriver(IDriver *driver) deleteVertexProgram(); // Then rebuild VB format, and VertexProgram, if needed. // Do it only if VP supported by GPU. - setupVBFormatAndVertexProgram(_Driver->isVertexProgramSupported() && !_Driver->isVertexProgramEmulated()); + setupVBFormatAndVertexProgram(_Driver->supportVertexProgram() && !_Driver->isVertexProgramEmulated()); // must reallocate the VertexBuffer. if( _NumVerticesAllocated>0 ) diff --git a/code/nel/src/3d/meshvp_per_pixel_light.cpp b/code/nel/src/3d/meshvp_per_pixel_light.cpp index ab84492c4..dd06e951c 100644 --- a/code/nel/src/3d/meshvp_per_pixel_light.cpp +++ b/code/nel/src/3d/meshvp_per_pixel_light.cpp @@ -428,7 +428,7 @@ bool CMeshVPPerPixelLight::begin(IDriver *drv, { // test if supported by driver if (! - (drv->isVertexProgramSupported() + (drv->supportVertexProgram() && !drv->isVertexProgramEmulated() && drv->supportPerPixelLighting(SpecularLighting) ) diff --git a/code/nel/src/3d/meshvp_wind_tree.cpp b/code/nel/src/3d/meshvp_wind_tree.cpp index bf04c6096..579557012 100644 --- a/code/nel/src/3d/meshvp_wind_tree.cpp +++ b/code/nel/src/3d/meshvp_wind_tree.cpp @@ -287,7 +287,7 @@ inline void CMeshVPWindTree::setupPerInstanceConstants(IDriver *driver, CScene // *************************************************************************** bool CMeshVPWindTree::begin(IDriver *driver, CScene *scene, CMeshBaseInstance *mbi, const NLMISC::CMatrix &invertedModelMat, const NLMISC::CVector & /*viewerPos*/) { - if (!(driver->isVertexProgramSupported() && !driver->isVertexProgramEmulated())) return false; + if (!(driver->supportVertexProgram() && !driver->isVertexProgramEmulated())) return false; // precompute mesh @@ -367,7 +367,7 @@ bool CMeshVPWindTree::supportMeshBlockRendering() const // *************************************************************************** bool CMeshVPWindTree::isMBRVpOk(IDriver *driver) const { - return driver->isVertexProgramSupported() && !driver->isVertexProgramEmulated(); + return driver->supportVertexProgram() && !driver->isVertexProgramEmulated(); } // *************************************************************************** diff --git a/code/nel/src/3d/ps_particle_basic.cpp b/code/nel/src/3d/ps_particle_basic.cpp index 1cb57d2bc..c2d6b6357 100644 --- a/code/nel/src/3d/ps_particle_basic.cpp +++ b/code/nel/src/3d/ps_particle_basic.cpp @@ -786,7 +786,7 @@ void CPSMultiTexturedParticle::setupMaterial(ITexture *primary, IDriver *driver, /// if bump is used, the matrix must be setupped each time (not a material field) if (!_ForceBasicCaps && isMultiTextureEnabled() && _MainOp == EnvBumpMap) { - if (driver->isTextureAddrModeSupported(CMaterial::OffsetTexture)) + if (driver->supportTextureAddrMode(CMaterial::OffsetTexture)) { CTextureBump *tb = dynamic_cast((ITexture *) _Texture2); if (tb != NULL) @@ -858,7 +858,7 @@ void CPSMultiTexturedParticle::setupMaterial(ITexture *primary, IDriver *driver, } else { - if (!_ForceBasicCaps && (driver->isTextureAddrModeSupported(CMaterial::OffsetTexture) || driver->supportEMBM())) // envbumpmap supported ? + if (!_ForceBasicCaps && (driver->supportTextureAddrMode(CMaterial::OffsetTexture) || driver->supportEMBM())) // envbumpmap supported ? { CTextureBump *tb = dynamic_cast((ITexture *) _Texture2); if (tb != NULL) @@ -917,7 +917,7 @@ void CPSMultiTexturedParticle::setupMultiTexEnv(TOperator op, ITexture *tex1, IT mat.enableTexAddrMode(false); break; case EnvBumpMap: - if (drv.isTextureAddrModeSupported(CMaterial::OffsetTexture)) + if (drv.supportTextureAddrMode(CMaterial::OffsetTexture)) { mat.setTexture(0, tex2); mat.setTexture(1, tex1); @@ -1113,7 +1113,7 @@ void CPSMultiTexturedParticle::enumTexs(std::vector NL_PS_FUNC(CPSMultiTexturedParticle_enumTexs) if (_MainOp == EnvBumpMap && !_ForceBasicCaps) { - if (drv.isTextureAddrModeSupported(CMaterial::OffsetTexture) || drv.supportEMBM()) + if (drv.supportTextureAddrMode(CMaterial::OffsetTexture) || drv.supportEMBM()) { if (_Texture2) dest.push_back(_Texture2); } @@ -1132,7 +1132,7 @@ bool CPSMultiTexturedParticle::isAlternateTextureUsed(IDriver &driver) const NL_PS_FUNC(CPSMultiTexturedParticle_isAlternateTextureUsed) if (!isTouched() && areBasicCapsForcedLocal() == areBasicCapsForced()) return (_MultiTexState & AlternateTextureUsed) != 0; if (_MainOp != EnvBumpMap) return false; - return _ForceBasicCaps || (!driver.isTextureAddrModeSupported(CMaterial::OffsetTexture) && !driver.supportEMBM()); + return _ForceBasicCaps || (!driver.supportTextureAddrMode(CMaterial::OffsetTexture) && !driver.supportEMBM()); } } // NL3D diff --git a/code/nel/src/3d/vegetablevb_allocator.cpp b/code/nel/src/3d/vegetablevb_allocator.cpp index 9c801b179..efbd1f5a3 100644 --- a/code/nel/src/3d/vegetablevb_allocator.cpp +++ b/code/nel/src/3d/vegetablevb_allocator.cpp @@ -98,7 +98,7 @@ void CVegetableVBAllocator::updateDriver(IDriver *driver) _VBHardOk= false; // Driver must support VP. - nlassert(_Driver->isVertexProgramSupported()); + nlassert(_Driver->supportVertexProgram()); // must reallocate the VertexBuffer. if( _NumVerticesAllocated>0 ) diff --git a/code/nel/src/3d/water_model.cpp b/code/nel/src/3d/water_model.cpp index ddf3d35b2..e72d7bab9 100644 --- a/code/nel/src/3d/water_model.cpp +++ b/code/nel/src/3d/water_model.cpp @@ -61,7 +61,7 @@ void CWaterModel::setupVertexBuffer(CVertexBuffer &vb, uint numWantedVertices, I vb.setNumVertices(0); vb.setName("Water"); vb.setPreferredMemory(CVertexBuffer::AGPPreferred, false); - if (drv->isWaterShaderSupported()) + if (drv->supportWaterShader()) { vb.setVertexFormat(CVertexBuffer::PositionFlag); } @@ -377,7 +377,7 @@ void CWaterModel::traverseRender() #ifndef FORCE_SIMPLE_WATER_RENDER - if (!drv->isWaterShaderSupported()) + if (!drv->supportWaterShader()) #endif { doSimpleRender(drv); @@ -1363,7 +1363,7 @@ uint CWaterModel::getNumWantedVertices() uint CWaterModel::fillVB(void *datas, uint startTri, IDriver &drv) { H_AUTO( NL3D_Water_Render ); - if (drv.isWaterShaderSupported()) + if (drv.supportWaterShader()) { return fillVBHard(datas, startTri); } @@ -1657,7 +1657,7 @@ void CWaterModel::traverseRender() drv->setupModelMatrix(modelMat); bool isAbove = obsPos.z > getWorldMatrix().getPos().z; CVertexBuffer &vb = renderTrav.Scene->getWaterVB(); - if (drv->isWaterShaderSupported()) + if (drv->supportWaterShader()) { setupMaterialNVertexShader(drv, shape, obsPos, isAbove, zHeight); nlassert(vb.getNumVertices() > 0); diff --git a/code/nel/src/3d/water_shape.cpp b/code/nel/src/3d/water_shape.cpp index 0196294c3..227b85254 100644 --- a/code/nel/src/3d/water_shape.cpp +++ b/code/nel/src/3d/water_shape.cpp @@ -372,7 +372,7 @@ void CWaterShape::flushTextures (IDriver &driver, uint selectedTexture) /* if ( - (driver.supportTextureShaders() && driver.isTextureAddrModeSupported(CMaterial::OffsetTexture)) + (driver.supportTextureShaders() && driver.supportTextureAddrMode(CMaterial::OffsetTexture)) || driver.supportEMBM() ) { diff --git a/code/ryzom/client/src/decal.cpp b/code/ryzom/client/src/decal.cpp index 45c5311ac..7aa4b3c3a 100644 --- a/code/ryzom/client/src/decal.cpp +++ b/code/ryzom/client/src/decal.cpp @@ -560,7 +560,7 @@ void CDecalRenderList::renderAllDecals() NL3D::IDriver *drvInternal = ((CDriverUser *) Driver)->getDriver(); // static volatile bool forceNoVertexProgram = false; - if (drvInternal->isVertexProgramSupported() && !forceNoVertexProgram) + if (drvInternal->supportVertexProgram() && !forceNoVertexProgram) { //drvInternal->setConstantMatrix(0, NL3D::IDriver::ModelViewProjection, NL3D::IDriver::Identity); drvInternal->setConstant(7, _DistScale, _DistBias, 0.f, 1.f); diff --git a/code/snowballs2/client/src/commands.cpp b/code/snowballs2/client/src/commands.cpp index 46c655a5b..ed8a1dc7d 100644 --- a/code/snowballs2/client/src/commands.cpp +++ b/code/snowballs2/client/src/commands.cpp @@ -339,28 +339,28 @@ void initCommands() "mov oC0.xzw, c0.xyyx\n" "texld oC0.y, v0, s0\n"; NL3D::IDriver *d = dynamic_cast(Driver)->getDriver(); - if (d->isPixelProgramSupported(CPixelProgram::fp40)) + if (d->supportPixelProgram(CPixelProgram::fp40)) { nldebug("fp40"); a_DevPixelProgram = new CPixelProgram(program_fp40); } - else if (d->isPixelProgramSupported(CPixelProgram::arbfp1)) + else if (d->supportPixelProgram(CPixelProgram::arbfp1)) { nldebug("arbfp1"); a_DevPixelProgram = new CPixelProgram(program_arbfp1); } - /*else if (d->isPixelProgramSupported(CPixelProgram::ps_3_0)) + /*else if (d->supportPixelProgram(CPixelProgram::ps_3_0)) { nldebug("ps_3_0"); a_DevPixelProgram = new CPixelProgram(program_ps_3_0); // Textures do not seem to work with ps_3_0... }*/ - else if (d->isPixelProgramSupported(CPixelProgram::ps_2_0)) + else if (d->supportPixelProgram(CPixelProgram::ps_2_0)) { nldebug("ps_2_0"); a_DevPixelProgram = new CPixelProgram(program_ps_2_0); } - else if (d->isPixelProgramSupported(CPixelProgram::ps_1_1)) + else if (d->supportPixelProgram(CPixelProgram::ps_1_1)) { nldebug("ps_1_1"); a_DevPixelProgram = new CPixelProgram(program_ps_1_1); From 982bf804de601e7883d9f9a8304d718296dd9f83 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Thu, 20 Jun 2013 00:25:52 +0200 Subject: [PATCH 027/313] Added some notes --- code/nel/include/nel/3d/driver.h | 6 ++++-- code/nel/include/nel/3d/pixel_program.h | 5 +++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/code/nel/include/nel/3d/driver.h b/code/nel/include/nel/3d/driver.h index 32953e58b..14595e05c 100644 --- a/code/nel/include/nel/3d/driver.h +++ b/code/nel/include/nel/3d/driver.h @@ -1038,7 +1038,7 @@ public: virtual bool activePixelProgram (CPixelProgram *program) =0; /** - * Setup vertex program constant values. + * Setup vertex program constant (uniform) values. */ virtual void setConstant (uint index, float, float, float, float) =0; virtual void setConstant (uint index, double, double, double, double) =0; @@ -1048,9 +1048,10 @@ public: virtual void setConstant (uint index, uint num, const float *src) =0; /// setup several 4 double csts taken from the given tab virtual void setConstant (uint index, uint num, const double *src) =0; + // TODO: rename to setVertexProgramConstant /** - * Setup pixel program constant values. + * Setup pixel program constant (uniform) values. */ virtual void setPixelProgramConstant (uint index, float, float, float, float) =0; virtual void setPixelProgramConstant (uint index, double, double, double, double) =0; @@ -1060,6 +1061,7 @@ public: virtual void setPixelProgramConstant (uint index, uint num, const float *src) =0; /// setup several 4 double csts taken from the given tab virtual void setPixelProgramConstant (uint index, uint num, const double *src) =0; + // TODO: uint32 and sint32 uniform types supported in opengl from gp4fp and gp5fp and sint32 in d3d /** * Setup constants with a current matrix. diff --git a/code/nel/include/nel/3d/pixel_program.h b/code/nel/include/nel/3d/pixel_program.h index 6e14f0f04..183ad2d83 100644 --- a/code/nel/include/nel/3d/pixel_program.h +++ b/code/nel/include/nel/3d/pixel_program.h @@ -61,6 +61,11 @@ public: enum TProfile { + // TODO: + // If it's more useful, change this to a flags bitfield and + // change the d3d (and gl) code to generate the bitfield of + // supported modes instead of doing a >= version check. + // direct3d - 0xD3D0,major,minor ps_1_1 = 0xD3D00101, ps_1_2 = 0xD3D00102, From b45bdb88e6c84078d31cf622a0d3e25664229ccf Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 26 Jun 2013 02:45:49 +0200 Subject: [PATCH 028/313] List stereo devices, see #43 --- code/nel/include/nel/3d/stereo_ovr.h | 96 ++++++++ code/nel/src/3d/stereo_ovr.cpp | 209 ++++++++++++++++++ .../bin/snowballs_client_default.cfg | 13 +- code/snowballs2/client/src/camera.cpp | 40 ++++ .../client/src/snowballs_client.cpp | 32 ++- code/snowballs2/client/src/snowballs_config.h | 2 +- 6 files changed, 377 insertions(+), 15 deletions(-) create mode 100644 code/nel/include/nel/3d/stereo_ovr.h create mode 100644 code/nel/src/3d/stereo_ovr.cpp diff --git a/code/nel/include/nel/3d/stereo_ovr.h b/code/nel/include/nel/3d/stereo_ovr.h new file mode 100644 index 000000000..ce61c1c2e --- /dev/null +++ b/code/nel/include/nel/3d/stereo_ovr.h @@ -0,0 +1,96 @@ +/** + * \file stereo_ovr.h + * \brief CStereoOVR + * \date 2013-06-25 22:22GMT + * \author Jan Boon (Kaetemi) + * CStereoOVR + */ + +/* + * Copyright (C) 2013 by authors + * + * This file is part of NL3D. + * NL3D is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * NL3D 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 Affero General + * Public License for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with NL3D. If not, see + * . + * + * Linking this library statically or dynamically with other modules + * is making a combined work based on this library. Thus, the terms + * and conditions of the GNU General Public License cover the whole + * combination. + * + * As a special exception, the copyright holders of this library give + * you permission to link this library with the Oculus SDK to produce + * an executable, regardless of the license terms of the Oculus SDK, + * and distribute linked combinations including the two, provided that + * you also meet the terms and conditions of the license of the Oculus + * SDK. You must obey the GNU General Public License in all respects + * for all of the code used other than the Oculus SDK. If you modify + * this file, you may extend this exception to your version of the + * file, but you are not obligated to do so. If you do not wish to do + * so, delete this exception statement from your version. + */ + +#ifndef NL3D_STEREO_OVR_H +#define NL3D_STEREO_OVR_H +#include + +// STL includes + +// NeL includes +#include + +// Project includes + +namespace NL3D { + +struct CStereoDeviceInfo +{ +public: + uint8 Class; + uint8 Identifier; + NLMISC::CSmartPtr Factory; + + std::string Manufacturer; + std::string ProductName; +}; + +class CStereoOVRDevicePtr; + +/** + * \brief CStereoOVR + * \date 2013-06-25 22:22GMT + * \author Jan Boon (Kaetemi) + * CStereoOVR + */ +class CStereoOVR +{ +public: + CStereoOVR(const CStereoDeviceInfo &deviceInfo); + virtual ~CStereoOVR(); + + static void listDevices(std::vector &devicesOut); + static CStereoOVR *createDevice(const CStereoDeviceInfo &deviceInfo); + static bool isLibraryInUse(); + static void releaseLibrary(); + +private: + CStereoOVRDevicePtr *m_DevicePtr; + +}; /* class CStereoOVR */ + +} /* namespace NL3D */ + +#endif /* #ifndef NL3D_STEREO_OVR_H */ + +/* end of file */ diff --git a/code/nel/src/3d/stereo_ovr.cpp b/code/nel/src/3d/stereo_ovr.cpp new file mode 100644 index 000000000..c11d38774 --- /dev/null +++ b/code/nel/src/3d/stereo_ovr.cpp @@ -0,0 +1,209 @@ +/** + * \file stereo_ovr.cpp + * \brief CStereoOVR + * \date 2013-06-25 22:22GMT + * \author Jan Boon (Kaetemi) + * CStereoOVR + */ + +/* + * Copyright (C) 2013 by authors + * + * This file is part of NL3D. + * NL3D is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * NL3D 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 Affero General + * Public License for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with NL3D. If not, see + * . + * + * Linking this library statically or dynamically with other modules + * is making a combined work based on this library. Thus, the terms + * and conditions of the GNU General Public License cover the whole + * combination. + * + * As a special exception, the copyright holders of this library give + * you permission to link this library with the Oculus SDK to produce + * an executable, regardless of the license terms of the Oculus SDK, + * and distribute linked combinations including the two, provided that + * you also meet the terms and conditions of the license of the Oculus + * SDK. You must obey the GNU General Public License in all respects + * for all of the code used other than the Oculus SDK. If you modify + * this file, you may extend this exception to your version of the + * file, but you are not obligated to do so. If you do not wish to do + * so, delete this exception statement from your version. + */ + +#include +#include + +// STL includes + +// External includes +#include + +// NeL includes +// #include + +// Project includes + +using namespace std; +// using namespace NLMISC; + +namespace NL3D { + +namespace { + +class CStereoOVRLog : public OVR::Log +{ +public: + CStereoOVRLog(unsigned logMask = OVR::LogMask_All) : OVR::Log(logMask) + { + + } + + virtual void LogMessageVarg(OVR::LogMessageType messageType, const char* fmt, va_list argList) + { + if (NLMISC::INelContext::isContextInitialised()) + { + char buffer[MaxLogBufferMessageSize]; + FormatLog(buffer, MaxLogBufferMessageSize, messageType, fmt, argList); + if (IsDebugMessage(messageType)) + NLMISC::INelContext::getInstance().getDebugLog()->displayNL("OVR: %s", buffer); + else + NLMISC::INelContext::getInstance().getInfoLog()->displayNL("OVR: %s", buffer); + } + } +}; + +CStereoOVRLog *s_StereoOVRLog = NULL; +OVR::Ptr s_DeviceManager; + +class CStereoOVRSystem +{ +public: + ~CStereoOVRSystem() + { + Release(); + } + + void Init() + { + if (!s_StereoOVRLog) + { + nldebug("Initialize OVR"); + s_StereoOVRLog = new CStereoOVRLog(); + } + if (!OVR::System::IsInitialized()) + OVR::System::Init(s_StereoOVRLog); + if (!s_DeviceManager) + s_DeviceManager = OVR::DeviceManager::Create(); + } + + void Release() + { + if (s_DeviceManager) + { + nldebug("Release OVR"); + s_DeviceManager->Release(); + } + s_DeviceManager.Clear(); + if (OVR::System::IsInitialized()) + OVR::System::Destroy(); + if (s_StereoOVRLog) + nldebug("Release OVR Ok"); + delete s_StereoOVRLog; + s_StereoOVRLog = NULL; + } +}; + +CStereoOVRSystem s_StereoOVRSystem; + +class CStereoOVRDeviceHandle : public NLMISC::CRefCount +{ +public: + OVR::DeviceHandle DeviceHandle; +}; + +sint s_DeviceCounter = 0; + +} + +class CStereoOVRDevicePtr +{ +public: + OVR::Ptr HMDDevice; +}; + +CStereoOVR::CStereoOVR(const CStereoDeviceInfo &deviceInfo) +{ + ++s_DeviceCounter; + m_DevicePtr = new CStereoOVRDevicePtr(); + + // CStereoOVRDeviceHandle *handle = static_cast(deviceInfo.Factory.getPtr()); + // OVR::DeviceHandle dh = handle->DeviceHandle; + // dh.CreateDevice(); +} + +CStereoOVR::~CStereoOVR() +{ + // ... + + delete m_DevicePtr; + m_DevicePtr = NULL; + --s_DeviceCounter; +} + +void CStereoOVR::listDevices(std::vector &devicesOut) +{ + s_StereoOVRSystem.Init(); + OVR::DeviceEnumerator devices = s_DeviceManager->EnumerateDevices(); + uint8 id = 1; + do + { + CStereoDeviceInfo deviceInfoOut; + OVR::DeviceInfo deviceInfo; + if (devices.IsAvailable()) + { + devices.GetDeviceInfo(&deviceInfo); + CStereoOVRDeviceHandle *handle = new CStereoOVRDeviceHandle(); + deviceInfoOut.Factory = static_cast(handle); + handle->DeviceHandle = devices; + deviceInfoOut.Class = 1; // OVR::HMDDevice + deviceInfoOut.Identifier = id; + deviceInfoOut.Manufacturer = deviceInfo.Manufacturer; + deviceInfoOut.ProductName = deviceInfo.ProductName; + devicesOut.push_back(deviceInfoOut); + ++id; + } + + } while (devices.Next()); +} + +CStereoOVR *CStereoOVR::createDevice(const CStereoDeviceInfo &deviceInfo) +{ + return NULL; +} + +bool CStereoOVR::isLibraryInUse() +{ + nlassert(s_DeviceCounter >= 0); + return s_DeviceCounter > 0; +} + +void CStereoOVR::releaseLibrary() +{ + nlassert(s_DeviceCounter == 0); + s_StereoOVRSystem.Release(); +} + +} /* namespace NL3D */ + +/* end of file */ diff --git a/code/snowballs2/bin/snowballs_client_default.cfg b/code/snowballs2/bin/snowballs_client_default.cfg index ba9178e19..758bd8483 100755 --- a/code/snowballs2/bin/snowballs_client_default.cfg +++ b/code/snowballs2/bin/snowballs_client_default.cfg @@ -47,8 +47,8 @@ FpsSmoothing = 64; OpenGL = 1; // Resolution of the screen -ScreenWidth = 800; -ScreenHeight = 600; +ScreenWidth = 1280; +ScreenHeight = 800; ScreenDepth = 32; // If 1, run in fullscreen mode, 0 for windowed @@ -58,6 +58,15 @@ ScreenFull = 0; StartPoint = { 1840.0, -970.0, 0.0 }; +////////////////////////////////////////////////////////////////////////////// +// HMD Variables ///////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +HMDEnable = 1; +HMDDeviceId = 0; +HMDDevice = "Auto"; + + ////////////////////////////////////////////////////////////////////////////// // Sound Variables /////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// diff --git a/code/snowballs2/client/src/camera.cpp b/code/snowballs2/client/src/camera.cpp index b202fba63..d71ebc134 100644 --- a/code/snowballs2/client/src/camera.cpp +++ b/code/snowballs2/client/src/camera.cpp @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -33,6 +34,8 @@ #include #include +#include + #include "snowballs_client.h" #include "entities.h" #include "mouse_listener.h" @@ -67,12 +70,47 @@ static UInstance Sky = NULL; static UCloudScape *Clouds = NULL; +static CStereoOVR *s_StereoHMD = NULL; + // // Functions // void initCamera() { + if (ConfigFile->getVar("HMDEnable").asBool()) + { + std::vector devices; + CStereoOVR::listDevices(devices); + for (std::vector::iterator it(devices.begin()), end(devices.end()); it != end; ++it) + { + std::stringstream name; + name << std::string("[") << (uint32)it->Identifier << "] [" << it->Manufacturer << " - " << it->ProductName << "]"; + nlinfo("Stereo Device: %s", name.str().c_str()); + } + CStereoDeviceInfo *deviceInfo = NULL; + std::string hmdDeviceCfg = ConfigFile->getVar("HMDDevice").asString(); + if (hmdDeviceCfg == std::string("Auto") + && devices.begin() != devices.end()) + { + deviceInfo = &devices[0]; + } + else + { + uint8 hmdDeviceId = (ConfigFile->getVar("HMDDeviceId").asInt() & 0xFF); + for (std::vector::iterator it(devices.begin()), end(devices.end()); it != end; ++it) + { + std::stringstream name; + name << it->Manufacturer << " - " << it->ProductName; + if (name.str() == hmdDeviceCfg) + deviceInfo = &(*it); + if (hmdDeviceId == it->Identifier) + break; + } + } + //s_StereoHMD->createDevice( + } + // Set up directly the camera Camera = Scene->getCam(); Camera.setTransformMode (UTransformable::DirectMatrix); @@ -114,6 +152,8 @@ void releaseCamera() Driver->deleteScene(SkyScene); Scene->deleteInstance(Snow); VisualCollisionManager->deleteEntity(CamCollisionEntity); + + CStereoOVR::releaseLibrary(); } void updateCamera() diff --git a/code/snowballs2/client/src/snowballs_client.cpp b/code/snowballs2/client/src/snowballs_client.cpp index 1d7fefd60..e0f044174 100644 --- a/code/snowballs2/client/src/snowballs_client.cpp +++ b/code/snowballs2/client/src/snowballs_client.cpp @@ -356,6 +356,7 @@ void initIngame() //#ifdef NL_OS_WINDOWS // playMusic(SBCLIENT_MUSIC_WAIT); //#endif + displayLoadingState("Initialize"); // Create a scene Scene = Driver->createScene(false); @@ -366,7 +367,6 @@ void initIngame() CBloomEffect::instance().init(ConfigFile->getVar("OpenGL").asInt() == 1); CConfiguration::setAndCallback("SquareBloom", cbSquareBloom); CConfiguration::setAndCallback("DensityBloom", cbDensityBloom); - // Init the landscape using the previously created UScene displayLoadingState("Initialize Landscape"); initLandscape(); @@ -1141,21 +1141,29 @@ sint main(int argc, char **argv) FILE *f = _tfopen(_T(SBCLIENT_CONFIG_FILE_DEFAULT), _T("r")); if (!f) { - OutputDebugString(" ******************************** \n"); - OutputDebugString(" * CHANGING WORKING DIRECTORY * \n"); - OutputDebugString(" ******************************** \n\n"); - char cwd[256]; - _tgetcwd(cwd, 256); - tstring workdir(cwd); - workdir += "\\..\\bin\\"; - _tchdir(workdir.c_str()); - f = _tfopen(_T(SBCLIENT_CONFIG_FILE_DEFAULT), _T("r")); + f = _tfopen(_T(SBCLIENT_CONFIG_FILE), _T("r")); if (!f) { OutputDebugString(" ******************************** \n"); - OutputDebugString(" * DEFAULT CONFIG MISSING * \n"); + OutputDebugString(" * CHANGING WORKING DIRECTORY * \n"); OutputDebugString(" ******************************** \n\n"); - return EXIT_FAILURE; + char cwd[256]; + _tgetcwd(cwd, 256); + tstring workdir(cwd); + workdir = "R:\\build\\devw_x86\\bin\\Debug\\"; + _tchdir(workdir.c_str()); + f = _tfopen(_T(SBCLIENT_CONFIG_FILE_DEFAULT), _T("r")); + if (!f) + { + f = _tfopen(_T(SBCLIENT_CONFIG_FILE), _T("r")); + if (!f) + { + OutputDebugString(" ******************************** \n"); + OutputDebugString(" * DEFAULT CONFIG MISSING * \n"); + OutputDebugString(" ******************************** \n\n"); + return EXIT_FAILURE; + } + } } } fclose(f); diff --git a/code/snowballs2/client/src/snowballs_config.h b/code/snowballs2/client/src/snowballs_config.h index 9565fe4cf..fb304ba68 100644 --- a/code/snowballs2/client/src/snowballs_config.h +++ b/code/snowballs2/client/src/snowballs_config.h @@ -75,7 +75,7 @@ # ifndef SNOWBALLS_CONFIG # define SBCLIENT_CONFIG_FILE "snowballs_client.cfg" # else -# define SBCLIENT_CONFIG_FILE SNOWBALLS_CONFIG "client.cfg" +# define SBCLIENT_CONFIG_FILE SNOWBALLS_CONFIG "snowballs_client.cfg" # endif #endif From dc813a060e9f178a69d1496e7dd8f222e730d741 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 26 Jun 2013 02:55:15 +0200 Subject: [PATCH 029/313] Add library name to device name, re #43 --- code/nel/include/nel/3d/stereo_ovr.h | 1 + code/nel/src/3d/stereo_ovr.cpp | 1 + code/snowballs2/client/src/camera.cpp | 4 ++-- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/code/nel/include/nel/3d/stereo_ovr.h b/code/nel/include/nel/3d/stereo_ovr.h index ce61c1c2e..5f61c0595 100644 --- a/code/nel/include/nel/3d/stereo_ovr.h +++ b/code/nel/include/nel/3d/stereo_ovr.h @@ -61,6 +61,7 @@ public: uint8 Identifier; NLMISC::CSmartPtr Factory; + std::string Library; std::string Manufacturer; std::string ProductName; }; diff --git a/code/nel/src/3d/stereo_ovr.cpp b/code/nel/src/3d/stereo_ovr.cpp index c11d38774..40d94f901 100644 --- a/code/nel/src/3d/stereo_ovr.cpp +++ b/code/nel/src/3d/stereo_ovr.cpp @@ -177,6 +177,7 @@ void CStereoOVR::listDevices(std::vector &devicesOut) deviceInfoOut.Factory = static_cast(handle); handle->DeviceHandle = devices; deviceInfoOut.Class = 1; // OVR::HMDDevice + deviceInfoOut.Library = "Oculus SDK"; deviceInfoOut.Identifier = id; deviceInfoOut.Manufacturer = deviceInfo.Manufacturer; deviceInfoOut.ProductName = deviceInfo.ProductName; diff --git a/code/snowballs2/client/src/camera.cpp b/code/snowballs2/client/src/camera.cpp index d71ebc134..38db2d599 100644 --- a/code/snowballs2/client/src/camera.cpp +++ b/code/snowballs2/client/src/camera.cpp @@ -85,7 +85,7 @@ void initCamera() for (std::vector::iterator it(devices.begin()), end(devices.end()); it != end; ++it) { std::stringstream name; - name << std::string("[") << (uint32)it->Identifier << "] [" << it->Manufacturer << " - " << it->ProductName << "]"; + name << std::string("[") << (uint32)it->Identifier << "] [" << it->Library << " - " << it->Manufacturer << " - " << it->ProductName << "]"; nlinfo("Stereo Device: %s", name.str().c_str()); } CStereoDeviceInfo *deviceInfo = NULL; @@ -101,7 +101,7 @@ void initCamera() for (std::vector::iterator it(devices.begin()), end(devices.end()); it != end; ++it) { std::stringstream name; - name << it->Manufacturer << " - " << it->ProductName; + name << it->Library << " - " << it->Manufacturer << " - " << it->ProductName; if (name.str() == hmdDeviceCfg) deviceInfo = &(*it); if (hmdDeviceId == it->Identifier) From a7cf55c58e28acc0d6be6f9a4349a9120b55af1a Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 26 Jun 2013 04:57:58 +0200 Subject: [PATCH 030/313] Read sensor data and set camera in snowballs, ref #43 --- code/nel/include/nel/3d/stereo_ovr.h | 4 ++ code/nel/src/3d/stereo_ovr.cpp | 52 ++++++++++++++++--- code/snowballs2/client/src/camera.cpp | 20 ++++++- code/snowballs2/client/src/snowballs_config.h | 2 +- 4 files changed, 69 insertions(+), 9 deletions(-) diff --git a/code/nel/include/nel/3d/stereo_ovr.h b/code/nel/include/nel/3d/stereo_ovr.h index 5f61c0595..3858b046e 100644 --- a/code/nel/include/nel/3d/stereo_ovr.h +++ b/code/nel/include/nel/3d/stereo_ovr.h @@ -80,11 +80,15 @@ public: CStereoOVR(const CStereoDeviceInfo &deviceInfo); virtual ~CStereoOVR(); + virtual NLMISC::CQuat getOrientation(); + static void listDevices(std::vector &devicesOut); static CStereoOVR *createDevice(const CStereoDeviceInfo &deviceInfo); static bool isLibraryInUse(); static void releaseLibrary(); + bool isDeviceCreated(); + private: CStereoOVRDevicePtr *m_DevicePtr; diff --git a/code/nel/src/3d/stereo_ovr.cpp b/code/nel/src/3d/stereo_ovr.cpp index 40d94f901..44daae9ef 100644 --- a/code/nel/src/3d/stereo_ovr.cpp +++ b/code/nel/src/3d/stereo_ovr.cpp @@ -129,7 +129,7 @@ CStereoOVRSystem s_StereoOVRSystem; class CStereoOVRDeviceHandle : public NLMISC::CRefCount { public: - OVR::DeviceHandle DeviceHandle; + OVR::DeviceEnumerator DeviceHandle; }; sint s_DeviceCounter = 0; @@ -140,6 +140,8 @@ class CStereoOVRDevicePtr { public: OVR::Ptr HMDDevice; + OVR::Ptr SensorDevice; + OVR::SensorFusion SensorFusion; }; CStereoOVR::CStereoOVR(const CStereoDeviceInfo &deviceInfo) @@ -147,20 +149,53 @@ CStereoOVR::CStereoOVR(const CStereoDeviceInfo &deviceInfo) ++s_DeviceCounter; m_DevicePtr = new CStereoOVRDevicePtr(); - // CStereoOVRDeviceHandle *handle = static_cast(deviceInfo.Factory.getPtr()); - // OVR::DeviceHandle dh = handle->DeviceHandle; - // dh.CreateDevice(); + CStereoOVRDeviceHandle *handle = static_cast(deviceInfo.Factory.getPtr()); + OVR::DeviceEnumerator dh = handle->DeviceHandle; + m_DevicePtr->HMDDevice = dh.CreateDevice(); + + if (m_DevicePtr->HMDDevice) + { + m_DevicePtr->SensorDevice = m_DevicePtr->HMDDevice->GetSensor(); + m_DevicePtr->SensorFusion.AttachToSensor(m_DevicePtr->SensorDevice); + m_DevicePtr->SensorFusion.SetGravityEnabled(true); + m_DevicePtr->SensorFusion.SetPredictionEnabled(true); + m_DevicePtr->SensorFusion.SetYawCorrectionEnabled(true); + } } CStereoOVR::~CStereoOVR() { - // ... + if (m_DevicePtr->SensorDevice) + m_DevicePtr->SensorDevice->Release(); + m_DevicePtr->SensorDevice.Clear(); + if (m_DevicePtr->HMDDevice) + m_DevicePtr->HMDDevice->Release(); + m_DevicePtr->HMDDevice.Clear(); delete m_DevicePtr; m_DevicePtr = NULL; --s_DeviceCounter; } +NLMISC::CQuat CStereoOVR::getOrientation() +{ + OVR::Quatf quatovr = m_DevicePtr->SensorFusion.GetPredictedOrientation(); + NLMISC::CMatrix coordsys; + float csys[] = { + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, -1.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f, + }; + coordsys.set(csys); + NLMISC::CMatrix matovr; + matovr.setRot(NLMISC::CQuat(quatovr.x, quatovr.y, quatovr.z, quatovr.w)); + NLMISC::CMatrix matr; + matr.rotateX(NLMISC::Pi * 0.5f); // fix this properly... :) (note: removing this allows you to use rift while lying down) + NLMISC::CMatrix matnel = matr * matovr * coordsys; + return matnel.getRot(); +} + void CStereoOVR::listDevices(std::vector &devicesOut) { s_StereoOVRSystem.Init(); @@ -190,7 +225,7 @@ void CStereoOVR::listDevices(std::vector &devicesOut) CStereoOVR *CStereoOVR::createDevice(const CStereoDeviceInfo &deviceInfo) { - return NULL; + return new CStereoOVR(deviceInfo); } bool CStereoOVR::isLibraryInUse() @@ -205,6 +240,11 @@ void CStereoOVR::releaseLibrary() s_StereoOVRSystem.Release(); } +bool CStereoOVR::isDeviceCreated() +{ + return m_DevicePtr->HMDDevice != NULL; +} + } /* namespace NL3D */ /* end of file */ diff --git a/code/snowballs2/client/src/camera.cpp b/code/snowballs2/client/src/camera.cpp index 38db2d599..f1a76e552 100644 --- a/code/snowballs2/client/src/camera.cpp +++ b/code/snowballs2/client/src/camera.cpp @@ -108,13 +108,19 @@ void initCamera() break; } } - //s_StereoHMD->createDevice( + if (deviceInfo) + { + nlinfo("Create HMD device!"); + s_StereoHMD = CStereoOVR::createDevice(*deviceInfo); + } } // Set up directly the camera Camera = Scene->getCam(); Camera.setTransformMode (UTransformable::DirectMatrix); - Camera.setPerspective ((float)Pi/2.f, 1.33f, 0.1f, 1000); + Camera.setPerspective((float)Pi/2.f, + ConfigFile->getVar("ScreenWidth").asFloat() / ConfigFile->getVar("ScreenHeight").asFloat(), + 0.1f, 1000.f); Camera.lookAt (CVector(ConfigFile->getVar("StartPoint").asFloat(0), ConfigFile->getVar("StartPoint").asFloat(1), ConfigFile->getVar("StartPoint").asFloat(2)), @@ -153,11 +159,21 @@ void releaseCamera() Scene->deleteInstance(Snow); VisualCollisionManager->deleteEntity(CamCollisionEntity); + delete s_StereoHMD; + s_StereoHMD = NULL; CStereoOVR::releaseLibrary(); } void updateCamera() { + if (s_StereoHMD) + { + NLMISC::CQuat hmdOrient = s_StereoHMD->getOrientation(); + NLMISC::CMatrix camMatrix = Camera.getMatrix(); + NLMISC::CMatrix hmdMatrix; + hmdMatrix.setRot(hmdOrient); + Camera.setMatrix(camMatrix * hmdMatrix); + } // Set the new position of the snow emitter CMatrix mat = CMatrix::Identity; mat.setPos (Camera.getMatrix().getPos()/*+CVector (0.0f, 0.0f, -10.0f)*/); diff --git a/code/snowballs2/client/src/snowballs_config.h b/code/snowballs2/client/src/snowballs_config.h index fb304ba68..2e6b848fb 100644 --- a/code/snowballs2/client/src/snowballs_config.h +++ b/code/snowballs2/client/src/snowballs_config.h @@ -49,7 +49,7 @@ #define SBCLIENT_DEV_SOUND 0 #define SBCLIENT_DEV_STEREO 0 #define SBCLIENT_DEV_MEMLEAK 0 -#define SBCLIENT_DEV_PIXEL_PROGRAM 1 +#define SBCLIENT_DEV_PIXEL_PROGRAM 0 From e51d9e15d907496d8be6693524fbfc1c6d79653a Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 26 Jun 2013 08:00:19 +0200 Subject: [PATCH 031/313] Render from multiple cameras, see #43 --- code/nel/include/nel/3d/stereo_ovr.h | 27 ++++++ code/nel/src/3d/stereo_ovr.cpp | 97 ++++++++++++++++++- .../bin/snowballs_client_default.cfg | 2 +- code/snowballs2/client/src/camera.cpp | 26 +++-- code/snowballs2/client/src/camera.h | 4 + .../client/src/snowballs_client.cpp | 52 ++++++---- 6 files changed, 177 insertions(+), 31 deletions(-) diff --git a/code/nel/include/nel/3d/stereo_ovr.h b/code/nel/include/nel/3d/stereo_ovr.h index 3858b046e..89e6759d7 100644 --- a/code/nel/include/nel/3d/stereo_ovr.h +++ b/code/nel/include/nel/3d/stereo_ovr.h @@ -49,11 +49,15 @@ // NeL includes #include +#include +#include // Project includes namespace NL3D { +class UCamera; + struct CStereoDeviceInfo { public: @@ -80,6 +84,20 @@ public: CStereoOVR(const CStereoDeviceInfo &deviceInfo); virtual ~CStereoOVR(); + /// Gets the required screen resolution for this device + virtual void getScreenResolution(uint &width, uint &height); + /// Set latest camera position etcetera + virtual void updateCamera(const NL3D::UCamera *camera); + + /// Is there a next pass + virtual bool nextPass(); + /// Gets the current viewport + virtual const NL3D::CViewport &getCurrentViewport(); + /// Gets the current camera frustum + virtual void getCurrentFrustum(NL3D::UCamera *camera); + /// Gets the current camera matrix + virtual void getCurrentMatrix(NL3D::UCamera *camera); + virtual NLMISC::CQuat getOrientation(); static void listDevices(std::vector &devicesOut); @@ -87,10 +105,19 @@ public: static bool isLibraryInUse(); static void releaseLibrary(); + /// Calculates internal camera information based on the reference camera + void initCamera(const NL3D::UCamera *camera); + bool isDeviceCreated(); private: CStereoOVRDevicePtr *m_DevicePtr; + int m_Stage; + CViewport m_LeftViewport; + CViewport m_RightViewport; + CFrustum m_LeftFrustum; + CFrustum m_RightFrustum; + CMatrix m_CameraMatrix; }; /* class CStereoOVR */ diff --git a/code/nel/src/3d/stereo_ovr.cpp b/code/nel/src/3d/stereo_ovr.cpp index 44daae9ef..32d5bffb7 100644 --- a/code/nel/src/3d/stereo_ovr.cpp +++ b/code/nel/src/3d/stereo_ovr.cpp @@ -51,6 +51,7 @@ // NeL includes // #include +#include // Project includes @@ -142,9 +143,10 @@ public: OVR::Ptr HMDDevice; OVR::Ptr SensorDevice; OVR::SensorFusion SensorFusion; + OVR::HMDInfo HMDInfo; }; -CStereoOVR::CStereoOVR(const CStereoDeviceInfo &deviceInfo) +CStereoOVR::CStereoOVR(const CStereoDeviceInfo &deviceInfo) : m_Stage(2) { ++s_DeviceCounter; m_DevicePtr = new CStereoOVRDevicePtr(); @@ -155,11 +157,30 @@ CStereoOVR::CStereoOVR(const CStereoDeviceInfo &deviceInfo) if (m_DevicePtr->HMDDevice) { + m_DevicePtr->HMDDevice->GetDeviceInfo(&m_DevicePtr->HMDInfo); + nldebug("OVR: HScreenSize: %f, VScreenSize: %f", m_DevicePtr->HMDInfo.HScreenSize, m_DevicePtr->HMDInfo.VScreenSize); + nldebug("OVR: VScreenCenter: %f", m_DevicePtr->HMDInfo.VScreenCenter); + nldebug("OVR: EyeToScreenDistance: %f", m_DevicePtr->HMDInfo.EyeToScreenDistance); + nldebug("OVR: LensSeparationDistance: %f", m_DevicePtr->HMDInfo.LensSeparationDistance); + nldebug("OVR: InterpupillaryDistance: %f", m_DevicePtr->HMDInfo.InterpupillaryDistance); + nldebug("OVR: HResolution: %i, VResolution: %i", m_DevicePtr->HMDInfo.HResolution, m_DevicePtr->HMDInfo.VResolution); + nldebug("OVR: DistortionK[0]: %f, DistortionK[1]: %f", m_DevicePtr->HMDInfo.DistortionK[0], m_DevicePtr->HMDInfo.DistortionK[1]); + nldebug("OVR: DistortionK[2]: %f, DistortionK[3]: %f", m_DevicePtr->HMDInfo.DistortionK[2], m_DevicePtr->HMDInfo.DistortionK[3]); + //2013/06/26 05:31:51 DBG 17a0 snowballs_client.exe stereo_ovr.cpp 160 NL3D::CStereoOVR::CStereoOVR : OVR: HScreenSize: 0.149760, VScreenSize: 0.093600 + //2013/06/26 05:31:51 DBG 17a0 snowballs_client.exe stereo_ovr.cpp 161 NL3D::CStereoOVR::CStereoOVR : OVR: VScreenCenter: 0.046800 + //2013/06/26 05:31:51 DBG 17a0 snowballs_client.exe stereo_ovr.cpp 162 NL3D::CStereoOVR::CStereoOVR : OVR: EyeToScreenDistance: 0.041000 + //2013/06/26 05:31:51 DBG 17a0 snowballs_client.exe stereo_ovr.cpp 163 NL3D::CStereoOVR::CStereoOVR : OVR: LensSeparationDistance: 0.063500 + //2013/06/26 05:31:51 DBG 17a0 snowballs_client.exe stereo_ovr.cpp 164 NL3D::CStereoOVR::CStereoOVR : OVR: InterpupillaryDistance: 0.064000 + //2013/06/26 05:31:51 DBG 17a0 snowballs_client.exe stereo_ovr.cpp 165 NL3D::CStereoOVR::CStereoOVR : OVR: HResolution: 1280, VResolution: 800 + //2013/06/26 05:31:51 DBG 17a0 snowballs_client.exe stereo_ovr.cpp 166 NL3D::CStereoOVR::CStereoOVR : OVR: DistortionK[0]: 1.000000, DistortionK[1]: 0.220000 + //2013/06/26 05:31:51 DBG 17a0 snowballs_client.exe stereo_ovr.cpp 167 NL3D::CStereoOVR::CStereoOVR : OVR: DistortionK[2]: 0.240000, DistortionK[3]: 0.000000 m_DevicePtr->SensorDevice = m_DevicePtr->HMDDevice->GetSensor(); m_DevicePtr->SensorFusion.AttachToSensor(m_DevicePtr->SensorDevice); m_DevicePtr->SensorFusion.SetGravityEnabled(true); m_DevicePtr->SensorFusion.SetPredictionEnabled(true); m_DevicePtr->SensorFusion.SetYawCorrectionEnabled(true); + m_LeftViewport.init(0.f, 0.f, 0.5f, 1.0f); + m_RightViewport.init(0.5f, 0.f, 0.5f, 1.0f); } } @@ -177,6 +198,74 @@ CStereoOVR::~CStereoOVR() --s_DeviceCounter; } +void CStereoOVR::getScreenResolution(uint &width, uint &height) +{ + width = m_DevicePtr->HMDInfo.HResolution; + height = m_DevicePtr->HMDInfo.VResolution; +} + +void CStereoOVR::initCamera(const NL3D::UCamera *camera) +{ + float ar = (float)m_DevicePtr->HMDInfo.HResolution / ((float)m_DevicePtr->HMDInfo.VResolution * 2.0f); + float fov = 2.0f * atanf((m_DevicePtr->HMDInfo.VScreenSize / 2.0f) / m_DevicePtr->HMDInfo.EyeToScreenDistance); //(float)NLMISC::Pi/2.f; // 2.0f * atanf(m_DevicePtr->HMDInfo.VScreenSize / 2.0f * m_DevicePtr->HMDInfo.EyeToScreenDistance); + m_LeftFrustum.initPerspective(fov, ar, camera->getFrustum().Near, camera->getFrustum().Far); + m_RightFrustum = m_LeftFrustum; + float viewCenter = m_DevicePtr->HMDInfo.HScreenSize * 0.25f; + float eyeProjectionShift = viewCenter - m_DevicePtr->HMDInfo.LensSeparationDistance * 0.5f; + float projectionCenterOffset = 4.0f * eyeProjectionShift / m_DevicePtr->HMDInfo.HScreenSize; + nldebug("OVR: projectionCenterOffset = %f", projectionCenterOffset); + projectionCenterOffset *= (m_LeftFrustum.Left - m_LeftFrustum.Right) * 0.5f; // made this up ... + m_LeftFrustum.Left += projectionCenterOffset; + m_LeftFrustum.Right += projectionCenterOffset; + m_RightFrustum.Left -= projectionCenterOffset; + m_RightFrustum.Right -= projectionCenterOffset; +} + +void CStereoOVR::updateCamera(const NL3D::UCamera *camera) +{ + if (camera->getFrustum().Near != m_LeftFrustum.Near + || camera->getFrustum().Far != m_LeftFrustum.Far) + CStereoOVR::initCamera(camera); + m_CameraMatrix = camera->getMatrix(); +} + +bool CStereoOVR::nextPass() +{ + switch (m_Stage) + { + case 0: + ++m_Stage; + return true; + case 1: + ++m_Stage; + return false; + case 2: + m_Stage = 0; + return true; + } +} + +const NL3D::CViewport &CStereoOVR::getCurrentViewport() +{ + if (m_Stage) return m_RightViewport; + else return m_LeftViewport; +} + +void CStereoOVR::getCurrentFrustum(NL3D::UCamera *camera) +{ + if (m_Stage) camera->setFrustum(m_RightFrustum); + else camera->setFrustum(m_LeftFrustum); +} + +void CStereoOVR::getCurrentMatrix(NL3D::UCamera *camera) +{ + CMatrix translate; + if (m_Stage) translate.translate(CVector(m_DevicePtr->HMDInfo.InterpupillaryDistance * -0.5f, 0.f, 0.f)); + else translate.translate(CVector(m_DevicePtr->HMDInfo.InterpupillaryDistance * 0.5f, 0.f, 0.f)); + camera->setTransformMode(NL3D::UTransformable::DirectMatrix); + camera->setMatrix(m_CameraMatrix * translate); // or switch? +} + NLMISC::CQuat CStereoOVR::getOrientation() { OVR::Quatf quatovr = m_DevicePtr->SensorFusion.GetPredictedOrientation(); @@ -225,7 +314,11 @@ void CStereoOVR::listDevices(std::vector &devicesOut) CStereoOVR *CStereoOVR::createDevice(const CStereoDeviceInfo &deviceInfo) { - return new CStereoOVR(deviceInfo); + CStereoOVR *stereo = new CStereoOVR(deviceInfo); + if (stereo->isDeviceCreated()) + return stereo; + delete stereo; + return NULL; } bool CStereoOVR::isLibraryInUse() diff --git a/code/snowballs2/bin/snowballs_client_default.cfg b/code/snowballs2/bin/snowballs_client_default.cfg index 758bd8483..9d1429f83 100755 --- a/code/snowballs2/bin/snowballs_client_default.cfg +++ b/code/snowballs2/bin/snowballs_client_default.cfg @@ -52,7 +52,7 @@ ScreenHeight = 800; ScreenDepth = 32; // If 1, run in fullscreen mode, 0 for windowed -ScreenFull = 0; +ScreenFull = 1; // Start position of the player (the z is always 0) StartPoint = { 1840.0, -970.0, 0.0 }; diff --git a/code/snowballs2/client/src/camera.cpp b/code/snowballs2/client/src/camera.cpp index f1a76e552..1911f9694 100644 --- a/code/snowballs2/client/src/camera.cpp +++ b/code/snowballs2/client/src/camera.cpp @@ -64,13 +64,13 @@ UVisualCollisionEntity *CamCollisionEntity = NULL; static UInstance Snow = NULL; // The sky 3D objects -static UScene *SkyScene = NULL; -static UCamera SkyCamera = NULL; +UScene *SkyScene = NULL; +UCamera SkyCamera = NULL; static UInstance Sky = NULL; static UCloudScape *Clouds = NULL; -static CStereoOVR *s_StereoHMD = NULL; +CStereoOVR *StereoHMD = NULL; // // Functions @@ -111,7 +111,7 @@ void initCamera() if (deviceInfo) { nlinfo("Create HMD device!"); - s_StereoHMD = CStereoOVR::createDevice(*deviceInfo); + StereoHMD = CStereoOVR::createDevice(*deviceInfo); } } @@ -129,6 +129,13 @@ void initCamera() CamCollisionEntity = VisualCollisionManager->createEntity(); CamCollisionEntity->setCeilMode(true); + if (StereoHMD) + { + StereoHMD->nextPass(); // test + + StereoHMD->initCamera(&Camera); + } + // Create the snowing particle system Snow = Scene->createInstance("snow.ps"); // And setup it @@ -159,16 +166,16 @@ void releaseCamera() Scene->deleteInstance(Snow); VisualCollisionManager->deleteEntity(CamCollisionEntity); - delete s_StereoHMD; - s_StereoHMD = NULL; + delete StereoHMD; + StereoHMD = NULL; CStereoOVR::releaseLibrary(); } void updateCamera() { - if (s_StereoHMD) + if (StereoHMD) { - NLMISC::CQuat hmdOrient = s_StereoHMD->getOrientation(); + NLMISC::CQuat hmdOrient = StereoHMD->getOrientation(); NLMISC::CMatrix camMatrix = Camera.getMatrix(); NLMISC::CMatrix hmdMatrix; hmdMatrix.setRot(hmdOrient); @@ -217,7 +224,8 @@ void updateSky() // Must clear ZBuffer For incoming rendering. Driver->clearZBuffer(); - Clouds->render(); + if (!StereoHMD) // Cloudscape not supported (fix Viewport please) + Clouds->render(); } } /* namespace SBCLIENT */ diff --git a/code/snowballs2/client/src/camera.h b/code/snowballs2/client/src/camera.h index c7718c653..5eae47c53 100644 --- a/code/snowballs2/client/src/camera.h +++ b/code/snowballs2/client/src/camera.h @@ -30,6 +30,7 @@ namespace NL3D { class UVisualCollisionEntity; + class CStereoOVR; }; namespace SBCLIENT { @@ -39,7 +40,10 @@ namespace SBCLIENT { // extern NL3D::UCamera Camera; +extern NL3D::UCamera SkyCamera; extern NL3D::UVisualCollisionEntity *CamCollisionEntity; +extern NL3D::CStereoOVR *StereoHMD; +extern NL3D::UScene *SkyScene; // // External functions diff --git a/code/snowballs2/client/src/snowballs_client.cpp b/code/snowballs2/client/src/snowballs_client.cpp index e0f044174..5ed15b9d7 100644 --- a/code/snowballs2/client/src/snowballs_client.cpp +++ b/code/snowballs2/client/src/snowballs_client.cpp @@ -51,6 +51,7 @@ #if SBCLIENT_DEV_STEREO # include #endif /* #if SBCLIENT_DEV_STEREO */ +#include // Project includes #include "pacs.h" @@ -709,6 +710,7 @@ void loopIngame() // 09. Update Camera (depends on entities) updateCamera(); + StereoHMD->updateCamera(&Camera); // 10. Update Interface (login, ui, etc) // ... @@ -729,17 +731,28 @@ void loopIngame() else { // call all 3d render thingies - Driver->clearBuffers(CRGBA(127, 0, 0)); // if you see red, there's a problem with bloom or stereo render + // Driver->clearBuffers(CRGBA(127, 0, 0)); // if you see red, there's a problem with bloom or stereo render + + // 01. Render Driver (background color) + // BLOOM CBloomEffect::instance().initBloom(); // start bloom effect (just before the first scene element render) + Driver->clearBuffers(CRGBA(0, 0, 127)); // clear all buffers, if you see this blue there's a problem with scene rendering + #if SBCLIENT_DEV_STEREO _StereoRender->calculateCameras(Camera.getObjectPtr()); // calculate modified matrices for the current camera for (uint cameraId = 0; cameraId < _StereoRender->getCameraCount(); ++cameraId) { _StereoRender->getCamera(cameraId, Camera.getObjectPtr()); // get the matrix details for this camera -#endif /* #if SBCLIENT_DEV_STEREO */ - // 01. Render Driver (background color) - CBloomEffect::instance().initBloom(); // start bloom effect (just before the first scene element render) - Driver->clearBuffers(CRGBA(0, 0, 127)); // clear all buffers, if you see this blue there's a problem with scene rendering +#endif /* #if SBCLIENT_DEV_STEREO */ + while (StereoHMD->nextPass()) + { + const CViewport &vp = StereoHMD->getCurrentViewport(); + Driver->setViewport(vp); + Scene->setViewport(vp); + SkyScene->setViewport(vp); + StereoHMD->getCurrentFrustum(&Camera); + StereoHMD->getCurrentFrustum(&SkyCamera); + StereoHMD->getCurrentMatrix(&Camera); // 02. Render Sky (sky scene) updateSky(); // Render the sky scene before the main scene @@ -749,10 +762,10 @@ void loopIngame() // 05. Render Effects (flare) updateLensFlare(); // Render the lens flare - CBloomEffect::instance().endBloom(); // end the actual bloom effect visible in the scene + // BLOOM CBloomEffect::instance().endBloom(); // end the actual bloom effect visible in the scene // 06. Render Interface 3D (player names) - CBloomEffect::instance().endInterfacesDisplayBloom(); // end bloom effect system after drawing the 3d interface (z buffer related) + // BLOOM CBloomEffect::instance().endInterfacesDisplayBloom(); // end bloom effect system after drawing the 3d interface (z buffer related) #if SBCLIENT_DEV_STEREO _StereoRender->copyBufferToTexture(cameraId); // copy current buffer to the active stereorender texture @@ -761,19 +774,20 @@ void loopIngame() _StereoRender->render(); // render everything together in the current mode #endif /* #if SBCLIENT_DEV_STEREO */ - // 07. Render Interface 2D (chatboxes etc, optionally does have 3d) - updateCompass(); // Update the compass - updateRadar(); // Update the radar - updateGraph(); // Update the radar - if (ShowCommands) updateCommands(); // Update the commands panel - updateAnimation(); - renderEntitiesNames(); // Render the name on top of the other players - updateInterface(); // Update interface - renderInformation(); - update3dLogo(); + // 07. Render Interface 2D (chatboxes etc, optionally does have 3d) + updateCompass(); // Update the compass + updateRadar(); // Update the radar + updateGraph(); // Update the radar + if (ShowCommands) updateCommands(); // Update the commands panel + updateAnimation(); + renderEntitiesNames(); // Render the name on top of the other players + updateInterface(); // Update interface + renderInformation(); + update3dLogo(); - // 08. Render Debug (stuff for dev) - // ... + // 08. Render Debug (stuff for dev) + // ... + } // 09. Render Buffer Driver->swapBuffers(); From 7672ab28127afe1a92e5ed0ec2fdba43044a8e87 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 26 Jun 2013 15:49:31 +0200 Subject: [PATCH 032/313] Cleanup and make bloom work with stereo rendering, re #43 --- code/nel/include/nel/3d/stereo_ovr.h | 23 +++- code/nel/src/3d/stereo_ovr.cpp | 120 +++++++++++++++-- .../bin/snowballs_client_default.cfg | 2 +- code/snowballs2/client/src/camera.cpp | 2 - code/snowballs2/client/src/mouse_listener.cpp | 5 + .../client/src/snowballs_client.cpp | 127 +++++++++++------- 6 files changed, 209 insertions(+), 70 deletions(-) diff --git a/code/nel/include/nel/3d/stereo_ovr.h b/code/nel/include/nel/3d/stereo_ovr.h index 89e6759d7..68cb8fd1c 100644 --- a/code/nel/include/nel/3d/stereo_ovr.h +++ b/code/nel/include/nel/3d/stereo_ovr.h @@ -84,6 +84,7 @@ public: CStereoOVR(const CStereoDeviceInfo &deviceInfo); virtual ~CStereoOVR(); + /// Gets the required screen resolution for this device virtual void getScreenResolution(uint &width, uint &height); /// Set latest camera position etcetera @@ -92,19 +93,33 @@ public: /// Is there a next pass virtual bool nextPass(); /// Gets the current viewport - virtual const NL3D::CViewport &getCurrentViewport(); + virtual const NL3D::CViewport &getCurrentViewport() const; /// Gets the current camera frustum - virtual void getCurrentFrustum(NL3D::UCamera *camera); + virtual void getCurrentFrustum(NL3D::UCamera *camera) const; /// Gets the current camera matrix - virtual void getCurrentMatrix(NL3D::UCamera *camera); + virtual void getCurrentMatrix(NL3D::UCamera *camera) const; + + virtual bool beginClear(); + virtual void endClear(); + + virtual bool beginScene(); + virtual void endScene(); + + virtual bool beginInterface3D(); + virtual void endInterface3D(); + + virtual bool beginInterface2D(); + virtual void endInterface2D(); + + virtual NLMISC::CQuat getOrientation() const; - virtual NLMISC::CQuat getOrientation(); static void listDevices(std::vector &devicesOut); static CStereoOVR *createDevice(const CStereoDeviceInfo &deviceInfo); static bool isLibraryInUse(); static void releaseLibrary(); + /// Calculates internal camera information based on the reference camera void initCamera(const NL3D::UCamera *camera); diff --git a/code/nel/src/3d/stereo_ovr.cpp b/code/nel/src/3d/stereo_ovr.cpp index 32d5bffb7..b9c829d5b 100644 --- a/code/nel/src/3d/stereo_ovr.cpp +++ b/code/nel/src/3d/stereo_ovr.cpp @@ -146,7 +146,7 @@ public: OVR::HMDInfo HMDInfo; }; -CStereoOVR::CStereoOVR(const CStereoDeviceInfo &deviceInfo) : m_Stage(2) +CStereoOVR::CStereoOVR(const CStereoDeviceInfo &deviceInfo) : m_Stage(0) { ++s_DeviceCounter; m_DevicePtr = new CStereoOVRDevicePtr(); @@ -235,38 +235,130 @@ bool CStereoOVR::nextPass() { case 0: ++m_Stage; + // stage 1: + // (initBloom) + // clear buffer + // draw scene left return true; case 1: ++m_Stage; - return false; - case 2: - m_Stage = 0; + // stage 2: + // draw scene right return true; + case 2: + ++m_Stage; + // stage 3: + // (endBloom) + // draw interface 3d left + return true; + case 3: + ++m_Stage; + // stage 4: + // draw interface 3d right + return true; + case 4: + ++m_Stage; + // stage 5: + // (endInterfacesDisplayBloom) + // draw interface 2d left + return true; + case 5: + ++m_Stage; + // stage 6: + // draw interface 2d right + return true; + case 6: + m_Stage = 0; + // present + return false; } } -const NL3D::CViewport &CStereoOVR::getCurrentViewport() +const NL3D::CViewport &CStereoOVR::getCurrentViewport() const { - if (m_Stage) return m_RightViewport; - else return m_LeftViewport; + if (m_Stage % 2) return m_LeftViewport; + else return m_RightViewport; } -void CStereoOVR::getCurrentFrustum(NL3D::UCamera *camera) +void CStereoOVR::getCurrentFrustum(NL3D::UCamera *camera) const { - if (m_Stage) camera->setFrustum(m_RightFrustum); - else camera->setFrustum(m_LeftFrustum); + if (m_Stage % 2) camera->setFrustum(m_LeftFrustum); + else camera->setFrustum(m_RightFrustum); } -void CStereoOVR::getCurrentMatrix(NL3D::UCamera *camera) +void CStereoOVR::getCurrentMatrix(NL3D::UCamera *camera) const { CMatrix translate; - if (m_Stage) translate.translate(CVector(m_DevicePtr->HMDInfo.InterpupillaryDistance * -0.5f, 0.f, 0.f)); + if (m_Stage % 2) translate.translate(CVector(m_DevicePtr->HMDInfo.InterpupillaryDistance * -0.5f, 0.f, 0.f)); else translate.translate(CVector(m_DevicePtr->HMDInfo.InterpupillaryDistance * 0.5f, 0.f, 0.f)); camera->setTransformMode(NL3D::UTransformable::DirectMatrix); - camera->setMatrix(m_CameraMatrix * translate); // or switch? + camera->setMatrix(m_CameraMatrix * translate); } -NLMISC::CQuat CStereoOVR::getOrientation() +bool CStereoOVR::beginClear() +{ + switch (m_Stage) + { + case 1: + return true; + } + return false; +} + +void CStereoOVR::endClear() +{ + +} + +bool CStereoOVR::beginScene() +{ + switch (m_Stage) + { + case 1: + case 2: + return true; + } + return false; +} + +void CStereoOVR::endScene() +{ + +} + +bool CStereoOVR::beginInterface3D() +{ + switch (m_Stage) + { + case 3: + case 4: + return true; + } + return false; +} + +void CStereoOVR::endInterface3D() +{ + +} + +bool CStereoOVR::beginInterface2D() +{ + switch (m_Stage) + { + case 5: + case 6: + return true; + } + return false; +} + +void CStereoOVR::endInterface2D() +{ + +} + +NLMISC::CQuat CStereoOVR::getOrientation() const { OVR::Quatf quatovr = m_DevicePtr->SensorFusion.GetPredictedOrientation(); NLMISC::CMatrix coordsys; diff --git a/code/snowballs2/bin/snowballs_client_default.cfg b/code/snowballs2/bin/snowballs_client_default.cfg index 9d1429f83..758bd8483 100755 --- a/code/snowballs2/bin/snowballs_client_default.cfg +++ b/code/snowballs2/bin/snowballs_client_default.cfg @@ -52,7 +52,7 @@ ScreenHeight = 800; ScreenDepth = 32; // If 1, run in fullscreen mode, 0 for windowed -ScreenFull = 1; +ScreenFull = 0; // Start position of the player (the z is always 0) StartPoint = { 1840.0, -970.0, 0.0 }; diff --git a/code/snowballs2/client/src/camera.cpp b/code/snowballs2/client/src/camera.cpp index 1911f9694..030ebe0d9 100644 --- a/code/snowballs2/client/src/camera.cpp +++ b/code/snowballs2/client/src/camera.cpp @@ -131,8 +131,6 @@ void initCamera() if (StereoHMD) { - StereoHMD->nextPass(); // test - StereoHMD->initCamera(&Camera); } diff --git a/code/snowballs2/client/src/mouse_listener.cpp b/code/snowballs2/client/src/mouse_listener.cpp index c459f6bd2..67508e281 100644 --- a/code/snowballs2/client/src/mouse_listener.cpp +++ b/code/snowballs2/client/src/mouse_listener.cpp @@ -427,6 +427,11 @@ void C3dMouseListener::updateCamera() cpos = snapped+CVector(0.0f, 0.0f, GroundCamLimit); _ViewHeight = cpos.z - getPosition().z; } + if (StereoHMD) + { + // look at straight forward + tpos.z = cpos.z; + } _Camera.lookAt(cpos, tpos); } diff --git a/code/snowballs2/client/src/snowballs_client.cpp b/code/snowballs2/client/src/snowballs_client.cpp index 5ed15b9d7..9563ad774 100644 --- a/code/snowballs2/client/src/snowballs_client.cpp +++ b/code/snowballs2/client/src/snowballs_client.cpp @@ -710,7 +710,7 @@ void loopIngame() // 09. Update Camera (depends on entities) updateCamera(); - StereoHMD->updateCamera(&Camera); + if (StereoHMD) StereoHMD->updateCamera(&Camera); // 10. Update Interface (login, ui, etc) // ... @@ -730,63 +730,92 @@ void loopIngame() if (Driver->isLost()) nlSleep(10); else { - // call all 3d render thingies - // Driver->clearBuffers(CRGBA(127, 0, 0)); // if you see red, there's a problem with bloom or stereo render - - // 01. Render Driver (background color) - // BLOOM CBloomEffect::instance().initBloom(); // start bloom effect (just before the first scene element render) - Driver->clearBuffers(CRGBA(0, 0, 127)); // clear all buffers, if you see this blue there's a problem with scene rendering - -#if SBCLIENT_DEV_STEREO - _StereoRender->calculateCameras(Camera.getObjectPtr()); // calculate modified matrices for the current camera - for (uint cameraId = 0; cameraId < _StereoRender->getCameraCount(); ++cameraId) + uint i = 0; + uint bloomStage = 0; + while ((!StereoHMD && i == 0) || (StereoHMD && StereoHMD->nextPass())) { - _StereoRender->getCamera(cameraId, Camera.getObjectPtr()); // get the matrix details for this camera + ++i; + if (StereoHMD) + { + const CViewport &vp = StereoHMD->getCurrentViewport(); + Driver->setViewport(vp); + Scene->setViewport(vp); + SkyScene->setViewport(vp); + StereoHMD->getCurrentFrustum(&Camera); + StereoHMD->getCurrentFrustum(&SkyCamera); + StereoHMD->getCurrentMatrix(&Camera); + } + + if (!StereoHMD || StereoHMD->beginClear()) + { + nlassert(bloomStage == 0); + CBloomEffect::instance().initBloom(); // start bloom effect (just before the first scene element render) + bloomStage = 1; -#endif /* #if SBCLIENT_DEV_STEREO */ - while (StereoHMD->nextPass()) - { - const CViewport &vp = StereoHMD->getCurrentViewport(); - Driver->setViewport(vp); - Scene->setViewport(vp); - SkyScene->setViewport(vp); - StereoHMD->getCurrentFrustum(&Camera); - StereoHMD->getCurrentFrustum(&SkyCamera); - StereoHMD->getCurrentMatrix(&Camera); + // 01. Render Driver (background color) + Driver->clearBuffers(CRGBA(0, 0, 127)); // clear all buffers, if you see this blue there's a problem with scene rendering + + if (StereoHMD) StereoHMD->endClear(); + } - // 02. Render Sky (sky scene) - updateSky(); // Render the sky scene before the main scene + if (!StereoHMD || StereoHMD->beginScene()) + { + // 02. Render Sky (sky scene) + updateSky(); // Render the sky scene before the main scene - // 04. Render Scene (entity scene) - Scene->render(); // Render + // 04. Render Scene (entity scene) + Scene->render(); // Render - // 05. Render Effects (flare) - updateLensFlare(); // Render the lens flare - // BLOOM CBloomEffect::instance().endBloom(); // end the actual bloom effect visible in the scene + // 05. Render Effects (flare) + if (!StereoHMD) updateLensFlare(); // Render the lens flare (left eye stretched with stereo...) - // 06. Render Interface 3D (player names) - // BLOOM CBloomEffect::instance().endInterfacesDisplayBloom(); // end bloom effect system after drawing the 3d interface (z buffer related) + if (StereoHMD) StereoHMD->endScene(); + } -#if SBCLIENT_DEV_STEREO - _StereoRender->copyBufferToTexture(cameraId); // copy current buffer to the active stereorender texture - } - _StereoRender->restoreCamera(Camera.getObjectPtr()); // restore the camera - _StereoRender->render(); // render everything together in the current mode -#endif /* #if SBCLIENT_DEV_STEREO */ + if (!StereoHMD || StereoHMD->beginInterface3D()) + { + if (bloomStage == 1) + { + // End the actual bloom effect visible in the scene. + if (StereoHMD) Driver->setViewport(NL3D::CViewport()); + CBloomEffect::instance().endBloom(); + if (StereoHMD) Driver->setViewport(StereoHMD->getCurrentViewport()); + bloomStage = 2; + } - // 07. Render Interface 2D (chatboxes etc, optionally does have 3d) - updateCompass(); // Update the compass - updateRadar(); // Update the radar - updateGraph(); // Update the radar - if (ShowCommands) updateCommands(); // Update the commands panel - updateAnimation(); - renderEntitiesNames(); // Render the name on top of the other players - updateInterface(); // Update interface - renderInformation(); - update3dLogo(); + // 06. Render Interface 3D (player names) + // ... + + if (StereoHMD) StereoHMD->endInterface3D(); + } - // 08. Render Debug (stuff for dev) - // ... + if (!StereoHMD || StereoHMD->beginInterface2D()) + { + if (bloomStage == 2) + { + // End bloom effect system after drawing the 3d interface (z buffer related). + if (StereoHMD) Driver->setViewport(NL3D::CViewport()); + CBloomEffect::instance().endInterfacesDisplayBloom(); + if (StereoHMD) Driver->setViewport(StereoHMD->getCurrentViewport()); + bloomStage = 0; + } + + // 07. Render Interface 2D (chatboxes etc, optionally does have 3d) + updateCompass(); // Update the compass + updateRadar(); // Update the radar + updateGraph(); // Update the radar + if (ShowCommands) updateCommands(); // Update the commands panel + updateAnimation(); + renderEntitiesNames(); // Render the name on top of the other players + updateInterface(); // Update interface + renderInformation(); + if (!StereoHMD) update3dLogo(); // broken with stereo + + // 08. Render Debug (stuff for dev) + // ... + + if (StereoHMD) StereoHMD->endInterface2D(); + } } // 09. Render Buffer From cbd72f73ffc307d2b49afd51b21b6506cb312c77 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 26 Jun 2013 15:55:39 +0200 Subject: [PATCH 033/313] Add some comments, see #43 --- code/nel/include/nel/3d/stereo_ovr.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/code/nel/include/nel/3d/stereo_ovr.h b/code/nel/include/nel/3d/stereo_ovr.h index 68cb8fd1c..9a91e0bd0 100644 --- a/code/nel/include/nel/3d/stereo_ovr.h +++ b/code/nel/include/nel/3d/stereo_ovr.h @@ -99,18 +99,25 @@ public: /// Gets the current camera matrix virtual void getCurrentMatrix(NL3D::UCamera *camera) const; + /// At the start of a new render target virtual bool beginClear(); + // virtual void *getRenderTarget() const; virtual void endClear(); + /// The 3D scene virtual bool beginScene(); virtual void endScene(); + /// Interface within the 3D scene virtual bool beginInterface3D(); virtual void endInterface3D(); + /// 2D Interface virtual bool beginInterface2D(); virtual void endInterface2D(); + + /// Get the HMD orientation virtual NLMISC::CQuat getOrientation() const; From 78ae99731d3a89db4b65a4439c9d9404d3209aa1 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 26 Jun 2013 16:08:48 +0200 Subject: [PATCH 034/313] Correctly adjust text to viewport, re #43 --- code/nel/src/3d/computed_string.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/code/nel/src/3d/computed_string.cpp b/code/nel/src/3d/computed_string.cpp index 0c2cd48de..71ea58948 100644 --- a/code/nel/src/3d/computed_string.cpp +++ b/code/nel/src/3d/computed_string.cpp @@ -21,6 +21,7 @@ #include "nel/3d/index_buffer.h" #include "nel/3d/material.h" #include "nel/3d/frustum.h" +#include "nel/3d/viewport.h" #include "nel/misc/smart_ptr.h" #include "nel/misc/debug.h" @@ -85,6 +86,10 @@ void CComputedString::render2D (IDriver& driver, // get window size uint32 wndWidth, wndHeight; driver.getWindowSize(wndWidth, wndHeight); + CViewport vp; + driver.getViewport(vp); + wndWidth = (uint32)((float)wndWidth * vp.getWidth()); + wndHeight = (uint32)((float)wndHeight * vp.getHeight()); // scale to window size. x*= wndWidth; z*= wndHeight; From 1b8ddaa87b325484e7c9575a1ab1d034ea5a7d97 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 26 Jun 2013 16:59:08 +0200 Subject: [PATCH 035/313] Add 2D interface shifting calculations, see #43 --- code/nel/include/nel/3d/stereo_ovr.h | 6 ++++ code/nel/src/3d/stereo_ovr.cpp | 31 +++++++++++++++++-- code/snowballs2/client/src/camera.h | 1 - code/snowballs2/client/src/commands.cpp | 15 +++++++++ code/snowballs2/client/src/snowballs_client.h | 2 ++ 5 files changed, 52 insertions(+), 3 deletions(-) diff --git a/code/nel/include/nel/3d/stereo_ovr.h b/code/nel/include/nel/3d/stereo_ovr.h index 9a91e0bd0..cd466a7aa 100644 --- a/code/nel/include/nel/3d/stereo_ovr.h +++ b/code/nel/include/nel/3d/stereo_ovr.h @@ -95,6 +95,8 @@ public: /// Gets the current viewport virtual const NL3D::CViewport &getCurrentViewport() const; /// Gets the current camera frustum + virtual const NL3D::CFrustum &getCurrentFrustum() const; + /// Gets the current camera frustum virtual void getCurrentFrustum(NL3D::UCamera *camera) const; /// Gets the current camera matrix virtual void getCurrentMatrix(NL3D::UCamera *camera) const; @@ -119,6 +121,8 @@ public: /// Get the HMD orientation virtual NLMISC::CQuat getOrientation() const; + /// Get GUI center (1 = width, 1 = height, 0 = center) (todo: move to CStereoHMD) + void getInterface2DShift(float &x, float &y, float distance); static void listDevices(std::vector &devicesOut); @@ -140,6 +144,8 @@ private: CFrustum m_LeftFrustum; CFrustum m_RightFrustum; CMatrix m_CameraMatrix; + mutable bool m_OrientationCached; + mutable NLMISC::CQuat m_OrientationCache; }; /* class CStereoOVR */ diff --git a/code/nel/src/3d/stereo_ovr.cpp b/code/nel/src/3d/stereo_ovr.cpp index b9c829d5b..09e079f81 100644 --- a/code/nel/src/3d/stereo_ovr.cpp +++ b/code/nel/src/3d/stereo_ovr.cpp @@ -146,7 +146,7 @@ public: OVR::HMDInfo HMDInfo; }; -CStereoOVR::CStereoOVR(const CStereoDeviceInfo &deviceInfo) : m_Stage(0) +CStereoOVR::CStereoOVR(const CStereoDeviceInfo &deviceInfo) : m_Stage(0), m_OrientationCached(false) { ++s_DeviceCounter; m_DevicePtr = new CStereoOVRDevicePtr(); @@ -270,6 +270,7 @@ bool CStereoOVR::nextPass() case 6: m_Stage = 0; // present + m_OrientationCached = false; return false; } } @@ -280,6 +281,12 @@ const NL3D::CViewport &CStereoOVR::getCurrentViewport() const else return m_RightViewport; } +const NL3D::CFrustum &CStereoOVR::getCurrentFrustum() const +{ + if (m_Stage % 2) return m_LeftFrustum; + else return m_RightFrustum; +} + void CStereoOVR::getCurrentFrustum(NL3D::UCamera *camera) const { if (m_Stage % 2) camera->setFrustum(m_LeftFrustum); @@ -360,6 +367,9 @@ void CStereoOVR::endInterface2D() NLMISC::CQuat CStereoOVR::getOrientation() const { + if (m_OrientationCached) + return m_OrientationCache; + OVR::Quatf quatovr = m_DevicePtr->SensorFusion.GetPredictedOrientation(); NLMISC::CMatrix coordsys; float csys[] = { @@ -374,7 +384,24 @@ NLMISC::CQuat CStereoOVR::getOrientation() const NLMISC::CMatrix matr; matr.rotateX(NLMISC::Pi * 0.5f); // fix this properly... :) (note: removing this allows you to use rift while lying down) NLMISC::CMatrix matnel = matr * matovr * coordsys; - return matnel.getRot(); + NLMISC::CQuat finalquat = matnel.getRot(); + m_OrientationCache = finalquat; + m_OrientationCached = true; + return finalquat; +} + +/// Get GUI shift (todo: move to CStereoHMD) +void CStereoOVR::getInterface2DShift(float &x, float &y, float distance) +{ + NLMISC::CVector vector = CVector(0.f, -distance, 0.f); + NLMISC::CQuat rot = getOrientation(); + rot.invert(); + NLMISC::CMatrix mat; + mat.rotate(rot); + mat.translate(vector); + CVector proj = getCurrentFrustum().project(mat.getPos()); + x = proj.x - 0.5f; + y = proj.y - 0.5f; } void CStereoOVR::listDevices(std::vector &devicesOut) diff --git a/code/snowballs2/client/src/camera.h b/code/snowballs2/client/src/camera.h index 5eae47c53..c2d61d866 100644 --- a/code/snowballs2/client/src/camera.h +++ b/code/snowballs2/client/src/camera.h @@ -42,7 +42,6 @@ namespace SBCLIENT { extern NL3D::UCamera Camera; extern NL3D::UCamera SkyCamera; extern NL3D::UVisualCollisionEntity *CamCollisionEntity; -extern NL3D::CStereoOVR *StereoHMD; extern NL3D::UScene *SkyScene; // diff --git a/code/snowballs2/client/src/commands.cpp b/code/snowballs2/client/src/commands.cpp index ed8a1dc7d..795371cb6 100644 --- a/code/snowballs2/client/src/commands.cpp +++ b/code/snowballs2/client/src/commands.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include "network.h" #include "snowballs_client.h" @@ -374,6 +375,9 @@ void updateCommands() uint32 _width, _height; Driver->getWindowSize(_width, _height); float width = (float)_width, height = (float)_height; + NL3D::CViewport vp = Driver->getViewport(); + width *= vp.getWidth(); + height *= vp.getHeight(); float CommandsLineHeight = CommandsFontSize / height; float CommandsBoxX = ((float)(sint32)(SBCLIENT::CommandsBoxX * width)) / width; float CommandsBoxWidth = ((float)(sint32)(SBCLIENT::CommandsBoxWidth * width)) / width; @@ -381,6 +385,17 @@ void updateCommands() float CommandsBoxHeight = ((float)(sint32)((CommandsNbLines + 1) * CommandsLineHeight * width)) / width; float CommandsBoxBorderX = ((float)(sint32)(SBCLIENT::CommandsBoxBorder * width)) / width; float CommandsBoxBorderY = ((float)(sint32)(SBCLIENT::CommandsBoxBorder * height)) / height; + if (StereoHMD) + { + float xshift, yshift; + StereoHMD->getInterface2DShift(xshift, yshift, 1.0f); + // snap to pixels + xshift = ((float)(sint32)(xshift * width)) / width; + yshift = ((float)(sint32)(yshift * height)) / height; + // adjust + CommandsBoxX += xshift; + CommandsBoxY += yshift; + } // Display the background Driver->setMatrixMode2D11 (); diff --git a/code/snowballs2/client/src/snowballs_client.h b/code/snowballs2/client/src/snowballs_client.h index 67116f7e6..bc955d317 100644 --- a/code/snowballs2/client/src/snowballs_client.h +++ b/code/snowballs2/client/src/snowballs_client.h @@ -42,6 +42,7 @@ namespace NL3D { class UScene; class UTextContext; class ULandscape; + class CStereoOVR; } namespace SBCLIENT { @@ -58,6 +59,7 @@ public: }; extern NL3D::UDriver *Driver; +extern NL3D::CStereoOVR *StereoHMD; extern NL3D::UScene *Scene; extern NL3D::UTextContext *TextContext; extern NLMISC::CConfigFile *ConfigFile; From 8230238b5f16db76ac616625df528210cdf73b52 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 26 Jun 2013 17:24:23 +0200 Subject: [PATCH 036/313] Add eye distance in 2D interface shift, re #43 --- code/nel/include/nel/3d/stereo_ovr.h | 4 ++-- code/nel/src/3d/stereo_ovr.cpp | 6 ++++-- code/snowballs2/client/src/commands.cpp | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/code/nel/include/nel/3d/stereo_ovr.h b/code/nel/include/nel/3d/stereo_ovr.h index cd466a7aa..bb85438ad 100644 --- a/code/nel/include/nel/3d/stereo_ovr.h +++ b/code/nel/include/nel/3d/stereo_ovr.h @@ -121,8 +121,8 @@ public: /// Get the HMD orientation virtual NLMISC::CQuat getOrientation() const; - /// Get GUI center (1 = width, 1 = height, 0 = center) (todo: move to CStereoHMD) - void getInterface2DShift(float &x, float &y, float distance); + /// Get GUI center (1 = width, 1 = height, 0 = center) + virtual void getInterface2DShift(float &x, float &y, float distance); static void listDevices(std::vector &devicesOut); diff --git a/code/nel/src/3d/stereo_ovr.cpp b/code/nel/src/3d/stereo_ovr.cpp index 09e079f81..cba1c004f 100644 --- a/code/nel/src/3d/stereo_ovr.cpp +++ b/code/nel/src/3d/stereo_ovr.cpp @@ -390,7 +390,7 @@ NLMISC::CQuat CStereoOVR::getOrientation() const return finalquat; } -/// Get GUI shift (todo: move to CStereoHMD) +/// Get GUI shift void CStereoOVR::getInterface2DShift(float &x, float &y, float distance) { NLMISC::CVector vector = CVector(0.f, -distance, 0.f); @@ -398,8 +398,10 @@ void CStereoOVR::getInterface2DShift(float &x, float &y, float distance) rot.invert(); NLMISC::CMatrix mat; mat.rotate(rot); + if (m_Stage % 2) mat.translate(CVector(m_DevicePtr->HMDInfo.InterpupillaryDistance * -0.5f, 0.f, 0.f)); + else mat.translate(CVector(m_DevicePtr->HMDInfo.InterpupillaryDistance * 0.5f, 0.f, 0.f)); mat.translate(vector); - CVector proj = getCurrentFrustum().project(mat.getPos()); + CVector proj = CStereoOVR::getCurrentFrustum().project(mat.getPos()); x = proj.x - 0.5f; y = proj.y - 0.5f; } diff --git a/code/snowballs2/client/src/commands.cpp b/code/snowballs2/client/src/commands.cpp index 795371cb6..4a36db8bb 100644 --- a/code/snowballs2/client/src/commands.cpp +++ b/code/snowballs2/client/src/commands.cpp @@ -388,7 +388,7 @@ void updateCommands() if (StereoHMD) { float xshift, yshift; - StereoHMD->getInterface2DShift(xshift, yshift, 1.0f); + StereoHMD->getInterface2DShift(xshift, yshift, 4.0f); // snap to pixels xshift = ((float)(sint32)(xshift * width)) / width; yshift = ((float)(sint32)(yshift * height)) / height; From 06e96929afe825d91928c51fc72beebd9f02cc0b Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 26 Jun 2013 17:38:20 +0200 Subject: [PATCH 037/313] Move snowballs compass, see #43 --- code/snowballs2/client/src/compass.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/code/snowballs2/client/src/compass.cpp b/code/snowballs2/client/src/compass.cpp index 11d7ddefa..b915cdb52 100644 --- a/code/snowballs2/client/src/compass.cpp +++ b/code/snowballs2/client/src/compass.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include "mouse_listener.h" #include "camera.h" @@ -94,6 +95,13 @@ void updateCompass () { float x = CompassPosX; float y = CompassPosY; + if (StereoHMD) + { + float xshift, yshift; + StereoHMD->getInterface2DShift(xshift, yshift, 4.0f); + x += xshift; + y += yshift; + } float radius = CompassRadius; // tri @@ -109,7 +117,8 @@ void updateCompass () quad.V2.set ( radius, radius, 0); quad.V3.set (-radius, radius, 0); - Driver->setMatrixMode2D43 (); + if (StereoHMD) Driver->setMatrixMode2D11(); + else Driver->setMatrixMode2D43(); CMatrix mtx; @@ -152,7 +161,7 @@ void updateCompass () Driver->setModelMatrix (mtx); Driver->drawQuad (quad, CompassMaterial); - x *= 3.0/4.0f; + if (!StereoHMD) x *= 3.0/4.0f; // Print position TextContext->setHotSpot(UTextContext::MiddleTop); From eb196fd99ae06c81b5769992411f899d74318da7 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 26 Jun 2013 19:58:27 +0200 Subject: [PATCH 038/313] Adjust 2D interface shift, re #43 --- code/nel/src/3d/stereo_ovr.cpp | 43 +++++++++++++++++-- .../bin/snowballs_client_default.cfg | 2 +- code/snowballs2/client/src/commands.cpp | 2 +- code/snowballs2/client/src/compass.cpp | 2 +- 4 files changed, 42 insertions(+), 7 deletions(-) diff --git a/code/nel/src/3d/stereo_ovr.cpp b/code/nel/src/3d/stereo_ovr.cpp index cba1c004f..1f62213eb 100644 --- a/code/nel/src/3d/stereo_ovr.cpp +++ b/code/nel/src/3d/stereo_ovr.cpp @@ -393,17 +393,52 @@ NLMISC::CQuat CStereoOVR::getOrientation() const /// Get GUI shift void CStereoOVR::getInterface2DShift(float &x, float &y, float distance) { +#if 0 + NLMISC::CVector vector = CVector(0.f, -distance, 0.f); NLMISC::CQuat rot = getOrientation(); rot.invert(); NLMISC::CMatrix mat; mat.rotate(rot); - if (m_Stage % 2) mat.translate(CVector(m_DevicePtr->HMDInfo.InterpupillaryDistance * -0.5f, 0.f, 0.f)); - else mat.translate(CVector(m_DevicePtr->HMDInfo.InterpupillaryDistance * 0.5f, 0.f, 0.f)); + //if (m_Stage % 2) mat.translate(CVector(m_DevicePtr->HMDInfo.InterpupillaryDistance * -0.5f, 0.f, 0.f)); + //else mat.translate(CVector(m_DevicePtr->HMDInfo.InterpupillaryDistance * 0.5f, 0.f, 0.f)); mat.translate(vector); CVector proj = CStereoOVR::getCurrentFrustum().project(mat.getPos()); - x = proj.x - 0.5f; - y = proj.y - 0.5f; + + NLMISC::CVector ipd; + if (m_Stage % 2) ipd = CVector(m_DevicePtr->HMDInfo.InterpupillaryDistance * -0.5f, 0.f, 0.f); + else ipd = CVector(m_DevicePtr->HMDInfo.InterpupillaryDistance * 0.5f, 0.f, 0.f); + CVector projipd = CStereoOVR::getCurrentFrustum().project(vector + ipd); + CVector projvec = CStereoOVR::getCurrentFrustum().project(vector); + + x = (proj.x + projipd.x - projvec.x - 0.5f); + y = (proj.y + projipd.y - projvec.y - 0.5f); + +#elif 1 + + // Alternative method + + NLMISC::CVector vec = CVector(0.f, -distance, 0.f); + NLMISC::CVector ipd; + if (m_Stage % 2) ipd = CVector(m_DevicePtr->HMDInfo.InterpupillaryDistance * -0.5f, 0.f, 0.f); + else ipd = CVector(m_DevicePtr->HMDInfo.InterpupillaryDistance * 0.5f, 0.f, 0.f); + + + NLMISC::CQuat rot = getOrientation(); + NLMISC::CQuat modrot = NLMISC::CQuat(CVector(0.f, 1.f, 0.f), NLMISC::Pi); + rot = rot * modrot; + float p = NLMISC::Pi + atan2f(2.0f * ((rot.x * rot.y) + (rot.z * rot.w)), 1.0f - 2.0f * ((rot.y * rot.y) + (rot.w * rot.w))); + if (p > NLMISC::Pi) p -= NLMISC::Pi * 2.0f; + float t = -atan2f(2.0f * ((rot.x * rot.w) + (rot.y * rot.z)), 1.0f - 2.0f * ((rot.z * rot.z) + (rot.w * rot.w)));// // asinf(2.0f * ((rot.x * rot.z) - (rot.w * rot.y))); + + CVector rotshift = CVector(p, 0.f, t) * -distance; + + CVector proj = CStereoOVR::getCurrentFrustum().project(vec + ipd + rotshift); + + x = (proj.x - 0.5f); + y = (proj.y - 0.5f); + +#endif } void CStereoOVR::listDevices(std::vector &devicesOut) diff --git a/code/snowballs2/bin/snowballs_client_default.cfg b/code/snowballs2/bin/snowballs_client_default.cfg index 758bd8483..9d1429f83 100755 --- a/code/snowballs2/bin/snowballs_client_default.cfg +++ b/code/snowballs2/bin/snowballs_client_default.cfg @@ -52,7 +52,7 @@ ScreenHeight = 800; ScreenDepth = 32; // If 1, run in fullscreen mode, 0 for windowed -ScreenFull = 0; +ScreenFull = 1; // Start position of the player (the z is always 0) StartPoint = { 1840.0, -970.0, 0.0 }; diff --git a/code/snowballs2/client/src/commands.cpp b/code/snowballs2/client/src/commands.cpp index 4a36db8bb..bd44411e1 100644 --- a/code/snowballs2/client/src/commands.cpp +++ b/code/snowballs2/client/src/commands.cpp @@ -388,7 +388,7 @@ void updateCommands() if (StereoHMD) { float xshift, yshift; - StereoHMD->getInterface2DShift(xshift, yshift, 4.0f); + StereoHMD->getInterface2DShift(xshift, yshift, 1.f); // snap to pixels xshift = ((float)(sint32)(xshift * width)) / width; yshift = ((float)(sint32)(yshift * height)) / height; diff --git a/code/snowballs2/client/src/compass.cpp b/code/snowballs2/client/src/compass.cpp index b915cdb52..285ba3bdc 100644 --- a/code/snowballs2/client/src/compass.cpp +++ b/code/snowballs2/client/src/compass.cpp @@ -98,7 +98,7 @@ void updateCompass () if (StereoHMD) { float xshift, yshift; - StereoHMD->getInterface2DShift(xshift, yshift, 4.0f); + StereoHMD->getInterface2DShift(xshift, yshift, 1.f); x += xshift; y += yshift; } From 4a579d0af236f2696ec0858115da38e4e5fcf9bb Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 26 Jun 2013 20:10:30 +0200 Subject: [PATCH 039/313] Fix warning --- code/nel/src/3d/stereo_ovr.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/code/nel/src/3d/stereo_ovr.cpp b/code/nel/src/3d/stereo_ovr.cpp index 1f62213eb..5ef7f9933 100644 --- a/code/nel/src/3d/stereo_ovr.cpp +++ b/code/nel/src/3d/stereo_ovr.cpp @@ -273,6 +273,10 @@ bool CStereoOVR::nextPass() m_OrientationCached = false; return false; } + nlassert(false); + m_Stage = 0; + m_OrientationCached = false; + return false; } const NL3D::CViewport &CStereoOVR::getCurrentViewport() const From 2f4867ab781b2b0c1e2125bc31c10a2ef194af9d Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 26 Jun 2013 20:57:37 +0200 Subject: [PATCH 040/313] Allow multiple user cameras to be calculated separately, useful for sky etc, re #43 --- code/nel/include/nel/3d/stereo_ovr.h | 20 ++++---- code/nel/src/3d/stereo_ovr.cpp | 46 +++++++++---------- code/snowballs2/client/src/camera.cpp | 2 +- code/snowballs2/client/src/commands.cpp | 2 +- code/snowballs2/client/src/compass.cpp | 2 +- .../client/src/snowballs_client.cpp | 8 ++-- 6 files changed, 41 insertions(+), 39 deletions(-) diff --git a/code/nel/include/nel/3d/stereo_ovr.h b/code/nel/include/nel/3d/stereo_ovr.h index bb85438ad..552995457 100644 --- a/code/nel/include/nel/3d/stereo_ovr.h +++ b/code/nel/include/nel/3d/stereo_ovr.h @@ -72,6 +72,8 @@ public: class CStereoOVRDevicePtr; +#define NL_STEREO_MAX_USER_CAMERAS 8 + /** * \brief CStereoOVR * \date 2013-06-25 22:22GMT @@ -88,18 +90,18 @@ public: /// Gets the required screen resolution for this device virtual void getScreenResolution(uint &width, uint &height); /// Set latest camera position etcetera - virtual void updateCamera(const NL3D::UCamera *camera); + virtual void updateCamera(uint cid, const NL3D::UCamera *camera); /// Is there a next pass virtual bool nextPass(); /// Gets the current viewport virtual const NL3D::CViewport &getCurrentViewport() const; /// Gets the current camera frustum - virtual const NL3D::CFrustum &getCurrentFrustum() const; + virtual const NL3D::CFrustum &getCurrentFrustum(uint cid) const; /// Gets the current camera frustum - virtual void getCurrentFrustum(NL3D::UCamera *camera) const; + virtual void getCurrentFrustum(uint cid, NL3D::UCamera *camera) const; /// Gets the current camera matrix - virtual void getCurrentMatrix(NL3D::UCamera *camera) const; + virtual void getCurrentMatrix(uint cid, NL3D::UCamera *camera) const; /// At the start of a new render target virtual bool beginClear(); @@ -122,7 +124,7 @@ public: /// Get the HMD orientation virtual NLMISC::CQuat getOrientation() const; /// Get GUI center (1 = width, 1 = height, 0 = center) - virtual void getInterface2DShift(float &x, float &y, float distance); + virtual void getInterface2DShift(uint cid, float &x, float &y, float distance); static void listDevices(std::vector &devicesOut); @@ -132,7 +134,7 @@ public: /// Calculates internal camera information based on the reference camera - void initCamera(const NL3D::UCamera *camera); + void initCamera(uint cid, const NL3D::UCamera *camera); bool isDeviceCreated(); @@ -141,9 +143,9 @@ private: int m_Stage; CViewport m_LeftViewport; CViewport m_RightViewport; - CFrustum m_LeftFrustum; - CFrustum m_RightFrustum; - CMatrix m_CameraMatrix; + CFrustum m_LeftFrustum[NL_STEREO_MAX_USER_CAMERAS]; + CFrustum m_RightFrustum[NL_STEREO_MAX_USER_CAMERAS]; + CMatrix m_CameraMatrix[NL_STEREO_MAX_USER_CAMERAS]; mutable bool m_OrientationCached; mutable NLMISC::CQuat m_OrientationCache; diff --git a/code/nel/src/3d/stereo_ovr.cpp b/code/nel/src/3d/stereo_ovr.cpp index 5ef7f9933..4b2417571 100644 --- a/code/nel/src/3d/stereo_ovr.cpp +++ b/code/nel/src/3d/stereo_ovr.cpp @@ -204,29 +204,29 @@ void CStereoOVR::getScreenResolution(uint &width, uint &height) height = m_DevicePtr->HMDInfo.VResolution; } -void CStereoOVR::initCamera(const NL3D::UCamera *camera) +void CStereoOVR::initCamera(uint cid, const NL3D::UCamera *camera) { float ar = (float)m_DevicePtr->HMDInfo.HResolution / ((float)m_DevicePtr->HMDInfo.VResolution * 2.0f); float fov = 2.0f * atanf((m_DevicePtr->HMDInfo.VScreenSize / 2.0f) / m_DevicePtr->HMDInfo.EyeToScreenDistance); //(float)NLMISC::Pi/2.f; // 2.0f * atanf(m_DevicePtr->HMDInfo.VScreenSize / 2.0f * m_DevicePtr->HMDInfo.EyeToScreenDistance); - m_LeftFrustum.initPerspective(fov, ar, camera->getFrustum().Near, camera->getFrustum().Far); - m_RightFrustum = m_LeftFrustum; + m_LeftFrustum[cid].initPerspective(fov, ar, camera->getFrustum().Near, camera->getFrustum().Far); + m_RightFrustum[cid] = m_LeftFrustum[cid]; float viewCenter = m_DevicePtr->HMDInfo.HScreenSize * 0.25f; float eyeProjectionShift = viewCenter - m_DevicePtr->HMDInfo.LensSeparationDistance * 0.5f; float projectionCenterOffset = 4.0f * eyeProjectionShift / m_DevicePtr->HMDInfo.HScreenSize; nldebug("OVR: projectionCenterOffset = %f", projectionCenterOffset); - projectionCenterOffset *= (m_LeftFrustum.Left - m_LeftFrustum.Right) * 0.5f; // made this up ... - m_LeftFrustum.Left += projectionCenterOffset; - m_LeftFrustum.Right += projectionCenterOffset; - m_RightFrustum.Left -= projectionCenterOffset; - m_RightFrustum.Right -= projectionCenterOffset; + projectionCenterOffset *= (m_LeftFrustum[cid].Left - m_LeftFrustum[cid].Right) * 0.5f; // made this up ... + m_LeftFrustum[cid].Left += projectionCenterOffset; + m_LeftFrustum[cid].Right += projectionCenterOffset; + m_RightFrustum[cid].Left -= projectionCenterOffset; + m_RightFrustum[cid].Right -= projectionCenterOffset; } -void CStereoOVR::updateCamera(const NL3D::UCamera *camera) +void CStereoOVR::updateCamera(uint cid, const NL3D::UCamera *camera) { - if (camera->getFrustum().Near != m_LeftFrustum.Near - || camera->getFrustum().Far != m_LeftFrustum.Far) - CStereoOVR::initCamera(camera); - m_CameraMatrix = camera->getMatrix(); + if (camera->getFrustum().Near != m_LeftFrustum[cid].Near + || camera->getFrustum().Far != m_LeftFrustum[cid].Far) + CStereoOVR::initCamera(cid, camera); + m_CameraMatrix[cid] = camera->getMatrix(); } bool CStereoOVR::nextPass() @@ -285,25 +285,25 @@ const NL3D::CViewport &CStereoOVR::getCurrentViewport() const else return m_RightViewport; } -const NL3D::CFrustum &CStereoOVR::getCurrentFrustum() const +const NL3D::CFrustum &CStereoOVR::getCurrentFrustum(uint cid) const { - if (m_Stage % 2) return m_LeftFrustum; - else return m_RightFrustum; + if (m_Stage % 2) return m_LeftFrustum[cid]; + else return m_RightFrustum[cid]; } -void CStereoOVR::getCurrentFrustum(NL3D::UCamera *camera) const +void CStereoOVR::getCurrentFrustum(uint cid, NL3D::UCamera *camera) const { - if (m_Stage % 2) camera->setFrustum(m_LeftFrustum); - else camera->setFrustum(m_RightFrustum); + if (m_Stage % 2) camera->setFrustum(m_LeftFrustum[cid]); + else camera->setFrustum(m_RightFrustum[cid]); } -void CStereoOVR::getCurrentMatrix(NL3D::UCamera *camera) const +void CStereoOVR::getCurrentMatrix(uint cid, NL3D::UCamera *camera) const { CMatrix translate; if (m_Stage % 2) translate.translate(CVector(m_DevicePtr->HMDInfo.InterpupillaryDistance * -0.5f, 0.f, 0.f)); else translate.translate(CVector(m_DevicePtr->HMDInfo.InterpupillaryDistance * 0.5f, 0.f, 0.f)); camera->setTransformMode(NL3D::UTransformable::DirectMatrix); - camera->setMatrix(m_CameraMatrix * translate); + camera->setMatrix(m_CameraMatrix[cid] * translate); } bool CStereoOVR::beginClear() @@ -395,7 +395,7 @@ NLMISC::CQuat CStereoOVR::getOrientation() const } /// Get GUI shift -void CStereoOVR::getInterface2DShift(float &x, float &y, float distance) +void CStereoOVR::getInterface2DShift(uint cid, float &x, float &y, float distance) { #if 0 @@ -437,7 +437,7 @@ void CStereoOVR::getInterface2DShift(float &x, float &y, float distance) CVector rotshift = CVector(p, 0.f, t) * -distance; - CVector proj = CStereoOVR::getCurrentFrustum().project(vec + ipd + rotshift); + CVector proj = CStereoOVR::getCurrentFrustum(cid).project(vec + ipd + rotshift); x = (proj.x - 0.5f); y = (proj.y - 0.5f); diff --git a/code/snowballs2/client/src/camera.cpp b/code/snowballs2/client/src/camera.cpp index 030ebe0d9..ef65a7a9f 100644 --- a/code/snowballs2/client/src/camera.cpp +++ b/code/snowballs2/client/src/camera.cpp @@ -131,7 +131,7 @@ void initCamera() if (StereoHMD) { - StereoHMD->initCamera(&Camera); + StereoHMD->initCamera(0, &Camera); } // Create the snowing particle system diff --git a/code/snowballs2/client/src/commands.cpp b/code/snowballs2/client/src/commands.cpp index bd44411e1..e6e7a0086 100644 --- a/code/snowballs2/client/src/commands.cpp +++ b/code/snowballs2/client/src/commands.cpp @@ -388,7 +388,7 @@ void updateCommands() if (StereoHMD) { float xshift, yshift; - StereoHMD->getInterface2DShift(xshift, yshift, 1.f); + StereoHMD->getInterface2DShift(0, xshift, yshift, 1.f); // snap to pixels xshift = ((float)(sint32)(xshift * width)) / width; yshift = ((float)(sint32)(yshift * height)) / height; diff --git a/code/snowballs2/client/src/compass.cpp b/code/snowballs2/client/src/compass.cpp index 285ba3bdc..e01173f66 100644 --- a/code/snowballs2/client/src/compass.cpp +++ b/code/snowballs2/client/src/compass.cpp @@ -98,7 +98,7 @@ void updateCompass () if (StereoHMD) { float xshift, yshift; - StereoHMD->getInterface2DShift(xshift, yshift, 1.f); + StereoHMD->getInterface2DShift(0, xshift, yshift, 1.f); x += xshift; y += yshift; } diff --git a/code/snowballs2/client/src/snowballs_client.cpp b/code/snowballs2/client/src/snowballs_client.cpp index 9563ad774..292465266 100644 --- a/code/snowballs2/client/src/snowballs_client.cpp +++ b/code/snowballs2/client/src/snowballs_client.cpp @@ -710,7 +710,7 @@ void loopIngame() // 09. Update Camera (depends on entities) updateCamera(); - if (StereoHMD) StereoHMD->updateCamera(&Camera); + if (StereoHMD) StereoHMD->updateCamera(0, &Camera); // 10. Update Interface (login, ui, etc) // ... @@ -741,9 +741,9 @@ void loopIngame() Driver->setViewport(vp); Scene->setViewport(vp); SkyScene->setViewport(vp); - StereoHMD->getCurrentFrustum(&Camera); - StereoHMD->getCurrentFrustum(&SkyCamera); - StereoHMD->getCurrentMatrix(&Camera); + StereoHMD->getCurrentFrustum(0, &Camera); + StereoHMD->getCurrentFrustum(0, &SkyCamera); + StereoHMD->getCurrentMatrix(0, &Camera); } if (!StereoHMD || StereoHMD->beginClear()) From d404c1228cc497e6f4f3950a12b49ecc8af58bb8 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 26 Jun 2013 21:35:36 +0200 Subject: [PATCH 041/313] Create a clipping frustum, ref #43 --- code/nel/include/nel/3d/stereo_ovr.h | 3 +++ code/nel/src/3d/stereo_ovr.cpp | 11 +++++++++++ 2 files changed, 14 insertions(+) diff --git a/code/nel/include/nel/3d/stereo_ovr.h b/code/nel/include/nel/3d/stereo_ovr.h index 552995457..ba8d6eac6 100644 --- a/code/nel/include/nel/3d/stereo_ovr.h +++ b/code/nel/include/nel/3d/stereo_ovr.h @@ -91,6 +91,8 @@ public: virtual void getScreenResolution(uint &width, uint &height); /// Set latest camera position etcetera virtual void updateCamera(uint cid, const NL3D::UCamera *camera); + /// Get the frustum to use for clipping + virtual void getClippingFrustum(uint cid, NL3D::UCamera *camera) const; /// Is there a next pass virtual bool nextPass(); @@ -143,6 +145,7 @@ private: int m_Stage; CViewport m_LeftViewport; CViewport m_RightViewport; + CFrustum m_ClippingFrustum[NL_STEREO_MAX_USER_CAMERAS]; CFrustum m_LeftFrustum[NL_STEREO_MAX_USER_CAMERAS]; CFrustum m_RightFrustum[NL_STEREO_MAX_USER_CAMERAS]; CMatrix m_CameraMatrix[NL_STEREO_MAX_USER_CAMERAS]; diff --git a/code/nel/src/3d/stereo_ovr.cpp b/code/nel/src/3d/stereo_ovr.cpp index 4b2417571..f6c063de4 100644 --- a/code/nel/src/3d/stereo_ovr.cpp +++ b/code/nel/src/3d/stereo_ovr.cpp @@ -219,6 +219,17 @@ void CStereoOVR::initCamera(uint cid, const NL3D::UCamera *camera) m_LeftFrustum[cid].Right += projectionCenterOffset; m_RightFrustum[cid].Left -= projectionCenterOffset; m_RightFrustum[cid].Right -= projectionCenterOffset; + + // TODO: Clipping frustum should also take into account the IPD + m_ClippingFrustum[cid] = m_LeftFrustum[cid]; + m_ClippingFrustum[cid].Left = min(m_LeftFrustum[cid].Left, m_RightFrustum[cid].Left); + m_ClippingFrustum[cid].Right = max(m_LeftFrustum[cid].Right, m_RightFrustum[cid].Right); +} + +/// Get the frustum to use for clipping +void CStereoOVR::getClippingFrustum(uint cid, NL3D::UCamera *camera) const +{ + camera->setFrustum(m_ClippingFrustum[cid]); } void CStereoOVR::updateCamera(uint cid, const NL3D::UCamera *camera) From 9526910d4ba7017b2947606742502787f9d5eb53 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 26 Jun 2013 21:36:21 +0200 Subject: [PATCH 042/313] Return view as CQuat, see #43 --- code/ryzom/client/src/global.cpp | 2 ++ code/ryzom/client/src/global.h | 3 +++ code/ryzom/client/src/main_loop.cpp | 12 ++++++++---- code/ryzom/client/src/view.cpp | 9 ++++++++- code/ryzom/client/src/view.h | 2 ++ 5 files changed, 23 insertions(+), 5 deletions(-) diff --git a/code/ryzom/client/src/global.cpp b/code/ryzom/client/src/global.cpp index 8bd6f10fa..bb60f5c04 100644 --- a/code/ryzom/client/src/global.cpp +++ b/code/ryzom/client/src/global.cpp @@ -26,6 +26,8 @@ using namespace NLMISC; // *************************************************************************** // Main System NL3D::UDriver *Driver = 0; // The main 3D Driver +NL3D::CStereoOVR *StereoDisplay = NULL; // Stereo display +NL3D::CStereoOVR *StereoHMD = NULL; // Head mount display CSoundManager *SoundMngr = 0; // the sound manager NL3D::UMaterial GenericMat; // Generic Material NL3D::UTextContext *TextContext = 0; // Context for all the text in the client. diff --git a/code/ryzom/client/src/global.h b/code/ryzom/client/src/global.h index a6b7a03c6..6ec3db7ec 100644 --- a/code/ryzom/client/src/global.h +++ b/code/ryzom/client/src/global.h @@ -40,6 +40,7 @@ namespace NL3D class UMaterial; class UTextContext; class UWaterEnvMap; + class CStereoOVR; } class CEntityAnimationManager; @@ -77,6 +78,8 @@ const float ExtraZoneLoadingVision = 100.f; // *************************************************************************** // Main System extern NL3D::UDriver *Driver; // The main 3D Driver +extern NL3D::CStereoOVR *StereoDisplay; // Stereo display +extern NL3D::CStereoOVR *StereoHMD; extern CSoundManager *SoundMngr; // the sound manager extern NL3D::UMaterial GenericMat; // Generic Material extern NL3D::UTextContext *TextContext; // Context for all the text in the client. diff --git a/code/ryzom/client/src/main_loop.cpp b/code/ryzom/client/src/main_loop.cpp index 2c362aceb..c034eaa83 100644 --- a/code/ryzom/client/src/main_loop.cpp +++ b/code/ryzom/client/src/main_loop.cpp @@ -42,6 +42,7 @@ #include "nel/3d/u_material.h" #include "nel/3d/u_instance_material.h" #include "nel/3d/u_cloud_scape.h" +#include "nel/3d/stereo_ovr.h" // game share #include "game_share/brick_types.h" #include "game_share/light_cycle.h" @@ -136,6 +137,7 @@ #include "nel/3d/driver_user.h" + #ifdef USE_WATER_ENV_MAP #include "water_env_map_rdr.h" #endif @@ -452,6 +454,8 @@ void validateDialogs(const CGameContextMenu &gcm); void buildCameraClippingPyramid (vector &planes) { + if (StereoDisplay) StereoDisplay->getClippingFrustum(0, &MainCam); + // Compute pyramid in view basis. CVector pfoc(0,0,0); const CFrustum &frustum = MainCam.getFrustum(); @@ -1006,7 +1010,7 @@ static void renderCanopyPart(UScene::TRenderPart renderPart) { // Update Camera Position/Rotation. camRoot.setPos(View.currentViewPos()); - camRoot.setRotQuat(View.currentView()); + camRoot.setRotQuat(View.currentViewQuat()); } // Render the root scene SceneRoot->renderPart(renderPart); @@ -1859,9 +1863,9 @@ bool mainLoop() // Update Camera Position/Orientation. CVector currViewPos = View.currentViewPos(); - MainCam.setPos(currViewPos);; - MainCam.setRotQuat(View.currentView()); - + MainCam.setPos(currViewPos); + MainCam.setRotQuat(View.currentViewQuat()); + if (StereoDisplay) StereoDisplay->updateCamera(0, &MainCam); // see if camera is below water (useful for sort order) if (ContinentMngr.cur()) diff --git a/code/ryzom/client/src/view.cpp b/code/ryzom/client/src/view.cpp index ded81013d..4bc52af08 100644 --- a/code/ryzom/client/src/view.cpp +++ b/code/ryzom/client/src/view.cpp @@ -183,7 +183,6 @@ CVector CView::currentViewPos() const //----------------------------------------------- // currentView : -// Set the user position. //----------------------------------------------- CVector CView::currentView() const { @@ -200,6 +199,14 @@ CVector CView::currentView() const return _View; }// currentView // +NLMISC::CQuat CView::currentViewQuat() const +{ + CMatrix mat; + mat.setRot(CVector::I, currentView(), CVector::K); + mat.normalize(CMatrix::YZX); + return mat.getRot(); +} + //----------------------------------------------- // currentCameraTarget : //----------------------------------------------- diff --git a/code/ryzom/client/src/view.h b/code/ryzom/client/src/view.h index 69fcea994..e1c57d4ca 100644 --- a/code/ryzom/client/src/view.h +++ b/code/ryzom/client/src/view.h @@ -115,6 +115,8 @@ public: CVector currentViewPos() const; // Return the current view (rear or normal) CVector currentView() const; + // Return the current view as a quaternion + NLMISC::CQuat currentViewQuat() const; // Return the current Camera Target (for 3rd person only. 1st person: return currentViewPos()) CVector currentCameraTarget() const; From b9334e372228e18938c74ad1c47569a9c692cbd2 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Thu, 27 Jun 2013 01:23:53 +0200 Subject: [PATCH 043/313] Pull CPing out of main_loop.cpp, refs #43 --- code/ryzom/client/src/main_loop.cpp | 80 +---------------------------- code/ryzom/client/src/ping.cpp | 73 ++++++++++++++++++++++++++ code/ryzom/client/src/ping.h | 62 ++++++++++++++++++++++ 3 files changed, 137 insertions(+), 78 deletions(-) create mode 100644 code/ryzom/client/src/ping.cpp create mode 100644 code/ryzom/client/src/ping.h diff --git a/code/ryzom/client/src/main_loop.cpp b/code/ryzom/client/src/main_loop.cpp index c034eaa83..8cccb99f2 100644 --- a/code/ryzom/client/src/main_loop.cpp +++ b/code/ryzom/client/src/main_loop.cpp @@ -150,6 +150,8 @@ #include "nel/gui/lua_manager.h" #include "nel/gui/group_table.h" +#include "ping.h" + /////////// // USING // @@ -218,84 +220,6 @@ uint64 SimulatedServerTick = 0; -/////////// -// CLASS // -/////////// -/** - * Class to manage the ping computed with the database. - * \author Guillaume PUZIN - * \author Nevrax France - * \date 2003 - */ -class CPing : public ICDBNode::IPropertyObserver -{ -private: - uint32 _Ping; - bool _RdyToPing; - -public: - // Constructor. - CPing() {_Ping = 0; _RdyToPing = true;} - // Destructor. - ~CPing() {;} - - // Add an observer on the database for the ping. - void init() - { - CInterfaceManager *pIM = CInterfaceManager::getInstance(); - if(pIM) - { - CCDBNodeLeaf *pNodeLeaf = NLGUI::CDBManager::getInstance()->getDbProp("SERVER:DEBUG_INFO:Ping", false); - if(pNodeLeaf) - { - ICDBNode::CTextId textId; - pNodeLeaf->addObserver(this, textId); - // nlwarning("CPing: cannot add the observer"); - } - else - nlwarning("CPing: 'SERVER:DEBUG_INFO:Ping' does not exist."); - } - } - - // Release the observer on the database for the ping. - void release() - { - CInterfaceManager *pIM = CInterfaceManager::getInstance(); - if(pIM) - { - CCDBNodeLeaf *pNodeLeaf = NLGUI::CDBManager::getInstance()->getDbProp("SERVER:DEBUG_INFO:Ping", false); - if(pNodeLeaf) - { - ICDBNode::CTextId textId; - pNodeLeaf->removeObserver(this, textId); - } - else - nlwarning("CPing: 'SERVER:DEBUG_INFO:Ping' does not exist."); - } - } - - // Method called when the ping message is back. - virtual void update(ICDBNode* node) - { - CCDBNodeLeaf *leaf = safe_cast(node); - uint32 before = (uint32)leaf->getValue32(); - uint32 current = (uint32)(0xFFFFFFFF & ryzomGetLocalTime()); - if(before > current) - { - //nlwarning("DB PING Pb before '%u' after '%u'.", before, current); - if(ClientCfg.Check) - nlstop; - } - _Ping = current - before; - _RdyToPing = true; - } - - // return the ping in ms. - uint32 getValue() {return _Ping;} - - void rdyToPing(bool rdy) {_RdyToPing = rdy;} - bool rdyToPing() const {return _RdyToPing;} -}; ///////////// // GLOBALS // diff --git a/code/ryzom/client/src/ping.cpp b/code/ryzom/client/src/ping.cpp new file mode 100644 index 000000000..a6d5c2e2c --- /dev/null +++ b/code/ryzom/client/src/ping.cpp @@ -0,0 +1,73 @@ +// Ryzom - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#include "ping.h" + +#include "interface_v3/interface_manager.h" +#include "time_client.h" + +using namespace NLMISC; +using namespace NLGUI; + +void CPing::init() +{ + CInterfaceManager *pIM = CInterfaceManager::getInstance(); + if(pIM) + { + CCDBNodeLeaf *pNodeLeaf = CDBManager::getInstance()->getDbProp("SERVER:DEBUG_INFO:Ping", false); + if(pNodeLeaf) + { + ICDBNode::CTextId textId; + pNodeLeaf->addObserver(this, textId); + // nlwarning("CPing: cannot add the observer"); + } + else + nlwarning("CPing: 'SERVER:DEBUG_INFO:Ping' does not exist."); + } +} + +void CPing::release() +{ + CInterfaceManager *pIM = CInterfaceManager::getInstance(); + if(pIM) + { + CCDBNodeLeaf *pNodeLeaf = CDBManager::getInstance()->getDbProp("SERVER:DEBUG_INFO:Ping", false); + if(pNodeLeaf) + { + ICDBNode::CTextId textId; + pNodeLeaf->removeObserver(this, textId); + } + else + nlwarning("CPing: 'SERVER:DEBUG_INFO:Ping' does not exist."); + } +} + +void CPing::update(NLMISC::ICDBNode* node) +{ + CCDBNodeLeaf *leaf = safe_cast(node); + uint32 before = (uint32)leaf->getValue32(); + uint32 current = (uint32)(0xFFFFFFFF & ryzomGetLocalTime()); + if(before > current) + { + //nlwarning("DB PING Pb before '%u' after '%u'.", before, current); + if(ClientCfg.Check) + nlstop; + } + _Ping = current - before; + _RdyToPing = true; +} + +/* end of file */ \ No newline at end of file diff --git a/code/ryzom/client/src/ping.h b/code/ryzom/client/src/ping.h new file mode 100644 index 000000000..46a7e2c13 --- /dev/null +++ b/code/ryzom/client/src/ping.h @@ -0,0 +1,62 @@ +// Ryzom - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#ifndef CL_PING_H +#define CL_PING_H + +#include +#include + +/////////// +// CLASS // +/////////// +/** + * Class to manage the ping computed with the database. + * \author Guillaume PUZIN + * \author Nevrax France + * \date 2003 + */ +class CPing : public NLMISC::ICDBNode::IPropertyObserver +{ +private: + uint32 _Ping; + bool _RdyToPing; + +public: + // Constructor. + CPing() {_Ping = 0; _RdyToPing = true;} + // Destructor. + ~CPing() {;} + + // Add an observer on the database for the ping. + void init(); + + // Release the observer on the database for the ping. + void release(); + + // Method called when the ping message is back. + virtual void update(NLMISC::ICDBNode* node); + + // return the ping in ms. + uint32 getValue() {return _Ping;} + + void rdyToPing(bool rdy) {_RdyToPing = rdy;} + bool rdyToPing() const {return _RdyToPing;} +}; + +#endif // CL_PING_H + +/* end of file */ \ No newline at end of file From 825ebb3335e5f2509b657467ff9df0d7bf562a7e Mon Sep 17 00:00:00 2001 From: kaetemi Date: Thu, 27 Jun 2013 01:43:04 +0200 Subject: [PATCH 044/313] Separate some profiling code from main_loop.cpp, see #43 --- code/ryzom/client/src/main_loop.cpp | 119 +-------------------- code/ryzom/client/src/profiling.cpp | 159 ++++++++++++++++++++++++++++ code/ryzom/client/src/profiling.h | 36 +++++++ 3 files changed, 197 insertions(+), 117 deletions(-) create mode 100644 code/ryzom/client/src/profiling.cpp create mode 100644 code/ryzom/client/src/profiling.h diff --git a/code/ryzom/client/src/main_loop.cpp b/code/ryzom/client/src/main_loop.cpp index 8cccb99f2..bd79d3490 100644 --- a/code/ryzom/client/src/main_loop.cpp +++ b/code/ryzom/client/src/main_loop.cpp @@ -150,7 +150,9 @@ #include "nel/gui/lua_manager.h" #include "nel/gui/group_table.h" +// pulled from main_loop.cpp #include "ping.h" +#include "profiling.h" /////////// @@ -253,12 +255,6 @@ uint8 ShowInfos = 0; // 0=no info 1=text info 2=graph info bool bZeroCpu = false; // For no Cpu use if application is minimize TODO: intercept minimize message, called by CTRL + Z at this -bool Profiling = false; // Are we in Profile mode? -uint ProfileNumFrame = 0; -bool WantProfiling = false; -bool ProfilingVBLock = false; -bool WantProfilingVBLock = false; - bool MovieShooterSaving= false; // Are we in Shooting mode? @@ -415,119 +411,8 @@ void buildCameraClippingPyramid (vector &planes) } -//--------------------------------------------------- -// Test Profiling and run? -//--------------------------------------------------- -void testLaunchProfile() -{ - if(!WantProfiling) - return; - - // comes from ActionHandler - WantProfiling= false; - -#ifdef _PROFILE_ON_ - if( !Profiling ) - { - // start the bench. - NLMISC::CHTimer::startBench(); - ProfileNumFrame = 0; - Driver->startBench(); - if (SoundMngr) - SoundMngr->getMixer()->startDriverBench(); - // state - Profiling= true; - } - else - { - // end the bench. - if (SoundMngr) - SoundMngr->getMixer()->endDriverBench(); - NLMISC::CHTimer::endBench(); - Driver->endBench(); - // Display and save profile to a File. - CLog log; - CFileDisplayer fileDisplayer(NLMISC::CFile::findNewFile(getLogDirectory() + "profile.log")); - CStdDisplayer stdDisplayer; - log.addDisplayer(&fileDisplayer); - log.addDisplayer(&stdDisplayer); - // diplay - NLMISC::CHTimer::displayHierarchicalByExecutionPathSorted(&log, CHTimer::TotalTime, true, 48, 2); - NLMISC::CHTimer::displayHierarchical(&log, true, 48, 2); - NLMISC::CHTimer::displayByExecutionPath(&log, CHTimer::TotalTime); - NLMISC::CHTimer::display(&log, CHTimer::TotalTime); - NLMISC::CHTimer::display(&log, CHTimer::TotalTimeWithoutSons); - Driver->displayBench(&log); - - if (SoundMngr) - SoundMngr->getMixer()->displayDriverBench(&log); - - // state - Profiling= false; - } -#endif // #ifdef _PROFILE_ON_ -} - - -//--------------------------------------------------- -// Test ProfilingVBLock and run? -//--------------------------------------------------- -void testLaunchProfileVBLock() -{ - // If running, must stop for this frame. - if(ProfilingVBLock) - { - vector strs; - Driver->endProfileVBHardLock(strs); - nlinfo("Profile VBLock"); - nlinfo("**************"); - for(uint i=0;iprofileVBHardAllocation(strs); - for(uint i=0;iendProfileIBLock(strs); - nlinfo("Profile Index Buffer Lock"); - nlinfo("**************"); - for(uint i=0;iprofileIBAllocation(strs); - for(uint i=0;istartProfileVBHardLock(); - Driver->startProfileIBLock(); - } -} //--------------------------------------------------- diff --git a/code/ryzom/client/src/profiling.cpp b/code/ryzom/client/src/profiling.cpp new file mode 100644 index 000000000..54f830211 --- /dev/null +++ b/code/ryzom/client/src/profiling.cpp @@ -0,0 +1,159 @@ +// Ryzom - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#include "profiling.h" + +// NeL includes +#include + +// Project includes +#include "misc.h" +#include "sound_manager.h" + +/////////// +// USING // +/////////// +using namespace NLMISC; +using namespace NL3D; + +//////////// +// EXTERN // +//////////// +extern UDriver *Driver; + +///////////// +// GLOBALS // +///////////// +bool Profiling = false; // Are we in Profile mode? +uint ProfileNumFrame = 0; +bool WantProfiling = false; +bool ProfilingVBLock = false; +bool WantProfilingVBLock = false; + +// ******************************************************************** + +/// Test Profiling and run? +void testLaunchProfile() +{ + if(!WantProfiling) + return; + + // comes from ActionHandler + WantProfiling= false; + +#ifdef _PROFILE_ON_ + if( !Profiling ) + { + // start the bench. + NLMISC::CHTimer::startBench(); + ProfileNumFrame = 0; + Driver->startBench(); + if (SoundMngr) + SoundMngr->getMixer()->startDriverBench(); + // state + Profiling= true; + } + else + { + // end the bench. + if (SoundMngr) + SoundMngr->getMixer()->endDriverBench(); + NLMISC::CHTimer::endBench(); + Driver->endBench(); + + + // Display and save profile to a File. + CLog log; + CFileDisplayer fileDisplayer(NLMISC::CFile::findNewFile(getLogDirectory() + "profile.log")); + CStdDisplayer stdDisplayer; + log.addDisplayer(&fileDisplayer); + log.addDisplayer(&stdDisplayer); + // diplay + NLMISC::CHTimer::displayHierarchicalByExecutionPathSorted(&log, CHTimer::TotalTime, true, 48, 2); + NLMISC::CHTimer::displayHierarchical(&log, true, 48, 2); + NLMISC::CHTimer::displayByExecutionPath(&log, CHTimer::TotalTime); + NLMISC::CHTimer::display(&log, CHTimer::TotalTime); + NLMISC::CHTimer::display(&log, CHTimer::TotalTimeWithoutSons); + Driver->displayBench(&log); + + if (SoundMngr) + SoundMngr->getMixer()->displayDriverBench(&log); + + // state + Profiling= false; + } +#endif // #ifdef _PROFILE_ON_ +} + +// ******************************************************************** + +/// Test ProfilingVBLock and run? +void testLaunchProfileVBLock() +{ + // If running, must stop for this frame. + if(ProfilingVBLock) + { + std::vector strs; + Driver->endProfileVBHardLock(strs); + nlinfo("Profile VBLock"); + nlinfo("**************"); + for(uint i=0;iprofileVBHardAllocation(strs); + for(uint i=0;iendProfileIBLock(strs); + nlinfo("Profile Index Buffer Lock"); + nlinfo("**************"); + for(uint i=0;iprofileIBAllocation(strs); + for(uint i=0;istartProfileVBHardLock(); + Driver->startProfileIBLock(); + } +} + +/* end of file */ \ No newline at end of file diff --git a/code/ryzom/client/src/profiling.h b/code/ryzom/client/src/profiling.h new file mode 100644 index 000000000..cb7a55495 --- /dev/null +++ b/code/ryzom/client/src/profiling.h @@ -0,0 +1,36 @@ +// Ryzom - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#ifndef CL_MAIN_LOOP_PROFILING_H +#define CL_MAIN_LOOP_PROFILING_H + +#include + +extern bool Profiling; // Are we in Profile mode? +extern uint ProfileNumFrame; +extern bool WantProfiling; +extern bool ProfilingVBLock; +extern bool WantProfilingVBLock; + +/// Test Profiling and run? +void testLaunchProfile(); + +/// Test ProfilingVBLock and run? +void testLaunchProfileVBLock(); + +#endif // CL_MAIN_LOOP_PROFILING_H + +/* end of file */ \ No newline at end of file From a046b1c494614ca66599bbf71e9a65c866cd9a79 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Thu, 27 Jun 2013 02:13:48 +0200 Subject: [PATCH 045/313] Take some debug render code out of main_loop.cpp, see #43 --- code/ryzom/client/src/main_loop.cpp | 497 +------------------ code/ryzom/client/src/main_loop_debug.cpp | 560 ++++++++++++++++++++++ code/ryzom/client/src/main_loop_debug.h | 31 ++ code/ryzom/client/src/ping.cpp | 1 + code/ryzom/client/src/profiling.cpp | 1 + code/ryzom/client/src/profiling.h | 6 +- 6 files changed, 601 insertions(+), 495 deletions(-) create mode 100644 code/ryzom/client/src/main_loop_debug.cpp create mode 100644 code/ryzom/client/src/main_loop_debug.h diff --git a/code/ryzom/client/src/main_loop.cpp b/code/ryzom/client/src/main_loop.cpp index bd79d3490..4ed395d29 100644 --- a/code/ryzom/client/src/main_loop.cpp +++ b/code/ryzom/client/src/main_loop.cpp @@ -153,6 +153,7 @@ // pulled from main_loop.cpp #include "ping.h" #include "profiling.h" +#include "main_loop_debug.h" /////////// @@ -178,7 +179,6 @@ static void viewportToScissor(const CViewport &vp, CScissor &scissor) //////////// // EXTERN // //////////// -extern std::set LodCharactersNotFound; extern UDriver *Driver; extern IMouseDevice *MouseDevice; extern UScene *Scene; @@ -193,13 +193,16 @@ extern TTime UniversalTime; extern UMaterial GenericMat; extern UCamera MainCam; extern CEventsListener EventsListener; -extern uint32 NbDatabaseChanges; extern CMatrix MainSceneViewMatrix; extern CMatrix InvMainSceneViewMatrix; extern std::vector LogoBitmaps; extern bool IsInRingSession; extern std::string UsedFSAddr; +// temp +extern NLMISC::CValueSmoother smoothFPS; +extern NLMISC::CValueSmoother moreSmoothFPS; + void loadBackgroundBitmap (TBackground background); void destroyLoadingBitmap (); void drawLoadingBitmap (float progress); @@ -301,8 +304,6 @@ CGameContextMenu GameContextMenu; -NLMISC::CValueSmoother smoothFPS; -NLMISC::CValueSmoother moreSmoothFPS(64); // Profile @@ -344,12 +345,6 @@ H_AUTO_DECL ( RZ_Client_Main_Loop_Net ) /////////////// // FUNCTIONS // /////////////// -// Display some debug infos. -void displayDebug(); -void displayDebugFps(); -void displayDebugUIUnderMouse(); -// Display an Help. -void displayHelp(); //update the sound manager (listener pos, user walk/run sound...) void updateSound(); @@ -3056,488 +3051,6 @@ class CHandlerDebugUiDumpElementUnderMouse : public IActionHandler REGISTER_ACTION_HANDLER( CHandlerDebugUiDumpElementUnderMouse, "debug_ui_inspect_element_under_mouse"); -//--------------------------------------------------- -// displayDebug : -// Display some debug infos. -//--------------------------------------------------- -void displayDebug() -{ - float lineStep = ClientCfg.DebugLineStep; - float line; - - // Initialize Pen // - //----------------// - // Create a shadow when displaying a text. - TextContext->setShaded(true); - // Set the font size. - TextContext->setFontSize(ClientCfg.DebugFontSize); - // Set the text color - TextContext->setColor(ClientCfg.DebugFontColor); - - // TOP LEFT // - //----------// - TextContext->setHotSpot(UTextContext::TopLeft); - line = 0.9f; - // FPS and Ms per frame - { - // smooth across frames. - double deltaTime = smoothFPS.getSmoothValue (); - // FPS and Ms per frame - if(deltaTime != 0.f) - TextContext->printfAt(0.f, line,"%.1f fps", 1.f/deltaTime); - else - TextContext->printfAt(0.f, line,"%.1f fps", 0.f); - TextContext->printfAt(0.1f, line, "%d ms", (uint)(deltaTime*1000)); - } - line -= lineStep; - line -= lineStep; - - // USER - // Front - TextContext->printfAt(0.0f, line, " %f (%f,%f,%f) front", atan2(UserEntity->front().y, UserEntity->front().x), UserEntity->front().x, UserEntity->front().y, UserEntity->front().z); - line -= lineStep; - // Dir - TextContext->printfAt(0.0f, line, " %f (%f,%f,%f) dir", atan2(UserEntity->dir().y, UserEntity->dir().x), UserEntity->dir().x, UserEntity->dir().y, UserEntity->dir().z); - line -= lineStep; - // NB Stage - TextContext->printfAt(0.0f, line, " NB Stage: %d", UserEntity->nbStage()); - line -= lineStep; - // NB Animation FXs still remaining in the remove list. - TextContext->printfAt(0.0f, line, " NB FXs to remove: %d", UserEntity->nbAnimFXToRemove()); - line -= lineStep; - // Mode. - TextContext->printfAt(0.0f, line, " Mode: %d (%s)", (sint)UserEntity->mode(), MBEHAV::modeToString(UserEntity->mode()).c_str()); - line -= lineStep; - // Behaviour. - TextContext->printfAt(0.0f, line, " Behaviour: %d (%s)", (sint)UserEntity->behaviour(), MBEHAV::behaviourToString(UserEntity->behaviour()).c_str()); - line -= lineStep; - // Display the target mount. - TextContext->printfAt(0.0f, line, " Mount: %d", UserEntity->mount()); - line -= lineStep; - // Display the target rider. - TextContext->printfAt(0.0f, line, " Rider: %d", UserEntity->rider()); - line -= lineStep; - // Display the current animation name. - TextContext->printfAt(0.0f, line, " Current Animation Name: %s", UserEntity->currentAnimationName().c_str()); - line -= lineStep; - // Display the current move animation set name. - TextContext->printfAt(0.0f, line, " Current AnimationSet Name (MOVE): %s", UserEntity->currentAnimationSetName(MOVE).c_str()); - line -= lineStep; - // Display Missing Animations - if(::CAnimation::MissingAnim.empty() == false) - { - TextContext->printfAt(0.0f, line, " '%u' Missing Animations, 1st: '%s'", ::CAnimation::MissingAnim.size(), (*(::CAnimation::MissingAnim.begin())).c_str()); - line -= lineStep; - } - // Display Missing LoD - if(LodCharactersNotFound.empty() == false) - { - TextContext->printfAt(0.0f, line, " '%u' Missing LoD, 1st: '%s'", LodCharactersNotFound.size(), (*(LodCharactersNotFound.begin())).c_str()); - line -= lineStep; - } - - // Watched Entity - line -= lineStep; - // Now Displaying the selection. - TextContext->printfAt(0.0f, line, "--*** Watched entity ***--"); - line -= lineStep; - // Display information about the debug entity slot. - if(WatchedEntitySlot != CLFECOMMON::INVALID_SLOT) - { - // Get a pointer on the target. - CEntityCL *watchedEntity = EntitiesMngr.entity(WatchedEntitySlot); - if(watchedEntity) - { - // Display Debug Information about the Selection. - watchedEntity->displayDebug(0.0f, line, -lineStep); - - // Distance of the target - CVectorD diffvector = UserEntity->pos() - watchedEntity->pos(); - TextContext->printfAt(0.0f, line, " Distance: %10.2f (Manhattan: %.2f)", diffvector.norm(), fabs(diffvector.x) + fabs(diffvector.y) ); - line -= lineStep; - } - // Target not allocated - else - { - TextContext->printfAt(0.0f, line, "Not allocated (%d)", WatchedEntitySlot); - line -= lineStep; - } - } - // No Target - else - { - TextContext->printfAt(0.0f, line, "None"); - line -= lineStep; - } - - /* Ca rame grave ! - - uint nMem = NLMEMORY::GetAllocatedMemory(); - line -= lineStep; - TextContext->printfAt(0.0f, line, "Mem Used: %d",nMem);*/ - - // 3D Filters information: -#ifdef _PROFILE_ON_ - line-= lineStep; - TextContext->printfAt(0.0f, line, "3D Filters:"); - line-= lineStep; - TextContext->printfAt(0.0f, line, "MeshNoVP: %s", Filter3D[FilterMeshNoVP]?"Ok":"NOT RENDERED!"); - line-= lineStep; - TextContext->printfAt(0.0f, line, "MeshVP: %s", Filter3D[FilterMeshVP]?"Ok":"NOT RENDERED!"); - line-= lineStep; - TextContext->printfAt(0.0f, line, "FXs: %s", Filter3D[FilterFXs]?"Ok":"NOT RENDERED!"); - line-= lineStep; - if (Landscape) - { - TextContext->printfAt(0.0f, line, "Landscape: %s", Filter3D[FilterLandscape]?"Ok":"NOT RENDERED!"); - line-= lineStep; - } - else - { - TextContext->printfAt(0.0f, line, "Landscape not enabled"); - } - TextContext->printfAt(0.0f, line, "Vegetable: %s", Filter3D[FilterVegetable]?"Ok":"NOT RENDERED!"); - line-= lineStep; - TextContext->printfAt(0.0f, line, "Skeleton: %s", Filter3D[FilterSkeleton]?"Ok":"NOT RENDERED!"); - line-= lineStep; - TextContext->printfAt(0.0f, line, "Water: %s", Filter3D[FilterWater]?"Ok":"NOT RENDERED!"); - line-= lineStep; - TextContext->printfAt(0.0f, line, "Cloud: %s", Filter3D[FilterCloud]?"Ok":"NOT RENDERED!"); - line-= lineStep; - TextContext->printfAt(0.0f, line, "CoarseMesh: %s", Filter3D[FilterCoarseMesh]?"Ok":"NOT RENDERED!"); - line-= lineStep; - TextContext->printfAt(0.0f, line, "Sky: %s", Filter3D[FilterSky]?"Ok":"NOT RENDERED!"); - line-= lineStep; - // Materials Infos - TextContext->printfAt(0.0f, line, "SetupedMatrix: %d", Driver->profileSetupedModelMatrix() ); - line-= lineStep; - TextContext->printfAt(0.0f, line, "SetupedMaterials: %d", Driver->profileSetupedMaterials() ); - line-= lineStep; - // Display camera cluster system - TextContext->printfAt(0.0f, line, "ClusterSystem: %p", MainCam.getClusterSystem() ); - line-= 2 * lineStep; - // Lua stuffs - CInterfaceManager *pIM = CInterfaceManager::getInstance(); - TextContext->printfAt(0.0f, line, "Lua mem (kb) : %d / %d", CLuaManager::getInstance().getLuaState()->getGCCount(), CLuaManager::getInstance().getLuaState()->getGCThreshold()); - line-= lineStep; - TextContext->printfAt(0.0f, line, "Lua stack size = %d", CLuaManager::getInstance().getLuaState()->getTop()); - line-= lineStep; - -#endif - - // TOP LEFT // - //-----------// - TextContext->setHotSpot(UTextContext::TopLeft); - line = 1.f; - string str; -#if FINAL_VERSION - str = "FV"; -#else - str = "DEV"; -#endif - if(ClientCfg.ExtendedCommands) - str += "_E"; - str += " "RYZOM_VERSION; - TextContext->printfAt(0.f, line, "Version %s", str.c_str()); - - // TOP MIDDLE // - //------------// - TextContext->setHotSpot(UTextContext::MiddleTop); - line = 1.f; - // Motion Mode - TextContext->printfAt(0.5f, line, "%s", UserControls.modeStr().c_str()); - line -= lineStep; - - // TOP RIGHT // - //-----------// - TextContext->setHotSpot(UTextContext::TopRight); - line = 1.f; - //// 3D Infos - // Video mem allocated. - TextContext->printfAt(1.f, line, "Video mem. : %f", Driver->profileAllocatedTextureMemory()/(1024.f*1024.f)); - line -= lineStep; - // Video mem used since last swapBuffers(). - TextContext->printfAt(1.f, line, "Video mem. since last swap buffer: %f", Driver->getUsedTextureMemory()/(1024.f*1024.f)); - line -= lineStep; - // Get the last face count asked from the main scene before reduction. - TextContext->printfAt(1.f, line, "Nb Skin Face Asked: %f", Scene->getGroupNbFaceAsked("Skin")); - line -= lineStep; - TextContext->printfAt(1.f, line, "Nb Fx Face Asked: %f", Scene->getGroupNbFaceAsked("Fx")); - line -= lineStep; - // All Triangles In - CPrimitiveProfile pIn; - CPrimitiveProfile pOut; - Driver->profileRenderedPrimitives(pIn, pOut); - TextContext->printfAt(1.f, line, "Tri In : %d", pIn.NTriangles+2*pIn.NQuads); - line -= lineStep; - // All Triangles Out - TextContext->printfAt(1.f, line, "Tri Out : %d", pOut.NTriangles+2*pIn.NQuads); - line -= lineStep; - // Current Cluster - string strPos; - // Check there is a PACS Primitive before using it. - if(UserEntity->getPrimitive() && GR) - { - UGlobalPosition gPos; - UserEntity->getPrimitive()->getGlobalPosition(gPos, dynamicWI); - string strPos = GR->getIdentifier(gPos); - } - else - strPos = "No Primitive"; - TextContext->printfAt(1.f, line, "Cluster : %s", strPos.c_str()); - line -= lineStep; - //// SOUND Infos - line -= lineStep; - if(SoundMngr) - { - TextContext->printfAt(1.f, line, "Sound source instance: %u", SoundMngr->getSourcesInstanceCount()); - line -= lineStep; - TextContext->printfAt(1.f, line, "Logical playing SoundSource: %u", SoundMngr->getMixer()->getPlayingSourcesCount ()); - line -= lineStep; - TextContext->printfAt(1.f, line, "Audio tracks: %u/%u", SoundMngr->getMixer()->getUsedTracksCount(), SoundMngr->getMixer()->getPolyphony()); - line -= lineStep; - if (SoundMngr->getMixer()->getMutedPlayingSourcesCount() > 0) - { - TextContext->printfAt(1.f, line, "Source muted: %u !", SoundMngr->getMixer()->getMutedPlayingSourcesCount()); - line -= lineStep; - } - TextContext->printfAt(1.f, line, "Samples in memory: %g MB", SoundMngr->getLoadingSamplesSize() / (1024.0f*1024.0f)); - line -= lineStep; - - } - - // BOTTOM RIGHT // - //--------------// - TextContext->setHotSpot(UTextContext::BottomRight); - line = 0.f; - //// POSITION - CVector postmp = View.viewPos(); - // Pos - TextContext->printfAt(1.f, line, "Position : %d %d %d",(int)postmp.x,(int)postmp.y,(int)postmp.z); - line += lineStep; - // Body Heading - TextContext->printfAt(1.f, line, "Front : %.2f %.2f %.2f", UserEntity->front().x, UserEntity->front().y, UserEntity->front().z); - line += lineStep; - // Speed - TextContext->printfAt(1.f, line, "Speed : %.2f", (float) UserEntity->speed()); - line += lineStep; - // Zone - if (!ClientCfg.Light) - { - if (Landscape) - { - TextContext->printfAt(1.f, line, "Zone: %s", Landscape->getZoneName(postmp).c_str()); - line += lineStep; - } - } - // Prim File - string primFile = PrimFiles.getCurrentPrimitive (); - if (!primFile.empty ()) - { - TextContext->printfAt(1.f, line, "Prim File: %s", primFile.c_str ()); - line += lineStep; - } - - //// CONNECTION - line += lineStep; - // Ryzom Day. - TextContext->printfAt(1.f, line, "Ryzom Day : %d", RT.getRyzomDay()); - line += lineStep; - // hour in the game - float dayNightCycleHour = (float)RT.getRyzomTime(); - TextContext->printfAt(1.f, line, "Ryzom Time : %2u:%02u", int(dayNightCycleHour), int((dayNightCycleHour-int(dayNightCycleHour))*60.0f)); - line += lineStep; - // light hour in the game, used to display te day/night - TextContext->printfAt(1.f, line, "Ryzom Light Time : %2u:%02u (%s)", int(DayNightCycleHour), int((DayNightCycleHour-int(DayNightCycleHour))*60.0f), LightCycleManager.getStateString().c_str()); - line += lineStep; - // Server GameCycle - TextContext->printfAt(1.f, line, "Server GameCycle : %u", (uint)NetMngr.getCurrentServerTick()); - line += lineStep; - // Current GameCycle - TextContext->printfAt(1.f, line, "Current GameCycle : %u", (uint)NetMngr.getCurrentClientTick()); - line += lineStep; - // Current GameCycle - TextContext->printfAt(1.f, line, "Ms per Cycle : %d", NetMngr.getMsPerTick()); - line += lineStep; - // Packet Loss - TextContext->printfAt(1.f, line, "Packet Loss : %.1f %%", NetMngr.getMeanPacketLoss()*100.0f); - line += lineStep; - // Packet Loss - TextContext->printfAt(1.f, line, "Packets Lost : %u", NetMngr.getTotalLostPackets()); - line += lineStep; - // Mean Upload - TextContext->printfAt(1.f, line, "Mean Upld : %.3f kbps", NetMngr.getMeanUpload()); - line += lineStep; - // Mean Download - TextContext->printfAt(1.f, line, "Mean Dnld : %.3f kbps", NetMngr.getMeanDownload()); - line += lineStep; - - // Mean Download - TextContext->printfAt(1.f, line, "Nb in Vision : %d(%d,%d,%d)", - EntitiesMngr.nbEntitiesAllocated(), - EntitiesMngr.nbUser(), - EntitiesMngr.nbPlayer(), - EntitiesMngr.nbChar()); - line += lineStep; - - // Number of database changes - TextContext->printfAt(1.f, line, "DB Changes : %u", NbDatabaseChanges ); - line += lineStep; - - // Ping - TextContext->printfAt(1.f, line, "DB Ping : %u ms", Ping.getValue()); - line += lineStep; - - - - - - // Manual weather setup - { - if(ContinentMngr.cur()) // Only usable if there is a continent loaded. - { - if (!ForceTrueWeatherValue) - { - const CWeatherFunction &wf = ContinentMngr.cur()->WeatherFunction[CurrSeason]; - float wv; - if (ClientCfg.ManualWeatherSetup) - { - wv = std::max(wf.getNumWeatherSetups() - 1, 0u) * ManualWeatherValue; - } - else - { - wv = std::max(wf.getNumWeatherSetups() - 1, 0u) * ::getBlendedWeather(RT.getRyzomDay(), RT.getRyzomTime(), *WeatherFunctionParams, ContinentMngr.cur()->WeatherFunction); - } - const CWeatherSetup *ws = wf.getWeatherSetup((uint) floorf(wv)); - std::string name0 = ws ? NLMISC::CStringMapper::unmap(ws->SetupName) : "???"; - ws = wf.getWeatherSetup(std::min((uint) (floorf(wv) + 1), wf.getNumWeatherSetups() - 1)); - std::string name1 = ws ? NLMISC::CStringMapper::unmap(ws->SetupName) : "???"; - TextContext->printfAt(1.f, line, "Weather value : %.02f : %s -> %s", ws ? wv : 0.f, name0.c_str(), name1.c_str()); - line += lineStep; - } - else - { - TextContext->printfAt(1.f, line, "Weather value : %.02f", WeatherManager.getWeatherValue() * std::max(ContinentMngr.cur()->WeatherFunction[CurrSeason].getNumWeatherSetups() - 1, 0u)); - line += lineStep; - TextContext->printfAt(1.f, line, "TEST WEATHER FUNCTION"); - line += lineStep; - } - // season - TextContext->printfAt(1.f, line, "Season : %s", EGSPD::CSeason::toString(CurrSeason).c_str()); - line += lineStep; - } - } - - // fog dist - if (ContinentMngr.cur()) - { - TextContext->printfAt(1.f, line, "Continent fog min near = %.1f, max far = %.1f", ContinentMngr.cur()->FogStart, ContinentMngr.cur()->FogEnd); - line += lineStep; - CFogState tmpFog; - ContinentMngr.getFogState(MainFog, LightCycleManager.getLightLevel(), LightCycleManager.getLightDesc().DuskRatio, LightCycleManager.getState(), View.viewPos(), tmpFog); - TextContext->printfAt(1.f, line, "Continent fog curr near = %.1f, curr far = %.1f", tmpFog.FogStartDist, tmpFog.FogEndDist); - line += lineStep; - } - const CWeatherState &ws = WeatherManager.getCurrWeatherState(); - TextContext->printfAt(1.f, line, "Weather fog near = %.1f, far = %.1f", ws.FogNear[MainFog], ws.FogFar[MainFog]); - line += lineStep; - TextContext->printfAt(1.f, line, "Final fog near = %.1f, far = %.1f", MainFogState.FogStartDist, MainFogState.FogEndDist); - line += lineStep; - float left, right, bottom, top, znear, zfar; - Scene->getCam().getFrustum(left, right, bottom, top, znear, zfar); - TextContext->printfAt(1.f, line, "Clip near = %.1f, far = %.1f", znear, zfar); - line += lineStep; - - // Connection states - TextContext->printfAt(1.f, line, "State : %s", NetMngr.getConnectionStateCStr() ); - line += lineStep; - -// UGlobalPosition globalPos; -// UserEntity->getPrimitive()->getGlobalPosition(globalPos, dynamicWI); -// uint32 material = GR->getMaterial( globalPos ); -// TextContext->printfAt(0.5f,0.5f,"Material : %d Gpos=(inst=%d,surf=%d,x=%.2f,y=%.2f",material, globalPos.InstanceId, globalPos.LocalPosition.Surface, globalPos.LocalPosition.Estimation.x, globalPos.LocalPosition.Estimation.y); - - // No more shadow when displaying a text. - TextContext->setShaded(false); -}// displayDebug // - -//----------------------------------------------- -// Macro to Display a Text -//----------------------------------------------- -#define DISP_TEXT(x, text) \ - /* Display the text at the right place */ \ - TextContext->printfAt(x, line, text); \ - /* Change the line */ \ - line += lineStep; \ - -//--------------------------------------------------- -// displayHelp : -// Display an Help. -//--------------------------------------------------- -void displayHelp() -{ - float line = 1.f; - float lineStep = -ClientCfg.HelpLineStep; - - // Create a shadow when displaying a text. - TextContext->setShaded(true); - // Set the font size. - TextContext->setFontSize(ClientCfg.HelpFontSize); - // Set the text color - TextContext->setColor(ClientCfg.HelpFontColor); - - - line = 1.f; - TextContext->setHotSpot(UTextContext::TopLeft); - DISP_TEXT(0.0f, "SHIFT + F1 : This Menu") - DISP_TEXT(0.0f, "SHIFT + F2 : Display Debug Infos") - DISP_TEXT(0.0f, "SHIFT + F3 : Wire mode"); - DISP_TEXT(0.0f, "SHIFT + F4 : Do not Render the Scene"); - DISP_TEXT(0.0f, "SHIFT + F5 : Toogle Display OSD interfaces"); -// DISP_TEXT(0.0f, "SHIFT + F6 : Not used"); - DISP_TEXT(0.0f, "SHIFT + F7 : Compass Mode (User/Camera)"); - DISP_TEXT(0.0f, "SHIFT + F8 : Camera Mode (INSERT to change your position)"); - DISP_TEXT(0.0f, "SHIFT + F9 : Free Mouse"); - DISP_TEXT(0.0f, "SHIFT + F10 : Take a Screen Shot (+CTRL) for jpg"); -// DISP_TEXT(0.0f, "SHIFT + F11 : Test"); - DISP_TEXT(0.0f, "SHIFT + ESCAPE : Quit"); - DISP_TEXT(0.0f, "SHIFT + C : First/Third Person View"); - - line = 1.f; - TextContext->setHotSpot(UTextContext::TopRight); - DISP_TEXT(1.0f, "UP : FORWARD"); - DISP_TEXT(1.0f, "DOWN : BACKWARD"); - DISP_TEXT(1.0f, "LEFT : ROTATE LEFT"); - DISP_TEXT(1.0f, "RIGHT : ROTATE RIGHT"); - DISP_TEXT(1.0f, "CTRL + LEFT : STRAFE LEFT"); - DISP_TEXT(1.0f, "CTRL + RIGHT : STRAFE RIGHT"); - DISP_TEXT(1.0f, "END : Auto Walk"); - DISP_TEXT(1.0f, "DELETE : Walk/Run"); - DISP_TEXT(1.0f, "PG UP : Look Up"); - DISP_TEXT(1.0f, "PG DOWN : Look Down"); -// DISP_TEXT(1.0f, "CTRL + I : Inventory"); -// DISP_TEXT(1.0f, "CTRL + C : Spells composition interface"); -// DISP_TEXT(1.0f, "CTRL + S : Memorized Spells interface"); - DISP_TEXT(1.0f, "CTRL + B : Show/Hide PACS Borders"); - DISP_TEXT(1.0f, "CTRL + P : Player target himself"); - DISP_TEXT(1.0f, "CTRL + D : Unselect target"); - DISP_TEXT(1.0f, "CTRL + TAB : Next Chat Mode (say/shout"); - DISP_TEXT(1.0f, "CTRL + R : Reload Client.cfg File"); -// DISP_TEXT(1.0f, "CTRL + N : Toggle Night / Day lighting"); - DISP_TEXT(1.0f, "CTRL + F2 : Profile on / off"); - DISP_TEXT(1.0f, "CTRL + F3 : Movie Shooter record / stop"); - DISP_TEXT(1.0f, "CTRL + F4 : Movie Shooter replay"); - DISP_TEXT(1.0f, "CTRL + F5 : Movie Shooter save"); -#ifndef NL_USE_DEFAULT_MEMORY_MANAGER - DISP_TEXT(1.0f, "CTRL + F6 : Save memory stat report"); -#endif // NL_USE_DEFAULT_MEMORY_MANAGER - DISP_TEXT(1.0f, "CTRL + F7 : Show / hide prim file"); - DISP_TEXT(1.0f, "CTRL + F8 : Change prim file UP"); - DISP_TEXT(1.0f, "CTRL + F9 : Change prim file DOWN"); - - // No more shadow when displaying a text. - TextContext->setShaded(false); -}// displayHelp // //--------------------------------------------------- diff --git a/code/ryzom/client/src/main_loop_debug.cpp b/code/ryzom/client/src/main_loop_debug.cpp new file mode 100644 index 000000000..fc0e7801c --- /dev/null +++ b/code/ryzom/client/src/main_loop_debug.cpp @@ -0,0 +1,560 @@ +// Ryzom - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#include +#include "main_loop_debug.h" + +#include + +#include "game_share/ryzom_version.h" + +#include "global.h" +#include "client_cfg.h" +#include "user_entity.h" +#include "debug_client.h" +#include "entities.h" +#include "motion/user_controls.h" +#include "pacs_client.h" +#include "sound_manager.h" +#include "view.h" +#include "prim_file.h" +#include "weather.h" +#include "light_cycle_manager.h" +#include "net_manager.h" +#include "ping.h" +#include "world_database_manager.h" +#include "continent_manager.h" +#include "client_sheets/weather_function_params_sheet.h" +#include "weather_manager_client.h" +#include "fog_map.h" + +using namespace NLMISC; +using namespace NL3D; + +// ******************************************************************** +// ******************************************************************** +// ******************************************************************** +// ******************************************************************** +// ******************************************************************** + +extern std::set LodCharactersNotFound; +extern uint32 NbDatabaseChanges; +extern CFogState MainFogState; +extern CPing Ping; + +//namespace /* anonymous */ { + +NLMISC::CValueSmoother smoothFPS; +NLMISC::CValueSmoother moreSmoothFPS(64); + +//} /* anonymous namespace */ + +//--------------------------------------------------- +// displayDebug : +// Display some debug infos. +//--------------------------------------------------- +void displayDebug() +{ + float lineStep = ClientCfg.DebugLineStep; + float line; + + // Initialize Pen // + //----------------// + // Create a shadow when displaying a text. + TextContext->setShaded(true); + // Set the font size. + TextContext->setFontSize(ClientCfg.DebugFontSize); + // Set the text color + TextContext->setColor(ClientCfg.DebugFontColor); + + // TOP LEFT // + //----------// + TextContext->setHotSpot(UTextContext::TopLeft); + line = 0.9f; + // FPS and Ms per frame + { + // smooth across frames. + double deltaTime = smoothFPS.getSmoothValue (); + // FPS and Ms per frame + if(deltaTime != 0.f) + TextContext->printfAt(0.f, line,"%.1f fps", 1.f/deltaTime); + else + TextContext->printfAt(0.f, line,"%.1f fps", 0.f); + TextContext->printfAt(0.1f, line, "%d ms", (uint)(deltaTime*1000)); + } + line -= lineStep; + line -= lineStep; + + // USER + // Front + TextContext->printfAt(0.0f, line, " %f (%f,%f,%f) front", atan2(UserEntity->front().y, UserEntity->front().x), UserEntity->front().x, UserEntity->front().y, UserEntity->front().z); + line -= lineStep; + // Dir + TextContext->printfAt(0.0f, line, " %f (%f,%f,%f) dir", atan2(UserEntity->dir().y, UserEntity->dir().x), UserEntity->dir().x, UserEntity->dir().y, UserEntity->dir().z); + line -= lineStep; + // NB Stage + TextContext->printfAt(0.0f, line, " NB Stage: %d", UserEntity->nbStage()); + line -= lineStep; + // NB Animation FXs still remaining in the remove list. + TextContext->printfAt(0.0f, line, " NB FXs to remove: %d", UserEntity->nbAnimFXToRemove()); + line -= lineStep; + // Mode. + TextContext->printfAt(0.0f, line, " Mode: %d (%s)", (sint)UserEntity->mode(), MBEHAV::modeToString(UserEntity->mode()).c_str()); + line -= lineStep; + // Behaviour. + TextContext->printfAt(0.0f, line, " Behaviour: %d (%s)", (sint)UserEntity->behaviour(), MBEHAV::behaviourToString(UserEntity->behaviour()).c_str()); + line -= lineStep; + // Display the target mount. + TextContext->printfAt(0.0f, line, " Mount: %d", UserEntity->mount()); + line -= lineStep; + // Display the target rider. + TextContext->printfAt(0.0f, line, " Rider: %d", UserEntity->rider()); + line -= lineStep; + // Display the current animation name. + TextContext->printfAt(0.0f, line, " Current Animation Name: %s", UserEntity->currentAnimationName().c_str()); + line -= lineStep; + // Display the current move animation set name. + TextContext->printfAt(0.0f, line, " Current AnimationSet Name (MOVE): %s", UserEntity->currentAnimationSetName(MOVE).c_str()); + line -= lineStep; + // Display Missing Animations + if(::CAnimation::MissingAnim.empty() == false) + { + TextContext->printfAt(0.0f, line, " '%u' Missing Animations, 1st: '%s'", ::CAnimation::MissingAnim.size(), (*(::CAnimation::MissingAnim.begin())).c_str()); + line -= lineStep; + } + // Display Missing LoD + if(LodCharactersNotFound.empty() == false) + { + TextContext->printfAt(0.0f, line, " '%u' Missing LoD, 1st: '%s'", LodCharactersNotFound.size(), (*(LodCharactersNotFound.begin())).c_str()); + line -= lineStep; + } + + // Watched Entity + line -= lineStep; + // Now Displaying the selection. + TextContext->printfAt(0.0f, line, "--*** Watched entity ***--"); + line -= lineStep; + // Display information about the debug entity slot. + if(WatchedEntitySlot != CLFECOMMON::INVALID_SLOT) + { + // Get a pointer on the target. + CEntityCL *watchedEntity = EntitiesMngr.entity(WatchedEntitySlot); + if(watchedEntity) + { + // Display Debug Information about the Selection. + watchedEntity->displayDebug(0.0f, line, -lineStep); + + // Distance of the target + CVectorD diffvector = UserEntity->pos() - watchedEntity->pos(); + TextContext->printfAt(0.0f, line, " Distance: %10.2f (Manhattan: %.2f)", diffvector.norm(), fabs(diffvector.x) + fabs(diffvector.y) ); + line -= lineStep; + } + // Target not allocated + else + { + TextContext->printfAt(0.0f, line, "Not allocated (%d)", WatchedEntitySlot); + line -= lineStep; + } + } + // No Target + else + { + TextContext->printfAt(0.0f, line, "None"); + line -= lineStep; + } + + /* Ca rame grave ! + + uint nMem = NLMEMORY::GetAllocatedMemory(); + line -= lineStep; + TextContext->printfAt(0.0f, line, "Mem Used: %d",nMem);*/ + + // 3D Filters information: +#ifdef _PROFILE_ON_ + line-= lineStep; + TextContext->printfAt(0.0f, line, "3D Filters:"); + line-= lineStep; + TextContext->printfAt(0.0f, line, "MeshNoVP: %s", Filter3D[FilterMeshNoVP]?"Ok":"NOT RENDERED!"); + line-= lineStep; + TextContext->printfAt(0.0f, line, "MeshVP: %s", Filter3D[FilterMeshVP]?"Ok":"NOT RENDERED!"); + line-= lineStep; + TextContext->printfAt(0.0f, line, "FXs: %s", Filter3D[FilterFXs]?"Ok":"NOT RENDERED!"); + line-= lineStep; + if (Landscape) + { + TextContext->printfAt(0.0f, line, "Landscape: %s", Filter3D[FilterLandscape]?"Ok":"NOT RENDERED!"); + line-= lineStep; + } + else + { + TextContext->printfAt(0.0f, line, "Landscape not enabled"); + } + TextContext->printfAt(0.0f, line, "Vegetable: %s", Filter3D[FilterVegetable]?"Ok":"NOT RENDERED!"); + line-= lineStep; + TextContext->printfAt(0.0f, line, "Skeleton: %s", Filter3D[FilterSkeleton]?"Ok":"NOT RENDERED!"); + line-= lineStep; + TextContext->printfAt(0.0f, line, "Water: %s", Filter3D[FilterWater]?"Ok":"NOT RENDERED!"); + line-= lineStep; + TextContext->printfAt(0.0f, line, "Cloud: %s", Filter3D[FilterCloud]?"Ok":"NOT RENDERED!"); + line-= lineStep; + TextContext->printfAt(0.0f, line, "CoarseMesh: %s", Filter3D[FilterCoarseMesh]?"Ok":"NOT RENDERED!"); + line-= lineStep; + TextContext->printfAt(0.0f, line, "Sky: %s", Filter3D[FilterSky]?"Ok":"NOT RENDERED!"); + line-= lineStep; + // Materials Infos + TextContext->printfAt(0.0f, line, "SetupedMatrix: %d", Driver->profileSetupedModelMatrix() ); + line-= lineStep; + TextContext->printfAt(0.0f, line, "SetupedMaterials: %d", Driver->profileSetupedMaterials() ); + line-= lineStep; + // Display camera cluster system + TextContext->printfAt(0.0f, line, "ClusterSystem: %p", MainCam.getClusterSystem() ); + line-= 2 * lineStep; + // Lua stuffs + CInterfaceManager *pIM = CInterfaceManager::getInstance(); + TextContext->printfAt(0.0f, line, "Lua mem (kb) : %d / %d", CLuaManager::getInstance().getLuaState()->getGCCount(), CLuaManager::getInstance().getLuaState()->getGCThreshold()); + line-= lineStep; + TextContext->printfAt(0.0f, line, "Lua stack size = %d", CLuaManager::getInstance().getLuaState()->getTop()); + line-= lineStep; + +#endif + + // TOP LEFT // + //-----------// + TextContext->setHotSpot(UTextContext::TopLeft); + line = 1.f; + string str; +#if FINAL_VERSION + str = "FV"; +#else + str = "DEV"; +#endif + if(ClientCfg.ExtendedCommands) + str += "_E"; + str += " "RYZOM_VERSION; + TextContext->printfAt(0.f, line, "Version %s", str.c_str()); + + // TOP MIDDLE // + //------------// + TextContext->setHotSpot(UTextContext::MiddleTop); + line = 1.f; + // Motion Mode + TextContext->printfAt(0.5f, line, "%s", UserControls.modeStr().c_str()); + line -= lineStep; + + // TOP RIGHT // + //-----------// + TextContext->setHotSpot(UTextContext::TopRight); + line = 1.f; + //// 3D Infos + // Video mem allocated. + TextContext->printfAt(1.f, line, "Video mem. : %f", Driver->profileAllocatedTextureMemory()/(1024.f*1024.f)); + line -= lineStep; + // Video mem used since last swapBuffers(). + TextContext->printfAt(1.f, line, "Video mem. since last swap buffer: %f", Driver->getUsedTextureMemory()/(1024.f*1024.f)); + line -= lineStep; + // Get the last face count asked from the main scene before reduction. + TextContext->printfAt(1.f, line, "Nb Skin Face Asked: %f", Scene->getGroupNbFaceAsked("Skin")); + line -= lineStep; + TextContext->printfAt(1.f, line, "Nb Fx Face Asked: %f", Scene->getGroupNbFaceAsked("Fx")); + line -= lineStep; + // All Triangles In + CPrimitiveProfile pIn; + CPrimitiveProfile pOut; + Driver->profileRenderedPrimitives(pIn, pOut); + TextContext->printfAt(1.f, line, "Tri In : %d", pIn.NTriangles+2*pIn.NQuads); + line -= lineStep; + // All Triangles Out + TextContext->printfAt(1.f, line, "Tri Out : %d", pOut.NTriangles+2*pIn.NQuads); + line -= lineStep; + // Current Cluster + string strPos; + // Check there is a PACS Primitive before using it. + if(UserEntity->getPrimitive() && GR) + { + UGlobalPosition gPos; + UserEntity->getPrimitive()->getGlobalPosition(gPos, dynamicWI); + string strPos = GR->getIdentifier(gPos); + } + else + strPos = "No Primitive"; + TextContext->printfAt(1.f, line, "Cluster : %s", strPos.c_str()); + line -= lineStep; + //// SOUND Infos + line -= lineStep; + if(SoundMngr) + { + TextContext->printfAt(1.f, line, "Sound source instance: %u", SoundMngr->getSourcesInstanceCount()); + line -= lineStep; + TextContext->printfAt(1.f, line, "Logical playing SoundSource: %u", SoundMngr->getMixer()->getPlayingSourcesCount ()); + line -= lineStep; + TextContext->printfAt(1.f, line, "Audio tracks: %u/%u", SoundMngr->getMixer()->getUsedTracksCount(), SoundMngr->getMixer()->getPolyphony()); + line -= lineStep; + if (SoundMngr->getMixer()->getMutedPlayingSourcesCount() > 0) + { + TextContext->printfAt(1.f, line, "Source muted: %u !", SoundMngr->getMixer()->getMutedPlayingSourcesCount()); + line -= lineStep; + } + TextContext->printfAt(1.f, line, "Samples in memory: %g MB", SoundMngr->getLoadingSamplesSize() / (1024.0f*1024.0f)); + line -= lineStep; + + } + + // BOTTOM RIGHT // + //--------------// + TextContext->setHotSpot(UTextContext::BottomRight); + line = 0.f; + //// POSITION + CVector postmp = View.viewPos(); + // Pos + TextContext->printfAt(1.f, line, "Position : %d %d %d",(int)postmp.x,(int)postmp.y,(int)postmp.z); + line += lineStep; + // Body Heading + TextContext->printfAt(1.f, line, "Front : %.2f %.2f %.2f", UserEntity->front().x, UserEntity->front().y, UserEntity->front().z); + line += lineStep; + // Speed + TextContext->printfAt(1.f, line, "Speed : %.2f", (float) UserEntity->speed()); + line += lineStep; + // Zone + if (!ClientCfg.Light) + { + if (Landscape) + { + TextContext->printfAt(1.f, line, "Zone: %s", Landscape->getZoneName(postmp).c_str()); + line += lineStep; + } + } + // Prim File + string primFile = PrimFiles.getCurrentPrimitive (); + if (!primFile.empty ()) + { + TextContext->printfAt(1.f, line, "Prim File: %s", primFile.c_str ()); + line += lineStep; + } + + //// CONNECTION + line += lineStep; + // Ryzom Day. + TextContext->printfAt(1.f, line, "Ryzom Day : %d", RT.getRyzomDay()); + line += lineStep; + // hour in the game + float dayNightCycleHour = (float)RT.getRyzomTime(); + TextContext->printfAt(1.f, line, "Ryzom Time : %2u:%02u", int(dayNightCycleHour), int((dayNightCycleHour-int(dayNightCycleHour))*60.0f)); + line += lineStep; + // light hour in the game, used to display te day/night + TextContext->printfAt(1.f, line, "Ryzom Light Time : %2u:%02u (%s)", int(DayNightCycleHour), int((DayNightCycleHour-int(DayNightCycleHour))*60.0f), LightCycleManager.getStateString().c_str()); + line += lineStep; + // Server GameCycle + TextContext->printfAt(1.f, line, "Server GameCycle : %u", (uint)NetMngr.getCurrentServerTick()); + line += lineStep; + // Current GameCycle + TextContext->printfAt(1.f, line, "Current GameCycle : %u", (uint)NetMngr.getCurrentClientTick()); + line += lineStep; + // Current GameCycle + TextContext->printfAt(1.f, line, "Ms per Cycle : %d", NetMngr.getMsPerTick()); + line += lineStep; + // Packet Loss + TextContext->printfAt(1.f, line, "Packet Loss : %.1f %%", NetMngr.getMeanPacketLoss()*100.0f); + line += lineStep; + // Packet Loss + TextContext->printfAt(1.f, line, "Packets Lost : %u", NetMngr.getTotalLostPackets()); + line += lineStep; + // Mean Upload + TextContext->printfAt(1.f, line, "Mean Upld : %.3f kbps", NetMngr.getMeanUpload()); + line += lineStep; + // Mean Download + TextContext->printfAt(1.f, line, "Mean Dnld : %.3f kbps", NetMngr.getMeanDownload()); + line += lineStep; + + // Mean Download + TextContext->printfAt(1.f, line, "Nb in Vision : %d(%d,%d,%d)", + EntitiesMngr.nbEntitiesAllocated(), + EntitiesMngr.nbUser(), + EntitiesMngr.nbPlayer(), + EntitiesMngr.nbChar()); + line += lineStep; + + // Number of database changes + TextContext->printfAt(1.f, line, "DB Changes : %u", NbDatabaseChanges ); + line += lineStep; + + // Ping + TextContext->printfAt(1.f, line, "DB Ping : %u ms", Ping.getValue()); + line += lineStep; + + + + + + // Manual weather setup + { + if(ContinentMngr.cur()) // Only usable if there is a continent loaded. + { + if (!ForceTrueWeatherValue) + { + const CWeatherFunction &wf = ContinentMngr.cur()->WeatherFunction[CurrSeason]; + float wv; + if (ClientCfg.ManualWeatherSetup) + { + wv = std::max(wf.getNumWeatherSetups() - 1, 0u) * ManualWeatherValue; + } + else + { + wv = std::max(wf.getNumWeatherSetups() - 1, 0u) * ::getBlendedWeather(RT.getRyzomDay(), RT.getRyzomTime(), *WeatherFunctionParams, ContinentMngr.cur()->WeatherFunction); + } + const CWeatherSetup *ws = wf.getWeatherSetup((uint) floorf(wv)); + std::string name0 = ws ? NLMISC::CStringMapper::unmap(ws->SetupName) : "???"; + ws = wf.getWeatherSetup(std::min((uint) (floorf(wv) + 1), wf.getNumWeatherSetups() - 1)); + std::string name1 = ws ? NLMISC::CStringMapper::unmap(ws->SetupName) : "???"; + TextContext->printfAt(1.f, line, "Weather value : %.02f : %s -> %s", ws ? wv : 0.f, name0.c_str(), name1.c_str()); + line += lineStep; + } + else + { + TextContext->printfAt(1.f, line, "Weather value : %.02f", WeatherManager.getWeatherValue() * std::max(ContinentMngr.cur()->WeatherFunction[CurrSeason].getNumWeatherSetups() - 1, 0u)); + line += lineStep; + TextContext->printfAt(1.f, line, "TEST WEATHER FUNCTION"); + line += lineStep; + } + // season + TextContext->printfAt(1.f, line, "Season : %s", EGSPD::CSeason::toString(CurrSeason).c_str()); + line += lineStep; + } + } + + // fog dist + if (ContinentMngr.cur()) + { + TextContext->printfAt(1.f, line, "Continent fog min near = %.1f, max far = %.1f", ContinentMngr.cur()->FogStart, ContinentMngr.cur()->FogEnd); + line += lineStep; + CFogState tmpFog; + ContinentMngr.getFogState(MainFog, LightCycleManager.getLightLevel(), LightCycleManager.getLightDesc().DuskRatio, LightCycleManager.getState(), View.viewPos(), tmpFog); + TextContext->printfAt(1.f, line, "Continent fog curr near = %.1f, curr far = %.1f", tmpFog.FogStartDist, tmpFog.FogEndDist); + line += lineStep; + } + const CWeatherState &ws = WeatherManager.getCurrWeatherState(); + TextContext->printfAt(1.f, line, "Weather fog near = %.1f, far = %.1f", ws.FogNear[MainFog], ws.FogFar[MainFog]); + line += lineStep; + TextContext->printfAt(1.f, line, "Final fog near = %.1f, far = %.1f", MainFogState.FogStartDist, MainFogState.FogEndDist); + line += lineStep; + float left, right, bottom, top, znear, zfar; + Scene->getCam().getFrustum(left, right, bottom, top, znear, zfar); + TextContext->printfAt(1.f, line, "Clip near = %.1f, far = %.1f", znear, zfar); + line += lineStep; + + // Connection states + TextContext->printfAt(1.f, line, "State : %s", NetMngr.getConnectionStateCStr() ); + line += lineStep; + +// UGlobalPosition globalPos; +// UserEntity->getPrimitive()->getGlobalPosition(globalPos, dynamicWI); +// uint32 material = GR->getMaterial( globalPos ); +// TextContext->printfAt(0.5f,0.5f,"Material : %d Gpos=(inst=%d,surf=%d,x=%.2f,y=%.2f",material, globalPos.InstanceId, globalPos.LocalPosition.Surface, globalPos.LocalPosition.Estimation.x, globalPos.LocalPosition.Estimation.y); + + // No more shadow when displaying a text. + TextContext->setShaded(false); +}// displayDebug // + +// ******************************************************************** +// ******************************************************************** +// ******************************************************************** +// ******************************************************************** +// ******************************************************************** + +//----------------------------------------------- +// Macro to Display a Text +//----------------------------------------------- +#define DISP_TEXT(x, text) \ + /* Display the text at the right place */ \ + TextContext->printfAt(x, line, text); \ + /* Change the line */ \ + line += lineStep; \ + +//--------------------------------------------------- +// displayHelp : +// Display an Help. +//--------------------------------------------------- +void displayHelp() +{ + float line = 1.f; + float lineStep = -ClientCfg.HelpLineStep; + + // Create a shadow when displaying a text. + TextContext->setShaded(true); + // Set the font size. + TextContext->setFontSize(ClientCfg.HelpFontSize); + // Set the text color + TextContext->setColor(ClientCfg.HelpFontColor); + + + line = 1.f; + TextContext->setHotSpot(UTextContext::TopLeft); + DISP_TEXT(0.0f, "SHIFT + F1 : This Menu") + DISP_TEXT(0.0f, "SHIFT + F2 : Display Debug Infos") + DISP_TEXT(0.0f, "SHIFT + F3 : Wire mode"); + DISP_TEXT(0.0f, "SHIFT + F4 : Do not Render the Scene"); + DISP_TEXT(0.0f, "SHIFT + F5 : Toogle Display OSD interfaces"); +// DISP_TEXT(0.0f, "SHIFT + F6 : Not used"); + DISP_TEXT(0.0f, "SHIFT + F7 : Compass Mode (User/Camera)"); + DISP_TEXT(0.0f, "SHIFT + F8 : Camera Mode (INSERT to change your position)"); + DISP_TEXT(0.0f, "SHIFT + F9 : Free Mouse"); + DISP_TEXT(0.0f, "SHIFT + F10 : Take a Screen Shot (+CTRL) for jpg"); +// DISP_TEXT(0.0f, "SHIFT + F11 : Test"); + DISP_TEXT(0.0f, "SHIFT + ESCAPE : Quit"); + DISP_TEXT(0.0f, "SHIFT + C : First/Third Person View"); + + line = 1.f; + TextContext->setHotSpot(UTextContext::TopRight); + DISP_TEXT(1.0f, "UP : FORWARD"); + DISP_TEXT(1.0f, "DOWN : BACKWARD"); + DISP_TEXT(1.0f, "LEFT : ROTATE LEFT"); + DISP_TEXT(1.0f, "RIGHT : ROTATE RIGHT"); + DISP_TEXT(1.0f, "CTRL + LEFT : STRAFE LEFT"); + DISP_TEXT(1.0f, "CTRL + RIGHT : STRAFE RIGHT"); + DISP_TEXT(1.0f, "END : Auto Walk"); + DISP_TEXT(1.0f, "DELETE : Walk/Run"); + DISP_TEXT(1.0f, "PG UP : Look Up"); + DISP_TEXT(1.0f, "PG DOWN : Look Down"); +// DISP_TEXT(1.0f, "CTRL + I : Inventory"); +// DISP_TEXT(1.0f, "CTRL + C : Spells composition interface"); +// DISP_TEXT(1.0f, "CTRL + S : Memorized Spells interface"); + DISP_TEXT(1.0f, "CTRL + B : Show/Hide PACS Borders"); + DISP_TEXT(1.0f, "CTRL + P : Player target himself"); + DISP_TEXT(1.0f, "CTRL + D : Unselect target"); + DISP_TEXT(1.0f, "CTRL + TAB : Next Chat Mode (say/shout"); + DISP_TEXT(1.0f, "CTRL + R : Reload Client.cfg File"); +// DISP_TEXT(1.0f, "CTRL + N : Toggle Night / Day lighting"); + DISP_TEXT(1.0f, "CTRL + F2 : Profile on / off"); + DISP_TEXT(1.0f, "CTRL + F3 : Movie Shooter record / stop"); + DISP_TEXT(1.0f, "CTRL + F4 : Movie Shooter replay"); + DISP_TEXT(1.0f, "CTRL + F5 : Movie Shooter save"); +#ifndef NL_USE_DEFAULT_MEMORY_MANAGER + DISP_TEXT(1.0f, "CTRL + F6 : Save memory stat report"); +#endif // NL_USE_DEFAULT_MEMORY_MANAGER + DISP_TEXT(1.0f, "CTRL + F7 : Show / hide prim file"); + DISP_TEXT(1.0f, "CTRL + F8 : Change prim file UP"); + DISP_TEXT(1.0f, "CTRL + F9 : Change prim file DOWN"); + + // No more shadow when displaying a text. + TextContext->setShaded(false); +}// displayHelp // + +// ******************************************************************** +// ******************************************************************** +// ******************************************************************** +// ******************************************************************** +// ******************************************************************** + +/* end of file */ \ No newline at end of file diff --git a/code/ryzom/client/src/main_loop_debug.h b/code/ryzom/client/src/main_loop_debug.h new file mode 100644 index 000000000..70139290e --- /dev/null +++ b/code/ryzom/client/src/main_loop_debug.h @@ -0,0 +1,31 @@ +// Ryzom - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#ifndef CL_MAIN_LOOP_DEBUG_H +#define CL_MAIN_LOOP_DEBUG_H + +#include + +// Display some debug infos. +void displayDebug(); +void displayDebugFps(); +void displayDebugUIUnderMouse(); +// Display an Help. +void displayHelp(); + +#endif // CL_MAIN_LOOP_DEBUG_H + +/* end of file */ \ No newline at end of file diff --git a/code/ryzom/client/src/ping.cpp b/code/ryzom/client/src/ping.cpp index a6d5c2e2c..5a07a2b9d 100644 --- a/code/ryzom/client/src/ping.cpp +++ b/code/ryzom/client/src/ping.cpp @@ -14,6 +14,7 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . +#include #include "ping.h" #include "interface_v3/interface_manager.h" diff --git a/code/ryzom/client/src/profiling.cpp b/code/ryzom/client/src/profiling.cpp index 54f830211..a5a0f770f 100644 --- a/code/ryzom/client/src/profiling.cpp +++ b/code/ryzom/client/src/profiling.cpp @@ -14,6 +14,7 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . +#include #include "profiling.h" // NeL includes diff --git a/code/ryzom/client/src/profiling.h b/code/ryzom/client/src/profiling.h index cb7a55495..2499fa20b 100644 --- a/code/ryzom/client/src/profiling.h +++ b/code/ryzom/client/src/profiling.h @@ -14,8 +14,8 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -#ifndef CL_MAIN_LOOP_PROFILING_H -#define CL_MAIN_LOOP_PROFILING_H +#ifndef CL_PROFILING_H +#define CL_PROFILING_H #include @@ -31,6 +31,6 @@ void testLaunchProfile(); /// Test ProfilingVBLock and run? void testLaunchProfileVBLock(); -#endif // CL_MAIN_LOOP_PROFILING_H +#endif // CL_PROFILING_H /* end of file */ \ No newline at end of file From d91959109339b826e9420446e76f3abe0986ff84 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Thu, 27 Jun 2013 02:43:51 +0200 Subject: [PATCH 046/313] Moved some temp code out of main_loop.cpp, ref #43 --- code/ryzom/client/src/main_loop.cpp | 174 +---------------- code/ryzom/client/src/main_loop_temp.cpp | 226 +++++++++++++++++++++++ code/ryzom/client/src/main_loop_temp.h | 30 +++ 3 files changed, 261 insertions(+), 169 deletions(-) create mode 100644 code/ryzom/client/src/main_loop_temp.cpp create mode 100644 code/ryzom/client/src/main_loop_temp.h diff --git a/code/ryzom/client/src/main_loop.cpp b/code/ryzom/client/src/main_loop.cpp index 4ed395d29..3e91ffda7 100644 --- a/code/ryzom/client/src/main_loop.cpp +++ b/code/ryzom/client/src/main_loop.cpp @@ -78,7 +78,7 @@ #include "world_database_manager.h" #include "continent_manager.h" #include "ig_callback.h" -#include "fog_map.h" +//#include "fog_map.h" #include "movie_shooter.h" #include "sound_manager.h" #include "graph.h" @@ -154,6 +154,7 @@ #include "ping.h" #include "profiling.h" #include "main_loop_debug.h" +#include "main_loop_temp.h" /////////// @@ -166,14 +167,7 @@ using namespace NLNET; using namespace std; -// TMP TMP -static void viewportToScissor(const CViewport &vp, CScissor &scissor) -{ - scissor.X = vp.getX(); - scissor.Y = vp.getY(); - scissor.Width = vp.getWidth(); - scissor.Height = vp.getHeight(); -} + //////////// @@ -2385,169 +2379,11 @@ bool mainLoop() // TMP TMP static volatile bool dumpValidPolys = false; - if (dumpValidPolys) - { - struct CPolyDisp : public CInterfaceElementVisitor - { - virtual void visitCtrl(CCtrlBase *ctrl) - { - CCtrlPolygon *cp = dynamic_cast(ctrl); - if (cp) - { - sint32 cornerX, cornerY; - cp->getParent()->getCorner(cornerX, cornerY, cp->getParentPosRef()); - for(sint32 y = 0; y < (sint32) Screen.getHeight(); ++y) - { - for(sint32 x = 0; x < (sint32) Screen.getWidth(); ++x) - { - if (cp->contains(CVector2f((float) (x - cornerX), (float) (y - cornerY)))) - { - ((CRGBA *) &Screen.getPixels()[0])[x + (Screen.getHeight() - 1 - y) * Screen.getWidth()] = CRGBA::Magenta; - } - } - } - } - } - CBitmap Screen; - } polyDisp; - Driver->getBuffer(polyDisp.Screen); - CInterfaceManager::getInstance()->visit(&polyDisp); - COFile output("poly.tga"); - polyDisp.Screen.writeTGA(output); - dumpValidPolys = false; - }; + if (dumpValidPolys) { tempDumpValidPolys(); dumpValidPolys = false; } // TMP TMP static volatile bool dumpColPolys = false; - if (dumpColPolys) - { - CPackedWorld *pw = R2::getEditor().getIslandCollision().getPackedIsland(); - if (pw) - { - static CMaterial material; - static CMaterial wiredMaterial; - static CMaterial texturedMaterial; - static CVertexBuffer vb; - static bool initDone = false; - if (!initDone) - { - vb.setVertexFormat(CVertexBuffer::PositionFlag); - vb.setPreferredMemory(CVertexBuffer::AGPVolatile, false); - material.initUnlit(); - material.setDoubleSided(true); - material.setZFunc(CMaterial::lessequal); - wiredMaterial.initUnlit(); - wiredMaterial.setDoubleSided(true); - wiredMaterial.setZFunc(CMaterial::lessequal); - wiredMaterial.setColor(CRGBA(255, 255, 255, 250)); - wiredMaterial.texEnvOpAlpha(0, CMaterial::Replace); - wiredMaterial.texEnvArg0Alpha(0, CMaterial::Diffuse, CMaterial::SrcAlpha); - wiredMaterial.setBlend(true); - wiredMaterial.setBlendFunc(CMaterial::srcalpha, CMaterial::invsrcalpha); - texturedMaterial.initUnlit(); - texturedMaterial.setDoubleSided(true); - texturedMaterial.setZFunc(CMaterial::lessequal); - initDone = true; - } - // just add a projected texture - R2::getEditor().getIslandCollision().loadEntryPoints(); - R2::CScenarioEntryPoints &sep = R2::CScenarioEntryPoints::getInstance(); - CVectorD playerPos = UserEntity->pos(); - R2::CScenarioEntryPoints::CCompleteIsland *island = sep.getCompleteIslandFromCoords(CVector2f((float) playerPos.x, (float) playerPos.y)); - static CSString currIsland; - if (island && island->Island != currIsland) - { - currIsland = island->Island; - CTextureFile *newTex = new CTextureFile(currIsland + "_sp.tga"); - newTex->setWrapS(ITexture::Clamp); - newTex->setWrapT(ITexture::Clamp); - texturedMaterial.setTexture(0, newTex); - texturedMaterial.texEnvOpRGB(0, CMaterial::Replace); - texturedMaterial.texEnvArg0RGB(0, CMaterial::Texture, CMaterial::SrcColor); - texturedMaterial.setTexCoordGen(0, true); - texturedMaterial.setTexCoordGenMode(0, CMaterial::TexCoordGenObjectSpace); - CMatrix mat; - CVector scale((float) (island->XMax - island->XMin), - (float) (island->YMax - island->YMin), 0.f); - scale.x = 1.f / favoid0(scale.x); - scale.y = 1.f / favoid0(scale.y); - scale.z = 0.f; - mat.setScale(scale); - mat.setPos(CVector(- island->XMin * scale.x, - island->YMin * scale.y, 0.f)); - // - CMatrix uvScaleMat; - // - uint texWidth = (uint) (island->XMax - island->XMin); - uint texHeight = (uint) (island->YMax - island->YMin); - float UScale = (float) texWidth / raiseToNextPowerOf2(texWidth); - float VScale = (float) texHeight / raiseToNextPowerOf2(texHeight); - // - uvScaleMat.setScale(CVector(UScale, - VScale, 0.f)); - uvScaleMat.setPos(CVector(0.f, VScale, 0.f)); - // - texturedMaterial.enableUserTexMat(0, true); - texturedMaterial.setUserTexMat(0, uvScaleMat * mat); - } - const CFrustum &frust = MainCam.getFrustum(); - - // - IDriver *driver = ((CDriverUser *) Driver)->getDriver(); - - driver->enableFog(true); - const CRGBA clearColor = CRGBA(0, 0, 127, 0); - driver->setupFog(frust.Far * 0.8f, frust.Far, clearColor); - CViewport vp; - vp.init(0.f, 0.f, 1.f, 1.f); - driver->setupViewport(vp); - CScissor scissor; - viewportToScissor(vp, scissor); - driver->setupScissor(scissor); - // - driver->setFrustum(frust.Left, frust.Right, frust.Bottom, frust.Top, frust.Near, frust.Far, frust.Perspective); - driver->setupViewMatrix(MainCam.getMatrix().inverted()); - driver->setupModelMatrix(CMatrix::Identity); - // - // - const CVector localFrustCorners[8] = - { - CVector(frust.Left, frust.Near, frust.Top), - CVector(frust.Right, frust.Near, frust.Top), - CVector(frust.Right, frust.Near, frust.Bottom), - CVector(frust.Left, frust.Near, frust.Bottom), - CVector(frust.Left * frust.Far / frust.Near, frust.Far, frust.Top * frust.Far / frust.Near), - CVector(frust.Right * frust.Far / frust.Near, frust.Far, frust.Top * frust.Far / frust.Near), - CVector(frust.Right * frust.Far / frust.Near, frust.Far, frust.Bottom * frust.Far / frust.Near), - CVector(frust.Left * frust.Far / frust.Near, frust.Far, frust.Bottom * frust.Far / frust.Near) - }; - // roughly compute covered zones - // - /* - sint frustZoneMinX = INT_MAX; - sint frustZoneMaxX = INT_MIN; - sint frustZoneMinY = INT_MAX; - sint frustZoneMaxY = INT_MIN; - for(uint k = 0; k < sizeofarray(localFrustCorners); ++k) - { - CVector corner = camMat * localFrustCorners[k]; - sint zoneX = (sint) (corner.x / 160.f) - zoneMinX; - sint zoneY = (sint) floorf(corner.y / 160.f) - zoneMinY; - frustZoneMinX = std::min(frustZoneMinX, zoneX); - frustZoneMinY = std::min(frustZoneMinY, zoneY); - frustZoneMaxX = std::max(frustZoneMaxX, zoneX); - frustZoneMaxY = std::max(frustZoneMaxY, zoneY); - } - */ - - const uint TRI_BATCH_SIZE = 10000; // batch size for rendering - static std::vector zones; - zones.clear(); - pw->getZones(zones); - for(uint k = 0; k < zones.size(); ++k) - { - zones[k]->render(vb, *driver, texturedMaterial, wiredMaterial, MainCam.getMatrix(), TRI_BATCH_SIZE, localFrustCorners); - } - } - } + if (dumpColPolys) { tempDumpColPolys(); } if (ClientCfg.R2EDEnabled) { diff --git a/code/ryzom/client/src/main_loop_temp.cpp b/code/ryzom/client/src/main_loop_temp.cpp new file mode 100644 index 000000000..0b3f24fa3 --- /dev/null +++ b/code/ryzom/client/src/main_loop_temp.cpp @@ -0,0 +1,226 @@ +// Ryzom - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#include +#include "main_loop_temp.h" + +#include "global.h" + +// tempDumpValidPolys +#include +#include +#include "interface_v3/interface_manager.h" + +// tempDumpColPolys +#include +#include "r2/editor.h" +#include "user_entity.h" +#include + +using namespace NLMISC; +using namespace NL3D; + +// ******************************************************************** +// ******************************************************************** +// ******************************************************************** +// ******************************************************************** +// ******************************************************************** + +// TMP TMP +void tempDumpValidPolys() +{ + struct CPolyDisp : public CInterfaceElementVisitor + { + virtual void visitCtrl(CCtrlBase *ctrl) + { + CCtrlPolygon *cp = dynamic_cast(ctrl); + if (cp) + { + sint32 cornerX, cornerY; + cp->getParent()->getCorner(cornerX, cornerY, cp->getParentPosRef()); + for(sint32 y = 0; y < (sint32) Screen.getHeight(); ++y) + { + for(sint32 x = 0; x < (sint32) Screen.getWidth(); ++x) + { + if (cp->contains(CVector2f((float) (x - cornerX), (float) (y - cornerY)))) + { + ((CRGBA *) &Screen.getPixels()[0])[x + (Screen.getHeight() - 1 - y) * Screen.getWidth()] = CRGBA::Magenta; + } + } + } + } + } + CBitmap Screen; + } polyDisp; + Driver->getBuffer(polyDisp.Screen); + CInterfaceManager::getInstance()->visit(&polyDisp); + COFile output("poly.tga"); + polyDisp.Screen.writeTGA(output); +} + +// ******************************************************************** +// ******************************************************************** +// ******************************************************************** +// ******************************************************************** +// ******************************************************************** + +// TMP TMP +static void viewportToScissor(const CViewport &vp, CScissor &scissor) +{ + scissor.X = vp.getX(); + scissor.Y = vp.getY(); + scissor.Width = vp.getWidth(); + scissor.Height = vp.getHeight(); +} + +// TMP TMP +void tempDumpColPolys() +{ + CPackedWorld *pw = R2::getEditor().getIslandCollision().getPackedIsland(); + if (pw) + { + static CMaterial material; + static CMaterial wiredMaterial; + static CMaterial texturedMaterial; + static CVertexBuffer vb; + static bool initDone = false; + if (!initDone) + { + vb.setVertexFormat(CVertexBuffer::PositionFlag); + vb.setPreferredMemory(CVertexBuffer::AGPVolatile, false); + material.initUnlit(); + material.setDoubleSided(true); + material.setZFunc(CMaterial::lessequal); + wiredMaterial.initUnlit(); + wiredMaterial.setDoubleSided(true); + wiredMaterial.setZFunc(CMaterial::lessequal); + wiredMaterial.setColor(CRGBA(255, 255, 255, 250)); + wiredMaterial.texEnvOpAlpha(0, CMaterial::Replace); + wiredMaterial.texEnvArg0Alpha(0, CMaterial::Diffuse, CMaterial::SrcAlpha); + wiredMaterial.setBlend(true); + wiredMaterial.setBlendFunc(CMaterial::srcalpha, CMaterial::invsrcalpha); + texturedMaterial.initUnlit(); + texturedMaterial.setDoubleSided(true); + texturedMaterial.setZFunc(CMaterial::lessequal); + initDone = true; + } + // just add a projected texture + R2::getEditor().getIslandCollision().loadEntryPoints(); + R2::CScenarioEntryPoints &sep = R2::CScenarioEntryPoints::getInstance(); + CVectorD playerPos = UserEntity->pos(); + R2::CScenarioEntryPoints::CCompleteIsland *island = sep.getCompleteIslandFromCoords(CVector2f((float) playerPos.x, (float) playerPos.y)); + static CSString currIsland; + if (island && island->Island != currIsland) + { + currIsland = island->Island; + CTextureFile *newTex = new CTextureFile(currIsland + "_sp.tga"); + newTex->setWrapS(ITexture::Clamp); + newTex->setWrapT(ITexture::Clamp); + texturedMaterial.setTexture(0, newTex); + texturedMaterial.texEnvOpRGB(0, CMaterial::Replace); + texturedMaterial.texEnvArg0RGB(0, CMaterial::Texture, CMaterial::SrcColor); + texturedMaterial.setTexCoordGen(0, true); + texturedMaterial.setTexCoordGenMode(0, CMaterial::TexCoordGenObjectSpace); + CMatrix mat; + CVector scale((float) (island->XMax - island->XMin), + (float) (island->YMax - island->YMin), 0.f); + scale.x = 1.f / favoid0(scale.x); + scale.y = 1.f / favoid0(scale.y); + scale.z = 0.f; + mat.setScale(scale); + mat.setPos(CVector(- island->XMin * scale.x, - island->YMin * scale.y, 0.f)); + // + CMatrix uvScaleMat; + // + uint texWidth = (uint) (island->XMax - island->XMin); + uint texHeight = (uint) (island->YMax - island->YMin); + float UScale = (float) texWidth / raiseToNextPowerOf2(texWidth); + float VScale = (float) texHeight / raiseToNextPowerOf2(texHeight); + // + uvScaleMat.setScale(CVector(UScale, - VScale, 0.f)); + uvScaleMat.setPos(CVector(0.f, VScale, 0.f)); + // + texturedMaterial.enableUserTexMat(0, true); + texturedMaterial.setUserTexMat(0, uvScaleMat * mat); + } + const CFrustum &frust = MainCam.getFrustum(); + + // + IDriver *driver = ((CDriverUser *) Driver)->getDriver(); + + driver->enableFog(true); + const CRGBA clearColor = CRGBA(0, 0, 127, 0); + driver->setupFog(frust.Far * 0.8f, frust.Far, clearColor); + CViewport vp; + vp.init(0.f, 0.f, 1.f, 1.f); + driver->setupViewport(vp); + CScissor scissor; + viewportToScissor(vp, scissor); + driver->setupScissor(scissor); + // + driver->setFrustum(frust.Left, frust.Right, frust.Bottom, frust.Top, frust.Near, frust.Far, frust.Perspective); + driver->setupViewMatrix(MainCam.getMatrix().inverted()); + driver->setupModelMatrix(CMatrix::Identity); + // + // + const CVector localFrustCorners[8] = + { + CVector(frust.Left, frust.Near, frust.Top), + CVector(frust.Right, frust.Near, frust.Top), + CVector(frust.Right, frust.Near, frust.Bottom), + CVector(frust.Left, frust.Near, frust.Bottom), + CVector(frust.Left * frust.Far / frust.Near, frust.Far, frust.Top * frust.Far / frust.Near), + CVector(frust.Right * frust.Far / frust.Near, frust.Far, frust.Top * frust.Far / frust.Near), + CVector(frust.Right * frust.Far / frust.Near, frust.Far, frust.Bottom * frust.Far / frust.Near), + CVector(frust.Left * frust.Far / frust.Near, frust.Far, frust.Bottom * frust.Far / frust.Near) + }; + // roughly compute covered zones + // + /* + sint frustZoneMinX = INT_MAX; + sint frustZoneMaxX = INT_MIN; + sint frustZoneMinY = INT_MAX; + sint frustZoneMaxY = INT_MIN; + for(uint k = 0; k < sizeofarray(localFrustCorners); ++k) + { + CVector corner = camMat * localFrustCorners[k]; + sint zoneX = (sint) (corner.x / 160.f) - zoneMinX; + sint zoneY = (sint) floorf(corner.y / 160.f) - zoneMinY; + frustZoneMinX = std::min(frustZoneMinX, zoneX); + frustZoneMinY = std::min(frustZoneMinY, zoneY); + frustZoneMaxX = std::max(frustZoneMaxX, zoneX); + frustZoneMaxY = std::max(frustZoneMaxY, zoneY); + } + */ + + const uint TRI_BATCH_SIZE = 10000; // batch size for rendering + static std::vector zones; + zones.clear(); + pw->getZones(zones); + for(uint k = 0; k < zones.size(); ++k) + { + zones[k]->render(vb, *driver, texturedMaterial, wiredMaterial, MainCam.getMatrix(), TRI_BATCH_SIZE, localFrustCorners); + } + } +} + +// ******************************************************************** +// ******************************************************************** +// ******************************************************************** +// ******************************************************************** +// ******************************************************************** + +/* end of file */ \ No newline at end of file diff --git a/code/ryzom/client/src/main_loop_temp.h b/code/ryzom/client/src/main_loop_temp.h new file mode 100644 index 000000000..4f24972f7 --- /dev/null +++ b/code/ryzom/client/src/main_loop_temp.h @@ -0,0 +1,30 @@ +// Ryzom - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#ifndef CL_MAIN_LOOP_TEMP_H +#define CL_MAIN_LOOP_TEMP_H + +#include + +// TMP TMP +void tempDumpValidPolys(); + +// TMP TMP +void tempDumpColPolys(); + +#endif // CL_MAIN_LOOP_TEMP_H + +/* end of file */ \ No newline at end of file From 1e461ec4f3c8835666f4a113d9145d85e6ec9cb0 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Thu, 27 Jun 2013 03:04:40 +0200 Subject: [PATCH 047/313] Some more debug functions moved, see #43 --- code/ryzom/client/src/main_loop.cpp | 210 +-------------------- code/ryzom/client/src/main_loop_debug.cpp | 214 ++++++++++++++++++++++ 2 files changed, 221 insertions(+), 203 deletions(-) diff --git a/code/ryzom/client/src/main_loop.cpp b/code/ryzom/client/src/main_loop.cpp index 3e91ffda7..4191e22b7 100644 --- a/code/ryzom/client/src/main_loop.cpp +++ b/code/ryzom/client/src/main_loop.cpp @@ -2536,6 +2536,9 @@ bool mainLoop() frameToSkip--; } + /////////////// + // FAR_TP -> // + /////////////// // Enter a network loop during the FarTP process, without doing the whole real main loop. // This code must remain at the very end of the main loop. if(LoginSM.getCurrentState() == CLoginStateMachine::st_enter_far_tp_main_loop) @@ -2619,7 +2622,10 @@ bool mainLoop() connectionState = NetMngr.getConnectionState(); CLuaManager::getInstance().executeLuaScript("game:onFarTpEnd()"); - } + } + /////////////// + // <- FAR_TP // + /////////////// } // end of main loop @@ -2687,208 +2693,6 @@ bool mainLoop() return ryzom_exit || (Driver == NULL) || (!Driver->isActive ()); }// mainLoop // -//--------------------------------------------------- -// displayDebug : -// Display some debug infos. -//--------------------------------------------------- -void displayDebugFps() -{ - float lineStep = ClientCfg.DebugLineStep; - float line; - - // Initialize Pen // - //----------------// - // Create a shadow when displaying a text. - TextContext->setShaded(true); - // Set the font size. - TextContext->setFontSize(ClientCfg.DebugFontSize); - // Set the text color - TextContext->setColor(ClientCfg.DebugFontColor); - - // TOP LEFT // - //----------// - TextContext->setHotSpot(UTextContext::TopLeft); - line = 0.9f; - // Ms per frame - { - float spf = smoothFPS.getSmoothValue (); - // Ms per frame - TextContext->printfAt(0.1f, line, "FPS %.1f ms - %.1f fps", spf*1000, 1.f/spf); - line-= lineStep; - // More Smoothed Ms per frame - spf = moreSmoothFPS.getSmoothValue (); - TextContext->printfAt(0.1f, line, "Smoothed FPS %.1f ms - %.1f fps", spf*1000, 1.f/spf); - line-= lineStep; - } -} - -static NLMISC::CRefPtr HighlightedDebugUI; - -// displayDebug : -// Display information about ui elements that are under the mouse -//--------------------------------------------------- -void displayDebugUIUnderMouse() -{ - float lineStep = ClientCfg.DebugLineStep; - float line; - - // Initialize Pen // - //----------------// - // Create a shadow when displaying a text. - TextContext->setShaded(true); - // Set the font size. - TextContext->setFontSize(ClientCfg.DebugFontSize); - - - - // TOP LEFT // - //----------// - TextContext->setHotSpot(UTextContext::TopLeft); - line = 0.9f; - - CInterfaceManager *pIM = CInterfaceManager::getInstance(); - // for now only accessible with R2ED - if (ClientCfg.R2EDEnabled) - { - TextContext->setColor(CRGBA::Cyan); - TextContext->printfAt(0.1f, line, "Press default key (ctrl+shift+A) to cycle prev"); - line-= lineStep; - TextContext->printfAt(0.1f, line, "Press default key (ctrl+shift+Q) to cycle next"); - line-= lineStep; - TextContext->printfAt(0.1f, line, "Press default key (ctrl+shift+W) to inspect element"); - line-= 2 * lineStep; - } - // - const vector &rICL = CWidgetManager::getInstance()->getCtrlsUnderPointer (); - const vector &rIGL = CWidgetManager::getInstance()->getGroupsUnderPointer (); - // If previous highlighted element is found in the list, then keep it, else reset to first element - if (std::find(rICL.begin(), rICL.end(), HighlightedDebugUI) == rICL.end() && - std::find(rIGL.begin(), rIGL.end(), HighlightedDebugUI) == rIGL.end()) - { - if (!rICL.empty()) - { - HighlightedDebugUI = rICL[0]; - } - else - if (!rIGL.empty()) - { - HighlightedDebugUI = rIGL[0]; - } - else - { - HighlightedDebugUI = NULL; - } - } - // - TextContext->setColor(CRGBA::Green); - TextContext->printfAt(0.1f, line, "Controls under cursor "); - line -= lineStep * 1.4f; - TextContext->printfAt(0.1f, line, "----------------------"); - line -= lineStep; - for(uint k = 0; k < rICL.size(); ++k) - { - if (rICL[k]) - { - TextContext->setColor(rICL[k] != HighlightedDebugUI ? ClientCfg.DebugFontColor : CRGBA::Red); - TextContext->printfAt(0.1f, line, "id = %s, address = 0x%p, parent = 0x%p", rICL[k]->getId().c_str(), rICL[k], rICL[k]->getParent()); - } - else - { - TextContext->setColor(CRGBA::Blue); - TextContext->printfAt(0.1f, line, " control found !!!"); - } - line-= lineStep; - } - // - TextContext->setColor(CRGBA::Green); - TextContext->printfAt(0.1f, line, "Groups under cursor "); - line -= lineStep * 1.4f; - TextContext->printfAt(0.1f, line, "----------------------"); - line-= lineStep; - for(uint k = 0; k < rIGL.size(); ++k) - { - if (rIGL[k]) - { - TextContext->setColor(rIGL[k] != HighlightedDebugUI ? ClientCfg.DebugFontColor : CRGBA::Red); - TextContext->printfAt(0.1f, line, "id = %s, address = 0x%p, parent = 0x%p", rIGL[k]->getId().c_str(), rIGL[k], rIGL[k]->getParent()); - } - else - { - TextContext->setColor(CRGBA::Blue); - TextContext->printfAt(0.1f, line, " group found !!!"); - } - line-= lineStep; - } -} - - - -// get all element under the mouse in a single vector -static void getElementsUnderMouse(vector &ielem) -{ - CInterfaceManager *pIM = CInterfaceManager::getInstance(); - const vector &rICL = CWidgetManager::getInstance()->getCtrlsUnderPointer(); - const vector &rIGL = CWidgetManager::getInstance()->getGroupsUnderPointer(); - ielem.clear(); - ielem.insert(ielem.end(), rICL.begin(), rICL.end()); - ielem.insert(ielem.end(), rIGL.begin(), rIGL.end()); -} - -class CHandlerDebugUiPrevElementUnderMouse : public IActionHandler -{ - virtual void execute (CCtrlBase * /* pCaller */, const std::string &/* sParams */) - { - vector ielem; - getElementsUnderMouse(ielem); - for(uint k = 0; k < ielem.size(); ++k) - { - if (HighlightedDebugUI == ielem[k]) - { - HighlightedDebugUI = ielem[k == 0 ? ielem.size() - 1 : k - 1]; - return; - } - } - } -}; -REGISTER_ACTION_HANDLER( CHandlerDebugUiPrevElementUnderMouse, "debug_ui_prev_element_under_mouse"); - -class CHandlerDebugUiNextElementUnderMouse : public IActionHandler -{ - virtual void execute (CCtrlBase * /* pCaller */, const std::string &/* sParams */) - { - vector ielem; - getElementsUnderMouse(ielem); - for(uint k = 0; k < ielem.size(); ++k) - { - if (HighlightedDebugUI == ielem[k]) - { - HighlightedDebugUI = ielem[(k + 1) % ielem.size()]; - return; - } - } - } -}; -REGISTER_ACTION_HANDLER( CHandlerDebugUiNextElementUnderMouse, "debug_ui_next_element_under_mouse"); - -class CHandlerDebugUiDumpElementUnderMouse : public IActionHandler -{ - virtual void execute (CCtrlBase * /* pCaller */, const std::string &/* sParams */) - { - if (HighlightedDebugUI == NULL) return; - CLuaState *lua = CLuaManager::getInstance().getLuaState(); - if (!lua) return; - CLuaStackRestorer lsr(lua, 0); - CLuaIHM::pushUIOnStack(*lua, HighlightedDebugUI); - lua->pushGlobalTable(); - CLuaObject env(*lua); - env["inspect"].callNoThrow(1, 0); - } -}; -REGISTER_ACTION_HANDLER( CHandlerDebugUiDumpElementUnderMouse, "debug_ui_inspect_element_under_mouse"); - - - - //--------------------------------------------------- // Just Display some text with ... anim at some place. //--------------------------------------------------- diff --git a/code/ryzom/client/src/main_loop_debug.cpp b/code/ryzom/client/src/main_loop_debug.cpp index fc0e7801c..dbcb47b96 100644 --- a/code/ryzom/client/src/main_loop_debug.cpp +++ b/code/ryzom/client/src/main_loop_debug.cpp @@ -18,6 +18,7 @@ #include "main_loop_debug.h" #include +#include #include "game_share/ryzom_version.h" @@ -40,9 +41,12 @@ #include "client_sheets/weather_function_params_sheet.h" #include "weather_manager_client.h" #include "fog_map.h" +#include "misc.h" +#include "interface_v3/interface_manager.h" using namespace NLMISC; using namespace NL3D; +using namespace NLGUI; // ******************************************************************** // ******************************************************************** @@ -54,6 +58,7 @@ extern std::set LodCharactersNotFound; extern uint32 NbDatabaseChanges; extern CFogState MainFogState; extern CPing Ping; +extern bool Filter3D[RYZOM_MAX_FILTER_3D]; //namespace /* anonymous */ { @@ -473,6 +478,215 @@ void displayDebug() // ******************************************************************** // ******************************************************************** +//--------------------------------------------------- +// displayDebug : +// Display some debug infos. +//--------------------------------------------------- +void displayDebugFps() +{ + float lineStep = ClientCfg.DebugLineStep; + float line; + + // Initialize Pen // + //----------------// + // Create a shadow when displaying a text. + TextContext->setShaded(true); + // Set the font size. + TextContext->setFontSize(ClientCfg.DebugFontSize); + // Set the text color + TextContext->setColor(ClientCfg.DebugFontColor); + + // TOP LEFT // + //----------// + TextContext->setHotSpot(UTextContext::TopLeft); + line = 0.9f; + // Ms per frame + { + float spf = smoothFPS.getSmoothValue (); + // Ms per frame + TextContext->printfAt(0.1f, line, "FPS %.1f ms - %.1f fps", spf*1000, 1.f/spf); + line-= lineStep; + // More Smoothed Ms per frame + spf = moreSmoothFPS.getSmoothValue (); + TextContext->printfAt(0.1f, line, "Smoothed FPS %.1f ms - %.1f fps", spf*1000, 1.f/spf); + line-= lineStep; + } +} + +// ******************************************************************** +// ******************************************************************** +// ******************************************************************** +// ******************************************************************** +// ******************************************************************** + +static NLMISC::CRefPtr HighlightedDebugUI; + +// displayDebug : +// Display information about ui elements that are under the mouse +//--------------------------------------------------- +void displayDebugUIUnderMouse() +{ + float lineStep = ClientCfg.DebugLineStep; + float line; + + // Initialize Pen // + //----------------// + // Create a shadow when displaying a text. + TextContext->setShaded(true); + // Set the font size. + TextContext->setFontSize(ClientCfg.DebugFontSize); + + + + // TOP LEFT // + //----------// + TextContext->setHotSpot(UTextContext::TopLeft); + line = 0.9f; + + CInterfaceManager *pIM = CInterfaceManager::getInstance(); + // for now only accessible with R2ED + if (ClientCfg.R2EDEnabled) + { + TextContext->setColor(CRGBA::Cyan); + TextContext->printfAt(0.1f, line, "Press default key (ctrl+shift+A) to cycle prev"); + line-= lineStep; + TextContext->printfAt(0.1f, line, "Press default key (ctrl+shift+Q) to cycle next"); + line-= lineStep; + TextContext->printfAt(0.1f, line, "Press default key (ctrl+shift+W) to inspect element"); + line-= 2 * lineStep; + } + // + const std::vector &rICL = CWidgetManager::getInstance()->getCtrlsUnderPointer (); + const std::vector &rIGL = CWidgetManager::getInstance()->getGroupsUnderPointer (); + // If previous highlighted element is found in the list, then keep it, else reset to first element + if (std::find(rICL.begin(), rICL.end(), HighlightedDebugUI) == rICL.end() && + std::find(rIGL.begin(), rIGL.end(), HighlightedDebugUI) == rIGL.end()) + { + if (!rICL.empty()) + { + HighlightedDebugUI = rICL[0]; + } + else + if (!rIGL.empty()) + { + HighlightedDebugUI = rIGL[0]; + } + else + { + HighlightedDebugUI = NULL; + } + } + // + TextContext->setColor(CRGBA::Green); + TextContext->printfAt(0.1f, line, "Controls under cursor "); + line -= lineStep * 1.4f; + TextContext->printfAt(0.1f, line, "----------------------"); + line -= lineStep; + for(uint k = 0; k < rICL.size(); ++k) + { + if (rICL[k]) + { + TextContext->setColor(rICL[k] != HighlightedDebugUI ? ClientCfg.DebugFontColor : CRGBA::Red); + TextContext->printfAt(0.1f, line, "id = %s, address = 0x%p, parent = 0x%p", rICL[k]->getId().c_str(), rICL[k], rICL[k]->getParent()); + } + else + { + TextContext->setColor(CRGBA::Blue); + TextContext->printfAt(0.1f, line, " control found !!!"); + } + line-= lineStep; + } + // + TextContext->setColor(CRGBA::Green); + TextContext->printfAt(0.1f, line, "Groups under cursor "); + line -= lineStep * 1.4f; + TextContext->printfAt(0.1f, line, "----------------------"); + line-= lineStep; + for(uint k = 0; k < rIGL.size(); ++k) + { + if (rIGL[k]) + { + TextContext->setColor(rIGL[k] != HighlightedDebugUI ? ClientCfg.DebugFontColor : CRGBA::Red); + TextContext->printfAt(0.1f, line, "id = %s, address = 0x%p, parent = 0x%p", rIGL[k]->getId().c_str(), rIGL[k], rIGL[k]->getParent()); + } + else + { + TextContext->setColor(CRGBA::Blue); + TextContext->printfAt(0.1f, line, " group found !!!"); + } + line-= lineStep; + } +} + +// get all element under the mouse in a single vector +static void getElementsUnderMouse(std::vector &ielem) +{ + CInterfaceManager *pIM = CInterfaceManager::getInstance(); + const std::vector &rICL = CWidgetManager::getInstance()->getCtrlsUnderPointer(); + const std::vector &rIGL = CWidgetManager::getInstance()->getGroupsUnderPointer(); + ielem.clear(); + ielem.insert(ielem.end(), rICL.begin(), rICL.end()); + ielem.insert(ielem.end(), rIGL.begin(), rIGL.end()); +} + +class CHandlerDebugUiPrevElementUnderMouse : public IActionHandler +{ + virtual void execute (CCtrlBase * /* pCaller */, const std::string &/* sParams */) + { + std::vector ielem; + getElementsUnderMouse(ielem); + for(uint k = 0; k < ielem.size(); ++k) + { + if (HighlightedDebugUI == ielem[k]) + { + HighlightedDebugUI = ielem[k == 0 ? ielem.size() - 1 : k - 1]; + return; + } + } + } +}; +REGISTER_ACTION_HANDLER( CHandlerDebugUiPrevElementUnderMouse, "debug_ui_prev_element_under_mouse"); + +class CHandlerDebugUiNextElementUnderMouse : public IActionHandler +{ + virtual void execute (CCtrlBase * /* pCaller */, const std::string &/* sParams */) + { + std::vector ielem; + getElementsUnderMouse(ielem); + for(uint k = 0; k < ielem.size(); ++k) + { + if (HighlightedDebugUI == ielem[k]) + { + HighlightedDebugUI = ielem[(k + 1) % ielem.size()]; + return; + } + } + } +}; +REGISTER_ACTION_HANDLER( CHandlerDebugUiNextElementUnderMouse, "debug_ui_next_element_under_mouse"); + +class CHandlerDebugUiDumpElementUnderMouse : public IActionHandler +{ + virtual void execute (CCtrlBase * /* pCaller */, const std::string &/* sParams */) + { + if (HighlightedDebugUI == NULL) return; + CLuaState *lua = CLuaManager::getInstance().getLuaState(); + if (!lua) return; + CLuaStackRestorer lsr(lua, 0); + CLuaIHM::pushUIOnStack(*lua, HighlightedDebugUI); + lua->pushGlobalTable(); + CLuaObject env(*lua); + env["inspect"].callNoThrow(1, 0); + } +}; +REGISTER_ACTION_HANDLER( CHandlerDebugUiDumpElementUnderMouse, "debug_ui_inspect_element_under_mouse"); + +// ******************************************************************** +// ******************************************************************** +// ******************************************************************** +// ******************************************************************** +// ******************************************************************** + //----------------------------------------------- // Macro to Display a Text //----------------------------------------------- From 1928c9c6793fedb3d43112783e8ad2250591c5b7 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Thu, 27 Jun 2013 03:16:53 +0200 Subject: [PATCH 048/313] Remove some unused includes, re #43 --- code/ryzom/client/src/main_loop.cpp | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/code/ryzom/client/src/main_loop.cpp b/code/ryzom/client/src/main_loop.cpp index 4191e22b7..36be09144 100644 --- a/code/ryzom/client/src/main_loop.cpp +++ b/code/ryzom/client/src/main_loop.cpp @@ -125,17 +125,6 @@ #include "nel/misc/check_fpu.h" -// TMP TMP -#include "nel/gui/ctrl_polygon.h" -// TMP TMP -#include "game_share/scenario_entry_points.h" -#include "nel/3d/driver.h" -#include "nel/3d/texture_file.h" - -#include "nel/3d/packed_world.h" -#include "nel/3d/packed_zone.h" -#include "nel/3d/driver_user.h" - #ifdef USE_WATER_ENV_MAP @@ -399,11 +388,6 @@ void buildCameraClippingPyramid (vector &planes) } } - - - - - //--------------------------------------------------- // update the camera perspective setup //--------------------------------------------------- From f84f5807c14535c0ef99a949fcdbd283536620ea Mon Sep 17 00:00:00 2001 From: kaetemi Date: Thu, 27 Jun 2013 03:49:30 +0200 Subject: [PATCH 049/313] Move some config stuff out of main_loop.cpp, see #43 --- code/ryzom/client/src/main_loop.cpp | 320 +--------------- code/ryzom/client/src/main_loop_utilities.cpp | 359 ++++++++++++++++++ code/ryzom/client/src/main_loop_utilities.h | 35 ++ 3 files changed, 395 insertions(+), 319 deletions(-) create mode 100644 code/ryzom/client/src/main_loop_utilities.cpp create mode 100644 code/ryzom/client/src/main_loop_utilities.h diff --git a/code/ryzom/client/src/main_loop.cpp b/code/ryzom/client/src/main_loop.cpp index 36be09144..64379b7c2 100644 --- a/code/ryzom/client/src/main_loop.cpp +++ b/code/ryzom/client/src/main_loop.cpp @@ -144,6 +144,7 @@ #include "profiling.h" #include "main_loop_debug.h" #include "main_loop_temp.h" +#include "main_loop_utilities.h" /////////// @@ -388,325 +389,6 @@ void buildCameraClippingPyramid (vector &planes) } } -//--------------------------------------------------- -// update the camera perspective setup -//--------------------------------------------------- -void updateCameraPerspective() -{ - float fov, aspectRatio; - computeCurrentFovAspectRatio(fov, aspectRatio); - - // change the perspective of the scene - if(!MainCam.empty()) - MainCam.setPerspective(fov, aspectRatio, CameraSetupZNear, ClientCfg.Vision); - // change the perspective of the root scene - if(SceneRoot) - { - UCamera cam= SceneRoot->getCam(); - cam.setPerspective(fov, aspectRatio, SceneRootCameraZNear, SceneRootCameraZFar); - } -} - -//--------------------------------------------------- -// Compare ClientCfg and LastClientCfg to know what we must update -//--------------------------------------------------- -void updateFromClientCfg() -{ - CClientConfig::setValues(); - ClientCfg.IsInvalidated = false; - - // GRAPHICS - GENERAL - //--------------------------------------------------- - if ((ClientCfg.Windowed != LastClientCfg.Windowed) || - (ClientCfg.Width != LastClientCfg.Width) || - (ClientCfg.Height != LastClientCfg.Height) || - (ClientCfg.Depth != LastClientCfg.Depth) || - (ClientCfg.Frequency != LastClientCfg.Frequency)) - { - setVideoMode(UDriver::CMode(ClientCfg.Width, ClientCfg.Height, (uint8)ClientCfg.Depth, - ClientCfg.Windowed, ClientCfg.Frequency)); - } - - if (ClientCfg.DivideTextureSizeBy2 != LastClientCfg.DivideTextureSizeBy2) - { - if (ClientCfg.DivideTextureSizeBy2) - Driver->forceTextureResize(2); - else - Driver->forceTextureResize(1); - } - - //--------------------------------------------------- - if (ClientCfg.WaitVBL != LastClientCfg.WaitVBL) - { - if(ClientCfg.WaitVBL) - Driver->setSwapVBLInterval(1); - else - Driver->setSwapVBLInterval(0); - } - - // GRAPHICS - LANDSCAPE - //--------------------------------------------------- - if (ClientCfg.LandscapeThreshold != LastClientCfg.LandscapeThreshold) - { - if (Landscape) Landscape->setThreshold(ClientCfg.getActualLandscapeThreshold()); - } - - //--------------------------------------------------- - if (ClientCfg.LandscapeTileNear != LastClientCfg.LandscapeTileNear) - { - if (Landscape) Landscape->setTileNear(ClientCfg.LandscapeTileNear); - } - - //--------------------------------------------------- - if (Landscape) - { - if (ClientCfg.Vision != LastClientCfg.Vision) - { - if (!ClientCfg.Light) - { - // Not in an indoor ? - if (ContinentMngr.cur() && !ContinentMngr.cur()->Indoor) - { - // Refresh All Zone in streaming according to the refine position - vector zonesAdded; - vector zonesRemoved; - const R2::CScenarioEntryPoints::CCompleteIsland *ci = R2::CScenarioEntryPoints::getInstance().getCompleteIslandFromCoords(CVector2f((float) UserEntity->pos().x, (float) UserEntity->pos().y)); - Landscape->refreshAllZonesAround(View.refinePos(), ClientCfg.Vision + ExtraZoneLoadingVision, zonesAdded, zonesRemoved, ProgressBar, ci ? &(ci->ZoneIDs) : NULL); - LandscapeIGManager.unloadArrayZoneIG(zonesRemoved); - LandscapeIGManager.loadArrayZoneIG(zonesAdded); - } - } - } - } - - //--------------------------------------------------- - if (ClientCfg.Vision != LastClientCfg.Vision || ClientCfg.FoV!=LastClientCfg.FoV || - ClientCfg.Windowed != LastClientCfg.Windowed || ClientCfg.ScreenAspectRatio != LastClientCfg.ScreenAspectRatio ) - { - updateCameraPerspective(); - } - - //--------------------------------------------------- - if (Landscape) - { - if (ClientCfg.MicroVeget != LastClientCfg.MicroVeget) - { - if(ClientCfg.MicroVeget) - { - // if configured, enable the vegetable and load the texture. - Landscape->enableVegetable(true); - // Default setup. TODO later by gameDev. - Landscape->setVegetableWind(CVector(0.5, 0.5, 0).normed(), 0.5, 1, 0); - // Default setup. should work well for night/day transition in 30 minutes. - // Because all vegetables will be updated every 20 seconds => 90 steps. - Landscape->setVegetableUpdateLightingFrequency(1/20.f); - // Density (percentage to ratio) - Landscape->setVegetableDensity(ClientCfg.MicroVegetDensity/100.f); - } - else - { - Landscape->enableVegetable(false); - } - } - } - - //--------------------------------------------------- - if (ClientCfg.MicroVegetDensity != LastClientCfg.MicroVegetDensity) - { - // Density (percentage to ratio) - if (Landscape) Landscape->setVegetableDensity(ClientCfg.MicroVegetDensity/100.f); - } - - // GRAPHICS - SPECIAL EFFECTS - //--------------------------------------------------- - if (ClientCfg.FxNbMaxPoly != LastClientCfg.FxNbMaxPoly) - { - if (Scene->getGroupLoadMaxPolygon("Fx") != ClientCfg.FxNbMaxPoly) - Scene->setGroupLoadMaxPolygon("Fx", ClientCfg.FxNbMaxPoly); - } - - //--------------------------------------------------- - if (ClientCfg.Cloud != LastClientCfg.Cloud) - { - if (ClientCfg.Cloud) - { - InitCloudScape = true; - CloudScape = Scene->createCloudScape(); - } - else - { - if (CloudScape != NULL) - Scene->deleteCloudScape(CloudScape); - CloudScape = NULL; - } - } - - //--------------------------------------------------- - if (ClientCfg.CloudQuality != LastClientCfg.CloudQuality) - { - if (CloudScape != NULL) - CloudScape->setQuality(ClientCfg.CloudQuality); - } - - //--------------------------------------------------- - if (ClientCfg.CloudUpdate != LastClientCfg.CloudUpdate) - { - if (CloudScape != NULL) - CloudScape->setNbCloudToUpdateIn80ms(ClientCfg.CloudUpdate); - } - - //--------------------------------------------------- - if (ClientCfg.Shadows != LastClientCfg.Shadows) - { - // Enable/Disable Receive on Landscape - if(Landscape) - { - Landscape->enableReceiveShadowMap(ClientCfg.Shadows); - } - // Enable/Disable Cast for all entities - for(uint i=0;iupdateCastShadowMap(); - } - } - - // GRAPHICS - CHARACTERS - //--------------------------------------------------- - if (ClientCfg.SkinNbMaxPoly != LastClientCfg.SkinNbMaxPoly) - { - if (Scene->getGroupLoadMaxPolygon("Skin") != ClientCfg.SkinNbMaxPoly) - Scene->setGroupLoadMaxPolygon("Skin", ClientCfg.SkinNbMaxPoly); - } - - //--------------------------------------------------- - if (ClientCfg.NbMaxSkeletonNotCLod != LastClientCfg.NbMaxSkeletonNotCLod ) - { - Scene->setMaxSkeletonsInNotCLodForm(ClientCfg.NbMaxSkeletonNotCLod); - } - - //--------------------------------------------------- - if (ClientCfg.CharacterFarClip != LastClientCfg.CharacterFarClip) - { - // Nothing to do - } - - //--------------------------------------------------- - if (ClientCfg.HDEntityTexture != LastClientCfg.HDEntityTexture) - { - // Don't reload Texture, will be done at next Game Start - } - - // INTERFACE works - - - // INPUTS - //--------------------------------------------------- - if (ClientCfg.CursorSpeed != LastClientCfg.CursorSpeed) - SetMouseSpeed (ClientCfg.CursorSpeed); - - if (ClientCfg.CursorAcceleration != LastClientCfg.CursorAcceleration) - SetMouseAcceleration (ClientCfg.CursorAcceleration); - - if (ClientCfg.HardwareCursor != LastClientCfg.HardwareCursor) - { - if (ClientCfg.HardwareCursor != IsMouseCursorHardware()) - { - InitMouseWithCursor (ClientCfg.HardwareCursor); - } - } - - - // SOUND - //--------------------------------------------------- - bool mustReloadSoundMngrContinent= false; - - // disable/enable sound? - if (ClientCfg.SoundOn != LastClientCfg.SoundOn) - { - if (SoundMngr && !ClientCfg.SoundOn) - { - nlwarning("Deleting sound manager..."); - delete SoundMngr; - SoundMngr = NULL; - } - else if (SoundMngr == NULL && ClientCfg.SoundOn) - { - nlwarning("Creating sound manager..."); - SoundMngr = new CSoundManager(); - try - { - SoundMngr->init(NULL); - } - catch(const Exception &e) - { - nlwarning("init : Error when creating 'SoundMngr' : %s", e.what()); - SoundMngr = 0; - } - - // re-init with good SFX/Music Volume - if(SoundMngr) - { - SoundMngr->setSFXVolume(ClientCfg.SoundSFXVolume); - SoundMngr->setGameMusicVolume(ClientCfg.SoundGameMusicVolume); - } - } - else - { - nlwarning("Sound config error !"); - } - - mustReloadSoundMngrContinent= true; - } - - // change EAX? - if ( SoundMngr && LastClientCfg.SoundOn && - (ClientCfg.UseEax != LastClientCfg.UseEax) ) - { - SoundMngr->reset(); - - mustReloadSoundMngrContinent= true; - } - - // change SoundForceSoftwareBuffer? - if ( SoundMngr && LastClientCfg.SoundOn && - (ClientCfg.SoundForceSoftwareBuffer != LastClientCfg.SoundForceSoftwareBuffer) ) - { - SoundMngr->reset(); - - mustReloadSoundMngrContinent= true; - } - - // change MaxTrack? don't reset - if ( SoundMngr && LastClientCfg.SoundOn && - (ClientCfg.MaxTrack != LastClientCfg.MaxTrack)) - { - SoundMngr->getMixer()->changeMaxTrack(ClientCfg.MaxTrack); - } - - // change SoundFX Volume? don't reset - if (SoundMngr && ClientCfg.SoundSFXVolume != LastClientCfg.SoundSFXVolume) - { - SoundMngr->setSFXVolume(ClientCfg.SoundSFXVolume); - } - - // change Game Music Volume? don't reset - if (SoundMngr && ClientCfg.SoundGameMusicVolume != LastClientCfg.SoundGameMusicVolume) - { - SoundMngr->setGameMusicVolume(ClientCfg.SoundGameMusicVolume); - } - - // reload only if active and reseted - if (mustReloadSoundMngrContinent && SoundMngr && ContinentMngr.cur() && !ContinentMngr.cur()->Indoor && UserEntity) - { - SoundMngr->loadContinent(ContinentMngr.getCurrentContinentSelectName(), UserEntity->pos()); - } - - // Ok backup the new clientcfg - LastClientCfg = ClientCfg; -} - void preRenderNewSky () { CSky &sky = ContinentMngr.cur()->CurrentSky; diff --git a/code/ryzom/client/src/main_loop_utilities.cpp b/code/ryzom/client/src/main_loop_utilities.cpp new file mode 100644 index 000000000..26be0ab0d --- /dev/null +++ b/code/ryzom/client/src/main_loop_utilities.cpp @@ -0,0 +1,359 @@ +// Ryzom - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#include +#include "main_loop_utilities.h" + +#include +#include + +#include "game_share/scenario_entry_points.h" + +#include "client_cfg.h" +#include "misc.h" +#include "global.h" +#include "world_database_manager.h" +#include "continent_manager.h" +#include "user_entity.h" +#include "view.h" +#include "ig_client.h" +#include "entities.h" +#include "input.h" +#include "sound_manager.h" + +using namespace NLMISC; +using namespace NL3D; + +//--------------------------------------------------- +// update the camera perspective setup +//--------------------------------------------------- +void updateCameraPerspective() +{ + float fov, aspectRatio; + computeCurrentFovAspectRatio(fov, aspectRatio); + + // change the perspective of the scene + if(!MainCam.empty()) + MainCam.setPerspective(fov, aspectRatio, CameraSetupZNear, ClientCfg.Vision); + // change the perspective of the root scene + if(SceneRoot) + { + UCamera cam= SceneRoot->getCam(); + cam.setPerspective(fov, aspectRatio, SceneRootCameraZNear, SceneRootCameraZFar); + } +} + +//--------------------------------------------------- +// Compare ClientCfg and LastClientCfg to know what we must update +//--------------------------------------------------- +void updateFromClientCfg() +{ + CClientConfig::setValues(); + ClientCfg.IsInvalidated = false; + + // GRAPHICS - GENERAL + //--------------------------------------------------- + if ((ClientCfg.Windowed != LastClientCfg.Windowed) || + (ClientCfg.Width != LastClientCfg.Width) || + (ClientCfg.Height != LastClientCfg.Height) || + (ClientCfg.Depth != LastClientCfg.Depth) || + (ClientCfg.Frequency != LastClientCfg.Frequency)) + { + setVideoMode(UDriver::CMode(ClientCfg.Width, ClientCfg.Height, (uint8)ClientCfg.Depth, + ClientCfg.Windowed, ClientCfg.Frequency)); + } + + if (ClientCfg.DivideTextureSizeBy2 != LastClientCfg.DivideTextureSizeBy2) + { + if (ClientCfg.DivideTextureSizeBy2) + Driver->forceTextureResize(2); + else + Driver->forceTextureResize(1); + } + + //--------------------------------------------------- + if (ClientCfg.WaitVBL != LastClientCfg.WaitVBL) + { + if(ClientCfg.WaitVBL) + Driver->setSwapVBLInterval(1); + else + Driver->setSwapVBLInterval(0); + } + + // GRAPHICS - LANDSCAPE + //--------------------------------------------------- + if (ClientCfg.LandscapeThreshold != LastClientCfg.LandscapeThreshold) + { + if (Landscape) Landscape->setThreshold(ClientCfg.getActualLandscapeThreshold()); + } + + //--------------------------------------------------- + if (ClientCfg.LandscapeTileNear != LastClientCfg.LandscapeTileNear) + { + if (Landscape) Landscape->setTileNear(ClientCfg.LandscapeTileNear); + } + + //--------------------------------------------------- + if (Landscape) + { + if (ClientCfg.Vision != LastClientCfg.Vision) + { + if (!ClientCfg.Light) + { + // Not in an indoor ? + if (ContinentMngr.cur() && !ContinentMngr.cur()->Indoor) + { + // Refresh All Zone in streaming according to the refine position + std::vector zonesAdded; + std::vector zonesRemoved; + const R2::CScenarioEntryPoints::CCompleteIsland *ci = R2::CScenarioEntryPoints::getInstance().getCompleteIslandFromCoords(CVector2f((float) UserEntity->pos().x, (float) UserEntity->pos().y)); + Landscape->refreshAllZonesAround(View.refinePos(), ClientCfg.Vision + ExtraZoneLoadingVision, zonesAdded, zonesRemoved, ProgressBar, ci ? &(ci->ZoneIDs) : NULL); + LandscapeIGManager.unloadArrayZoneIG(zonesRemoved); + LandscapeIGManager.loadArrayZoneIG(zonesAdded); + } + } + } + } + + //--------------------------------------------------- + if (ClientCfg.Vision != LastClientCfg.Vision || ClientCfg.FoV!=LastClientCfg.FoV || + ClientCfg.Windowed != LastClientCfg.Windowed || ClientCfg.ScreenAspectRatio != LastClientCfg.ScreenAspectRatio ) + { + updateCameraPerspective(); + } + + //--------------------------------------------------- + if (Landscape) + { + if (ClientCfg.MicroVeget != LastClientCfg.MicroVeget) + { + if(ClientCfg.MicroVeget) + { + // if configured, enable the vegetable and load the texture. + Landscape->enableVegetable(true); + // Default setup. TODO later by gameDev. + Landscape->setVegetableWind(CVector(0.5, 0.5, 0).normed(), 0.5, 1, 0); + // Default setup. should work well for night/day transition in 30 minutes. + // Because all vegetables will be updated every 20 seconds => 90 steps. + Landscape->setVegetableUpdateLightingFrequency(1/20.f); + // Density (percentage to ratio) + Landscape->setVegetableDensity(ClientCfg.MicroVegetDensity/100.f); + } + else + { + Landscape->enableVegetable(false); + } + } + } + + //--------------------------------------------------- + if (ClientCfg.MicroVegetDensity != LastClientCfg.MicroVegetDensity) + { + // Density (percentage to ratio) + if (Landscape) Landscape->setVegetableDensity(ClientCfg.MicroVegetDensity/100.f); + } + + // GRAPHICS - SPECIAL EFFECTS + //--------------------------------------------------- + if (ClientCfg.FxNbMaxPoly != LastClientCfg.FxNbMaxPoly) + { + if (Scene->getGroupLoadMaxPolygon("Fx") != ClientCfg.FxNbMaxPoly) + Scene->setGroupLoadMaxPolygon("Fx", ClientCfg.FxNbMaxPoly); + } + + //--------------------------------------------------- + if (ClientCfg.Cloud != LastClientCfg.Cloud) + { + if (ClientCfg.Cloud) + { + InitCloudScape = true; + CloudScape = Scene->createCloudScape(); + } + else + { + if (CloudScape != NULL) + Scene->deleteCloudScape(CloudScape); + CloudScape = NULL; + } + } + + //--------------------------------------------------- + if (ClientCfg.CloudQuality != LastClientCfg.CloudQuality) + { + if (CloudScape != NULL) + CloudScape->setQuality(ClientCfg.CloudQuality); + } + + //--------------------------------------------------- + if (ClientCfg.CloudUpdate != LastClientCfg.CloudUpdate) + { + if (CloudScape != NULL) + CloudScape->setNbCloudToUpdateIn80ms(ClientCfg.CloudUpdate); + } + + //--------------------------------------------------- + if (ClientCfg.Shadows != LastClientCfg.Shadows) + { + // Enable/Disable Receive on Landscape + if(Landscape) + { + Landscape->enableReceiveShadowMap(ClientCfg.Shadows); + } + // Enable/Disable Cast for all entities + for(uint i=0;iupdateCastShadowMap(); + } + } + + // GRAPHICS - CHARACTERS + //--------------------------------------------------- + if (ClientCfg.SkinNbMaxPoly != LastClientCfg.SkinNbMaxPoly) + { + if (Scene->getGroupLoadMaxPolygon("Skin") != ClientCfg.SkinNbMaxPoly) + Scene->setGroupLoadMaxPolygon("Skin", ClientCfg.SkinNbMaxPoly); + } + + //--------------------------------------------------- + if (ClientCfg.NbMaxSkeletonNotCLod != LastClientCfg.NbMaxSkeletonNotCLod ) + { + Scene->setMaxSkeletonsInNotCLodForm(ClientCfg.NbMaxSkeletonNotCLod); + } + + //--------------------------------------------------- + if (ClientCfg.CharacterFarClip != LastClientCfg.CharacterFarClip) + { + // Nothing to do + } + + //--------------------------------------------------- + if (ClientCfg.HDEntityTexture != LastClientCfg.HDEntityTexture) + { + // Don't reload Texture, will be done at next Game Start + } + + // INTERFACE works + + + // INPUTS + //--------------------------------------------------- + if (ClientCfg.CursorSpeed != LastClientCfg.CursorSpeed) + SetMouseSpeed (ClientCfg.CursorSpeed); + + if (ClientCfg.CursorAcceleration != LastClientCfg.CursorAcceleration) + SetMouseAcceleration (ClientCfg.CursorAcceleration); + + if (ClientCfg.HardwareCursor != LastClientCfg.HardwareCursor) + { + if (ClientCfg.HardwareCursor != IsMouseCursorHardware()) + { + InitMouseWithCursor (ClientCfg.HardwareCursor); + } + } + + + // SOUND + //--------------------------------------------------- + bool mustReloadSoundMngrContinent= false; + + // disable/enable sound? + if (ClientCfg.SoundOn != LastClientCfg.SoundOn) + { + if (SoundMngr && !ClientCfg.SoundOn) + { + nlwarning("Deleting sound manager..."); + delete SoundMngr; + SoundMngr = NULL; + } + else if (SoundMngr == NULL && ClientCfg.SoundOn) + { + nlwarning("Creating sound manager..."); + SoundMngr = new CSoundManager(); + try + { + SoundMngr->init(NULL); + } + catch(const Exception &e) + { + nlwarning("init : Error when creating 'SoundMngr' : %s", e.what()); + SoundMngr = 0; + } + + // re-init with good SFX/Music Volume + if(SoundMngr) + { + SoundMngr->setSFXVolume(ClientCfg.SoundSFXVolume); + SoundMngr->setGameMusicVolume(ClientCfg.SoundGameMusicVolume); + } + } + else + { + nlwarning("Sound config error !"); + } + + mustReloadSoundMngrContinent= true; + } + + // change EAX? + if ( SoundMngr && LastClientCfg.SoundOn && + (ClientCfg.UseEax != LastClientCfg.UseEax) ) + { + SoundMngr->reset(); + + mustReloadSoundMngrContinent= true; + } + + // change SoundForceSoftwareBuffer? + if ( SoundMngr && LastClientCfg.SoundOn && + (ClientCfg.SoundForceSoftwareBuffer != LastClientCfg.SoundForceSoftwareBuffer) ) + { + SoundMngr->reset(); + + mustReloadSoundMngrContinent= true; + } + + // change MaxTrack? don't reset + if ( SoundMngr && LastClientCfg.SoundOn && + (ClientCfg.MaxTrack != LastClientCfg.MaxTrack)) + { + SoundMngr->getMixer()->changeMaxTrack(ClientCfg.MaxTrack); + } + + // change SoundFX Volume? don't reset + if (SoundMngr && ClientCfg.SoundSFXVolume != LastClientCfg.SoundSFXVolume) + { + SoundMngr->setSFXVolume(ClientCfg.SoundSFXVolume); + } + + // change Game Music Volume? don't reset + if (SoundMngr && ClientCfg.SoundGameMusicVolume != LastClientCfg.SoundGameMusicVolume) + { + SoundMngr->setGameMusicVolume(ClientCfg.SoundGameMusicVolume); + } + + // reload only if active and reseted + if (mustReloadSoundMngrContinent && SoundMngr && ContinentMngr.cur() && !ContinentMngr.cur()->Indoor && UserEntity) + { + SoundMngr->loadContinent(ContinentMngr.getCurrentContinentSelectName(), UserEntity->pos()); + } + + // Ok backup the new clientcfg + LastClientCfg = ClientCfg; +} + +/* end of file */ \ No newline at end of file diff --git a/code/ryzom/client/src/main_loop_utilities.h b/code/ryzom/client/src/main_loop_utilities.h new file mode 100644 index 000000000..48d6a3880 --- /dev/null +++ b/code/ryzom/client/src/main_loop_utilities.h @@ -0,0 +1,35 @@ +// Ryzom - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#ifndef CL_MAIN_LOOP_UTILITIES_H +#define CL_MAIN_LOOP_UTILITIES_H + +#include + +// Update Utilities (configuration etc) +// Only put utility update functions here that are used in the main loop. +// This is mainly for system configuration related functions +// such as config file changes. + +/// does not belong here +void updateCameraPerspective(); + +/// Compare ClientCfg and LastClientCfg to know what we must update +void updateFromClientCfg(); + +#endif // CL_MAIN_LOOP_UTILITIES_H + +/* end of file */ From 3a4204e091146dd4fba4ccf283df8b7264cf9b9d Mon Sep 17 00:00:00 2001 From: kaetemi Date: Thu, 27 Jun 2013 04:11:09 +0200 Subject: [PATCH 050/313] Separate some camera related functions, ref #43 --- code/ryzom/client/src/camera.cpp | 85 +++++++++++++++++++ code/ryzom/client/src/camera.h | 29 +++++++ code/ryzom/client/src/main_loop.cpp | 39 +-------- code/ryzom/client/src/main_loop_utilities.cpp | 20 +---- code/ryzom/client/src/main_loop_utilities.h | 3 - 5 files changed, 116 insertions(+), 60 deletions(-) create mode 100644 code/ryzom/client/src/camera.cpp create mode 100644 code/ryzom/client/src/camera.h diff --git a/code/ryzom/client/src/camera.cpp b/code/ryzom/client/src/camera.cpp new file mode 100644 index 000000000..66aac6f76 --- /dev/null +++ b/code/ryzom/client/src/camera.cpp @@ -0,0 +1,85 @@ +// Ryzom - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#include +#include "camera.h" + +#include + +#include "global.h" +#include "misc.h" + +using namespace NLMISC; +using namespace NL3D; + +//--------------------------------------------------- +// update the camera perspective setup +//--------------------------------------------------- +void updateCameraPerspective() +{ + float fov, aspectRatio; + computeCurrentFovAspectRatio(fov, aspectRatio); + + // change the perspective of the scene + if(!MainCam.empty()) + MainCam.setPerspective(fov, aspectRatio, CameraSetupZNear, ClientCfg.Vision); + // change the perspective of the root scene + if(SceneRoot) + { + UCamera cam= SceneRoot->getCam(); + cam.setPerspective(fov, aspectRatio, SceneRootCameraZNear, SceneRootCameraZFar); + } +} + +void buildCameraClippingPyramid(std::vector &planes) +{ + if (StereoDisplay) StereoDisplay->getClippingFrustum(0, &MainCam); + + // Compute pyramid in view basis. + CVector pfoc(0,0,0); + const CFrustum &frustum = MainCam.getFrustum(); + InvMainSceneViewMatrix = MainCam.getMatrix(); + MainSceneViewMatrix = InvMainSceneViewMatrix; + MainSceneViewMatrix.invert(); + + CVector lb(frustum.Left, frustum.Near, frustum.Bottom ); + CVector lt(frustum.Left, frustum.Near, frustum.Top ); + CVector rb(frustum.Right, frustum.Near, frustum.Bottom ); + CVector rt(frustum.Right, frustum.Near, frustum.Top ); + + CVector lbFar(frustum.Left, ClientCfg.CharacterFarClip, frustum.Bottom); + CVector ltFar(frustum.Left, ClientCfg.CharacterFarClip, frustum.Top ); + CVector rtFar(frustum.Right, ClientCfg.CharacterFarClip, frustum.Top ); + + planes.resize (4); + // planes[0].make(lbFar, ltFar, rtFar); + planes[0].make(pfoc, lt, lb); + planes[1].make(pfoc, rt, lt); + planes[2].make(pfoc, rb, rt); + planes[3].make(pfoc, lb, rb); + + // Compute pyramid in World basis. + // The vector transformation M of a plane p is computed as p*M-1. + // Here, ViewMatrix== CamMatrix-1. Hence the following formula. + uint i; + + for (i = 0; i < 4; i++) + { + planes[i] = planes[i]*MainSceneViewMatrix; + } +} + +/* end of file */ \ No newline at end of file diff --git a/code/ryzom/client/src/camera.h b/code/ryzom/client/src/camera.h new file mode 100644 index 000000000..037661c3a --- /dev/null +++ b/code/ryzom/client/src/camera.h @@ -0,0 +1,29 @@ +// Ryzom - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#ifndef CL_CAMERA_H +#define CL_CAMERA_H + +#include + +#include + +void updateCameraPerspective(); +void buildCameraClippingPyramid(std::vector &planes); + +#endif // CL_CAMERA_H + +/* end of file */ diff --git a/code/ryzom/client/src/main_loop.cpp b/code/ryzom/client/src/main_loop.cpp index 64379b7c2..7338493c1 100644 --- a/code/ryzom/client/src/main_loop.cpp +++ b/code/ryzom/client/src/main_loop.cpp @@ -142,6 +142,7 @@ // pulled from main_loop.cpp #include "ping.h" #include "profiling.h" +#include "camera.h" #include "main_loop_debug.h" #include "main_loop_temp.h" #include "main_loop_utilities.h" @@ -351,44 +352,6 @@ void displaySceneProfiles(); // validate current dialogs (end them if the player is too far from its interlocutor) void validateDialogs(const CGameContextMenu &gcm); -void buildCameraClippingPyramid (vector &planes) -{ - if (StereoDisplay) StereoDisplay->getClippingFrustum(0, &MainCam); - - // Compute pyramid in view basis. - CVector pfoc(0,0,0); - const CFrustum &frustum = MainCam.getFrustum(); - InvMainSceneViewMatrix = MainCam.getMatrix(); - MainSceneViewMatrix = InvMainSceneViewMatrix; - MainSceneViewMatrix.invert(); - - CVector lb(frustum.Left, frustum.Near, frustum.Bottom ); - CVector lt(frustum.Left, frustum.Near, frustum.Top ); - CVector rb(frustum.Right, frustum.Near, frustum.Bottom ); - CVector rt(frustum.Right, frustum.Near, frustum.Top ); - - CVector lbFar(frustum.Left, ClientCfg.CharacterFarClip, frustum.Bottom); - CVector ltFar(frustum.Left, ClientCfg.CharacterFarClip, frustum.Top ); - CVector rtFar(frustum.Right, ClientCfg.CharacterFarClip, frustum.Top ); - - planes.resize (4); - // planes[0].make(lbFar, ltFar, rtFar); - planes[0].make(pfoc, lt, lb); - planes[1].make(pfoc, rt, lt); - planes[2].make(pfoc, rb, rt); - planes[3].make(pfoc, lb, rb); - - // Compute pyramid in World basis. - // The vector transformation M of a plane p is computed as p*M-1. - // Here, ViewMatrix== CamMatrix-1. Hence the following formula. - uint i; - - for (i = 0; i < 4; i++) - { - planes[i] = planes[i]*MainSceneViewMatrix; - } -} - void preRenderNewSky () { CSky &sky = ContinentMngr.cur()->CurrentSky; diff --git a/code/ryzom/client/src/main_loop_utilities.cpp b/code/ryzom/client/src/main_loop_utilities.cpp index 26be0ab0d..7afba0a9f 100644 --- a/code/ryzom/client/src/main_loop_utilities.cpp +++ b/code/ryzom/client/src/main_loop_utilities.cpp @@ -33,29 +33,11 @@ #include "entities.h" #include "input.h" #include "sound_manager.h" +#include "camera.h" using namespace NLMISC; using namespace NL3D; -//--------------------------------------------------- -// update the camera perspective setup -//--------------------------------------------------- -void updateCameraPerspective() -{ - float fov, aspectRatio; - computeCurrentFovAspectRatio(fov, aspectRatio); - - // change the perspective of the scene - if(!MainCam.empty()) - MainCam.setPerspective(fov, aspectRatio, CameraSetupZNear, ClientCfg.Vision); - // change the perspective of the root scene - if(SceneRoot) - { - UCamera cam= SceneRoot->getCam(); - cam.setPerspective(fov, aspectRatio, SceneRootCameraZNear, SceneRootCameraZFar); - } -} - //--------------------------------------------------- // Compare ClientCfg and LastClientCfg to know what we must update //--------------------------------------------------- diff --git a/code/ryzom/client/src/main_loop_utilities.h b/code/ryzom/client/src/main_loop_utilities.h index 48d6a3880..d8ea3dedf 100644 --- a/code/ryzom/client/src/main_loop_utilities.h +++ b/code/ryzom/client/src/main_loop_utilities.h @@ -24,9 +24,6 @@ // This is mainly for system configuration related functions // such as config file changes. -/// does not belong here -void updateCameraPerspective(); - /// Compare ClientCfg and LastClientCfg to know what we must update void updateFromClientCfg(); From b04d278a27618fb2badb4db98c7da0b69456c77e Mon Sep 17 00:00:00 2001 From: kaetemi Date: Fri, 28 Jun 2013 23:19:32 +0200 Subject: [PATCH 051/313] Create interface classes for stereo displays and head mounted displays, see #43 --- code/nel/include/nel/3d/stereo_display.h | 139 ++++++++++++++++++ code/nel/include/nel/3d/stereo_hmd.h | 69 +++++++++ code/nel/include/nel/3d/stereo_ovr.h | 29 +--- code/nel/src/3d/stereo_display.cpp | 98 ++++++++++++ code/nel/src/3d/stereo_hmd.cpp | 55 +++++++ code/nel/src/3d/stereo_ovr.cpp | 47 +++--- code/snowballs2/client/src/camera.cpp | 44 +++--- code/snowballs2/client/src/snowballs_client.h | 6 +- 8 files changed, 423 insertions(+), 64 deletions(-) create mode 100644 code/nel/include/nel/3d/stereo_display.h create mode 100644 code/nel/include/nel/3d/stereo_hmd.h create mode 100644 code/nel/src/3d/stereo_display.cpp create mode 100644 code/nel/src/3d/stereo_hmd.cpp diff --git a/code/nel/include/nel/3d/stereo_display.h b/code/nel/include/nel/3d/stereo_display.h new file mode 100644 index 000000000..b2547bef9 --- /dev/null +++ b/code/nel/include/nel/3d/stereo_display.h @@ -0,0 +1,139 @@ +/** + * \file stereo_display.h + * \brief IStereoDisplay + * \date 2013-06-27 16:29GMT + * \author Jan Boon (Kaetemi) + * IStereoDisplay + */ + +/* + * Copyright (C) 2013 by authors + * + * This file is part of NL3D. + * NL3D is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * NL3D 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 Affero General + * Public License for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with NL3D. If not, see + * . + */ + +#ifndef NL3D_STEREO_DISPLAY_H +#define NL3D_STEREO_DISPLAY_H +#include + +// STL includes + +// NeL includes +#include + +// Project includes + +namespace NL3D { + +class UCamera; +class CViewport; +class CFrustum; +class IStereoDisplay; + +class IStereoDeviceFactory : public NLMISC::CRefCount +{ +public: + IStereoDeviceFactory() { } + virtual ~IStereoDeviceFactory() { } + virtual IStereoDisplay *createDevice() const = 0; +}; + +struct CStereoDeviceInfo +{ +public: + enum TStereoDeviceClass + { + StereoDisplay, + StereoHMD, + }; + + enum TStereoDeviceLibrary + { + NeL3D, + OVR, + LibVR, + OpenHMD, + }; + + NLMISC::CSmartPtr Factory; + + TStereoDeviceLibrary Library; + TStereoDeviceClass Class; + std::string Manufacturer; + std::string ProductName; + std::string Serial; // A unique device identifier +}; + +/** + * \brief IStereoDisplay + * \date 2013-06-27 16:29GMT + * \author Jan Boon (Kaetemi) + * IStereoDisplay + */ +class IStereoDisplay +{ +public: + IStereoDisplay(); + virtual ~IStereoDisplay(); + + /// Gets the required screen resolution for this device + virtual void getScreenResolution(uint &width, uint &height) = 0; + /// Set latest camera position etcetera + virtual void updateCamera(uint cid, const NL3D::UCamera *camera) = 0; + /// Get the frustum to use for clipping + virtual void getClippingFrustum(uint cid, NL3D::UCamera *camera) const = 0; + + /// Is there a next pass + virtual bool nextPass() = 0; + /// Gets the current viewport + virtual const NL3D::CViewport &getCurrentViewport() const = 0; + /// Gets the current camera frustum + virtual const NL3D::CFrustum &getCurrentFrustum(uint cid) const = 0; + /// Gets the current camera frustum + virtual void getCurrentFrustum(uint cid, NL3D::UCamera *camera) const = 0; + /// Gets the current camera matrix + virtual void getCurrentMatrix(uint cid, NL3D::UCamera *camera) const = 0; + + /// At the start of a new render target + virtual bool beginClear() = 0; + // virtual void *getRenderTarget() const; + virtual void endClear() = 0; + + /// The 3D scene + virtual bool beginScene() = 0; + virtual void endScene() = 0; + + /// Interface within the 3D scene + virtual bool beginInterface3D() = 0; + virtual void endInterface3D() = 0; + + /// 2D Interface + virtual bool beginInterface2D() = 0; + virtual void endInterface2D() = 0; + + static const char *getLibraryName(CStereoDeviceInfo::TStereoDeviceLibrary library); + static void listDevices(std::vector &devicesOut); + static IStereoDisplay *createDevice(const CStereoDeviceInfo &deviceInfo); + static void releaseUnusedLibraries(); + static void releaseAllLibraries(); + +}; /* class IStereoDisplay */ + +} /* namespace NL3D */ + +#endif /* #ifndef NL3D_STEREO_DISPLAY_H */ + +/* end of file */ diff --git a/code/nel/include/nel/3d/stereo_hmd.h b/code/nel/include/nel/3d/stereo_hmd.h new file mode 100644 index 000000000..bbb80368e --- /dev/null +++ b/code/nel/include/nel/3d/stereo_hmd.h @@ -0,0 +1,69 @@ +/** + * \file stereo_hmd.h + * \brief IStereoHMD + * \date 2013-06-27 16:30GMT + * \author Jan Boon (Kaetemi) + * IStereoHMD + */ + +/* + * Copyright (C) 2013 by authors + * + * This file is part of NL3D. + * NL3D is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * NL3D 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 Affero General + * Public License for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with NL3D. If not, see + * . + */ + +#ifndef NL3D_STEREO_HMD_H +#define NL3D_STEREO_HMD_H +#include + +// STL includes + +// NeL includes + +// Project includes +#include + +namespace NL3D { + +/** + * \brief IStereoHMD + * \date 2013-06-27 16:30GMT + * \author Jan Boon (Kaetemi) + * IStereoHMD + */ +class IStereoHMD : public IStereoDisplay +{ +public: + IStereoHMD(); + virtual ~IStereoHMD(); + + /// Get the HMD orientation + virtual NLMISC::CQuat getOrientation() const = 0; + + /// Set the head model, eye position relative to orientation point + // virtual void setEyePosition(const NLMISC::CVector &v) = 0; + /// Get the head model, eye position relative to orientation point + // virtual const NLMISC::CVector &getEyePosition() const = 0; + /// Get GUI center (1 = width, 1 = height, 0 = center) + virtual void getInterface2DShift(uint cid, float &x, float &y, float distance) const = 0; + +}; /* class IStereoHMD */ + +} /* namespace NL3D */ + +#endif /* #ifndef NL3D_STEREO_HMD_H */ + +/* end of file */ diff --git a/code/nel/include/nel/3d/stereo_ovr.h b/code/nel/include/nel/3d/stereo_ovr.h index ba8d6eac6..e3c8361f8 100644 --- a/code/nel/include/nel/3d/stereo_ovr.h +++ b/code/nel/include/nel/3d/stereo_ovr.h @@ -49,28 +49,16 @@ // NeL includes #include + +// Project includes +#include #include #include -// Project includes - namespace NL3D { -class UCamera; - -struct CStereoDeviceInfo -{ -public: - uint8 Class; - uint8 Identifier; - NLMISC::CSmartPtr Factory; - - std::string Library; - std::string Manufacturer; - std::string ProductName; -}; - class CStereoOVRDevicePtr; +class CStereoOVRDeviceHandle; #define NL_STEREO_MAX_USER_CAMERAS 8 @@ -80,10 +68,10 @@ class CStereoOVRDevicePtr; * \author Jan Boon (Kaetemi) * CStereoOVR */ -class CStereoOVR +class CStereoOVR : public IStereoHMD { public: - CStereoOVR(const CStereoDeviceInfo &deviceInfo); + CStereoOVR(const CStereoOVRDeviceHandle *handle); virtual ~CStereoOVR(); @@ -126,18 +114,17 @@ public: /// Get the HMD orientation virtual NLMISC::CQuat getOrientation() const; /// Get GUI center (1 = width, 1 = height, 0 = center) - virtual void getInterface2DShift(uint cid, float &x, float &y, float distance); + virtual void getInterface2DShift(uint cid, float &x, float &y, float distance) const; static void listDevices(std::vector &devicesOut); - static CStereoOVR *createDevice(const CStereoDeviceInfo &deviceInfo); static bool isLibraryInUse(); static void releaseLibrary(); /// Calculates internal camera information based on the reference camera void initCamera(uint cid, const NL3D::UCamera *camera); - + /// Checks if the device used by this class was actually created bool isDeviceCreated(); private: diff --git a/code/nel/src/3d/stereo_display.cpp b/code/nel/src/3d/stereo_display.cpp new file mode 100644 index 000000000..09502a256 --- /dev/null +++ b/code/nel/src/3d/stereo_display.cpp @@ -0,0 +1,98 @@ +/** + * \file stereo_display.cpp + * \brief IStereoDisplay + * \date 2013-06-27 16:29GMT + * \author Jan Boon (Kaetemi) + * IStereoDisplay + */ + +/* + * Copyright (C) 2013 by authors + * + * This file is part of NL3D. + * NL3D is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * NL3D 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 Affero General + * Public License for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with NL3D. If not, see + * . + */ + +#include +#include + +// STL includes + +// NeL includes +// #include + +// Project includes +#include + +using namespace std; +// using namespace NLMISC; + +namespace NL3D { + +IStereoDisplay::IStereoDisplay() +{ + +} + +IStereoDisplay::~IStereoDisplay() +{ + +} + +const char *IStereoDisplay::getLibraryName(CStereoDeviceInfo::TStereoDeviceLibrary library) +{ + static const char *nel3dName = "NeL 3D"; + static const char *ovrName = "Oculus SDK"; + static const char *libvrName = "LibVR"; + static const char *openhmdName = "OpenHMD"; + switch (library) + { + case CStereoDeviceInfo::NeL3D: + return nel3dName; + case CStereoDeviceInfo::OVR: + return ovrName; + case CStereoDeviceInfo::LibVR: + return libvrName; + case CStereoDeviceInfo::OpenHMD: + return openhmdName; + } + nlerror("Invalid device library specified"); + return ""; +} + +void IStereoDisplay::listDevices(std::vector &devicesOut) +{ + CStereoOVR::listDevices(devicesOut); +} + +IStereoDisplay *IStereoDisplay::createDevice(const CStereoDeviceInfo &deviceInfo) +{ + return deviceInfo.Factory->createDevice(); +} + +void IStereoDisplay::releaseUnusedLibraries() +{ + if (!CStereoOVR::isLibraryInUse()) + CStereoOVR::releaseLibrary(); +} + +void IStereoDisplay::releaseAllLibraries() +{ + CStereoOVR::releaseLibrary(); +} + +} /* namespace NL3D */ + +/* end of file */ diff --git a/code/nel/src/3d/stereo_hmd.cpp b/code/nel/src/3d/stereo_hmd.cpp new file mode 100644 index 000000000..d28017482 --- /dev/null +++ b/code/nel/src/3d/stereo_hmd.cpp @@ -0,0 +1,55 @@ +/** + * \file stereo_hmd.cpp + * \brief IStereoHMD + * \date 2013-06-27 16:30GMT + * \author Jan Boon (Kaetemi) + * IStereoHMD + */ + +/* + * Copyright (C) 2013 by authors + * + * This file is part of NL3D. + * NL3D is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * NL3D 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 Affero General + * Public License for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with NL3D. If not, see + * . + */ + +#include +#include + +// STL includes + +// NeL includes +// #include + +// Project includes + +using namespace std; +// using namespace NLMISC; + +namespace NL3D { + +IStereoHMD::IStereoHMD() +{ + +} + +IStereoHMD::~IStereoHMD() +{ + +} + +} /* namespace NL3D */ + +/* end of file */ diff --git a/code/nel/src/3d/stereo_ovr.cpp b/code/nel/src/3d/stereo_ovr.cpp index f6c063de4..f4cb61e1a 100644 --- a/code/nel/src/3d/stereo_ovr.cpp +++ b/code/nel/src/3d/stereo_ovr.cpp @@ -45,6 +45,7 @@ #include // STL includes +#include // External includes #include @@ -127,16 +128,24 @@ public: CStereoOVRSystem s_StereoOVRSystem; -class CStereoOVRDeviceHandle : public NLMISC::CRefCount -{ -public: - OVR::DeviceEnumerator DeviceHandle; -}; - sint s_DeviceCounter = 0; } +class CStereoOVRDeviceHandle : public IStereoDeviceFactory +{ +public: + OVR::DeviceEnumerator DeviceHandle; + IStereoDisplay *createDevice() const + { + CStereoOVR *stereo = new CStereoOVR(this); + if (stereo->isDeviceCreated()) + return stereo; + delete stereo; + return NULL; + } +}; + class CStereoOVRDevicePtr { public: @@ -146,12 +155,11 @@ public: OVR::HMDInfo HMDInfo; }; -CStereoOVR::CStereoOVR(const CStereoDeviceInfo &deviceInfo) : m_Stage(0), m_OrientationCached(false) +CStereoOVR::CStereoOVR(const CStereoOVRDeviceHandle *handle) : m_Stage(0), m_OrientationCached(false) { ++s_DeviceCounter; m_DevicePtr = new CStereoOVRDevicePtr(); - CStereoOVRDeviceHandle *handle = static_cast(deviceInfo.Factory.getPtr()); OVR::DeviceEnumerator dh = handle->DeviceHandle; m_DevicePtr->HMDDevice = dh.CreateDevice(); @@ -406,7 +414,7 @@ NLMISC::CQuat CStereoOVR::getOrientation() const } /// Get GUI shift -void CStereoOVR::getInterface2DShift(uint cid, float &x, float &y, float distance) +void CStereoOVR::getInterface2DShift(uint cid, float &x, float &y, float distance) const { #if 0 @@ -460,7 +468,7 @@ void CStereoOVR::listDevices(std::vector &devicesOut) { s_StereoOVRSystem.Init(); OVR::DeviceEnumerator devices = s_DeviceManager->EnumerateDevices(); - uint8 id = 1; + uint id = 1; do { CStereoDeviceInfo deviceInfoOut; @@ -469,13 +477,15 @@ void CStereoOVR::listDevices(std::vector &devicesOut) { devices.GetDeviceInfo(&deviceInfo); CStereoOVRDeviceHandle *handle = new CStereoOVRDeviceHandle(); - deviceInfoOut.Factory = static_cast(handle); + deviceInfoOut.Factory = static_cast(handle); handle->DeviceHandle = devices; - deviceInfoOut.Class = 1; // OVR::HMDDevice - deviceInfoOut.Library = "Oculus SDK"; - deviceInfoOut.Identifier = id; + deviceInfoOut.Class = CStereoDeviceInfo::StereoHMD; // 1; // OVR::HMDDevice + deviceInfoOut.Library = CStereoDeviceInfo::OVR; // "Oculus SDK"; deviceInfoOut.Manufacturer = deviceInfo.Manufacturer; deviceInfoOut.ProductName = deviceInfo.ProductName; + stringstream ser; + ser << id; + deviceInfoOut.Serial = ser.str(); // can't get the real serial from the sdk... devicesOut.push_back(deviceInfoOut); ++id; } @@ -483,15 +493,6 @@ void CStereoOVR::listDevices(std::vector &devicesOut) } while (devices.Next()); } -CStereoOVR *CStereoOVR::createDevice(const CStereoDeviceInfo &deviceInfo) -{ - CStereoOVR *stereo = new CStereoOVR(deviceInfo); - if (stereo->isDeviceCreated()) - return stereo; - delete stereo; - return NULL; -} - bool CStereoOVR::isLibraryInUse() { nlassert(s_DeviceCounter >= 0); diff --git a/code/snowballs2/client/src/camera.cpp b/code/snowballs2/client/src/camera.cpp index ef65a7a9f..f4f5ea2b4 100644 --- a/code/snowballs2/client/src/camera.cpp +++ b/code/snowballs2/client/src/camera.cpp @@ -34,7 +34,7 @@ #include #include -#include +#include #include "snowballs_client.h" #include "entities.h" @@ -70,7 +70,8 @@ static UInstance Sky = NULL; static UCloudScape *Clouds = NULL; -CStereoOVR *StereoHMD = NULL; +IStereoDisplay *StereoDisplay = NULL; +IStereoHMD *StereoHMD = NULL; // // Functions @@ -81,12 +82,12 @@ void initCamera() if (ConfigFile->getVar("HMDEnable").asBool()) { std::vector devices; - CStereoOVR::listDevices(devices); + IStereoDisplay::listDevices(devices); for (std::vector::iterator it(devices.begin()), end(devices.end()); it != end; ++it) { std::stringstream name; - name << std::string("[") << (uint32)it->Identifier << "] [" << it->Library << " - " << it->Manufacturer << " - " << it->ProductName << "]"; - nlinfo("Stereo Device: %s", name.str().c_str()); + name << std::string("[") << it->Serial << "] [" << IStereoDisplay::getLibraryName(it->Library) << " - " << it->Manufacturer << " - " << it->ProductName << "]"; + nlinfo("Stereo Display: %s", name.str().c_str()); } CStereoDeviceInfo *deviceInfo = NULL; std::string hmdDeviceCfg = ConfigFile->getVar("HMDDevice").asString(); @@ -97,22 +98,28 @@ void initCamera() } else { - uint8 hmdDeviceId = (ConfigFile->getVar("HMDDeviceId").asInt() & 0xFF); + std::string hmdDeviceId = ConfigFile->getVar("HMDDeviceId").asString(); for (std::vector::iterator it(devices.begin()), end(devices.end()); it != end; ++it) { std::stringstream name; - name << it->Library << " - " << it->Manufacturer << " - " << it->ProductName; + name << IStereoDisplay::getLibraryName(it->Library) << " - " << it->Manufacturer << " - " << it->ProductName; if (name.str() == hmdDeviceCfg) deviceInfo = &(*it); - if (hmdDeviceId == it->Identifier) + if (hmdDeviceId == it->Serial) break; } } if (deviceInfo) { - nlinfo("Create HMD device!"); - StereoHMD = CStereoOVR::createDevice(*deviceInfo); + nlinfo("Create VR stereo display device"); + StereoDisplay = IStereoDisplay::createDevice(*deviceInfo); + if (deviceInfo->Class == CStereoDeviceInfo::StereoHMD) + { + nlinfo("Stereo display device is a HMD"); + StereoHMD = static_cast(StereoDisplay); + } } + IStereoDisplay::releaseUnusedLibraries(); } // Set up directly the camera @@ -129,11 +136,6 @@ void initCamera() CamCollisionEntity = VisualCollisionManager->createEntity(); CamCollisionEntity->setCeilMode(true); - if (StereoHMD) - { - StereoHMD->initCamera(0, &Camera); - } - // Create the snowing particle system Snow = Scene->createInstance("snow.ps"); // And setup it @@ -164,9 +166,15 @@ void releaseCamera() Scene->deleteInstance(Snow); VisualCollisionManager->deleteEntity(CamCollisionEntity); - delete StereoHMD; - StereoHMD = NULL; - CStereoOVR::releaseLibrary(); + if (StereoHMD) + { + delete StereoHMD; + StereoHMD = NULL; + StereoDisplay = NULL; + } + delete StereoDisplay; + StereoDisplay = NULL; + IStereoDisplay::releaseAllLibraries(); } void updateCamera() diff --git a/code/snowballs2/client/src/snowballs_client.h b/code/snowballs2/client/src/snowballs_client.h index bc955d317..d21f925f7 100644 --- a/code/snowballs2/client/src/snowballs_client.h +++ b/code/snowballs2/client/src/snowballs_client.h @@ -42,7 +42,8 @@ namespace NL3D { class UScene; class UTextContext; class ULandscape; - class CStereoOVR; + class IStereoDisplay; + class IStereoHMD; } namespace SBCLIENT { @@ -59,7 +60,8 @@ public: }; extern NL3D::UDriver *Driver; -extern NL3D::CStereoOVR *StereoHMD; +extern NL3D::IStereoDisplay *StereoDisplay; +extern NL3D::IStereoHMD *StereoHMD; extern NL3D::UScene *Scene; extern NL3D::UTextContext *TextContext; extern NLMISC::CConfigFile *ConfigFile; From ebba0c1917a1b8021e2c474d5c96780a9e477b9e Mon Sep 17 00:00:00 2001 From: kaetemi Date: Mon, 1 Jul 2013 18:45:43 +0200 Subject: [PATCH 052/313] Allow linking debug and release with same mysql lib if no specific debug lib. --- code/CMakeModules/FindMySQL.cmake | 2 ++ 1 file changed, 2 insertions(+) diff --git a/code/CMakeModules/FindMySQL.cmake b/code/CMakeModules/FindMySQL.cmake index 1da6157fa..eb2102c8d 100644 --- a/code/CMakeModules/FindMySQL.cmake +++ b/code/CMakeModules/FindMySQL.cmake @@ -58,6 +58,8 @@ ELSE(MYSQL_INCLUDE_DIR AND MYSQL_LIBRARIES) SET(MYSQL_LIBRARIES optimized ${MYSQL_LIBRARY_RELEASE}) IF(MYSQL_LIBRARY_DEBUG) SET(MYSQL_LIBRARIES ${MYSQL_LIBRARIES} debug ${MYSQL_LIBRARY_DEBUG}) + ELSE(MYSQL_LIBRARY_DEBUG) + SET(MYSQL_LIBRARIES ${MYSQL_LIBRARIES} debug ${MYSQL_LIBRARY_RELEASE}) ENDIF(MYSQL_LIBRARY_DEBUG) FIND_PACKAGE(OpenSSL) IF(OPENSSL_FOUND) From 9960b9dfdb15e1b50082d0000df3150997328457 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Mon, 1 Jul 2013 20:15:55 +0200 Subject: [PATCH 053/313] Cleanup --- code/nel/src/3d/CMakeLists.txt | 7 +++++++ code/nel/src/3d/stereo_ovr.cpp | 13 +++++++------ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/code/nel/src/3d/CMakeLists.txt b/code/nel/src/3d/CMakeLists.txt index 6f632e261..ec07b28b8 100644 --- a/code/nel/src/3d/CMakeLists.txt +++ b/code/nel/src/3d/CMakeLists.txt @@ -686,6 +686,13 @@ SOURCE_GROUP(Shadows FILES ../../include/nel/3d/shadow_map_manager.h shadow_poly_receiver.cpp ../../include/nel/3d/shadow_poly_receiver.h) +SOURCE_GROUP(Stereo FILES + stereo_display.cpp + ../../include/nel/3d/stereo_display.h + stereo_hmd.cpp + ../../include/nel/3d/stereo_hmd.h + stereo_ovr.cpp + ../../include/nel/3d/stereo_ovr.h) NL_TARGET_LIB(nel3d ${HEADERS} ${SRC}) diff --git a/code/nel/src/3d/stereo_ovr.cpp b/code/nel/src/3d/stereo_ovr.cpp index f4cb61e1a..de27a032e 100644 --- a/code/nel/src/3d/stereo_ovr.cpp +++ b/code/nel/src/3d/stereo_ovr.cpp @@ -218,15 +218,16 @@ void CStereoOVR::initCamera(uint cid, const NL3D::UCamera *camera) float fov = 2.0f * atanf((m_DevicePtr->HMDInfo.VScreenSize / 2.0f) / m_DevicePtr->HMDInfo.EyeToScreenDistance); //(float)NLMISC::Pi/2.f; // 2.0f * atanf(m_DevicePtr->HMDInfo.VScreenSize / 2.0f * m_DevicePtr->HMDInfo.EyeToScreenDistance); m_LeftFrustum[cid].initPerspective(fov, ar, camera->getFrustum().Near, camera->getFrustum().Far); m_RightFrustum[cid] = m_LeftFrustum[cid]; + float viewCenter = m_DevicePtr->HMDInfo.HScreenSize * 0.25f; float eyeProjectionShift = viewCenter - m_DevicePtr->HMDInfo.LensSeparationDistance * 0.5f; - float projectionCenterOffset = 4.0f * eyeProjectionShift / m_DevicePtr->HMDInfo.HScreenSize; + float projectionCenterOffset = (eyeProjectionShift / (m_DevicePtr->HMDInfo.HScreenSize * 0.5f)) * (m_LeftFrustum[cid].Right - m_LeftFrustum[cid].Left); // used logic for this one, but it ends up being the same as the one i made up nldebug("OVR: projectionCenterOffset = %f", projectionCenterOffset); - projectionCenterOffset *= (m_LeftFrustum[cid].Left - m_LeftFrustum[cid].Right) * 0.5f; // made this up ... - m_LeftFrustum[cid].Left += projectionCenterOffset; - m_LeftFrustum[cid].Right += projectionCenterOffset; - m_RightFrustum[cid].Left -= projectionCenterOffset; - m_RightFrustum[cid].Right -= projectionCenterOffset; + + m_LeftFrustum[cid].Left -= projectionCenterOffset; + m_LeftFrustum[cid].Right -= projectionCenterOffset; + m_RightFrustum[cid].Left += projectionCenterOffset; + m_RightFrustum[cid].Right += projectionCenterOffset; // TODO: Clipping frustum should also take into account the IPD m_ClippingFrustum[cid] = m_LeftFrustum[cid]; From 2b653d29041291f661cf9347f3d7dc53dbc0b115 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Mon, 1 Jul 2013 20:38:18 +0200 Subject: [PATCH 054/313] Allow turning off bloom in snowballs --- .../bin/snowballs_client_default.cfg | 1 + .../client/src/snowballs_client.cpp | 22 ++++++++++++++----- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/code/snowballs2/bin/snowballs_client_default.cfg b/code/snowballs2/bin/snowballs_client_default.cfg index 9d1429f83..49ec9e4d5 100755 --- a/code/snowballs2/bin/snowballs_client_default.cfg +++ b/code/snowballs2/bin/snowballs_client_default.cfg @@ -271,6 +271,7 @@ GlobalRetrieverName = "snowballs.gr"; SquareBloom = 1; DensityBloom = 128; +EnableBloom = 1; ////////////////////////////////////////////////////////////////////////////// diff --git a/code/snowballs2/client/src/snowballs_client.cpp b/code/snowballs2/client/src/snowballs_client.cpp index 292465266..5cb7aa8df 100644 --- a/code/snowballs2/client/src/snowballs_client.cpp +++ b/code/snowballs2/client/src/snowballs_client.cpp @@ -152,6 +152,8 @@ LoadedOnline = false, LoadedOffline = false; // state static IStereoRender *_StereoRender = NULL; #endif /* #if SBCLIENT_DEV_STEREO */ +static bool s_EnableBloom = false; + // // Prototypes // @@ -182,6 +184,7 @@ void releaseOffline(); void cbGraphicsDriver(CConfigFile::CVar &var); void cbSquareBloom(CConfigFile::CVar &var); void cbDensityBloom(CConfigFile::CVar &var); +void cbEnableBloom(CConfigFile::CVar &var); // // Functions @@ -368,6 +371,7 @@ void initIngame() CBloomEffect::instance().init(ConfigFile->getVar("OpenGL").asInt() == 1); CConfiguration::setAndCallback("SquareBloom", cbSquareBloom); CConfiguration::setAndCallback("DensityBloom", cbDensityBloom); + CConfiguration::setAndCallback("EnableBloom", cbEnableBloom); // Init the landscape using the previously created UScene displayLoadingState("Initialize Landscape"); initLandscape(); @@ -748,9 +752,12 @@ void loopIngame() if (!StereoHMD || StereoHMD->beginClear()) { - nlassert(bloomStage == 0); - CBloomEffect::instance().initBloom(); // start bloom effect (just before the first scene element render) - bloomStage = 1; + if (s_EnableBloom) + { + nlassert(bloomStage == 0); + CBloomEffect::instance().initBloom(); // start bloom effect (just before the first scene element render) + bloomStage = 1; + } // 01. Render Driver (background color) Driver->clearBuffers(CRGBA(0, 0, 127)); // clear all buffers, if you see this blue there's a problem with scene rendering @@ -774,7 +781,7 @@ void loopIngame() if (!StereoHMD || StereoHMD->beginInterface3D()) { - if (bloomStage == 1) + if (s_EnableBloom && bloomStage == 1) { // End the actual bloom effect visible in the scene. if (StereoHMD) Driver->setViewport(NL3D::CViewport()); @@ -791,7 +798,7 @@ void loopIngame() if (!StereoHMD || StereoHMD->beginInterface2D()) { - if (bloomStage == 2) + if (s_EnableBloom && bloomStage == 2) { // End bloom effect system after drawing the 3d interface (z buffer related). if (StereoHMD) Driver->setViewport(NL3D::CViewport()); @@ -944,6 +951,11 @@ void cbDensityBloom(CConfigFile::CVar &var) CBloomEffect::instance().setDensityBloom((uint8)(var.asInt() & 0xFF)); } +void cbEnableBloom(CConfigFile::CVar &var) +{ + s_EnableBloom = var.asBool(); +} + // // Loading state procedure // From f25a9da7184427732a4c61f2fab6f734878a2265 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Mon, 1 Jul 2013 21:23:47 +0200 Subject: [PATCH 055/313] Add interface for stereo display render targets, ref #43 --- code/nel/include/nel/3d/stereo_display.h | 26 ++++---- code/nel/include/nel/3d/stereo_ovr.h | 24 +++---- code/nel/src/3d/stereo_ovr.cpp | 62 ++++++++++++------- .../client/src/snowballs_client.cpp | 18 ++---- 4 files changed, 69 insertions(+), 61 deletions(-) diff --git a/code/nel/include/nel/3d/stereo_display.h b/code/nel/include/nel/3d/stereo_display.h index b2547bef9..ad75ad934 100644 --- a/code/nel/include/nel/3d/stereo_display.h +++ b/code/nel/include/nel/3d/stereo_display.h @@ -42,6 +42,8 @@ class UCamera; class CViewport; class CFrustum; class IStereoDisplay; +class UTexture; +class UDriver; class IStereoDeviceFactory : public NLMISC::CRefCount { @@ -88,6 +90,9 @@ class IStereoDisplay public: IStereoDisplay(); virtual ~IStereoDisplay(); + + /// Sets driver and generates necessary render targets + virtual void setDriver(NL3D::UDriver &driver) = 0; /// Gets the required screen resolution for this device virtual void getScreenResolution(uint &width, uint &height) = 0; @@ -108,21 +113,18 @@ public: virtual void getCurrentMatrix(uint cid, NL3D::UCamera *camera) const = 0; /// At the start of a new render target - virtual bool beginClear() = 0; - // virtual void *getRenderTarget() const; - virtual void endClear() = 0; - + virtual bool wantClear() = 0; /// The 3D scene - virtual bool beginScene() = 0; - virtual void endScene() = 0; - + virtual bool wantScene() = 0; /// Interface within the 3D scene - virtual bool beginInterface3D() = 0; - virtual void endInterface3D() = 0; - + virtual bool wantInterface3D() = 0; /// 2D Interface - virtual bool beginInterface2D() = 0; - virtual void endInterface2D() = 0; + virtual bool wantInterface2D() = 0; + + /// Returns non-NULL if a new render target was set + virtual UTexture *beginRenderTarget(bool set) = 0; + /// Returns true if a render target was fully drawn + virtual bool endRenderTarget(bool render) = 0; static const char *getLibraryName(CStereoDeviceInfo::TStereoDeviceLibrary library); static void listDevices(std::vector &devicesOut); diff --git a/code/nel/include/nel/3d/stereo_ovr.h b/code/nel/include/nel/3d/stereo_ovr.h index e3c8361f8..3fff84ffb 100644 --- a/code/nel/include/nel/3d/stereo_ovr.h +++ b/code/nel/include/nel/3d/stereo_ovr.h @@ -74,6 +74,8 @@ public: CStereoOVR(const CStereoOVRDeviceHandle *handle); virtual ~CStereoOVR(); + /// Sets driver and generates necessary render targets + virtual void setDriver(NL3D::UDriver &driver); /// Gets the required screen resolution for this device virtual void getScreenResolution(uint &width, uint &height); @@ -94,21 +96,18 @@ public: virtual void getCurrentMatrix(uint cid, NL3D::UCamera *camera) const; /// At the start of a new render target - virtual bool beginClear(); - // virtual void *getRenderTarget() const; - virtual void endClear(); - + virtual bool wantClear(); /// The 3D scene - virtual bool beginScene(); - virtual void endScene(); - + virtual bool wantScene(); /// Interface within the 3D scene - virtual bool beginInterface3D(); - virtual void endInterface3D(); - + virtual bool wantInterface3D(); /// 2D Interface - virtual bool beginInterface2D(); - virtual void endInterface2D(); + virtual bool wantInterface2D(); + + /// Returns non-NULL if a new render target was set, always NULL if not using render targets + virtual UTexture *beginRenderTarget(bool set); + /// Returns true if a render target was fully drawn, always false if not using render targets + virtual bool endRenderTarget(bool render); /// Get the HMD orientation @@ -130,6 +129,7 @@ public: private: CStereoOVRDevicePtr *m_DevicePtr; int m_Stage; + int m_SubStage; CViewport m_LeftViewport; CViewport m_RightViewport; CFrustum m_ClippingFrustum[NL_STEREO_MAX_USER_CAMERAS]; diff --git a/code/nel/src/3d/stereo_ovr.cpp b/code/nel/src/3d/stereo_ovr.cpp index de27a032e..a4b92b39e 100644 --- a/code/nel/src/3d/stereo_ovr.cpp +++ b/code/nel/src/3d/stereo_ovr.cpp @@ -155,7 +155,7 @@ public: OVR::HMDInfo HMDInfo; }; -CStereoOVR::CStereoOVR(const CStereoOVRDeviceHandle *handle) : m_Stage(0), m_OrientationCached(false) +CStereoOVR::CStereoOVR(const CStereoOVRDeviceHandle *handle) : m_Stage(0), m_SubStage(0), m_OrientationCached(false) { ++s_DeviceCounter; m_DevicePtr = new CStereoOVRDevicePtr(); @@ -206,6 +206,11 @@ CStereoOVR::~CStereoOVR() --s_DeviceCounter; } +void CStereoOVR::setDriver(NL3D::UDriver &driver) +{ + // ... +} + void CStereoOVR::getScreenResolution(uint &width, uint &height) { width = m_DevicePtr->HMDInfo.HResolution; @@ -220,7 +225,7 @@ void CStereoOVR::initCamera(uint cid, const NL3D::UCamera *camera) m_RightFrustum[cid] = m_LeftFrustum[cid]; float viewCenter = m_DevicePtr->HMDInfo.HScreenSize * 0.25f; - float eyeProjectionShift = viewCenter - m_DevicePtr->HMDInfo.LensSeparationDistance * 0.5f; + float eyeProjectionShift = viewCenter - m_DevicePtr->HMDInfo.LensSeparationDistance * 0.5f; // docs say LensSeparationDistance, why not InterpupillaryDistance? related to how the lenses work? float projectionCenterOffset = (eyeProjectionShift / (m_DevicePtr->HMDInfo.HScreenSize * 0.5f)) * (m_LeftFrustum[cid].Right - m_LeftFrustum[cid].Left); // used logic for this one, but it ends up being the same as the one i made up nldebug("OVR: projectionCenterOffset = %f", projectionCenterOffset); @@ -255,6 +260,7 @@ bool CStereoOVR::nextPass() { case 0: ++m_Stage; + m_SubStage = 0; // stage 1: // (initBloom) // clear buffer @@ -262,39 +268,46 @@ bool CStereoOVR::nextPass() return true; case 1: ++m_Stage; + m_SubStage = 0; // stage 2: // draw scene right return true; case 2: ++m_Stage; + m_SubStage = 0; // stage 3: // (endBloom) // draw interface 3d left return true; case 3: ++m_Stage; + m_SubStage = 0; // stage 4: // draw interface 3d right return true; case 4: ++m_Stage; + m_SubStage = 0; // stage 5: // (endInterfacesDisplayBloom) // draw interface 2d left return true; case 5: ++m_Stage; + m_SubStage = 0; // stage 6: // draw interface 2d right return true; case 6: m_Stage = 0; + m_SubStage = 0; // present m_OrientationCached = false; return false; } - nlassert(false); + nlerror("Invalid stage"); m_Stage = 0; + m_SubStage = 0; m_OrientationCached = false; return false; } @@ -326,67 +339,68 @@ void CStereoOVR::getCurrentMatrix(uint cid, NL3D::UCamera *camera) const camera->setMatrix(m_CameraMatrix[cid] * translate); } -bool CStereoOVR::beginClear() +bool CStereoOVR::wantClear() { switch (m_Stage) { case 1: + m_SubStage = 1; return true; } return false; } - -void CStereoOVR::endClear() -{ - -} -bool CStereoOVR::beginScene() +bool CStereoOVR::wantScene() { switch (m_Stage) { case 1: case 2: + m_SubStage = 2; return true; } return false; } -void CStereoOVR::endScene() -{ - -} - -bool CStereoOVR::beginInterface3D() +bool CStereoOVR::wantInterface3D() { switch (m_Stage) { case 3: case 4: + m_SubStage = 3; return true; } return false; } -void CStereoOVR::endInterface3D() -{ - -} - -bool CStereoOVR::beginInterface2D() +bool CStereoOVR::wantInterface2D() { switch (m_Stage) { case 5: case 6: + m_SubStage = 4; return true; } return false; } -void CStereoOVR::endInterface2D() -{ +/// Returns non-NULL if a new render target was set +UTexture *CStereoOVR::beginRenderTarget(bool set) +{ + // render target always set before driver clear + nlassert(m_SubStage == 1); + return NULL; +} + +/// Returns true if a render target was fully drawn +bool CStereoOVR::endRenderTarget( bool render) +{ + // after rendering of course + nlassert(m_SubStage > 1); + return false; } NLMISC::CQuat CStereoOVR::getOrientation() const diff --git a/code/snowballs2/client/src/snowballs_client.cpp b/code/snowballs2/client/src/snowballs_client.cpp index 5cb7aa8df..e6754388a 100644 --- a/code/snowballs2/client/src/snowballs_client.cpp +++ b/code/snowballs2/client/src/snowballs_client.cpp @@ -750,7 +750,7 @@ void loopIngame() StereoHMD->getCurrentMatrix(0, &Camera); } - if (!StereoHMD || StereoHMD->beginClear()) + if (!StereoHMD || StereoHMD->wantClear()) { if (s_EnableBloom) { @@ -761,11 +761,9 @@ void loopIngame() // 01. Render Driver (background color) Driver->clearBuffers(CRGBA(0, 0, 127)); // clear all buffers, if you see this blue there's a problem with scene rendering - - if (StereoHMD) StereoHMD->endClear(); } - if (!StereoHMD || StereoHMD->beginScene()) + if (!StereoHMD || StereoHMD->wantScene()) { // 02. Render Sky (sky scene) updateSky(); // Render the sky scene before the main scene @@ -775,11 +773,9 @@ void loopIngame() // 05. Render Effects (flare) if (!StereoHMD) updateLensFlare(); // Render the lens flare (left eye stretched with stereo...) - - if (StereoHMD) StereoHMD->endScene(); } - if (!StereoHMD || StereoHMD->beginInterface3D()) + if (!StereoHMD || StereoHMD->wantInterface3D()) { if (s_EnableBloom && bloomStage == 1) { @@ -791,12 +787,10 @@ void loopIngame() } // 06. Render Interface 3D (player names) - // ... - - if (StereoHMD) StereoHMD->endInterface3D(); + // ... } - if (!StereoHMD || StereoHMD->beginInterface2D()) + if (!StereoHMD || StereoHMD->wantInterface2D()) { if (s_EnableBloom && bloomStage == 2) { @@ -820,8 +814,6 @@ void loopIngame() // 08. Render Debug (stuff for dev) // ... - - if (StereoHMD) StereoHMD->endInterface2D(); } } From ffc91631e7fccb464f8f9fab535fcd360674dc92 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Mon, 1 Jul 2013 23:23:54 +0200 Subject: [PATCH 056/313] Create stereo render target, see #43 --- code/nel/include/nel/3d/stereo_display.h | 4 +- code/nel/include/nel/3d/stereo_ovr.h | 13 +++- code/nel/src/3d/CMakeLists.txt | 1 + code/nel/src/3d/stereo_ovr.cpp | 90 ++++++++++++++++++++++-- code/nel/src/3d/stereo_ovr_fp.cpp | 82 +++++++++++++++++++++ 5 files changed, 180 insertions(+), 10 deletions(-) create mode 100644 code/nel/src/3d/stereo_ovr_fp.cpp diff --git a/code/nel/include/nel/3d/stereo_display.h b/code/nel/include/nel/3d/stereo_display.h index ad75ad934..2f6592b60 100644 --- a/code/nel/include/nel/3d/stereo_display.h +++ b/code/nel/include/nel/3d/stereo_display.h @@ -92,7 +92,7 @@ public: virtual ~IStereoDisplay(); /// Sets driver and generates necessary render targets - virtual void setDriver(NL3D::UDriver &driver) = 0; + virtual void setDriver(NL3D::UDriver *driver) = 0; /// Gets the required screen resolution for this device virtual void getScreenResolution(uint &width, uint &height) = 0; @@ -124,7 +124,7 @@ public: /// Returns non-NULL if a new render target was set virtual UTexture *beginRenderTarget(bool set) = 0; /// Returns true if a render target was fully drawn - virtual bool endRenderTarget(bool render) = 0; + virtual bool endRenderTarget(bool unset) = 0; static const char *getLibraryName(CStereoDeviceInfo::TStereoDeviceLibrary library); static void listDevices(std::vector &devicesOut); diff --git a/code/nel/include/nel/3d/stereo_ovr.h b/code/nel/include/nel/3d/stereo_ovr.h index 3fff84ffb..f25ef10e0 100644 --- a/code/nel/include/nel/3d/stereo_ovr.h +++ b/code/nel/include/nel/3d/stereo_ovr.h @@ -49,14 +49,18 @@ // NeL includes #include +#include // Project includes #include #include #include +#include namespace NL3D { +class ITexture; +class CTextureUser; class CStereoOVRDevicePtr; class CStereoOVRDeviceHandle; @@ -75,7 +79,7 @@ public: virtual ~CStereoOVR(); /// Sets driver and generates necessary render targets - virtual void setDriver(NL3D::UDriver &driver); + virtual void setDriver(NL3D::UDriver *driver); /// Gets the required screen resolution for this device virtual void getScreenResolution(uint &width, uint &height); @@ -107,7 +111,7 @@ public: /// Returns non-NULL if a new render target was set, always NULL if not using render targets virtual UTexture *beginRenderTarget(bool set); /// Returns true if a render target was fully drawn, always false if not using render targets - virtual bool endRenderTarget(bool render); + virtual bool endRenderTarget(bool unset); /// Get the HMD orientation @@ -138,6 +142,11 @@ private: CMatrix m_CameraMatrix[NL_STEREO_MAX_USER_CAMERAS]; mutable bool m_OrientationCached; mutable NLMISC::CQuat m_OrientationCache; + UDriver *m_Driver; + NLMISC::CSmartPtr m_BarrelTex; + NL3D::CTextureUser *m_BarrelTexU; + NL3D::UMaterial m_BarrelMat; + NLMISC::CQuadUV m_BarrelQuad; }; /* class CStereoOVR */ diff --git a/code/nel/src/3d/CMakeLists.txt b/code/nel/src/3d/CMakeLists.txt index ec07b28b8..da0288561 100644 --- a/code/nel/src/3d/CMakeLists.txt +++ b/code/nel/src/3d/CMakeLists.txt @@ -692,6 +692,7 @@ SOURCE_GROUP(Stereo FILES stereo_hmd.cpp ../../include/nel/3d/stereo_hmd.h stereo_ovr.cpp + stereo_ovr_fp.cpp ../../include/nel/3d/stereo_ovr.h) NL_TARGET_LIB(nel3d ${HEADERS} ${SRC}) diff --git a/code/nel/src/3d/stereo_ovr.cpp b/code/nel/src/3d/stereo_ovr.cpp index a4b92b39e..8eb8e1f9b 100644 --- a/code/nel/src/3d/stereo_ovr.cpp +++ b/code/nel/src/3d/stereo_ovr.cpp @@ -53,6 +53,12 @@ // NeL includes // #include #include +#include +#include +#include +#include +#include +#include // Project includes @@ -61,6 +67,8 @@ using namespace std; namespace NL3D { +extern const char *g_StereoOVR_arbfp1; + namespace { class CStereoOVRLog : public OVR::Log @@ -155,7 +163,7 @@ public: OVR::HMDInfo HMDInfo; }; -CStereoOVR::CStereoOVR(const CStereoOVRDeviceHandle *handle) : m_Stage(0), m_SubStage(0), m_OrientationCached(false) +CStereoOVR::CStereoOVR(const CStereoOVRDeviceHandle *handle) : m_Stage(0), m_SubStage(0), m_OrientationCached(false), m_BarrelTexU(NULL) { ++s_DeviceCounter; m_DevicePtr = new CStereoOVRDevicePtr(); @@ -194,6 +202,17 @@ CStereoOVR::CStereoOVR(const CStereoOVRDeviceHandle *handle) : m_Stage(0), m_Sub CStereoOVR::~CStereoOVR() { + if (!m_BarrelMat.empty()) + { + m_BarrelMat.getObjectPtr()->setTexture(0, NULL); + m_Driver->deleteMaterial(m_BarrelMat); + } + delete m_BarrelTexU; + m_BarrelTexU = NULL; + m_BarrelTex = NULL; // CSmartPtr + + m_Driver = NULL; + if (m_DevicePtr->SensorDevice) m_DevicePtr->SensorDevice->Release(); m_DevicePtr->SensorDevice.Clear(); @@ -206,9 +225,51 @@ CStereoOVR::~CStereoOVR() --s_DeviceCounter; } -void CStereoOVR::setDriver(NL3D::UDriver &driver) +void CStereoOVR::setDriver(NL3D::UDriver *driver) { - // ... + m_Driver = driver; + // Do not allow weird stuff. + uint32 width, height; + driver->getWindowSize(width, height); + nlassert(width == m_DevicePtr->HMDInfo.HResolution); + nlassert(height == m_DevicePtr->HMDInfo.VResolution); + + NL3D::IDriver *drvInternal = (static_cast(driver))->getDriver(); + + m_BarrelTex = new CTextureBloom(); // lol bloom + m_BarrelTex->setReleasable(false); + m_BarrelTex->resize(width, height); + m_BarrelTex->setFilterMode(ITexture::Linear, ITexture::LinearMipMapOff); + m_BarrelTex->setWrapS(ITexture::Clamp); + m_BarrelTex->setWrapT(ITexture::Clamp); + m_BarrelTex->setRenderTarget(true); + drvInternal->setupTexture(*m_BarrelTex); + m_BarrelTexU = new CTextureUser(m_BarrelTex); + + m_BarrelMat = m_Driver->createMaterial(); + NL3D::CMaterial *barrelMat = m_BarrelMat.getObjectPtr(); + m_BarrelMat.initUnlit(); + m_BarrelMat.setColor(CRGBA::White); + m_BarrelMat.setBlend (false); + m_BarrelMat.setAlphaTest (false); + barrelMat->setBlendFunc(CMaterial::one, CMaterial::zero); + barrelMat->setZWrite(false); + barrelMat->setZFunc(CMaterial::always); + barrelMat->setDoubleSided(true); + barrelMat->setTexture(0, m_BarrelTex); + + m_BarrelQuad.V0 = CVector(0.f, 0.f, 0.5f); + m_BarrelQuad.V1 = CVector(1.f, 0.f, 0.5f); + m_BarrelQuad.V2 = CVector(1.f, 1.f, 0.5f); + m_BarrelQuad.V3 = CVector(0.f, 1.f, 0.5f); + + float newU = drvInternal->isTextureRectangle(m_BarrelTex) ? (float)width : 1.f; + float newV = drvInternal->isTextureRectangle(m_BarrelTex) ? (float)height : 1.f; + + m_BarrelQuad.Uv0 = CUV(0.f, 0.f); + m_BarrelQuad.Uv1 = CUV(newU, 0.f); + m_BarrelQuad.Uv2 = CUV(newU, newV); + m_BarrelQuad.Uv3 = CUV(0.f, newV); } void CStereoOVR::getScreenResolution(uint &width, uint &height) @@ -391,15 +452,32 @@ bool CStereoOVR::wantInterface2D() UTexture *CStereoOVR::beginRenderTarget(bool set) { // render target always set before driver clear - nlassert(m_SubStage == 1); + // nlassert(m_SubStage <= 1); + if (m_Stage == 1) + { + if (set) + { + (static_cast(m_Driver))->setRenderTarget(*m_BarrelTexU, 0, 0, 0, 0); + } + return m_BarrelTexU; + } return NULL; } /// Returns true if a render target was fully drawn -bool CStereoOVR::endRenderTarget( bool render) +bool CStereoOVR::endRenderTarget(bool unset) { // after rendering of course - nlassert(m_SubStage > 1); + // nlassert(m_SubStage > 1); + if (m_Stage == 4) + { + if (unset) + { + CTextureUser cu; + (static_cast(m_Driver))->setRenderTarget(cu); + } + return true; + } return false; } diff --git a/code/nel/src/3d/stereo_ovr_fp.cpp b/code/nel/src/3d/stereo_ovr_fp.cpp new file mode 100644 index 000000000..eaa80f14c --- /dev/null +++ b/code/nel/src/3d/stereo_ovr_fp.cpp @@ -0,0 +1,82 @@ +/************************************************************************************ + +Filename : stereo_ovf_fp.cpp +Content : Barrel fragment program compiled to a blob of assembly +Created : July 01, 2013 + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +************************************************************************************/ + +namespace NL3D { +const char *g_StereoOVR_arbfp1 = + "!!ARBfp1.0\n" + //# cgc version 3.1.0013, build date Apr 18 2012 + //# command line args: -profile arbfp1 + //# source file: pp_oculus_vr.cg + //#vendor NVIDIA Corporation + //#version 3.1.0.13 + //#profile arbfp1 + //#program pp_oculus_vr + //#semantic pp_oculus_vr.cLensCenter + //#semantic pp_oculus_vr.cScreenCenter + //#semantic pp_oculus_vr.cScale + //#semantic pp_oculus_vr.cScaleIn + //#semantic pp_oculus_vr.cHmdWarpParam + //#semantic pp_oculus_vr.cTex0 : TEX0 + //#var float2 texCoord : $vin.TEXCOORD0 : TEX0 : 0 : 1 + //#var float2 cLensCenter : : c[0] : 1 : 1 + //#var float2 cScreenCenter : : c[1] : 2 : 1 + //#var float2 cScale : : c[2] : 3 : 1 + //#var float2 cScaleIn : : c[3] : 4 : 1 + //#var float4 cHmdWarpParam : : c[4] : 5 : 1 + //#var sampler2D cTex0 : TEX0 : texunit 0 : 6 : 1 + //#var float4 oCol : $vout.COLOR : COL : 7 : 1 + //#const c[5] = 0.25 0.5 0 1 + "PARAM c[6] = { program.local[0..4],\n" + " { 0.25, 0.5, 0, 1 } };\n" + "TEMP R0;\n" + "TEMP R1;\n" + "ADD R0.xy, fragment.texcoord[0], -c[0];\n" + "MUL R0.xy, R0, c[3];\n" + "MUL R0.z, R0.y, R0.y;\n" + "MAD R0.z, R0.x, R0.x, R0;\n" + "MUL R0.w, R0.z, c[4];\n" + "MUL R0.w, R0, R0.z;\n" + "MAD R1.y, R0.z, c[4], c[4].x;\n" + "MUL R1.x, R0.z, c[4].z;\n" + "MAD R1.x, R0.z, R1, R1.y;\n" + "MAD R0.z, R0.w, R0, R1.x;\n" + "MUL R0.xy, R0, R0.z;\n" + "MOV R0.zw, c[5].xyxy;\n" + "ADD R1.xy, R0.zwzw, c[1];\n" + "MUL R0.xy, R0, c[2];\n" + "ADD R0.xy, R0, c[0];\n" + "MIN R1.xy, R1, R0;\n" + "ADD R0.zw, -R0, c[1].xyxy;\n" + "MAX R0.zw, R0, R1.xyxy;\n" + "ADD R0.zw, R0, -R0.xyxy;\n" + "ABS R0.zw, R0;\n" + "CMP R0.zw, -R0, c[5].z, c[5].w;\n" + "MUL R0.z, R0, R0.w;\n" + "ABS R0.z, R0;\n" + "CMP R0.z, -R0, c[5], c[5].w;\n" + "ABS R1.x, R0.z;\n" + "TEX R0, R0, texture[0], 2D;\n" + "CMP R1.x, -R1, c[5].z, c[5].w;\n" + "CMP result.color, -R1.x, R0, c[5].z;\n" + "END\n"; + //# 28 instructions, 2 R-regs +} \ No newline at end of file From 3b630492aaea8415f77bbb8b4abd03ec23ca10de Mon Sep 17 00:00:00 2001 From: kaetemi Date: Tue, 2 Jul 2013 00:53:45 +0200 Subject: [PATCH 057/313] Fix a render target issue, see #43 --- code/nel/include/nel/3d/driver.h | 2 ++ code/nel/src/3d/driver/direct3d/driver_direct3d.h | 1 + .../3d/driver/direct3d/driver_direct3d_texture.cpp | 5 +++++ code/nel/src/3d/driver/opengl/driver_opengl.cpp | 2 -- code/nel/src/3d/driver/opengl/driver_opengl.h | 4 +++- .../src/3d/driver/opengl/driver_opengl_texture.cpp | 11 ++++++++--- code/nel/src/3d/shadow_map_manager.cpp | 5 +++-- 7 files changed, 22 insertions(+), 8 deletions(-) diff --git a/code/nel/include/nel/3d/driver.h b/code/nel/include/nel/3d/driver.h index 14595e05c..655a9e62b 100644 --- a/code/nel/include/nel/3d/driver.h +++ b/code/nel/include/nel/3d/driver.h @@ -859,6 +859,8 @@ public: uint32 cubeFace = 0 ) = 0 ; + virtual ITexture *getRenderTarget() const = 0; + /** Trick method : copy the current texture target into another texture without updating the current texture. * * This method copies the current texture into another texture. diff --git a/code/nel/src/3d/driver/direct3d/driver_direct3d.h b/code/nel/src/3d/driver/direct3d/driver_direct3d.h index 779a62a5c..085bb7512 100644 --- a/code/nel/src/3d/driver/direct3d/driver_direct3d.h +++ b/code/nel/src/3d/driver/direct3d/driver_direct3d.h @@ -855,6 +855,7 @@ public: // todo hulud d3d buffers virtual void getZBufferPart (std::vector &zbuffer, NLMISC::CRect &rect); virtual bool setRenderTarget (ITexture *tex, uint32 x, uint32 y, uint32 width, uint32 height, uint32 mipmapLevel, uint32 cubeFace); + virtual ITexture *getRenderTarget() const; virtual bool copyTargetToTexture (ITexture *tex, uint32 offsetx, uint32 offsety, uint32 x, uint32 y, uint32 width, uint32 height, uint32 mipmapLevel); virtual bool getRenderTargetSize (uint32 &width, uint32 &height); diff --git a/code/nel/src/3d/driver/direct3d/driver_direct3d_texture.cpp b/code/nel/src/3d/driver/direct3d/driver_direct3d_texture.cpp index 7922b30ea..87f41678b 100644 --- a/code/nel/src/3d/driver/direct3d/driver_direct3d_texture.cpp +++ b/code/nel/src/3d/driver/direct3d/driver_direct3d_texture.cpp @@ -1187,6 +1187,11 @@ bool CDriverD3D::setRenderTarget (ITexture *tex, uint32 /* x */, uint32 /* y */, return true; } +ITexture *CDriverD3D::getRenderTarget() const +{ + return _RenderTarget.Texture; +} + // *************************************************************************** bool CDriverD3D::copyTargetToTexture (ITexture * /* tex */, uint32 /* offsetx */, uint32 /* offsety */, uint32 /* x */, uint32 /* y */, uint32 /* width */, diff --git a/code/nel/src/3d/driver/opengl/driver_opengl.cpp b/code/nel/src/3d/driver/opengl/driver_opengl.cpp index 42d8b4834..32244edbe 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl.cpp +++ b/code/nel/src/3d/driver/opengl/driver_opengl.cpp @@ -263,8 +263,6 @@ CDriverGL::CDriverGL() _CurrentFogColor[2]= 0; _CurrentFogColor[3]= 0; - _RenderTargetFBO = false; - _LightSetupDirty= false; _ModelViewMatrixDirty= false; _RenderSetupDirty= false; diff --git a/code/nel/src/3d/driver/opengl/driver_opengl.h b/code/nel/src/3d/driver/opengl/driver_opengl.h index f88936a8a..3f0675276 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl.h +++ b/code/nel/src/3d/driver/opengl/driver_opengl.h @@ -564,6 +564,8 @@ public: virtual bool setRenderTarget (ITexture *tex, uint32 x, uint32 y, uint32 width, uint32 height, uint32 mipmapLevel, uint32 cubeFace); + virtual ITexture *getRenderTarget() const; + virtual bool copyTargetToTexture (ITexture *tex, uint32 offsetx, uint32 offsety, uint32 x, uint32 y, uint32 width, uint32 height, uint32 mipmapLevel); @@ -889,7 +891,7 @@ private: // viewport before call to setRenderTarget, if BFO extension is supported CViewport _OldViewport; - bool _RenderTargetFBO; + CSmartPtr _RenderTargetFBO; // Num lights return by GL_MAX_LIGHTS diff --git a/code/nel/src/3d/driver/opengl/driver_opengl_texture.cpp b/code/nel/src/3d/driver/opengl/driver_opengl_texture.cpp index 77954a8e3..7ded32b27 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl_texture.cpp +++ b/code/nel/src/3d/driver/opengl/driver_opengl_texture.cpp @@ -2314,7 +2314,7 @@ bool CDriverGL::setRenderTarget (ITexture *tex, uint32 x, uint32 y, uint32 width newVP.init(0, 0, ((float)width/(float)w), ((float)height/(float)h)); setupViewport(newVP); - _RenderTargetFBO = true; + _RenderTargetFBO = tex; return activeFrameBufferObject(tex); } @@ -2334,7 +2334,7 @@ bool CDriverGL::setRenderTarget (ITexture *tex, uint32 x, uint32 y, uint32 width setupViewport(_OldViewport); _OldViewport = _CurrViewport; - _RenderTargetFBO = false; + _RenderTargetFBO = NULL; return false; } @@ -2347,12 +2347,17 @@ bool CDriverGL::setRenderTarget (ITexture *tex, uint32 x, uint32 y, uint32 width // Update the scissor setupScissor (_CurrScissor); - _RenderTargetFBO = false; + _RenderTargetFBO = NULL; _OldViewport = _CurrViewport; return true; } +ITexture *CDriverGL::getRenderTarget() const +{ + return _RenderTargetFBO ? _RenderTargetFBO : _TextureTarget; +} + // *************************************************************************** bool CDriverGL::copyTargetToTexture (ITexture *tex, diff --git a/code/nel/src/3d/shadow_map_manager.cpp b/code/nel/src/3d/shadow_map_manager.cpp index 79f4ade20..383a28184 100644 --- a/code/nel/src/3d/shadow_map_manager.cpp +++ b/code/nel/src/3d/shadow_map_manager.cpp @@ -244,11 +244,12 @@ void CShadowMapManager::addShadowReceiver(CTransform *model) void CShadowMapManager::renderGenerate(CScene *scene) { H_AUTO( NL3D_ShadowManager_Generate ); - + // Each frame, do a small garbage collector for unused free textures. garbageShadowTextures(scene); IDriver *driverForShadowGeneration= scene->getRenderTrav().getAuxDriver(); + CSmartPtr previousRenderTarget = driverForShadowGeneration->getRenderTarget(); // Init // ******** @@ -488,7 +489,7 @@ void CShadowMapManager::renderGenerate(CScene *scene) } // Set default render target - driverForShadowGeneration->setRenderTarget (NULL); + driverForShadowGeneration->setRenderTarget (previousRenderTarget); // Allow Writing on all. driverForShadowGeneration->setColorMask(true, true, true, true); From 3a12aa5894fa51d7382063e7a4b1f4ac728f5ad2 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Tue, 2 Jul 2013 00:55:13 +0200 Subject: [PATCH 058/313] Render the scene to a target texture for the stereo rendering filter, ref #43 --- code/nel/include/nel/3d/bloom_effect.h | 2 +- code/nel/include/nel/3d/stereo_display.h | 2 +- code/nel/include/nel/3d/stereo_ovr.h | 2 +- code/nel/src/3d/bloom_effect.cpp | 7 ++-- code/nel/src/3d/stereo_ovr.cpp | 20 +++++---- code/snowballs2/client/src/camera.cpp | 12 ++++-- .../client/src/snowballs_client.cpp | 41 +++++++++++-------- 7 files changed, 49 insertions(+), 37 deletions(-) diff --git a/code/nel/include/nel/3d/bloom_effect.h b/code/nel/include/nel/3d/bloom_effect.h index f5da7a4dd..b637fe937 100644 --- a/code/nel/include/nel/3d/bloom_effect.h +++ b/code/nel/include/nel/3d/bloom_effect.h @@ -83,7 +83,7 @@ public: // If window size exceeds 256*256 the textures used to apply blur are reinitialized with // 256*256 size. If a dimension is less than 256, the texture is initialized with the nearer // power of 2, lower than this window dimension. - void initBloom(UTexture &renderTarget); + void initBloom(UTexture *renderTarget); void initBloom(); // Called at the end of renderAll method in the main loop, recover stretched texture, apply diff --git a/code/nel/include/nel/3d/stereo_display.h b/code/nel/include/nel/3d/stereo_display.h index 2f6592b60..aa98dae50 100644 --- a/code/nel/include/nel/3d/stereo_display.h +++ b/code/nel/include/nel/3d/stereo_display.h @@ -124,7 +124,7 @@ public: /// Returns non-NULL if a new render target was set virtual UTexture *beginRenderTarget(bool set) = 0; /// Returns true if a render target was fully drawn - virtual bool endRenderTarget(bool unset) = 0; + virtual bool endRenderTarget() = 0; static const char *getLibraryName(CStereoDeviceInfo::TStereoDeviceLibrary library); static void listDevices(std::vector &devicesOut); diff --git a/code/nel/include/nel/3d/stereo_ovr.h b/code/nel/include/nel/3d/stereo_ovr.h index f25ef10e0..1a4ebf916 100644 --- a/code/nel/include/nel/3d/stereo_ovr.h +++ b/code/nel/include/nel/3d/stereo_ovr.h @@ -111,7 +111,7 @@ public: /// Returns non-NULL if a new render target was set, always NULL if not using render targets virtual UTexture *beginRenderTarget(bool set); /// Returns true if a render target was fully drawn, always false if not using render targets - virtual bool endRenderTarget(bool unset); + virtual bool endRenderTarget(); /// Get the HMD orientation diff --git a/code/nel/src/3d/bloom_effect.cpp b/code/nel/src/3d/bloom_effect.cpp index 0bc9c1e41..60b7d36b7 100644 --- a/code/nel/src/3d/bloom_effect.cpp +++ b/code/nel/src/3d/bloom_effect.cpp @@ -273,16 +273,15 @@ void CBloomEffect::initTexture(CSmartPtr & tex, bool isMode2D, uint32 void CBloomEffect::initBloom() { - CTextureUser cu; - initBloom(cu); + initBloom(NULL); } -void CBloomEffect::initBloom(UTexture &renderTarget) // clientcfg +void CBloomEffect::initBloom(UTexture *renderTarget) // clientcfg { if(!((CDriverUser *)_Driver)->getDriver()->supportBloomEffect()) return; - m_UserRenderTarget = dynamic_cast(renderTarget).getITexture(); + m_UserRenderTarget = renderTarget ? dynamic_cast(renderTarget)->getITexture() : NULL; // don't activate bloom when PolygonMode is different from Filled if (_Driver->getPolygonMode() != UDriver::Filled) return; diff --git a/code/nel/src/3d/stereo_ovr.cpp b/code/nel/src/3d/stereo_ovr.cpp index 8eb8e1f9b..68fd1400e 100644 --- a/code/nel/src/3d/stereo_ovr.cpp +++ b/code/nel/src/3d/stereo_ovr.cpp @@ -163,7 +163,7 @@ public: OVR::HMDInfo HMDInfo; }; -CStereoOVR::CStereoOVR(const CStereoOVRDeviceHandle *handle) : m_Stage(0), m_SubStage(0), m_OrientationCached(false), m_BarrelTexU(NULL) +CStereoOVR::CStereoOVR(const CStereoOVRDeviceHandle *handle) : m_Stage(0), m_SubStage(0), m_OrientationCached(false), m_Driver(NULL), m_BarrelTexU(NULL) { ++s_DeviceCounter; m_DevicePtr = new CStereoOVRDevicePtr(); @@ -453,7 +453,7 @@ UTexture *CStereoOVR::beginRenderTarget(bool set) { // render target always set before driver clear // nlassert(m_SubStage <= 1); - if (m_Stage == 1) + if (m_Driver && m_Stage == 1) { if (set) { @@ -465,17 +465,19 @@ UTexture *CStereoOVR::beginRenderTarget(bool set) } /// Returns true if a render target was fully drawn -bool CStereoOVR::endRenderTarget(bool unset) +bool CStereoOVR::endRenderTarget() { // after rendering of course // nlassert(m_SubStage > 1); - if (m_Stage == 4) + if (m_Driver && m_Stage == 4) { - if (unset) - { - CTextureUser cu; - (static_cast(m_Driver))->setRenderTarget(cu); - } + CTextureUser cu; + (static_cast(m_Driver))->setRenderTarget(cu); + + m_Driver->setMatrixMode2D11(); + m_Driver->setViewport(CViewport()); + m_Driver->drawQuad(m_BarrelQuad, m_BarrelMat); + return true; } return false; diff --git a/code/snowballs2/client/src/camera.cpp b/code/snowballs2/client/src/camera.cpp index f4f5ea2b4..92216fe98 100644 --- a/code/snowballs2/client/src/camera.cpp +++ b/code/snowballs2/client/src/camera.cpp @@ -113,10 +113,14 @@ void initCamera() { nlinfo("Create VR stereo display device"); StereoDisplay = IStereoDisplay::createDevice(*deviceInfo); - if (deviceInfo->Class == CStereoDeviceInfo::StereoHMD) + if (StereoDisplay) { - nlinfo("Stereo display device is a HMD"); - StereoHMD = static_cast(StereoDisplay); + if (deviceInfo->Class == CStereoDeviceInfo::StereoHMD) + { + nlinfo("Stereo display device is a HMD"); + StereoHMD = static_cast(StereoDisplay); + } + StereoDisplay->setDriver(Driver); // move after driver creation, move stereodisplay before driver creation } } IStereoDisplay::releaseUnusedLibraries(); @@ -212,7 +216,7 @@ void releaseSky() // -- -- random note: update and render makes more sense than animate and update void animateSky(double dt) { - Clouds->anim(dt); + if (!StereoDisplay) Clouds->anim(dt); } // this is actually render diff --git a/code/snowballs2/client/src/snowballs_client.cpp b/code/snowballs2/client/src/snowballs_client.cpp index e6754388a..e71136377 100644 --- a/code/snowballs2/client/src/snowballs_client.cpp +++ b/code/snowballs2/client/src/snowballs_client.cpp @@ -736,26 +736,28 @@ void loopIngame() { uint i = 0; uint bloomStage = 0; - while ((!StereoHMD && i == 0) || (StereoHMD && StereoHMD->nextPass())) + while ((!StereoDisplay && i == 0) || (StereoDisplay && StereoDisplay->nextPass())) { ++i; - if (StereoHMD) + if (StereoDisplay) { - const CViewport &vp = StereoHMD->getCurrentViewport(); + const CViewport &vp = StereoDisplay->getCurrentViewport(); Driver->setViewport(vp); Scene->setViewport(vp); SkyScene->setViewport(vp); - StereoHMD->getCurrentFrustum(0, &Camera); - StereoHMD->getCurrentFrustum(0, &SkyCamera); - StereoHMD->getCurrentMatrix(0, &Camera); + StereoDisplay->getCurrentFrustum(0, &Camera); + StereoDisplay->getCurrentFrustum(0, &SkyCamera); + StereoDisplay->getCurrentMatrix(0, &Camera); } - if (!StereoHMD || StereoHMD->wantClear()) + if (!StereoDisplay || StereoDisplay->wantClear()) { + NL3D::UTexture *rt = StereoDisplay ? StereoDisplay->beginRenderTarget(!s_EnableBloom) : NULL; + if (s_EnableBloom) { nlassert(bloomStage == 0); - CBloomEffect::instance().initBloom(); // start bloom effect (just before the first scene element render) + CBloomEffect::instance().initBloom(/*rt*/); // start bloom effect (just before the first scene element render) bloomStage = 1; } @@ -763,7 +765,7 @@ void loopIngame() Driver->clearBuffers(CRGBA(0, 0, 127)); // clear all buffers, if you see this blue there's a problem with scene rendering } - if (!StereoHMD || StereoHMD->wantScene()) + if (!StereoDisplay || StereoDisplay->wantScene()) { // 02. Render Sky (sky scene) updateSky(); // Render the sky scene before the main scene @@ -772,17 +774,17 @@ void loopIngame() Scene->render(); // Render // 05. Render Effects (flare) - if (!StereoHMD) updateLensFlare(); // Render the lens flare (left eye stretched with stereo...) + if (!StereoDisplay) updateLensFlare(); // Render the lens flare (left eye stretched with stereo...) } - if (!StereoHMD || StereoHMD->wantInterface3D()) + if (!StereoDisplay || StereoDisplay->wantInterface3D()) { if (s_EnableBloom && bloomStage == 1) { // End the actual bloom effect visible in the scene. - if (StereoHMD) Driver->setViewport(NL3D::CViewport()); + if (StereoDisplay) Driver->setViewport(NL3D::CViewport()); CBloomEffect::instance().endBloom(); - if (StereoHMD) Driver->setViewport(StereoHMD->getCurrentViewport()); + if (StereoDisplay) Driver->setViewport(StereoDisplay->getCurrentViewport()); bloomStage = 2; } @@ -790,14 +792,14 @@ void loopIngame() // ... } - if (!StereoHMD || StereoHMD->wantInterface2D()) + if (!StereoDisplay || StereoDisplay->wantInterface2D()) { if (s_EnableBloom && bloomStage == 2) { // End bloom effect system after drawing the 3d interface (z buffer related). - if (StereoHMD) Driver->setViewport(NL3D::CViewport()); + if (StereoDisplay) Driver->setViewport(NL3D::CViewport()); CBloomEffect::instance().endInterfacesDisplayBloom(); - if (StereoHMD) Driver->setViewport(StereoHMD->getCurrentViewport()); + if (StereoDisplay) Driver->setViewport(StereoDisplay->getCurrentViewport()); bloomStage = 0; } @@ -810,11 +812,16 @@ void loopIngame() renderEntitiesNames(); // Render the name on top of the other players updateInterface(); // Update interface renderInformation(); - if (!StereoHMD) update3dLogo(); // broken with stereo + if (!StereoDisplay) update3dLogo(); // broken with stereo // 08. Render Debug (stuff for dev) // ... } + + if (StereoDisplay) + { + StereoDisplay->endRenderTarget(); + } } // 09. Render Buffer From 76d7146a427aef729ab64c8fee61052a6634da62 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Tue, 2 Jul 2013 01:08:49 +0200 Subject: [PATCH 059/313] Workaround in snowballs for fullscreen bug with opengl driver --- code/snowballs2/client/src/snowballs_client.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/code/snowballs2/client/src/snowballs_client.cpp b/code/snowballs2/client/src/snowballs_client.cpp index e71136377..1faf14d67 100644 --- a/code/snowballs2/client/src/snowballs_client.cpp +++ b/code/snowballs2/client/src/snowballs_client.cpp @@ -311,12 +311,15 @@ void initCore() // Create the window with config file values Driver->setDisplay(UDriver::CMode(ConfigFile->getVar("ScreenWidth").asInt(), ConfigFile->getVar("ScreenHeight").asInt(), - ConfigFile->getVar("ScreenDepth").asInt(), - ConfigFile->getVar("ScreenFull").asInt()==0)); + ConfigFile->getVar("ScreenDepth").asInt())); // Set the cache size for the font manager(in bytes) Driver->setFontManagerMaxMemory(2097152); // Create a Text context for later text rendering displayLoadingState("Initialize Text"); + Driver->setMode(UDriver::CMode(ConfigFile->getVar("ScreenWidth").asInt(), + ConfigFile->getVar("ScreenHeight").asInt(), + ConfigFile->getVar("ScreenDepth").asInt(), + ConfigFile->getVar("ScreenFull").asInt()==0)); TextContext = Driver->createTextContext(CPath::lookup(ConfigFile->getVar("FontName").asString())); TextContext->setShaded(true); TextContext->setKeep800x600Ratio(false); From 6b8d43181d3ae90c608781b379a4595a60507f80 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Tue, 2 Jul 2013 02:40:27 +0200 Subject: [PATCH 060/313] Test the barrel shader, see #43 --- code/nel/include/nel/3d/stereo_ovr.h | 2 + code/nel/src/3d/stereo_ovr.cpp | 143 ++++++++++++++++++++------- code/nel/src/3d/stereo_ovr_fp.cpp | 60 +++++++++++ 3 files changed, 171 insertions(+), 34 deletions(-) diff --git a/code/nel/include/nel/3d/stereo_ovr.h b/code/nel/include/nel/3d/stereo_ovr.h index 1a4ebf916..8d4a924a6 100644 --- a/code/nel/include/nel/3d/stereo_ovr.h +++ b/code/nel/include/nel/3d/stereo_ovr.h @@ -63,6 +63,7 @@ class ITexture; class CTextureUser; class CStereoOVRDevicePtr; class CStereoOVRDeviceHandle; +class CPixelProgram; #define NL_STEREO_MAX_USER_CAMERAS 8 @@ -147,6 +148,7 @@ private: NL3D::CTextureUser *m_BarrelTexU; NL3D::UMaterial m_BarrelMat; NLMISC::CQuadUV m_BarrelQuad; + CPixelProgram *m_PixelProgram; }; /* class CStereoOVR */ diff --git a/code/nel/src/3d/stereo_ovr.cpp b/code/nel/src/3d/stereo_ovr.cpp index 68fd1400e..4405ad313 100644 --- a/code/nel/src/3d/stereo_ovr.cpp +++ b/code/nel/src/3d/stereo_ovr.cpp @@ -68,6 +68,7 @@ using namespace std; namespace NL3D { extern const char *g_StereoOVR_arbfp1; +extern const char *g_StereoOVR_ps_2_0; namespace { @@ -163,7 +164,7 @@ public: OVR::HMDInfo HMDInfo; }; -CStereoOVR::CStereoOVR(const CStereoOVRDeviceHandle *handle) : m_Stage(0), m_SubStage(0), m_OrientationCached(false), m_Driver(NULL), m_BarrelTexU(NULL) +CStereoOVR::CStereoOVR(const CStereoOVRDeviceHandle *handle) : m_Stage(0), m_SubStage(0), m_OrientationCached(false), m_Driver(NULL), m_BarrelTexU(NULL), m_PixelProgram(NULL) { ++s_DeviceCounter; m_DevicePtr = new CStereoOVRDevicePtr(); @@ -211,6 +212,9 @@ CStereoOVR::~CStereoOVR() m_BarrelTexU = NULL; m_BarrelTex = NULL; // CSmartPtr + delete m_PixelProgram; + m_PixelProgram = NULL; + m_Driver = NULL; if (m_DevicePtr->SensorDevice) @@ -227,49 +231,84 @@ CStereoOVR::~CStereoOVR() void CStereoOVR::setDriver(NL3D::UDriver *driver) { - m_Driver = driver; // Do not allow weird stuff. uint32 width, height; driver->getWindowSize(width, height); nlassert(width == m_DevicePtr->HMDInfo.HResolution); nlassert(height == m_DevicePtr->HMDInfo.VResolution); + nlassert(!m_PixelProgram); NL3D::IDriver *drvInternal = (static_cast(driver))->getDriver(); + /*static const char *program_arbfp1 = + "!!ARBfp1.0\n" + "PARAM c[1] = { { 1, 0 } };\n" + "MOV result.color.xzw, c[0].xyyx;\n" + "TEX result.color.y, fragment.texcoord[0], texture[0], 2D;\n" + "END\n"; + static const char *program_ps_2_0 = + "ps_2_0\n" + "dcl_2d s0\n" + "def c0, 1.00000000, 0.00000000, 0, 0\n" + "dcl t0.xy\n" + "texld r0, t0, s0\n" + "mov r0.z, c0.y\n" + "mov r0.xw, c0.x\n" + "mov oC0, r0\n";*/ + /*if (drvInternal->supportPixelProgram(CPixelProgram::arbfp1)) + { + nldebug("VR: arbfp1"); + m_PixelProgram = new CPixelProgram(program_arbfp1); + } + else */ if (drvInternal->supportPixelProgram(CPixelProgram::ps_2_0)) + { + nldebug("VR: ps_2_0"); + m_PixelProgram = new CPixelProgram(g_StereoOVR_ps_2_0); + } - m_BarrelTex = new CTextureBloom(); // lol bloom - m_BarrelTex->setReleasable(false); - m_BarrelTex->resize(width, height); - m_BarrelTex->setFilterMode(ITexture::Linear, ITexture::LinearMipMapOff); - m_BarrelTex->setWrapS(ITexture::Clamp); - m_BarrelTex->setWrapT(ITexture::Clamp); - m_BarrelTex->setRenderTarget(true); - drvInternal->setupTexture(*m_BarrelTex); - m_BarrelTexU = new CTextureUser(m_BarrelTex); + if (m_PixelProgram) + { + m_Driver = driver; - m_BarrelMat = m_Driver->createMaterial(); - NL3D::CMaterial *barrelMat = m_BarrelMat.getObjectPtr(); - m_BarrelMat.initUnlit(); - m_BarrelMat.setColor(CRGBA::White); - m_BarrelMat.setBlend (false); - m_BarrelMat.setAlphaTest (false); - barrelMat->setBlendFunc(CMaterial::one, CMaterial::zero); - barrelMat->setZWrite(false); - barrelMat->setZFunc(CMaterial::always); - barrelMat->setDoubleSided(true); - barrelMat->setTexture(0, m_BarrelTex); + m_BarrelTex = new CTextureBloom(); // lol bloom + m_BarrelTex->setReleasable(false); + m_BarrelTex->resize(width, height); + m_BarrelTex->setFilterMode(ITexture::Linear, ITexture::LinearMipMapOff); + m_BarrelTex->setWrapS(ITexture::Clamp); + m_BarrelTex->setWrapT(ITexture::Clamp); + m_BarrelTex->setRenderTarget(true); + drvInternal->setupTexture(*m_BarrelTex); + m_BarrelTexU = new CTextureUser(m_BarrelTex); - m_BarrelQuad.V0 = CVector(0.f, 0.f, 0.5f); - m_BarrelQuad.V1 = CVector(1.f, 0.f, 0.5f); - m_BarrelQuad.V2 = CVector(1.f, 1.f, 0.5f); - m_BarrelQuad.V3 = CVector(0.f, 1.f, 0.5f); - - float newU = drvInternal->isTextureRectangle(m_BarrelTex) ? (float)width : 1.f; - float newV = drvInternal->isTextureRectangle(m_BarrelTex) ? (float)height : 1.f; + m_BarrelMat = m_Driver->createMaterial(); + m_BarrelMat.initUnlit(); + m_BarrelMat.setColor(CRGBA::White); + m_BarrelMat.setBlend (false); + m_BarrelMat.setAlphaTest (false); + NL3D::CMaterial *barrelMat = m_BarrelMat.getObjectPtr(); + barrelMat->setShader(NL3D::CMaterial::PostProcessing); + barrelMat->setBlendFunc(CMaterial::one, CMaterial::zero); + barrelMat->setZWrite(false); + barrelMat->setZFunc(CMaterial::always); + barrelMat->setDoubleSided(true); + barrelMat->setTexture(0, m_BarrelTex); - m_BarrelQuad.Uv0 = CUV(0.f, 0.f); - m_BarrelQuad.Uv1 = CUV(newU, 0.f); - m_BarrelQuad.Uv2 = CUV(newU, newV); - m_BarrelQuad.Uv3 = CUV(0.f, newV); + m_BarrelQuad.V0 = CVector(0.f, 0.f, 0.5f); + m_BarrelQuad.V1 = CVector(1.f, 0.f, 0.5f); + m_BarrelQuad.V2 = CVector(1.f, 1.f, 0.5f); + m_BarrelQuad.V3 = CVector(0.f, 1.f, 0.5f); + + float newU = drvInternal->isTextureRectangle(m_BarrelTex) ? (float)width : 1.f; + float newV = drvInternal->isTextureRectangle(m_BarrelTex) ? (float)height : 1.f; + + m_BarrelQuad.Uv0 = CUV(0.f, 0.f); + m_BarrelQuad.Uv1 = CUV(newU, 0.f); + m_BarrelQuad.Uv2 = CUV(newU, newV); + m_BarrelQuad.Uv3 = CUV(0.f, newV); + } + else + { + nlwarning("VR: No pixel program support"); + } } void CStereoOVR::getScreenResolution(uint &width, uint &height) @@ -473,10 +512,46 @@ bool CStereoOVR::endRenderTarget() { CTextureUser cu; (static_cast(m_Driver))->setRenderTarget(cu); + bool fogEnabled = m_Driver->fogEnabled(); + m_Driver->enableFog(false); m_Driver->setMatrixMode2D11(); - m_Driver->setViewport(CViewport()); + CViewport vp = CViewport(); + m_Driver->setViewport(vp); + uint32 width, height; + m_Driver->getWindowSize(width, height); + NL3D::IDriver *drvInternal = (static_cast(m_Driver))->getDriver(); + NL3D::CMaterial *barrelMat = m_BarrelMat.getObjectPtr(); + barrelMat->setTexture(0, m_BarrelTex); + drvInternal->activePixelProgram(m_PixelProgram); + + float w = float(vp.getWidth()),// / float(width), + h = float(vp.getHeight()),// / float(height), + x = float(vp.getX()),/// / float(width), + y = float(vp.getY());// / float(height); + + float lensOffset = m_DevicePtr->HMDInfo.LensSeparationDistance * 0.5f; + float lensShift = m_DevicePtr->HMDInfo.HScreenSize * 0.25f - lensOffset; + float lensViewportShift = 4.0f * lensShift / m_DevicePtr->HMDInfo.HScreenSize; + + float lensCenterX = x + (w + lensViewportShift * 0.5f) * 0.5f; + float lensCenterY = y + h * 0.5f; + float screenCenterX = x + w * 0.5f; + float screenCenterY = y + h * 0.5f; + float scaleX = (w / 2); + float scaleY = (h / 2); + float scaleInX = (2 / w); + float scaleInY = (2 / h); + drvInternal->setPixelProgramConstant(0, lensCenterX, lensCenterY, 0.f, 0.f); + drvInternal->setPixelProgramConstant(1, screenCenterX, screenCenterY, 0.f, 0.f); + drvInternal->setPixelProgramConstant(2, scaleX, scaleY, 0.f, 0.f); + drvInternal->setPixelProgramConstant(3, scaleInX, scaleInY, 0.f, 0.f); + drvInternal->setPixelProgramConstant(4, 1, m_DevicePtr->HMDInfo.DistortionK); + + m_Driver->drawQuad(m_BarrelQuad, m_BarrelMat); + drvInternal->activePixelProgram(NULL); + m_Driver->enableFog(fogEnabled); return true; } diff --git a/code/nel/src/3d/stereo_ovr_fp.cpp b/code/nel/src/3d/stereo_ovr_fp.cpp index eaa80f14c..d33e5fa2c 100644 --- a/code/nel/src/3d/stereo_ovr_fp.cpp +++ b/code/nel/src/3d/stereo_ovr_fp.cpp @@ -78,5 +78,65 @@ const char *g_StereoOVR_arbfp1 = "CMP R1.x, -R1, c[5].z, c[5].w;\n" "CMP result.color, -R1.x, R0, c[5].z;\n" "END\n"; +const char *g_StereoOVR_ps_2_0 = //# 28 instructions, 2 R-regs + "ps_2_0\n" + // cgc version 3.1.0013, build date Apr 18 2012 + // command line args: -profile ps_2_0 + // source file: pp_oculus_vr.cg + //vendor NVIDIA Corporation + //version 3.1.0.13 + //profile ps_2_0 + //program pp_oculus_vr + //semantic pp_oculus_vr.cLensCenter + //semantic pp_oculus_vr.cScreenCenter + //semantic pp_oculus_vr.cScale + //semantic pp_oculus_vr.cScaleIn + //semantic pp_oculus_vr.cHmdWarpParam + //semantic pp_oculus_vr.cTex0 : TEX0 + //var float2 texCoord : $vin.TEXCOORD0 : TEX0 : 0 : 1 + //var float2 cLensCenter : : c[0] : 1 : 1 + //var float2 cScreenCenter : : c[1] : 2 : 1 + //var float2 cScale : : c[2] : 3 : 1 + //var float2 cScaleIn : : c[3] : 4 : 1 + //var float4 cHmdWarpParam : : c[4] : 5 : 1 + //var sampler2D cTex0 : TEX0 : texunit 0 : 6 : 1 + //var float4 oCol : $vout.COLOR : COL : 7 : 1 + //const c[5] = -0.25 -0.5 0.25 0.5 + //const c[6] = 1 0 + "dcl_2d s0\n" + "def c5, -0.25000000, -0.50000000, 0.25000000, 0.50000000\n" + "def c6, 1.00000000, 0.00000000, 0, 0\n" + "dcl t0.xy\n" + "add r0.xy, t0, -c0\n" + "mul r4.xy, r0, c3\n" + "mul r0.x, r4.y, r4.y\n" + "mad r0.x, r4, r4, r0\n" + "mul r1.x, r0, c4.w\n" + "mul r1.x, r1, r0\n" + "mad r3.x, r0, c4.y, c4\n" + "mul r2.x, r0, c4.z\n" + "mad r2.x, r0, r2, r3\n" + "mad r0.x, r1, r0, r2\n" + "mul r0.xy, r4, r0.x\n" + "mul r0.xy, r0, c2\n" + "add r3.xy, r0, c0\n" + "mov r1.x, c5.z\n" + "mov r1.y, c5.w\n" + "mov r2.xy, c1\n" + "add r2.xy, r1, r2\n" + "mov r1.xy, c1\n" + "min r2.xy, r2, r3\n" + "add r1.xy, c5, r1\n" + "max r1.xy, r1, r2\n" + "add r1.xy, r1, -r3\n" + "abs r1.xy, r1\n" + "cmp r1.xy, -r1, c6.x, c6.y\n" + "mul_pp r1.x, r1, r1.y\n" + "abs_pp r1.x, r1\n" + "cmp_pp r1.x, -r1, c6, c6.y\n" + "abs_pp r1.x, r1\n" + "texld r0, r3, s0\n" + "cmp r0, -r1.x, r0, c6.y\n" + "mov oC0, r0\n"; } \ No newline at end of file From a924479a5c3aa94b23ef2f326a5974b336bc0cce Mon Sep 17 00:00:00 2001 From: kaetemi Date: Tue, 2 Jul 2013 03:34:49 +0200 Subject: [PATCH 061/313] Render left and right deformed view, re #43 --- code/nel/include/nel/3d/stereo_ovr.h | 3 +- code/nel/src/3d/stereo_ovr.cpp | 46 +++++++++++++------ .../client/src/snowballs_client.cpp | 12 +++-- 3 files changed, 42 insertions(+), 19 deletions(-) diff --git a/code/nel/include/nel/3d/stereo_ovr.h b/code/nel/include/nel/3d/stereo_ovr.h index 8d4a924a6..857c5368f 100644 --- a/code/nel/include/nel/3d/stereo_ovr.h +++ b/code/nel/include/nel/3d/stereo_ovr.h @@ -147,7 +147,8 @@ private: NLMISC::CSmartPtr m_BarrelTex; NL3D::CTextureUser *m_BarrelTexU; NL3D::UMaterial m_BarrelMat; - NLMISC::CQuadUV m_BarrelQuad; + NLMISC::CQuadUV m_BarrelQuadLeft; + NLMISC::CQuadUV m_BarrelQuadRight; CPixelProgram *m_PixelProgram; }; /* class CStereoOVR */ diff --git a/code/nel/src/3d/stereo_ovr.cpp b/code/nel/src/3d/stereo_ovr.cpp index 4405ad313..63dd03d2f 100644 --- a/code/nel/src/3d/stereo_ovr.cpp +++ b/code/nel/src/3d/stereo_ovr.cpp @@ -292,18 +292,29 @@ void CStereoOVR::setDriver(NL3D::UDriver *driver) barrelMat->setDoubleSided(true); barrelMat->setTexture(0, m_BarrelTex); - m_BarrelQuad.V0 = CVector(0.f, 0.f, 0.5f); - m_BarrelQuad.V1 = CVector(1.f, 0.f, 0.5f); - m_BarrelQuad.V2 = CVector(1.f, 1.f, 0.5f); - m_BarrelQuad.V3 = CVector(0.f, 1.f, 0.5f); + m_BarrelQuadLeft.V0 = CVector(0.f, 0.f, 0.5f); + m_BarrelQuadLeft.V1 = CVector(0.5f, 0.f, 0.5f); + m_BarrelQuadLeft.V2 = CVector(0.5f, 1.f, 0.5f); + m_BarrelQuadLeft.V3 = CVector(0.f, 1.f, 0.5f); + + m_BarrelQuadRight.V0 = CVector(0.5f, 0.f, 0.5f); + m_BarrelQuadRight.V1 = CVector(1.f, 0.f, 0.5f); + m_BarrelQuadRight.V2 = CVector(1.f, 1.f, 0.5f); + m_BarrelQuadRight.V3 = CVector(0.5f, 1.f, 0.5f); + nlassert(!drvInternal->isTextureRectangle(m_BarrelTex)); // this code looks no good float newU = drvInternal->isTextureRectangle(m_BarrelTex) ? (float)width : 1.f; float newV = drvInternal->isTextureRectangle(m_BarrelTex) ? (float)height : 1.f; - m_BarrelQuad.Uv0 = CUV(0.f, 0.f); - m_BarrelQuad.Uv1 = CUV(newU, 0.f); - m_BarrelQuad.Uv2 = CUV(newU, newV); - m_BarrelQuad.Uv3 = CUV(0.f, newV); + m_BarrelQuadLeft.Uv0 = CUV(0.f, 0.f); + m_BarrelQuadLeft.Uv1 = CUV(newU * 0.5f, 0.f); + m_BarrelQuadLeft.Uv2 = CUV(newU * 0.5f, newV); + m_BarrelQuadLeft.Uv3 = CUV(0.f, newV); + + m_BarrelQuadRight.Uv0 = CUV(newU * 0.5f, 0.f); + m_BarrelQuadRight.Uv1 = CUV(newU, 0.f); + m_BarrelQuadRight.Uv2 = CUV(newU, newV); + m_BarrelQuadRight.Uv3 = CUV(newU * 0.5f, newV); } else { @@ -525,10 +536,10 @@ bool CStereoOVR::endRenderTarget() barrelMat->setTexture(0, m_BarrelTex); drvInternal->activePixelProgram(m_PixelProgram); - float w = float(vp.getWidth()),// / float(width), - h = float(vp.getHeight()),// / float(height), - x = float(vp.getX()),/// / float(width), - y = float(vp.getY());// / float(height); + float w = float(m_BarrelQuadLeft.V1.x),// / float(width), + h = float(m_BarrelQuadLeft.V2.y),// / float(height), + x = float(m_BarrelQuadLeft.V0.x),/// / float(width), + y = float(m_BarrelQuadLeft.V0.y);// / float(height); float lensOffset = m_DevicePtr->HMDInfo.LensSeparationDistance * 0.5f; float lensShift = m_DevicePtr->HMDInfo.HScreenSize * 0.25f - lensOffset; @@ -549,7 +560,16 @@ bool CStereoOVR::endRenderTarget() drvInternal->setPixelProgramConstant(4, 1, m_DevicePtr->HMDInfo.DistortionK); - m_Driver->drawQuad(m_BarrelQuad, m_BarrelMat); + m_Driver->drawQuad(m_BarrelQuadLeft, m_BarrelMat); + + x = w; + lensCenterX = x + (w - lensViewportShift * 0.5f) * 0.5f; + screenCenterX = x + w * 0.5f; + drvInternal->setPixelProgramConstant(0, lensCenterX, lensCenterY, 0.f, 0.f); + drvInternal->setPixelProgramConstant(1, screenCenterX, screenCenterY, 0.f, 0.f); + + m_Driver->drawQuad(m_BarrelQuadRight, m_BarrelMat); + drvInternal->activePixelProgram(NULL); m_Driver->enableFog(fogEnabled); diff --git a/code/snowballs2/client/src/snowballs_client.cpp b/code/snowballs2/client/src/snowballs_client.cpp index 1faf14d67..2522f48b6 100644 --- a/code/snowballs2/client/src/snowballs_client.cpp +++ b/code/snowballs2/client/src/snowballs_client.cpp @@ -311,15 +311,17 @@ void initCore() // Create the window with config file values Driver->setDisplay(UDriver::CMode(ConfigFile->getVar("ScreenWidth").asInt(), ConfigFile->getVar("ScreenHeight").asInt(), - ConfigFile->getVar("ScreenDepth").asInt())); + ConfigFile->getVar("ScreenDepth").asInt(), + (ConfigFile->getVar("OpenGL").asInt() == 1 ? true : ConfigFile->getVar("ScreenFull").asInt()==0))); // Set the cache size for the font manager(in bytes) Driver->setFontManagerMaxMemory(2097152); // Create a Text context for later text rendering displayLoadingState("Initialize Text"); - Driver->setMode(UDriver::CMode(ConfigFile->getVar("ScreenWidth").asInt(), - ConfigFile->getVar("ScreenHeight").asInt(), - ConfigFile->getVar("ScreenDepth").asInt(), - ConfigFile->getVar("ScreenFull").asInt()==0)); + if (ConfigFile->getVar("OpenGL").asInt() == 1) + Driver->setMode(UDriver::CMode(ConfigFile->getVar("ScreenWidth").asInt(), + ConfigFile->getVar("ScreenHeight").asInt(), + ConfigFile->getVar("ScreenDepth").asInt(), + ConfigFile->getVar("ScreenFull").asInt()==0)); TextContext = Driver->createTextContext(CPath::lookup(ConfigFile->getVar("FontName").asString())); TextContext->setShaded(true); TextContext->setKeep800x600Ratio(false); From 2315ae9c5a985d582cceb3a9a1c3fc6d1c5c94c0 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Tue, 2 Jul 2013 18:43:15 +0200 Subject: [PATCH 062/313] Prefer NPOT texture over RECT texture --- code/nel/src/3d/driver/opengl/driver_opengl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/nel/src/3d/driver/opengl/driver_opengl.cpp b/code/nel/src/3d/driver/opengl/driver_opengl.cpp index 32244edbe..830496681 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl.cpp +++ b/code/nel/src/3d/driver/opengl/driver_opengl.cpp @@ -701,7 +701,7 @@ bool CDriverGL::supportNonPowerOfTwoTextures() const // *************************************************************************** bool CDriverGL::isTextureRectangle(ITexture * tex) const { - return (supportTextureRectangle() && tex->isBloomTexture() && tex->mipMapOff() + return (!supportNonPowerOfTwoTextures() && supportTextureRectangle() && tex->isBloomTexture() && tex->mipMapOff() && (!isPowerOf2(tex->getWidth()) || !isPowerOf2(tex->getHeight()))); } From c6a7432d86e526a2ef12c6e9da107d385b5c68c3 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Tue, 2 Jul 2013 18:43:28 +0200 Subject: [PATCH 063/313] Properly scale seconds per frame graph in snowballs --- code/snowballs2/client/src/graph.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/snowballs2/client/src/graph.cpp b/code/snowballs2/client/src/graph.cpp index 73ac726f5..8fd3dd85c 100644 --- a/code/snowballs2/client/src/graph.cpp +++ b/code/snowballs2/client/src/graph.cpp @@ -115,7 +115,7 @@ void CGraph::addValue (float value) // CGraph FpsGraph ("fps", 10.0f, 10.0f, 100.0f, 100.0f, CRGBA(128,0,0,128), 1000, 40.0f); -CGraph SpfGraph ("spf", 10.0f, 110.0f, 100.0f, 100.0f, CRGBA(0,128,0,128), 0, 200.0f); +CGraph SpfGraph ("spf", 10.0f, 110.0f, 100.0f, 100.0f, CRGBA(0,128,0,128), 0, 0.1f); CGraph DownloadGraph ("download", 10.0f, 260.0f, 100.0f, 100.0f, CRGBA(0,0,128,128), 1000, 1000.0f); CGraph UploadGraph ("upload", 10.0f, 360.0f, 100.0f, 100.0f, CRGBA(0,128,128,128), 1000, 1000.0f); From 118340d821ff7f0a411ddb366373323620195fed Mon Sep 17 00:00:00 2001 From: kaetemi Date: Tue, 2 Jul 2013 18:48:29 +0200 Subject: [PATCH 064/313] Test arbfp1, see #43 --- code/nel/src/3d/stereo_ovr.cpp | 35 ++++++++++++---------------------- 1 file changed, 12 insertions(+), 23 deletions(-) diff --git a/code/nel/src/3d/stereo_ovr.cpp b/code/nel/src/3d/stereo_ovr.cpp index 63dd03d2f..745394576 100644 --- a/code/nel/src/3d/stereo_ovr.cpp +++ b/code/nel/src/3d/stereo_ovr.cpp @@ -239,27 +239,18 @@ void CStereoOVR::setDriver(NL3D::UDriver *driver) nlassert(!m_PixelProgram); NL3D::IDriver *drvInternal = (static_cast(driver))->getDriver(); - /*static const char *program_arbfp1 = + static const char *program_arbfp1 = "!!ARBfp1.0\n" "PARAM c[1] = { { 1, 0 } };\n" "MOV result.color.xzw, c[0].xyyx;\n" "TEX result.color.y, fragment.texcoord[0], texture[0], 2D;\n" "END\n"; - static const char *program_ps_2_0 = - "ps_2_0\n" - "dcl_2d s0\n" - "def c0, 1.00000000, 0.00000000, 0, 0\n" - "dcl t0.xy\n" - "texld r0, t0, s0\n" - "mov r0.z, c0.y\n" - "mov r0.xw, c0.x\n" - "mov oC0, r0\n";*/ - /*if (drvInternal->supportPixelProgram(CPixelProgram::arbfp1)) + if (drvInternal->supportPixelProgram(CPixelProgram::arbfp1) && drvInternal->supportBloomEffect() && drvInternal->supportNonPowerOfTwoTextures()) { nldebug("VR: arbfp1"); m_PixelProgram = new CPixelProgram(program_arbfp1); } - else */ if (drvInternal->supportPixelProgram(CPixelProgram::ps_2_0)) + else if (drvInternal->supportPixelProgram(CPixelProgram::ps_2_0)) { nldebug("VR: ps_2_0"); m_PixelProgram = new CPixelProgram(g_StereoOVR_ps_2_0); @@ -270,12 +261,12 @@ void CStereoOVR::setDriver(NL3D::UDriver *driver) m_Driver = driver; m_BarrelTex = new CTextureBloom(); // lol bloom + m_BarrelTex->setRenderTarget(true); m_BarrelTex->setReleasable(false); m_BarrelTex->resize(width, height); m_BarrelTex->setFilterMode(ITexture::Linear, ITexture::LinearMipMapOff); m_BarrelTex->setWrapS(ITexture::Clamp); m_BarrelTex->setWrapT(ITexture::Clamp); - m_BarrelTex->setRenderTarget(true); drvInternal->setupTexture(*m_BarrelTex); m_BarrelTexU = new CTextureUser(m_BarrelTex); @@ -302,19 +293,17 @@ void CStereoOVR::setDriver(NL3D::UDriver *driver) m_BarrelQuadRight.V2 = CVector(1.f, 1.f, 0.5f); m_BarrelQuadRight.V3 = CVector(0.5f, 1.f, 0.5f); - nlassert(!drvInternal->isTextureRectangle(m_BarrelTex)); // this code looks no good - float newU = drvInternal->isTextureRectangle(m_BarrelTex) ? (float)width : 1.f; - float newV = drvInternal->isTextureRectangle(m_BarrelTex) ? (float)height : 1.f; + nlassert(!drvInternal->isTextureRectangle(m_BarrelTex)); // not allowed m_BarrelQuadLeft.Uv0 = CUV(0.f, 0.f); - m_BarrelQuadLeft.Uv1 = CUV(newU * 0.5f, 0.f); - m_BarrelQuadLeft.Uv2 = CUV(newU * 0.5f, newV); - m_BarrelQuadLeft.Uv3 = CUV(0.f, newV); + m_BarrelQuadLeft.Uv1 = CUV(0.5f, 0.f); + m_BarrelQuadLeft.Uv2 = CUV(0.5f, 1.f); + m_BarrelQuadLeft.Uv3 = CUV(0.f, 1.f); - m_BarrelQuadRight.Uv0 = CUV(newU * 0.5f, 0.f); - m_BarrelQuadRight.Uv1 = CUV(newU, 0.f); - m_BarrelQuadRight.Uv2 = CUV(newU, newV); - m_BarrelQuadRight.Uv3 = CUV(newU * 0.5f, newV); + m_BarrelQuadRight.Uv0 = CUV(0.5f, 0.f); + m_BarrelQuadRight.Uv1 = CUV(1.f, 0.f); + m_BarrelQuadRight.Uv2 = CUV(1.f, 1.f); + m_BarrelQuadRight.Uv3 = CUV(0.5f, 1.f); } else { From e3784571cdec68f0e432f2446c2458812b7b35c1 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Tue, 2 Jul 2013 19:39:05 +0200 Subject: [PATCH 065/313] Add arbfp1 and fp40 barrel fragment programs, see #43 --- code/nel/src/3d/stereo_ovr.cpp | 18 ++++----- code/nel/src/3d/stereo_ovr_fp.cpp | 63 ++++++++++++++++++++++++++++++- 2 files changed, 70 insertions(+), 11 deletions(-) diff --git a/code/nel/src/3d/stereo_ovr.cpp b/code/nel/src/3d/stereo_ovr.cpp index 745394576..d6fc56cb9 100644 --- a/code/nel/src/3d/stereo_ovr.cpp +++ b/code/nel/src/3d/stereo_ovr.cpp @@ -67,6 +67,7 @@ using namespace std; namespace NL3D { +extern const char *g_StereoOVR_fp40; extern const char *g_StereoOVR_arbfp1; extern const char *g_StereoOVR_ps_2_0; @@ -238,17 +239,16 @@ void CStereoOVR::setDriver(NL3D::UDriver *driver) nlassert(height == m_DevicePtr->HMDInfo.VResolution); nlassert(!m_PixelProgram); - NL3D::IDriver *drvInternal = (static_cast(driver))->getDriver(); - static const char *program_arbfp1 = - "!!ARBfp1.0\n" - "PARAM c[1] = { { 1, 0 } };\n" - "MOV result.color.xzw, c[0].xyyx;\n" - "TEX result.color.y, fragment.texcoord[0], texture[0], 2D;\n" - "END\n"; - if (drvInternal->supportPixelProgram(CPixelProgram::arbfp1) && drvInternal->supportBloomEffect() && drvInternal->supportNonPowerOfTwoTextures()) + NL3D::IDriver *drvInternal = (static_cast(driver))->getDriver(); + if (drvInternal->supportPixelProgram(CPixelProgram::fp40) && drvInternal->supportBloomEffect() && drvInternal->supportNonPowerOfTwoTextures()) + { + nldebug("VR: fp40"); + m_PixelProgram = new CPixelProgram(g_StereoOVR_fp40); + } + else if (drvInternal->supportPixelProgram(CPixelProgram::arbfp1) && drvInternal->supportBloomEffect() && drvInternal->supportNonPowerOfTwoTextures()) { nldebug("VR: arbfp1"); - m_PixelProgram = new CPixelProgram(program_arbfp1); + m_PixelProgram = new CPixelProgram(g_StereoOVR_arbfp1); } else if (drvInternal->supportPixelProgram(CPixelProgram::ps_2_0)) { diff --git a/code/nel/src/3d/stereo_ovr_fp.cpp b/code/nel/src/3d/stereo_ovr_fp.cpp index d33e5fa2c..46fe1451b 100644 --- a/code/nel/src/3d/stereo_ovr_fp.cpp +++ b/code/nel/src/3d/stereo_ovr_fp.cpp @@ -21,6 +21,65 @@ limitations under the License. ************************************************************************************/ namespace NL3D { +const char *g_StereoOVR_fp40 = + "!!ARBfp1.0\n" + "OPTION NV_fragment_program2;\n" + //# cgc version 3.1.0013, build date Apr 18 2012 + //# command line args: -profile fp40 + //# source file: pp_oculus_vr.cg + //#vendor NVIDIA Corporation + //#version 3.1.0.13 + //#profile fp40 + //#program pp_oculus_vr + //#semantic pp_oculus_vr.cLensCenter + //#semantic pp_oculus_vr.cScreenCenter + //#semantic pp_oculus_vr.cScale + //#semantic pp_oculus_vr.cScaleIn + //#semantic pp_oculus_vr.cHmdWarpParam + //#semantic pp_oculus_vr.cTex0 : TEX0 + //#var float2 texCoord : $vin.TEXCOORD0 : TEX0 : 0 : 1 + //#var float2 cLensCenter : : c[0] : 1 : 1 + //#var float2 cScreenCenter : : c[1] : 2 : 1 + //#var float2 cScale : : c[2] : 3 : 1 + //#var float2 cScaleIn : : c[3] : 4 : 1 + //#var float4 cHmdWarpParam : : c[4] : 5 : 1 + //#var sampler2D cTex0 : TEX0 : texunit 0 : 6 : 1 + //#var float4 oCol : $vout.COLOR : COL : 7 : 1 + //#const c[5] = 0.25 0.5 0 + "PARAM c[6] = { program.env[0..4],\n" // program.local->program.env! + " { 0.25, 0.5, 0 } };\n" + "TEMP R0;\n" + "TEMP R1;\n" + "SHORT TEMP H0;\n" + "TEMP RC;\n" + "TEMP HC;\n" + "OUTPUT oCol = result.color;\n" + "ADDR R0.xy, fragment.texcoord[0], -c[0];\n" + "MULR R0.xy, R0, c[3];\n" + "MULR R0.z, R0.y, R0.y;\n" + "MADR R1.x, R0, R0, R0.z;\n" + "MULR R0.zw, R1.x, c[4].xywz;\n" + "MADR R1.y, R1.x, c[4], c[4].x;\n" + "MADR R0.w, R0, R1.x, R1.y;\n" + "MULR R0.z, R0, R1.x;\n" + "MADR R0.z, R0, R1.x, R0.w;\n" + "MULR R1.xy, R0, R0.z;\n" + "MOVR R0.xy, c[5];\n" + "ADDR R1.zw, R0.xyxy, c[1].xyxy;\n" + "MOVR R0.zw, c[0].xyxy;\n" + "MADR R0.zw, R1.xyxy, c[2].xyxy, R0;\n" + "MINR R1.xy, R0.zwzw, R1.zwzw;\n" + "ADDR R0.xy, -R0, c[1];\n" + "MAXR R0.xy, R0, R1;\n" + "SEQR H0.xy, R0, R0.zwzw;\n" + "MULXC HC.x, H0, H0.y;\n" + "IF EQ.x;\n" + "MOVR oCol, c[5].z;\n" + "ELSE;\n" + "TEX oCol, R0.zwzw, texture[0], 2D;\n" + "ENDIF;\n" + "END\n"; + //# 24 instructions, 2 R-regs, 1 H-regs const char *g_StereoOVR_arbfp1 = "!!ARBfp1.0\n" //# cgc version 3.1.0013, build date Apr 18 2012 @@ -45,7 +104,7 @@ const char *g_StereoOVR_arbfp1 = //#var sampler2D cTex0 : TEX0 : texunit 0 : 6 : 1 //#var float4 oCol : $vout.COLOR : COL : 7 : 1 //#const c[5] = 0.25 0.5 0 1 - "PARAM c[6] = { program.local[0..4],\n" + "PARAM c[6] = { program.env[0..4],\n" " { 0.25, 0.5, 0, 1 } };\n" "TEMP R0;\n" "TEMP R1;\n" @@ -78,8 +137,8 @@ const char *g_StereoOVR_arbfp1 = "CMP R1.x, -R1, c[5].z, c[5].w;\n" "CMP result.color, -R1.x, R0, c[5].z;\n" "END\n"; -const char *g_StereoOVR_ps_2_0 = //# 28 instructions, 2 R-regs +const char *g_StereoOVR_ps_2_0 = "ps_2_0\n" // cgc version 3.1.0013, build date Apr 18 2012 // command line args: -profile ps_2_0 From 9516851beec67d75e93c3dbb10c0c16f40367492 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 3 Jul 2013 03:21:57 +0200 Subject: [PATCH 066/313] Remove unnecessary user render target code from bloom, see #43 --- code/nel/include/nel/3d/bloom_effect.h | 5 +--- code/nel/src/3d/bloom_effect.cpp | 38 ++++---------------------- 2 files changed, 6 insertions(+), 37 deletions(-) diff --git a/code/nel/include/nel/3d/bloom_effect.h b/code/nel/include/nel/3d/bloom_effect.h index b637fe937..77673741c 100644 --- a/code/nel/include/nel/3d/bloom_effect.h +++ b/code/nel/include/nel/3d/bloom_effect.h @@ -83,7 +83,6 @@ public: // If window size exceeds 256*256 the textures used to apply blur are reinitialized with // 256*256 size. If a dimension is less than 256, the texture is initialized with the nearer // power of 2, lower than this window dimension. - void initBloom(UTexture *renderTarget); void initBloom(); // Called at the end of renderAll method in the main loop, recover stretched texture, apply @@ -132,15 +131,13 @@ private: uint8 _DensityBloom; // render target textures - // used to display scene (FIXME: redundant when user render target provided...) + // used to display scene NLMISC::CSmartPtr _InitText; // used as stretched texture from _InitText, as displayed texture in first blur pass, // and as render target in second blur pass. NLMISC::CSmartPtr _BlurFinalTex; // used as render target in first blur pass, and as displayed texture on second blur pass. NLMISC::CSmartPtr _BlurHorizontalTex; - /// User provided render target. - NLMISC::CSmartPtr m_UserRenderTarget; // materials diff --git a/code/nel/src/3d/bloom_effect.cpp b/code/nel/src/3d/bloom_effect.cpp index 60b7d36b7..927f8688c 100644 --- a/code/nel/src/3d/bloom_effect.cpp +++ b/code/nel/src/3d/bloom_effect.cpp @@ -271,18 +271,11 @@ void CBloomEffect::initTexture(CSmartPtr & tex, bool isMode2D, uint32 //----------------------------------------------------------------------------------------------------------- -void CBloomEffect::initBloom() -{ - initBloom(NULL); -} - -void CBloomEffect::initBloom(UTexture *renderTarget) // clientcfg +void CBloomEffect::initBloom() // clientcfg { if(!((CDriverUser *)_Driver)->getDriver()->supportBloomEffect()) return; - m_UserRenderTarget = renderTarget ? dynamic_cast(renderTarget)->getITexture() : NULL; - // don't activate bloom when PolygonMode is different from Filled if (_Driver->getPolygonMode() != UDriver::Filled) return; @@ -355,20 +348,8 @@ void CBloomEffect::initBloom(UTexture *renderTarget) // clientcfg _DisplaySquareBlurMat.getObjectPtr()->setTexture(1, _BlurFinalTex); } } - - // For now the user target must be the window size - // to be compatible with the existing code. - // TODO: Instead, if user render target is provided, - // assume the size of the user render target as - // the screen size to be used. - if (m_UserRenderTarget.getPtr()) - { - nlassert(_WndWidth == m_UserRenderTarget->getWidth()); - nlassert(_WndHeight == m_UserRenderTarget->getHeight()); - _DisplayInitMat.getObjectPtr()->setTexture(0, m_UserRenderTarget); - } - NL3D::CTextureUser txt = (_InitBloomEffect) ? (CTextureUser(m_UserRenderTarget.getPtr() ? m_UserRenderTarget : _InitText)) : (CTextureUser()); + NL3D::CTextureUser txt = (_InitBloomEffect) ? (CTextureUser(_InitText)) : (CTextureUser()); if(!((CDriverUser *) _Driver)->setRenderTarget(txt, 0, 0, _WndWidth, _WndHeight)) { nlwarning("setRenderTarget return false with initial texture for bloom effect\n"); @@ -389,7 +370,7 @@ void CBloomEffect::endBloom() // clientcfg if(_Driver->getWindowWidth()==0 || _Driver->getWindowHeight()==0) return; - CTextureUser txt1 = (_InitBloomEffect) ? (CTextureUser(m_UserRenderTarget.getPtr() ? m_UserRenderTarget : _InitText)) : (CTextureUser()); + CTextureUser txt1 = (_InitBloomEffect) ? (CTextureUser(_InitText)) : (CTextureUser()); CTextureUser txt2(_BlurFinalTex); CRect rect1(0, 0, _WndWidth, _WndHeight); CRect rect2(0, 0, _BlurWidth, _BlurHeight); @@ -416,7 +397,7 @@ void CBloomEffect::applyBlur() // in opengl, display in init texture if(_InitBloomEffect) { - CTextureUser txt(m_UserRenderTarget.getPtr() ? m_UserRenderTarget : _InitText); + CTextureUser txt(_InitText); if(!((CDriverUser *) _Driver)->setRenderTarget(txt, 0, 0, _WndWidth, _WndHeight)) { nlwarning("setRenderTarget return false with initial texture for bloom effect\n"); @@ -478,7 +459,7 @@ void CBloomEffect::endInterfacesDisplayBloom() // clientcfg { // Render from render target to screen if necessary. // Don't do this when the blend was done to the screen or when rendering to a user provided rendertarget. - if (_InitBloomEffect && m_UserRenderTarget.isNull()) + if (_InitBloomEffect) { if(!_Driver->supportBloomEffect() || !_Init) return; @@ -511,15 +492,6 @@ void CBloomEffect::endInterfacesDisplayBloom() // clientcfg _Driver->drawQuad(_DisplayQuad, _DisplayInitMat); _Driver->setMatrixMode3D(pCam); } - - if (m_UserRenderTarget.getPtr()) - { - if (_InitBloomEffect) - { - _DisplayInitMat.getObjectPtr()->setTexture(0, _InitText); - } - m_UserRenderTarget = NULL; - } } From 299b3ec4010b9226fe348b6eba87a750e3b164be Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 3 Jul 2013 03:53:32 +0200 Subject: [PATCH 067/313] Make bloom work together with render target used for the rift shader, ref #43 --- code/nel/include/nel/3d/bloom_effect.h | 2 + code/nel/include/nel/3d/stereo_display.h | 6 +-- code/nel/include/nel/3d/stereo_ovr.h | 4 +- code/nel/src/3d/bloom_effect.cpp | 38 +++++++++++++++---- code/nel/src/3d/stereo_ovr.cpp | 11 ++---- .../client/src/snowballs_client.cpp | 8 +++- 6 files changed, 47 insertions(+), 22 deletions(-) diff --git a/code/nel/include/nel/3d/bloom_effect.h b/code/nel/include/nel/3d/bloom_effect.h index 77673741c..c10967254 100644 --- a/code/nel/include/nel/3d/bloom_effect.h +++ b/code/nel/include/nel/3d/bloom_effect.h @@ -138,6 +138,8 @@ private: NLMISC::CSmartPtr _BlurFinalTex; // used as render target in first blur pass, and as displayed texture on second blur pass. NLMISC::CSmartPtr _BlurHorizontalTex; + // original render target + NLMISC::CSmartPtr _OriginalRenderTarget; // materials diff --git a/code/nel/include/nel/3d/stereo_display.h b/code/nel/include/nel/3d/stereo_display.h index aa98dae50..5819f21eb 100644 --- a/code/nel/include/nel/3d/stereo_display.h +++ b/code/nel/include/nel/3d/stereo_display.h @@ -121,9 +121,9 @@ public: /// 2D Interface virtual bool wantInterface2D() = 0; - /// Returns non-NULL if a new render target was set - virtual UTexture *beginRenderTarget(bool set) = 0; - /// Returns true if a render target was fully drawn + /// Returns true if a new render target was set, always fase if not using render targets + virtual bool beginRenderTarget() = 0; + /// Returns true if a render target was fully drawn, always false if not using render targets virtual bool endRenderTarget() = 0; static const char *getLibraryName(CStereoDeviceInfo::TStereoDeviceLibrary library); diff --git a/code/nel/include/nel/3d/stereo_ovr.h b/code/nel/include/nel/3d/stereo_ovr.h index 857c5368f..be3880bef 100644 --- a/code/nel/include/nel/3d/stereo_ovr.h +++ b/code/nel/include/nel/3d/stereo_ovr.h @@ -109,8 +109,8 @@ public: /// 2D Interface virtual bool wantInterface2D(); - /// Returns non-NULL if a new render target was set, always NULL if not using render targets - virtual UTexture *beginRenderTarget(bool set); + /// Returns true if a new render target was set, always fase if not using render targets + virtual bool beginRenderTarget(); /// Returns true if a render target was fully drawn, always false if not using render targets virtual bool endRenderTarget(); diff --git a/code/nel/src/3d/bloom_effect.cpp b/code/nel/src/3d/bloom_effect.cpp index 927f8688c..3eb24518e 100644 --- a/code/nel/src/3d/bloom_effect.cpp +++ b/code/nel/src/3d/bloom_effect.cpp @@ -285,6 +285,8 @@ void CBloomEffect::initBloom() // clientcfg if(!_Init) init(); + _OriginalRenderTarget = static_cast(_Driver)->getDriver()->getRenderTarget(); + // if window resize, reinitialize textures if(_WndWidth!=_Driver->getWindowWidth() || _WndHeight!=_Driver->getWindowHeight()) { @@ -349,11 +351,14 @@ void CBloomEffect::initBloom() // clientcfg } } - NL3D::CTextureUser txt = (_InitBloomEffect) ? (CTextureUser(_InitText)) : (CTextureUser()); - if(!((CDriverUser *) _Driver)->setRenderTarget(txt, 0, 0, _WndWidth, _WndHeight)) + if (!_OriginalRenderTarget) { - nlwarning("setRenderTarget return false with initial texture for bloom effect\n"); - return; + NL3D::CTextureUser txt = (_InitBloomEffect) ? (CTextureUser(_InitText)) : (CTextureUser()); + if(!(static_cast(_Driver)->setRenderTarget(txt, 0, 0, _WndWidth, _WndHeight))) + { + nlwarning("setRenderTarget return false with initial texture for bloom effect\n"); + return; + } } } @@ -370,7 +375,7 @@ void CBloomEffect::endBloom() // clientcfg if(_Driver->getWindowWidth()==0 || _Driver->getWindowHeight()==0) return; - CTextureUser txt1 = (_InitBloomEffect) ? (CTextureUser(_InitText)) : (CTextureUser()); + CTextureUser txt1 = _OriginalRenderTarget ? CTextureUser(_OriginalRenderTarget) : ((_InitBloomEffect) ? (CTextureUser(_InitText)) : (CTextureUser())); CTextureUser txt2(_BlurFinalTex); CRect rect1(0, 0, _WndWidth, _WndHeight); CRect rect2(0, 0, _BlurWidth, _BlurHeight); @@ -394,15 +399,30 @@ void CBloomEffect::applyBlur() { NL3D::IDriver *drvInternal = ((CDriverUser *) _Driver)->getDriver(); + /*if (_OriginalRenderTarget) + { + CTextureUser txt(_OriginalRenderTarget); + if(!(static_cast(_Driver)->setRenderTarget(txt, 0, 0, _WndWidth, _WndHeight))) + { + nlwarning("setRenderTarget return false with original render target for bloom effect\n"); + return; + } + } // in opengl, display in init texture - if(_InitBloomEffect) + else if(_InitBloomEffect) { CTextureUser txt(_InitText); - if(!((CDriverUser *) _Driver)->setRenderTarget(txt, 0, 0, _WndWidth, _WndHeight)) + if(!(static_cast(_Driver)->setRenderTarget(txt, 0, 0, _WndWidth, _WndHeight))) { nlwarning("setRenderTarget return false with initial texture for bloom effect\n"); return; } + }*/ + CTextureUser txtApply = _OriginalRenderTarget ? CTextureUser(_OriginalRenderTarget) : ((_InitBloomEffect) ? (CTextureUser(_InitText)) : (CTextureUser())); + if(!(static_cast(_Driver)->setRenderTarget(txtApply, 0, 0, _WndWidth, _WndHeight))) + { + nlwarning("setRenderTarget return false with initial texture for bloom effect\n"); + return; } // display blur texture @@ -459,7 +479,7 @@ void CBloomEffect::endInterfacesDisplayBloom() // clientcfg { // Render from render target to screen if necessary. // Don't do this when the blend was done to the screen or when rendering to a user provided rendertarget. - if (_InitBloomEffect) + if ((_OriginalRenderTarget.getPtr() == NULL) && _InitBloomEffect) { if(!_Driver->supportBloomEffect() || !_Init) return; @@ -492,6 +512,8 @@ void CBloomEffect::endInterfacesDisplayBloom() // clientcfg _Driver->drawQuad(_DisplayQuad, _DisplayInitMat); _Driver->setMatrixMode3D(pCam); } + + _OriginalRenderTarget = NULL; } diff --git a/code/nel/src/3d/stereo_ovr.cpp b/code/nel/src/3d/stereo_ovr.cpp index d6fc56cb9..a5397e77f 100644 --- a/code/nel/src/3d/stereo_ovr.cpp +++ b/code/nel/src/3d/stereo_ovr.cpp @@ -488,19 +488,16 @@ bool CStereoOVR::wantInterface2D() /// Returns non-NULL if a new render target was set -UTexture *CStereoOVR::beginRenderTarget(bool set) +bool CStereoOVR::beginRenderTarget() { // render target always set before driver clear // nlassert(m_SubStage <= 1); if (m_Driver && m_Stage == 1) { - if (set) - { - (static_cast(m_Driver))->setRenderTarget(*m_BarrelTexU, 0, 0, 0, 0); - } - return m_BarrelTexU; + static_cast(m_Driver)->setRenderTarget(*m_BarrelTexU, 0, 0, 0, 0); + return true; } - return NULL; + return false; } /// Returns true if a render target was fully drawn diff --git a/code/snowballs2/client/src/snowballs_client.cpp b/code/snowballs2/client/src/snowballs_client.cpp index 2522f48b6..c510af7ec 100644 --- a/code/snowballs2/client/src/snowballs_client.cpp +++ b/code/snowballs2/client/src/snowballs_client.cpp @@ -754,15 +754,19 @@ void loopIngame() StereoDisplay->getCurrentFrustum(0, &SkyCamera); StereoDisplay->getCurrentMatrix(0, &Camera); } + + if (StereoDisplay) + { + StereoDisplay->beginRenderTarget(); + } if (!StereoDisplay || StereoDisplay->wantClear()) { - NL3D::UTexture *rt = StereoDisplay ? StereoDisplay->beginRenderTarget(!s_EnableBloom) : NULL; if (s_EnableBloom) { nlassert(bloomStage == 0); - CBloomEffect::instance().initBloom(/*rt*/); // start bloom effect (just before the first scene element render) + CBloomEffect::instance().initBloom(); // start bloom effect (just before the first scene element render) bloomStage = 1; } From a531535dfa8118cda345706d354ae22c83810f17 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 3 Jul 2013 05:21:32 +0200 Subject: [PATCH 068/313] Add minimal head model and world scale, ref #43 --- code/nel/include/nel/3d/stereo_hmd.h | 14 +++++---- code/nel/include/nel/3d/stereo_ovr.h | 11 +++++++ code/nel/src/3d/stereo_ovr.cpp | 29 +++++++++++++++---- code/snowballs2/client/src/camera.cpp | 5 +++- code/snowballs2/client/src/commands.cpp | 2 +- code/snowballs2/client/src/compass.cpp | 2 +- .../client/src/snowballs_client.cpp | 2 ++ 7 files changed, 52 insertions(+), 13 deletions(-) diff --git a/code/nel/include/nel/3d/stereo_hmd.h b/code/nel/include/nel/3d/stereo_hmd.h index bbb80368e..95c159cfd 100644 --- a/code/nel/include/nel/3d/stereo_hmd.h +++ b/code/nel/include/nel/3d/stereo_hmd.h @@ -52,14 +52,18 @@ public: /// Get the HMD orientation virtual NLMISC::CQuat getOrientation() const = 0; - - /// Set the head model, eye position relative to orientation point - // virtual void setEyePosition(const NLMISC::CVector &v) = 0; - /// Get the head model, eye position relative to orientation point - // virtual const NLMISC::CVector &getEyePosition() const = 0; + /// Get GUI center (1 = width, 1 = height, 0 = center) virtual void getInterface2DShift(uint cid, float &x, float &y, float distance) const = 0; + /// Set the head model, eye position relative to orientation point + virtual void setEyePosition(const NLMISC::CVector &v) = 0; + /// Get the head model, eye position relative to orientation point + virtual const NLMISC::CVector &getEyePosition() const = 0; + + /// Set the scale of the game in units per meter + virtual void setScale(float s) = 0; + }; /* class IStereoHMD */ } /* namespace NL3D */ diff --git a/code/nel/include/nel/3d/stereo_ovr.h b/code/nel/include/nel/3d/stereo_ovr.h index be3880bef..770dd97c1 100644 --- a/code/nel/include/nel/3d/stereo_ovr.h +++ b/code/nel/include/nel/3d/stereo_ovr.h @@ -117,9 +117,18 @@ public: /// Get the HMD orientation virtual NLMISC::CQuat getOrientation() const; + /// Get GUI center (1 = width, 1 = height, 0 = center) virtual void getInterface2DShift(uint cid, float &x, float &y, float distance) const; + /// Set the head model, eye position relative to orientation point + virtual void setEyePosition(const NLMISC::CVector &v); + /// Get the head model, eye position relative to orientation point + virtual const NLMISC::CVector &getEyePosition() const; + + /// Set the scale of the game in units per meter + virtual void setScale(float s); + static void listDevices(std::vector &devicesOut); static bool isLibraryInUse(); @@ -150,6 +159,8 @@ private: NLMISC::CQuadUV m_BarrelQuadLeft; NLMISC::CQuadUV m_BarrelQuadRight; CPixelProgram *m_PixelProgram; + NLMISC::CVector m_EyePosition; + float m_Scale; }; /* class CStereoOVR */ diff --git a/code/nel/src/3d/stereo_ovr.cpp b/code/nel/src/3d/stereo_ovr.cpp index a5397e77f..f3534a10c 100644 --- a/code/nel/src/3d/stereo_ovr.cpp +++ b/code/nel/src/3d/stereo_ovr.cpp @@ -165,7 +165,7 @@ public: OVR::HMDInfo HMDInfo; }; -CStereoOVR::CStereoOVR(const CStereoOVRDeviceHandle *handle) : m_Stage(0), m_SubStage(0), m_OrientationCached(false), m_Driver(NULL), m_BarrelTexU(NULL), m_PixelProgram(NULL) +CStereoOVR::CStereoOVR(const CStereoOVRDeviceHandle *handle) : m_Stage(0), m_SubStage(0), m_OrientationCached(false), m_Driver(NULL), m_BarrelTexU(NULL), m_PixelProgram(NULL), m_EyePosition(0.0f, 0.09f, 0.15f), m_Scale(1.0f) { ++s_DeviceCounter; m_DevicePtr = new CStereoOVRDevicePtr(); @@ -433,8 +433,8 @@ void CStereoOVR::getCurrentFrustum(uint cid, NL3D::UCamera *camera) const void CStereoOVR::getCurrentMatrix(uint cid, NL3D::UCamera *camera) const { CMatrix translate; - if (m_Stage % 2) translate.translate(CVector(m_DevicePtr->HMDInfo.InterpupillaryDistance * -0.5f, 0.f, 0.f)); - else translate.translate(CVector(m_DevicePtr->HMDInfo.InterpupillaryDistance * 0.5f, 0.f, 0.f)); + if (m_Stage % 2) translate.translate(CVector((m_DevicePtr->HMDInfo.InterpupillaryDistance * m_Scale) * -0.5f, 0.f, 0.f)); + else translate.translate(CVector((m_DevicePtr->HMDInfo.InterpupillaryDistance * m_Scale) * 0.5f, 0.f, 0.f)); camera->setTransformMode(NL3D::UTransformable::DirectMatrix); camera->setMatrix(m_CameraMatrix[cid] * translate); } @@ -594,6 +594,8 @@ void CStereoOVR::getInterface2DShift(uint cid, float &x, float &y, float distanc { #if 0 + // todo: take into account m_EyePosition + NLMISC::CVector vector = CVector(0.f, -distance, 0.f); NLMISC::CQuat rot = getOrientation(); rot.invert(); @@ -616,11 +618,12 @@ void CStereoOVR::getInterface2DShift(uint cid, float &x, float &y, float distanc #elif 1 // Alternative method + // todo: take into account m_EyePosition NLMISC::CVector vec = CVector(0.f, -distance, 0.f); NLMISC::CVector ipd; - if (m_Stage % 2) ipd = CVector(m_DevicePtr->HMDInfo.InterpupillaryDistance * -0.5f, 0.f, 0.f); - else ipd = CVector(m_DevicePtr->HMDInfo.InterpupillaryDistance * 0.5f, 0.f, 0.f); + if (m_Stage % 2) ipd = CVector((m_DevicePtr->HMDInfo.InterpupillaryDistance * m_Scale) * -0.5f, 0.f, 0.f); + else ipd = CVector((m_DevicePtr->HMDInfo.InterpupillaryDistance * m_Scale) * 0.5f, 0.f, 0.f); NLMISC::CQuat rot = getOrientation(); @@ -640,6 +643,22 @@ void CStereoOVR::getInterface2DShift(uint cid, float &x, float &y, float distanc #endif } +void CStereoOVR::setEyePosition(const NLMISC::CVector &v) +{ + m_EyePosition = v; +} + +const NLMISC::CVector &CStereoOVR::getEyePosition() const +{ + return m_EyePosition; +} + +void CStereoOVR::setScale(float s) +{ + m_EyePosition = m_EyePosition * (s / m_Scale); + m_Scale = s; +} + void CStereoOVR::listDevices(std::vector &devicesOut) { s_StereoOVRSystem.Init(); diff --git a/code/snowballs2/client/src/camera.cpp b/code/snowballs2/client/src/camera.cpp index 92216fe98..ebb4827ab 100644 --- a/code/snowballs2/client/src/camera.cpp +++ b/code/snowballs2/client/src/camera.cpp @@ -119,6 +119,7 @@ void initCamera() { nlinfo("Stereo display device is a HMD"); StereoHMD = static_cast(StereoDisplay); + StereoHMD->setScale(3.0f); // snowballs is about 4 units per meter } StereoDisplay->setDriver(Driver); // move after driver creation, move stereodisplay before driver creation } @@ -189,7 +190,9 @@ void updateCamera() NLMISC::CMatrix camMatrix = Camera.getMatrix(); NLMISC::CMatrix hmdMatrix; hmdMatrix.setRot(hmdOrient); - Camera.setMatrix(camMatrix * hmdMatrix); + NLMISC::CMatrix posMatrix; // minimal head modeling, will be changed in the future + posMatrix.translate(StereoHMD->getEyePosition()); + Camera.setMatrix((camMatrix * hmdMatrix) * posMatrix); } // Set the new position of the snow emitter CMatrix mat = CMatrix::Identity; diff --git a/code/snowballs2/client/src/commands.cpp b/code/snowballs2/client/src/commands.cpp index e6e7a0086..6d9b23085 100644 --- a/code/snowballs2/client/src/commands.cpp +++ b/code/snowballs2/client/src/commands.cpp @@ -388,7 +388,7 @@ void updateCommands() if (StereoHMD) { float xshift, yshift; - StereoHMD->getInterface2DShift(0, xshift, yshift, 1.f); + StereoHMD->getInterface2DShift(0, xshift, yshift, 4.f); // snap to pixels xshift = ((float)(sint32)(xshift * width)) / width; yshift = ((float)(sint32)(yshift * height)) / height; diff --git a/code/snowballs2/client/src/compass.cpp b/code/snowballs2/client/src/compass.cpp index e01173f66..9cc6fac57 100644 --- a/code/snowballs2/client/src/compass.cpp +++ b/code/snowballs2/client/src/compass.cpp @@ -98,7 +98,7 @@ void updateCompass () if (StereoHMD) { float xshift, yshift; - StereoHMD->getInterface2DShift(0, xshift, yshift, 1.f); + StereoHMD->getInterface2DShift(0, xshift, yshift, 4.f); x += xshift; y += yshift; } diff --git a/code/snowballs2/client/src/snowballs_client.cpp b/code/snowballs2/client/src/snowballs_client.cpp index c510af7ec..ea0bc2648 100644 --- a/code/snowballs2/client/src/snowballs_client.cpp +++ b/code/snowballs2/client/src/snowballs_client.cpp @@ -933,6 +933,8 @@ void renderInformation() TextContext->setColor(CRGBA(255, 255, 255, 255)); TextContext->setFontSize(14); TextContext->printfAt(0.01f, 0.99f, "%.2f(%.2f)fps %.3fs", FramesPerSecondSmooth, FramesPerSecond, (float)LocalTimeDelta); + CVector camPos = Camera.getMatrix().getPos(); + TextContext->printfAt(0.01f, 0.89f, "CAM POS: %.3f %.3f %.3f", camPos.x, camPos.y, camPos.z); // one more frame FpsGraph.addValue(1.0f); From d57b7b39fe3725df3b6d5ae66fb236c7b848d237 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 3 Jul 2013 06:04:37 +0200 Subject: [PATCH 069/313] Use width instead of height of screen for hmd fov, re #43 --- code/nel/src/3d/stereo_ovr.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/nel/src/3d/stereo_ovr.cpp b/code/nel/src/3d/stereo_ovr.cpp index f3534a10c..e8b98c081 100644 --- a/code/nel/src/3d/stereo_ovr.cpp +++ b/code/nel/src/3d/stereo_ovr.cpp @@ -320,7 +320,7 @@ void CStereoOVR::getScreenResolution(uint &width, uint &height) void CStereoOVR::initCamera(uint cid, const NL3D::UCamera *camera) { float ar = (float)m_DevicePtr->HMDInfo.HResolution / ((float)m_DevicePtr->HMDInfo.VResolution * 2.0f); - float fov = 2.0f * atanf((m_DevicePtr->HMDInfo.VScreenSize / 2.0f) / m_DevicePtr->HMDInfo.EyeToScreenDistance); //(float)NLMISC::Pi/2.f; // 2.0f * atanf(m_DevicePtr->HMDInfo.VScreenSize / 2.0f * m_DevicePtr->HMDInfo.EyeToScreenDistance); + float fov = 2.0f * atanf((m_DevicePtr->HMDInfo.HScreenSize * 0.5f * 0.5f) / (m_DevicePtr->HMDInfo.EyeToScreenDistance)); //(float)NLMISC::Pi/2.f; // 2.0f * atanf(m_DevicePtr->HMDInfo.VScreenSize / 2.0f * m_DevicePtr->HMDInfo.EyeToScreenDistance); m_LeftFrustum[cid].initPerspective(fov, ar, camera->getFrustum().Near, camera->getFrustum().Far); m_RightFrustum[cid] = m_LeftFrustum[cid]; From fd1d606154a7d4edfeeea0fbad80866f0dca3110 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Thu, 4 Jul 2013 00:09:10 +0200 Subject: [PATCH 070/313] Add stereo debugger for visually comparing rendered frames, ref #43 --- code/nel/include/nel/3d/stereo_debugger.h | 130 ++++++++ code/nel/include/nel/3d/stereo_display.h | 2 +- code/nel/include/nel/3d/stereo_ovr.h | 2 +- code/nel/src/3d/CMakeLists.txt | 8 +- code/nel/src/3d/stereo_debugger.cpp | 350 ++++++++++++++++++++++ code/nel/src/3d/stereo_display.cpp | 4 + code/nel/src/3d/stereo_ovr.cpp | 4 +- code/nel/src/3d/stereo_ovr_fp.cpp | 1 + code/snowballs2/bin/pp_oculus_vr.cg | 54 ++++ code/snowballs2/bin/pp_stereo_debug.cg | 25 ++ code/snowballs2/client/src/camera.cpp | 2 +- 11 files changed, 576 insertions(+), 6 deletions(-) create mode 100644 code/nel/include/nel/3d/stereo_debugger.h create mode 100644 code/nel/src/3d/stereo_debugger.cpp create mode 100644 code/snowballs2/bin/pp_oculus_vr.cg create mode 100644 code/snowballs2/bin/pp_stereo_debug.cg diff --git a/code/nel/include/nel/3d/stereo_debugger.h b/code/nel/include/nel/3d/stereo_debugger.h new file mode 100644 index 000000000..b94ffaf6b --- /dev/null +++ b/code/nel/include/nel/3d/stereo_debugger.h @@ -0,0 +1,130 @@ +/** + * \file stereo_debugger.h + * \brief CStereoDebugger + * \date 2013-07-03 20:17GMT + * \author Jan Boon (Kaetemi) + * CStereoDebugger + */ + +/* + * Copyright (C) 2013 by authors + * + * This file is part of NL3D. + * NL3D is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * NL3D 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 Affero General + * Public License for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with NL3D. If not, see + * . + */ + +#if !FINAL_VERSION +#ifndef NL3D_STEREO_DEBUGGER_H +#define NL3D_STEREO_DEBUGGER_H +#include + +// STL includes + +// NeL includes +#include +#include + +// Project includes +#include +#include +#include +#include + +#define NL_STEREO_MAX_USER_CAMERAS 8 + +namespace NL3D { + +class ITexture; +class CTextureUser; +class CPixelProgram; + +/** + * \brief CStereoDebugger + * \date 2013-07-03 20:17GMT + * \author Jan Boon (Kaetemi) + * CStereoDebugger + */ +class CStereoDebugger : public IStereoDisplay +{ +public: + CStereoDebugger(); + virtual ~CStereoDebugger(); + + + /// Sets driver and generates necessary render targets + virtual void setDriver(NL3D::UDriver *driver); + + /// Gets the required screen resolution for this device + virtual bool getScreenResolution(uint &width, uint &height); + /// Set latest camera position etcetera + virtual void updateCamera(uint cid, const NL3D::UCamera *camera); + /// Get the frustum to use for clipping + virtual void getClippingFrustum(uint cid, NL3D::UCamera *camera) const; + + /// Is there a next pass + virtual bool nextPass(); + /// Gets the current viewport + virtual const NL3D::CViewport &getCurrentViewport() const; + /// Gets the current camera frustum + virtual const NL3D::CFrustum &getCurrentFrustum(uint cid) const; + /// Gets the current camera frustum + virtual void getCurrentFrustum(uint cid, NL3D::UCamera *camera) const; + /// Gets the current camera matrix + virtual void getCurrentMatrix(uint cid, NL3D::UCamera *camera) const; + + /// At the start of a new render target + virtual bool wantClear(); + /// The 3D scene + virtual bool wantScene(); + /// Interface within the 3D scene + virtual bool wantInterface3D(); + /// 2D Interface + virtual bool wantInterface2D(); + + /// Returns true if a new render target was set, always fase if not using render targets + virtual bool beginRenderTarget(); + /// Returns true if a render target was fully drawn, always false if not using render targets + virtual bool endRenderTarget(); + + + static void listDevices(std::vector &devicesOut); + +private: + UDriver *m_Driver; + + int m_Stage; + int m_SubStage; + + CViewport m_LeftViewport; + CViewport m_RightViewport; + CFrustum m_Frustum[NL_STEREO_MAX_USER_CAMERAS]; + CMatrix m_CameraMatrix[NL_STEREO_MAX_USER_CAMERAS]; + + NLMISC::CSmartPtr m_LeftTex; + NL3D::CTextureUser *m_LeftTexU; + NLMISC::CSmartPtr m_RightTex; + NL3D::CTextureUser *m_RightTexU; + NL3D::UMaterial m_Mat; + NLMISC::CQuadUV m_QuadUV; + CPixelProgram *m_PixelProgram; + +}; /* class CStereoDebugger */ + +} /* namespace NL3D */ + +#endif /* #ifndef NL3D_STEREO_DEBUGGER_H */ +#endif /* #if !FINAL_VERSION */ + +/* end of file */ diff --git a/code/nel/include/nel/3d/stereo_display.h b/code/nel/include/nel/3d/stereo_display.h index 5819f21eb..e3b5b3716 100644 --- a/code/nel/include/nel/3d/stereo_display.h +++ b/code/nel/include/nel/3d/stereo_display.h @@ -95,7 +95,7 @@ public: virtual void setDriver(NL3D::UDriver *driver) = 0; /// Gets the required screen resolution for this device - virtual void getScreenResolution(uint &width, uint &height) = 0; + virtual bool getScreenResolution(uint &width, uint &height) = 0; /// Set latest camera position etcetera virtual void updateCamera(uint cid, const NL3D::UCamera *camera) = 0; /// Get the frustum to use for clipping diff --git a/code/nel/include/nel/3d/stereo_ovr.h b/code/nel/include/nel/3d/stereo_ovr.h index 770dd97c1..3b417b871 100644 --- a/code/nel/include/nel/3d/stereo_ovr.h +++ b/code/nel/include/nel/3d/stereo_ovr.h @@ -83,7 +83,7 @@ public: virtual void setDriver(NL3D::UDriver *driver); /// Gets the required screen resolution for this device - virtual void getScreenResolution(uint &width, uint &height); + virtual bool getScreenResolution(uint &width, uint &height); /// Set latest camera position etcetera virtual void updateCamera(uint cid, const NL3D::UCamera *camera); /// Get the frustum to use for clipping diff --git a/code/nel/src/3d/CMakeLists.txt b/code/nel/src/3d/CMakeLists.txt index da0288561..ffdc876a9 100644 --- a/code/nel/src/3d/CMakeLists.txt +++ b/code/nel/src/3d/CMakeLists.txt @@ -164,7 +164,9 @@ SOURCE_GROUP(Driver FILES vertex_program.cpp ../../include/nel/3d/vertex_program.h vertex_program_parse.cpp - ../../include/nel/3d/vertex_program_parse.h) + ../../include/nel/3d/vertex_program_parse.h + pixel_program.cpp + ../../include/nel/3d/pixel_program.h) SOURCE_GROUP(Font FILES computed_string.cpp @@ -693,7 +695,9 @@ SOURCE_GROUP(Stereo FILES ../../include/nel/3d/stereo_hmd.h stereo_ovr.cpp stereo_ovr_fp.cpp - ../../include/nel/3d/stereo_ovr.h) + ../../include/nel/3d/stereo_ovr.h + stereo_debugger.cpp + ../../include/nel/3d/stereo_debugger.h) NL_TARGET_LIB(nel3d ${HEADERS} ${SRC}) diff --git a/code/nel/src/3d/stereo_debugger.cpp b/code/nel/src/3d/stereo_debugger.cpp new file mode 100644 index 000000000..c24bcdc93 --- /dev/null +++ b/code/nel/src/3d/stereo_debugger.cpp @@ -0,0 +1,350 @@ +/** + * \file stereo_debugger.cpp + * \brief CStereoDebugger + * \date 2013-07-03 20:17GMT + * \author Jan Boon (Kaetemi) + * CStereoDebugger + */ + +/* + * Copyright (C) 2013 by authors + * + * This file is part of NL3D. + * NL3D is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * NL3D 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 Affero General + * Public License for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with NL3D. If not, see + * . + */ + +#if !FINAL_VERSION +#include +#include + +// STL includes + +// NeL includes +// #include + +// Project includes +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +// using namespace NLMISC; + +namespace NL3D { + +namespace { + +const char *a_arbfp1 = + "!!ARBfp1.0\n" + "PARAM c[1] = { { 1, 0, 0.5 } };\n" + "TEMP R0;\n" + "TEMP R1;\n" + "TEMP R2;\n" + "TEX R0, fragment.texcoord[0], texture[0], 2D;\n" + "TEX R1, fragment.texcoord[0], texture[1], 2D;\n" + "ADD R2, R0, -R1;\n" + "ADD R1, R0, R1;\n" + "MUL R1, R1, c[0].z;\n" + "ABS R2, R2;\n" + "CMP R2, -R2, c[0].x, c[0].y;\n" + "ADD_SAT R2.x, R2, R2.y;\n" + "ADD_SAT R2.x, R2, R2.z;\n" + "ADD_SAT R2.x, R2, R2.w;\n" + "ABS R2.x, R2;\n" + "CMP R2.x, -R2, c[0].y, c[0];\n" + "ABS R0.x, R2;\n" + "CMP R2.x, -R0, c[0].y, c[0];\n" + "MOV R0.xzw, R1;\n" + "MAD R0.y, R1, c[0].z, c[0].z;\n" + "CMP R0, -R2.x, R1, R0;\n" + "MAD R1.x, R0, c[0].z, c[0].z;\n" + "CMP result.color.x, -R2, R1, R0;\n" + "MOV result.color.yzw, R0;\n" + "END\n"; + +class CStereoDebuggerFactory : public IStereoDeviceFactory +{ +public: + IStereoDisplay *createDevice() const + { + return new CStereoDebugger(); + } +}; + +} /* anonymous namespace */ + +CStereoDebugger::CStereoDebugger() : m_Driver(NULL), m_Stage(0), m_SubStage(0), m_LeftTexU(NULL), m_RightTexU(NULL), m_PixelProgram(NULL) +{ + +} + +CStereoDebugger::~CStereoDebugger() +{ + if (!m_Mat.empty()) + { + m_Mat.getObjectPtr()->setTexture(0, NULL); + m_Mat.getObjectPtr()->setTexture(1, NULL); + m_Driver->deleteMaterial(m_Mat); + } + + delete m_LeftTexU; + m_LeftTexU = NULL; + m_LeftTex = NULL; // CSmartPtr + + delete m_RightTexU; + m_RightTexU = NULL; + m_RightTex = NULL; // CSmartPtr + + delete m_PixelProgram; + m_PixelProgram = NULL; + + m_Driver = NULL; +} + +/// Sets driver and generates necessary render targets +void CStereoDebugger::setDriver(NL3D::UDriver *driver) +{ + nlassert(!m_PixelProgram); + + NL3D::IDriver *drvInternal = (static_cast(driver))->getDriver(); + /*if (drvInternal->supportPixelProgram(CPixelProgram::fp40) && drvInternal->supportBloomEffect() && drvInternal->supportNonPowerOfTwoTextures()) + { + nldebug("VR: fp40"); + m_PixelProgram = new CPixelProgram(a_fp40); + } + else*/ if (drvInternal->supportPixelProgram(CPixelProgram::arbfp1) && drvInternal->supportBloomEffect() && drvInternal->supportNonPowerOfTwoTextures()) + { + nldebug("VR: arbfp1"); + m_PixelProgram = new CPixelProgram(a_arbfp1); + } + /*else if (drvInternal->supportPixelProgram(CPixelProgram::ps_2_0)) + { + nldebug("VR: ps_2_0"); + m_PixelProgram = new CPixelProgram(a_ps_2_0); + }*/ + + if (m_PixelProgram) + { + m_Driver = driver; + + // todo: handle reso change! + + uint32 width, height; + driver->getWindowSize(width, height); + + m_LeftTex = new CTextureBloom(); + m_LeftTex->setRenderTarget(true); + m_LeftTex->setReleasable(false); + m_LeftTex->resize(width, height); + m_LeftTex->setFilterMode(ITexture::Linear, ITexture::LinearMipMapOff); + m_LeftTex->setWrapS(ITexture::Clamp); + m_LeftTex->setWrapT(ITexture::Clamp); + drvInternal->setupTexture(*m_LeftTex); + m_LeftTexU = new CTextureUser(m_LeftTex); + nlassert(!drvInternal->isTextureRectangle(m_LeftTex)); // not allowed + + m_RightTex = new CTextureBloom(); + m_RightTex->setRenderTarget(true); + m_RightTex->setReleasable(false); + m_RightTex->resize(width, height); + m_RightTex->setFilterMode(ITexture::Linear, ITexture::LinearMipMapOff); + m_RightTex->setWrapS(ITexture::Clamp); + m_RightTex->setWrapT(ITexture::Clamp); + drvInternal->setupTexture(*m_RightTex); + m_RightTexU = new CTextureUser(m_RightTex); + nlassert(!drvInternal->isTextureRectangle(m_RightTex)); // not allowed + + + m_Mat = m_Driver->createMaterial(); + m_Mat.initUnlit(); + m_Mat.setColor(CRGBA::White); + m_Mat.setBlend (false); + m_Mat.setAlphaTest (false); + NL3D::CMaterial *mat = m_Mat.getObjectPtr(); + mat->setShader(NL3D::CMaterial::PostProcessing); + mat->setBlendFunc(CMaterial::one, CMaterial::zero); + mat->setZWrite(false); + mat->setZFunc(CMaterial::always); + mat->setDoubleSided(true); + mat->setTexture(0, m_LeftTex); + mat->setTexture(1, m_RightTex); + + + m_QuadUV.V0 = CVector(0.f, 0.f, 0.5f); + m_QuadUV.V1 = CVector(1.f, 0.f, 0.5f); + m_QuadUV.V2 = CVector(1.f, 1.f, 0.5f); + m_QuadUV.V3 = CVector(0.f, 1.f, 0.5f); + + m_QuadUV.Uv0 = CUV(0.f, 0.f); + m_QuadUV.Uv1 = CUV(1.f, 0.f); + m_QuadUV.Uv2 = CUV(1.f, 1.f); + m_QuadUV.Uv3 = CUV(0.f, 1.f); + } +} + +/// Gets the required screen resolution for this device +bool CStereoDebugger::getScreenResolution(uint &width, uint &height) +{ + return false; +} + +/// Set latest camera position etcetera +void CStereoDebugger::updateCamera(uint cid, const NL3D::UCamera *camera) +{ + m_Frustum[cid] = camera->getFrustum(); +} + +/// Get the frustum to use for clipping +void CStereoDebugger::getClippingFrustum(uint cid, NL3D::UCamera *camera) const +{ + // do nothing +} + +/// Is there a next pass +bool CStereoDebugger::nextPass() +{ + switch (m_Stage) + { + case 0: + ++m_Stage; + m_SubStage = 0; + return true; + case 1: + ++m_Stage; + m_SubStage = 0; + return true; + case 2: + m_Stage = 0; + m_SubStage = 0; + return false; + } +} + +/// Gets the current viewport +const NL3D::CViewport &CStereoDebugger::getCurrentViewport() const +{ + if (m_Stage % 2) return m_LeftViewport; + else return m_RightViewport; +} + +/// Gets the current camera frustum +const NL3D::CFrustum &CStereoDebugger::getCurrentFrustum(uint cid) const +{ + return m_Frustum[cid]; +} + +/// Gets the current camera frustum +void CStereoDebugger::getCurrentFrustum(uint cid, NL3D::UCamera *camera) const +{ + // do nothing +} + +/// Gets the current camera matrix +void CStereoDebugger::getCurrentMatrix(uint cid, NL3D::UCamera *camera) const +{ + // do nothing +} + +/// At the start of a new render target +bool CStereoDebugger::wantClear() +{ + m_SubStage = 1; + return true; +} + +/// The 3D scene +bool CStereoDebugger::wantScene() +{ + m_SubStage = 2; + return true; +} + +/// Interface within the 3D scene +bool CStereoDebugger::wantInterface3D() +{ + m_SubStage = 3; + return true; +} + +/// 2D Interface +bool CStereoDebugger::wantInterface2D() +{ + m_SubStage = 4; + return true; +} + +/// Returns true if a new render target was set, always fase if not using render targets +bool CStereoDebugger::beginRenderTarget() +{ + if (m_Driver) + { + if (m_Stage % 2) static_cast(m_Driver)->setRenderTarget(*m_RightTexU, 0, 0, 0, 0); + else static_cast(m_Driver)->setRenderTarget(*m_LeftTexU, 0, 0, 0, 0); + return true; + } + return false; +} + +/// Returns true if a render target was fully drawn, always false if not using render targets +bool CStereoDebugger::endRenderTarget() +{ + if (m_Driver) + { + CTextureUser cu; + (static_cast(m_Driver))->setRenderTarget(cu); + bool fogEnabled = m_Driver->fogEnabled(); + m_Driver->enableFog(false); + + m_Driver->setMatrixMode2D11(); + CViewport vp = CViewport(); + m_Driver->setViewport(vp); + uint32 width, height; + NL3D::IDriver *drvInternal = (static_cast(m_Driver))->getDriver(); + NL3D::CMaterial *mat = m_Mat.getObjectPtr(); + mat->setTexture(0, m_LeftTex); + mat->setTexture(1, m_RightTex); + drvInternal->activePixelProgram(m_PixelProgram); + + m_Driver->drawQuad(m_QuadUV, m_Mat); + + drvInternal->activePixelProgram(NULL); + m_Driver->enableFog(fogEnabled); + + return true; + } + return false; +} + +void CStereoDebugger::listDevices(std::vector &devicesOut) +{ + CStereoDeviceInfo devInfo; + devInfo.Factory = new CStereoDebuggerFactory(); + devInfo.Library = CStereoDeviceInfo::NeL3D; + devInfo.Class = CStereoDeviceInfo::StereoDisplay; + devInfo.Manufacturer = "NeL"; + devInfo.ProductName = "Stereo Debugger"; + devInfo.Serial = "NL-3D-DEBUG"; + devicesOut.push_back(devInfo); +} + +} /* namespace NL3D */ + +#endif /* #if !FINAL_VERSION */ + +/* end of file */ diff --git a/code/nel/src/3d/stereo_display.cpp b/code/nel/src/3d/stereo_display.cpp index 09502a256..d2a8fd932 100644 --- a/code/nel/src/3d/stereo_display.cpp +++ b/code/nel/src/3d/stereo_display.cpp @@ -35,6 +35,7 @@ // Project includes #include +#include using namespace std; // using namespace NLMISC; @@ -75,6 +76,9 @@ const char *IStereoDisplay::getLibraryName(CStereoDeviceInfo::TStereoDeviceLibra void IStereoDisplay::listDevices(std::vector &devicesOut) { CStereoOVR::listDevices(devicesOut); +#if !FINAL_VERSION + CStereoDebugger::listDevices(devicesOut); +#endif } IStereoDisplay *IStereoDisplay::createDevice(const CStereoDeviceInfo &deviceInfo) diff --git a/code/nel/src/3d/stereo_ovr.cpp b/code/nel/src/3d/stereo_ovr.cpp index e8b98c081..ba03990b1 100644 --- a/code/nel/src/3d/stereo_ovr.cpp +++ b/code/nel/src/3d/stereo_ovr.cpp @@ -145,6 +145,7 @@ sint s_DeviceCounter = 0; class CStereoOVRDeviceHandle : public IStereoDeviceFactory { public: + // fixme: virtual destructor??? OVR::DeviceEnumerator DeviceHandle; IStereoDisplay *createDevice() const { @@ -311,10 +312,11 @@ void CStereoOVR::setDriver(NL3D::UDriver *driver) } } -void CStereoOVR::getScreenResolution(uint &width, uint &height) +bool CStereoOVR::getScreenResolution(uint &width, uint &height) { width = m_DevicePtr->HMDInfo.HResolution; height = m_DevicePtr->HMDInfo.VResolution; + return true; } void CStereoOVR::initCamera(uint cid, const NL3D::UCamera *camera) diff --git a/code/nel/src/3d/stereo_ovr_fp.cpp b/code/nel/src/3d/stereo_ovr_fp.cpp index 46fe1451b..b81ee8421 100644 --- a/code/nel/src/3d/stereo_ovr_fp.cpp +++ b/code/nel/src/3d/stereo_ovr_fp.cpp @@ -3,6 +3,7 @@ Filename : stereo_ovf_fp.cpp Content : Barrel fragment program compiled to a blob of assembly Created : July 01, 2013 +Modified by : Jan Boon (Kaetemi) Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. diff --git a/code/snowballs2/bin/pp_oculus_vr.cg b/code/snowballs2/bin/pp_oculus_vr.cg new file mode 100644 index 000000000..1d84e0d54 --- /dev/null +++ b/code/snowballs2/bin/pp_oculus_vr.cg @@ -0,0 +1,54 @@ +/************************************************************************************ + +Filename : pp_oculus_vr.cg +Content : Barrel fragment program ported from Oculus SDK Samples to Cg +Created : July 01, 2013 +Modified by : Jan Boon (Kaetemi) + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +************************************************************************************/ + + +void pp_oculus_vr( + // Per fragment parameters + float2 texCoord : TEXCOORD0, + + // Fragment program constants + uniform float2 cLensCenter, + uniform float2 cScreenCenter, + uniform float2 cScale, + uniform float2 cScaleIn, + uniform float4 cHmdWarpParam, + uniform sampler2D cTex0 : TEX0, + + // Output color + out float4 oCol : COLOR) +{ + float2 theta = (texCoord - cLensCenter) * cScaleIn; // Scales to [-1, 1] + float rSq = theta.x * theta.x + theta.y * theta.y; + float2 theta1 = theta * (cHmdWarpParam.x + cHmdWarpParam.y * rSq + + cHmdWarpParam.z * rSq * rSq + cHmdWarpParam.w * rSq * rSq * rSq); + float2 tc = cLensCenter + cScale * theta1; + + if (!all(equal(clamp(tc, cScreenCenter-float2(0.25,0.5), cScreenCenter+float2(0.25,0.5)), tc))) + { + oCol = float4(0, 0, 0, 0); + } + else + { + oCol = tex2D(cTex0, tc); + } +} diff --git a/code/snowballs2/bin/pp_stereo_debug.cg b/code/snowballs2/bin/pp_stereo_debug.cg new file mode 100644 index 000000000..3af27864d --- /dev/null +++ b/code/snowballs2/bin/pp_stereo_debug.cg @@ -0,0 +1,25 @@ + +void pp_stereo_debug( + // Per fragment parameters + float2 texCoord : TEXCOORD0, + + // Fragment program constants + uniform sampler2D cTex0 : TEX0, + uniform sampler2D cTex1 : TEX1, + + // Output color + out float4 oCol : COLOR) +{ + float4 t1 = tex2D(cTex0, texCoord); + float4 t2 = tex2D(cTex1, texCoord); + if (!any(t1 - t2)) + { + oCol = (t1 * 0.5) + (t2 * 0.5); + oCol.g = 0.5 + (oCol.g * 0.5); + } + else + { + oCol = (t1 * 0.5) + (t2 * 0.5); + oCol.r = 0.5 + (oCol.r * 0.5); + } +} diff --git a/code/snowballs2/client/src/camera.cpp b/code/snowballs2/client/src/camera.cpp index ebb4827ab..42c3b0e4d 100644 --- a/code/snowballs2/client/src/camera.cpp +++ b/code/snowballs2/client/src/camera.cpp @@ -124,8 +124,8 @@ void initCamera() StereoDisplay->setDriver(Driver); // move after driver creation, move stereodisplay before driver creation } } - IStereoDisplay::releaseUnusedLibraries(); } + IStereoDisplay::releaseUnusedLibraries(); // Set up directly the camera Camera = Scene->getCam(); From 4b2ea661cca84234f7e29153461653aa934cce54 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Thu, 4 Jul 2013 00:42:06 +0200 Subject: [PATCH 071/313] Fix particles being animated twice in stereo render, see #43 --- code/nel/include/nel/3d/scene.h | 3 ++- code/nel/src/3d/scene.cpp | 14 +++++++++++++- code/snowballs2/client/src/camera.cpp | 4 ++-- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/code/nel/include/nel/3d/scene.h b/code/nel/include/nel/3d/scene.h index 1d54ce7a6..e0648ebd3 100644 --- a/code/nel/include/nel/3d/scene.h +++ b/code/nel/include/nel/3d/scene.h @@ -826,7 +826,8 @@ private: void flushSSSModelRequests(); // common vb for water display CVertexBuffer _WaterVB; - + + bool _RequestParticlesAnimate; }; diff --git a/code/nel/src/3d/scene.cpp b/code/nel/src/3d/scene.cpp index f257c7206..76761bc71 100644 --- a/code/nel/src/3d/scene.cpp +++ b/code/nel/src/3d/scene.cpp @@ -191,6 +191,8 @@ CScene::CScene(bool bSmallScene) : LightTrav(bSmallScene) _WaterEnvMap = NULL; _GlobalSystemTime= 0.0; + + _RequestParticlesAnimate = false; } // *************************************************************************** void CScene::release() @@ -381,6 +383,9 @@ void CScene::endPartRender() drv->activeVertexProgram(NULL); drv->activePixelProgram(NULL); + // Ensure nothing animates on subsequent renders + _EllapsedTime = 0.f; + /* uint64 total = PSStatsRegisterPSModelObserver + PSStatsRemovePSModelObserver + @@ -617,7 +622,11 @@ void CScene::renderPart(UScene::TRenderPart rp, bool doHrcPass) // loadBalance LoadBalancingTrav.traverse(); // - _ParticleSystemManager.processAnimate(_EllapsedTime); // deals with permanently animated particle systems + if (_RequestParticlesAnimate) + { + _ParticleSystemManager.processAnimate(_EllapsedTime); // deals with permanently animated particle systems + _RequestParticlesAnimate = false; + } // Light LightTrav.traverse(); } @@ -863,6 +872,9 @@ void CScene::animate( TGlobalAnimationTime atTime ) // Rendered part are invalidate _RenderedPart = UScene::RenderNothing; + + // Particles are animated later due to dependencies + _RequestParticlesAnimate = true; } diff --git a/code/snowballs2/client/src/camera.cpp b/code/snowballs2/client/src/camera.cpp index 42c3b0e4d..28ce273bb 100644 --- a/code/snowballs2/client/src/camera.cpp +++ b/code/snowballs2/client/src/camera.cpp @@ -219,7 +219,8 @@ void releaseSky() // -- -- random note: update and render makes more sense than animate and update void animateSky(double dt) { - if (!StereoDisplay) Clouds->anim(dt); + if (!StereoHMD) Clouds->anim(dt); + SkyScene->animate(AnimationTime); } // this is actually render @@ -232,7 +233,6 @@ void updateSky() skyCameraMatrix.setPos(CVector::Null); SkyCamera.setMatrix(skyCameraMatrix); - SkyScene->animate(AnimationTime); SkyScene->render(); // Must clear ZBuffer For incoming rendering. Driver->clearZBuffer(); From 4402fbe1c265d8daf79a6ab11421e560b8e4bda1 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Thu, 4 Jul 2013 20:43:49 +0200 Subject: [PATCH 072/313] Correctly synchronize some more animation in snowballs, re #43 --- code/snowballs2/client/src/animation.cpp | 8 ++++---- code/snowballs2/client/src/entities.cpp | 10 +++++----- code/snowballs2/client/src/entities.h | 2 +- code/snowballs2/client/src/snowballs_client.cpp | 13 ++++++------- 4 files changed, 16 insertions(+), 17 deletions(-) diff --git a/code/snowballs2/client/src/animation.cpp b/code/snowballs2/client/src/animation.cpp index ab873ffbc..dd2296e25 100644 --- a/code/snowballs2/client/src/animation.cpp +++ b/code/snowballs2/client/src/animation.cpp @@ -97,7 +97,7 @@ Anim AnimIdArray[][2] = void computeAnimation (CEntity &entity, EAnim anim) { // Get the current time - double currentTime = double (CTime::getLocalTime ())/1000.0f; + double currentTime = AnimationTime; // nlinfo ("%d playing animation", anim); // nlinfo ("%d playing animation %s ct%f st%f et%f", anim, AnimIdArray[anim][0].Name, currentTime, AnimIdArray[anim][0].Animation->getBeginTime (), AnimIdArray[anim][0].Animation->getEndTime ()); @@ -153,7 +153,7 @@ void playAnimation (CEntity &entity, EAnim anim, bool force) // nlinfo ("playAnimation() %d", anim); // Get the current time - CAnimationTime currentTime = CAnimationTime(CTime::getLocalTime ())/1000.0f; + CAnimationTime currentTime = AnimationTime; // Can't do animation without skeleton if (entity.Skeleton.empty()) @@ -224,7 +224,7 @@ void initAnimation() void updateAnimation() { // Get the current time - CAnimationTime currentTime = CAnimationTime(CTime::getLocalTime ())/1000.0f; + CAnimationTime currentTime = AnimationTime; for (EIT eit = Entities.begin (); eit != Entities.end (); eit++) { @@ -270,7 +270,7 @@ void updateAnimation() } // compute new animation position depending of the current time - PlayListManager->animate (double(CTime::getLocalTime ())/1000.0f); + PlayListManager->animate (AnimationTime); } void releaseAnimation() diff --git a/code/snowballs2/client/src/entities.cpp b/code/snowballs2/client/src/entities.cpp index af879958c..2972aebf1 100644 --- a/code/snowballs2/client/src/entities.cpp +++ b/code/snowballs2/client/src/entities.cpp @@ -105,7 +105,7 @@ bool _TestCLS = false; void CEntity::setState (TState state) { State = state; - StateStartTime = CTime::getLocalTime (); + StateStartTime = LocalTime; } @@ -376,7 +376,7 @@ void deleteAllEntities() void stateAppear (CEntity &entity) { // after 1 second, show the instance - if (CTime::getLocalTime () > entity.StateStartTime + 1000) + if (LocalTime > entity.StateStartTime + 1.0) { if (entity.Instance.getVisibility () != UTransform::Show) entity.Instance.show (); @@ -384,7 +384,7 @@ void stateAppear (CEntity &entity) // after 5 seconds, delete the particle system (if any) // and pass the entity into the Normal state - if (CTime::getLocalTime () > entity.StateStartTime + 3000) + if (LocalTime > entity.StateStartTime + 3.0) { if (!entity.Particule.empty()) { @@ -403,7 +403,7 @@ void stateAppear (CEntity &entity) void stateDisappear (CEntity &entity) { // after 1 second, remove the mesh and all collision stuff - if (CTime::getLocalTime () > entity.StateStartTime + 1000) + if (LocalTime > entity.StateStartTime + 1.0) { if (entity.Instance.getVisibility () != UTransform::Hide) { @@ -419,7 +419,7 @@ void stateDisappear (CEntity &entity) } // after 5 seconds, remove the particle system and the entity entry - if (CTime::getLocalTime () > entity.StateStartTime + 3000) + if (LocalTime > entity.StateStartTime + 3.0) { deleteEntity (entity); } diff --git a/code/snowballs2/client/src/entities.h b/code/snowballs2/client/src/entities.h index 0ad7819ee..e85ffc569 100644 --- a/code/snowballs2/client/src/entities.h +++ b/code/snowballs2/client/src/entities.h @@ -108,7 +108,7 @@ public: // The state of this entity TState State; // The date of the beginning of this state - NLMISC::TTime StateStartTime; + NLMISC::TLocalTime StateStartTime; // The type enum of the entity enum TType { Self, Other, Snowball }; diff --git a/code/snowballs2/client/src/snowballs_client.cpp b/code/snowballs2/client/src/snowballs_client.cpp index ea0bc2648..6c3630a8b 100644 --- a/code/snowballs2/client/src/snowballs_client.cpp +++ b/code/snowballs2/client/src/snowballs_client.cpp @@ -691,14 +691,14 @@ void loopIngame() CGameTime::updateTime(); CGameTime::advanceTime(1.0); - // 03. Update Input (keyboard controls, etc) + // 03. Update Incoming (network, receive messages) + updateNetwork(); + + // 04. Update Input (keyboard controls, etc) Driver->EventServer.pump(); // Pump user input messages MouseListener->update(); MouseListener->updateCamera(); - // 04. Update Incoming (network, receive messages) - updateNetwork(); - // 05. Update Weather (sky, snow, wind, fog, sun) animateSky(LocalTimeDelta); @@ -706,7 +706,7 @@ void loopIngame() updateLandscape(); // Update the landscape // ... Update Animations (TEST) - // ... + updateAnimation(); // 07. Update Entities (collisions and actions) // - Move Other Entities (move//, animations, etc) @@ -783,7 +783,7 @@ void loopIngame() Scene->render(); // Render // 05. Render Effects (flare) - if (!StereoDisplay) updateLensFlare(); // Render the lens flare (left eye stretched with stereo...) + if (!StereoHMD) updateLensFlare(); // Render the lens flare (left eye stretched with stereo...) } if (!StereoDisplay || StereoDisplay->wantInterface3D()) @@ -817,7 +817,6 @@ void loopIngame() updateRadar(); // Update the radar updateGraph(); // Update the radar if (ShowCommands) updateCommands(); // Update the commands panel - updateAnimation(); renderEntitiesNames(); // Render the name on top of the other players updateInterface(); // Update interface renderInformation(); From 6113b07673e511f329cc8b481f86a49d608bc075 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Thu, 4 Jul 2013 21:12:11 +0200 Subject: [PATCH 073/313] Distort 2D gui as well, ref #43 --- code/nel/src/3d/stereo_ovr.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/code/nel/src/3d/stereo_ovr.cpp b/code/nel/src/3d/stereo_ovr.cpp index ba03990b1..e58e82892 100644 --- a/code/nel/src/3d/stereo_ovr.cpp +++ b/code/nel/src/3d/stereo_ovr.cpp @@ -507,7 +507,7 @@ bool CStereoOVR::endRenderTarget() { // after rendering of course // nlassert(m_SubStage > 1); - if (m_Driver && m_Stage == 4) + if (m_Driver && m_Stage == 6) // set to 4 to turn off distortion of 2d gui { CTextureUser cu; (static_cast(m_Driver))->setRenderTarget(cu); @@ -606,13 +606,13 @@ void CStereoOVR::getInterface2DShift(uint cid, float &x, float &y, float distanc //if (m_Stage % 2) mat.translate(CVector(m_DevicePtr->HMDInfo.InterpupillaryDistance * -0.5f, 0.f, 0.f)); //else mat.translate(CVector(m_DevicePtr->HMDInfo.InterpupillaryDistance * 0.5f, 0.f, 0.f)); mat.translate(vector); - CVector proj = CStereoOVR::getCurrentFrustum().project(mat.getPos()); + CVector proj = CStereoOVR::getCurrentFrustum(cid).project(mat.getPos()); NLMISC::CVector ipd; if (m_Stage % 2) ipd = CVector(m_DevicePtr->HMDInfo.InterpupillaryDistance * -0.5f, 0.f, 0.f); else ipd = CVector(m_DevicePtr->HMDInfo.InterpupillaryDistance * 0.5f, 0.f, 0.f); - CVector projipd = CStereoOVR::getCurrentFrustum().project(vector + ipd); - CVector projvec = CStereoOVR::getCurrentFrustum().project(vector); + CVector projipd = CStereoOVR::getCurrentFrustum(cid).project(vector + ipd); + CVector projvec = CStereoOVR::getCurrentFrustum(cid).project(vector); x = (proj.x + projipd.x - projvec.x - 0.5f); y = (proj.y + projipd.y - projvec.y - 0.5f); From ee227aaf9cad281846d3874ae38597b06df3ae2a Mon Sep 17 00:00:00 2001 From: kaetemi Date: Thu, 4 Jul 2013 22:18:19 +0200 Subject: [PATCH 074/313] Refactor fulldetail override --- .../src/interface_v3/action_handler_misc.cpp | 2 +- code/ryzom/client/src/landscape_poly_drawer.h | 6 +- code/ryzom/client/src/main_loop.cpp | 93 ++++++++++++------- code/ryzom/client/src/main_loop.h | 3 +- 4 files changed, 66 insertions(+), 38 deletions(-) diff --git a/code/ryzom/client/src/interface_v3/action_handler_misc.cpp b/code/ryzom/client/src/interface_v3/action_handler_misc.cpp index 6a21858f5..7a68f7872 100644 --- a/code/ryzom/client/src/interface_v3/action_handler_misc.cpp +++ b/code/ryzom/client/src/interface_v3/action_handler_misc.cpp @@ -542,7 +542,7 @@ void renderSceneScreenShot (uint left, uint right, uint top, uint bottom, uint s CCameraBackup cbScene = setupCameraForScreenshot(*Scene, left, right, top, bottom, screenShotWidth, screenShotHeight); CCameraBackup cbCanopy = setupCameraForScreenshot(*SceneRoot, left, right, top, bottom, screenShotWidth, screenShotHeight); // sky setup are copied from main scene before rendering so no setup done here - renderAll(ClientCfg.ScreenShotFullDetail); + renderScene(ClientCfg.ScreenShotFullDetail); restoreCamera(*Scene, cbScene); restoreCamera(*SceneRoot, cbCanopy); } diff --git a/code/ryzom/client/src/landscape_poly_drawer.h b/code/ryzom/client/src/landscape_poly_drawer.h index 0012b5dcb..73d7fcadc 100644 --- a/code/ryzom/client/src/landscape_poly_drawer.h +++ b/code/ryzom/client/src/landscape_poly_drawer.h @@ -95,11 +95,11 @@ public: private: - // renderAll is called in main loop. It can called beginRenderLandscapePolyPart and renderLandscapePolyPart + // renderScene is called in main loop. It can called beginRenderLandscapePolyPart and renderLandscapePolyPart // methods. - friend void renderAll(bool); + friend void renderScene(); - // Enable stencil test and initialize function and operation of stencil at the beginning of renderAll method, + // Enable stencil test and initialize function and operation of stencil at the beginning of renderScene method, // before opaque render of canopy and main scene parts. // The eighth bit will be written with a 0 during next render to mark stencil buffer parts which will // support Shadow Volume algorithm. diff --git a/code/ryzom/client/src/main_loop.cpp b/code/ryzom/client/src/main_loop.cpp index 7338493c1..6e11d3a2a 100644 --- a/code/ryzom/client/src/main_loop.cpp +++ b/code/ryzom/client/src/main_loop.cpp @@ -487,10 +487,55 @@ static void renderSkyPart(UScene::TRenderPart renderPart, TSkyMode skyMode) #endif } +struct CForceFullDetail +{ +public: + void backup() + { + maxFullDetailChar = Scene->getMaxSkeletonsInNotCLodForm(); + oldBalancingMode = Scene->getPolygonBalancingMode(); + oldSkyBalancingMode = UScene::PolygonBalancingOff; + UScene *skyScene = getSkyScene(); + if (skyScene) oldSkyBalancingMode = skyScene->getPolygonBalancingMode(); + } + void set() + { + Scene->setMaxSkeletonsInNotCLodForm(1000000); + Scene->setPolygonBalancingMode(UScene::PolygonBalancingOff); + UScene *skyScene = getSkyScene(); + if (skyScene) skyScene->setPolygonBalancingMode(UScene::PolygonBalancingOff); + } + void restore() + { + Scene->setMaxSkeletonsInNotCLodForm(maxFullDetailChar); + Scene->setPolygonBalancingMode(oldBalancingMode); + UScene *skyScene = getSkyScene(); + if (skyScene) skyScene->setPolygonBalancingMode(oldSkyBalancingMode); + } +private: + uint maxFullDetailChar; + UScene::TPolygonBalancingMode oldBalancingMode; + UScene::TPolygonBalancingMode oldSkyBalancingMode; +}; +static CForceFullDetail s_ForceFullDetail; + +void renderScene(bool forceFullDetail) +{ + if (forceFullDetail) + { + s_ForceFullDetail.backup(); + s_ForceFullDetail.set(); + } + renderScene(); + if (forceFullDetail) + { + s_ForceFullDetail.restore(); + } +} // *************************************************************************************************************************** // Render all scenes -void renderAll(bool forceFullDetail) +void renderScene() { if (ClientCfg.Bloom) { @@ -501,26 +546,6 @@ void renderAll(bool forceFullDetail) CBloomEffect::getInstance().initBloom(); } - // backup old balancing mode - uint maxFullDetailChar = Scene->getMaxSkeletonsInNotCLodForm(); - UScene *skyScene = getSkyScene(); - UScene::TPolygonBalancingMode oldBalancingMode = Scene->getPolygonBalancingMode(); - UScene::TPolygonBalancingMode oldSkyBalancingMode = UScene::PolygonBalancingOff; - if (skyScene) - { - oldSkyBalancingMode = skyScene->getPolygonBalancingMode(); - } - // disable load balancing for that frame only if asked - if (forceFullDetail) - { - Scene->setMaxSkeletonsInNotCLodForm(1000000); - Scene->setPolygonBalancingMode(UScene::PolygonBalancingOff); - if (skyScene) - { - skyScene->setPolygonBalancingMode(UScene::PolygonBalancingOff); - } - } - { H_AUTO_USE ( RZ_Client_Main_Loop_Sky_And_Weather ) @@ -649,17 +674,6 @@ void renderAll(bool forceFullDetail) // reset depth range Driver->setDepthRange(0.f, CANOPY_DEPTH_RANGE_START); - // restore load balancing mode - if (forceFullDetail) - { - Scene->setMaxSkeletonsInNotCLodForm(maxFullDetailChar); - Scene->setPolygonBalancingMode(oldBalancingMode); - if (skyScene) - { - skyScene->setPolygonBalancingMode(oldSkyBalancingMode); - } - } - // apply bloom effect if (ClientCfg.Bloom) CBloomEffect::getInstance().endBloom(); @@ -1524,7 +1538,20 @@ bool mainLoop() Scene->updateWaterEnvMaps(TimeInSec - FirstTimeInSec); } #endif - renderAll(ScreenshotRequest != ScreenshotRequestNone && ClientCfg.ScreenShotFullDetail); // nb : force full detail if a screenshot is asked + + // nb : force full detail if a screenshot is asked + // todo : move outside render code + bool fullDetail = ScreenshotRequest != ScreenshotRequestNone && ClientCfg.ScreenShotFullDetail; + if (fullDetail) + { + s_ForceFullDetail.backup(); + s_ForceFullDetail.set(); + } + renderScene(); + if (fullDetail) + { + s_ForceFullDetail.restore(); + } // for that frame and // tmp : display height grid diff --git a/code/ryzom/client/src/main_loop.h b/code/ryzom/client/src/main_loop.h index c8cd0af5a..f2f650b3e 100644 --- a/code/ryzom/client/src/main_loop.h +++ b/code/ryzom/client/src/main_loop.h @@ -29,7 +29,8 @@ const uint NUM_MISSION_OPTIONS = 8; bool mainLoop(); // render all -void renderAll(bool forceFullDetail = false); +void renderScene(); +void renderScene(bool forceFullDetail); void setDefaultChatWindow(CChatWindow *defaultChatWindow); void updateDayNightCycleHour(); From bfae91eb88f3e93bd9301245e9b4506744c32954 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Thu, 4 Jul 2013 22:26:31 +0200 Subject: [PATCH 075/313] Correctly apply bloom on oversize screenshots --- .../src/interface_v3/action_handler_misc.cpp | 2 +- code/ryzom/client/src/main_loop.cpp | 47 +++++++++++++------ code/ryzom/client/src/main_loop.h | 2 +- 3 files changed, 35 insertions(+), 16 deletions(-) diff --git a/code/ryzom/client/src/interface_v3/action_handler_misc.cpp b/code/ryzom/client/src/interface_v3/action_handler_misc.cpp index 7a68f7872..2d1fba931 100644 --- a/code/ryzom/client/src/interface_v3/action_handler_misc.cpp +++ b/code/ryzom/client/src/interface_v3/action_handler_misc.cpp @@ -542,7 +542,7 @@ void renderSceneScreenShot (uint left, uint right, uint top, uint bottom, uint s CCameraBackup cbScene = setupCameraForScreenshot(*Scene, left, right, top, bottom, screenShotWidth, screenShotHeight); CCameraBackup cbCanopy = setupCameraForScreenshot(*SceneRoot, left, right, top, bottom, screenShotWidth, screenShotHeight); // sky setup are copied from main scene before rendering so no setup done here - renderScene(ClientCfg.ScreenShotFullDetail); + renderScene(ClientCfg.ScreenShotFullDetail, ClientCfg.Bloom); restoreCamera(*Scene, cbScene); restoreCamera(*SceneRoot, cbCanopy); } diff --git a/code/ryzom/client/src/main_loop.cpp b/code/ryzom/client/src/main_loop.cpp index 6e11d3a2a..8cd0aea63 100644 --- a/code/ryzom/client/src/main_loop.cpp +++ b/code/ryzom/client/src/main_loop.cpp @@ -487,6 +487,8 @@ static void renderSkyPart(UScene::TRenderPart renderPart, TSkyMode skyMode) #endif } +// *************************************************************************************************************************** +// Utility to force full detail struct CForceFullDetail { public: @@ -519,8 +521,16 @@ private: }; static CForceFullDetail s_ForceFullDetail; -void renderScene(bool forceFullDetail) +void renderScene(bool forceFullDetail, bool bloom) { + if (bloom) + { + // set bloom parameters before applying bloom effect + CBloomEffect::getInstance().setSquareBloom(ClientCfg.SquareBloom); + CBloomEffect::getInstance().setDensityBloom((uint8)ClientCfg.DensityBloom); + // init bloom + CBloomEffect::getInstance().initBloom(); + } if (forceFullDetail) { s_ForceFullDetail.backup(); @@ -531,21 +541,18 @@ void renderScene(bool forceFullDetail) { s_ForceFullDetail.restore(); } + if (bloom) + { + // apply bloom effect + CBloomEffect::getInstance().endBloom(); + CBloomEffect::getInstance().endInterfacesDisplayBloom(); + } } // *************************************************************************************************************************** // Render all scenes void renderScene() { - if (ClientCfg.Bloom) - { - // set bloom parameters before applying bloom effect - CBloomEffect::getInstance().setSquareBloom(ClientCfg.SquareBloom); - CBloomEffect::getInstance().setDensityBloom((uint8)ClientCfg.DensityBloom); - // init bloom - CBloomEffect::getInstance().initBloom(); - } - { H_AUTO_USE ( RZ_Client_Main_Loop_Sky_And_Weather ) @@ -673,10 +680,6 @@ void renderScene() // reset depth range Driver->setDepthRange(0.f, CANOPY_DEPTH_RANGE_START); - - // apply bloom effect - if (ClientCfg.Bloom) - CBloomEffect::getInstance().endBloom(); } @@ -1539,6 +1542,14 @@ bool mainLoop() } #endif + if (ClientCfg.Bloom) + { + // set bloom parameters before applying bloom effect + CBloomEffect::getInstance().setSquareBloom(ClientCfg.SquareBloom); + CBloomEffect::getInstance().setDensityBloom((uint8)ClientCfg.DensityBloom); + // init bloom + CBloomEffect::getInstance().initBloom(); + } // nb : force full detail if a screenshot is asked // todo : move outside render code bool fullDetail = ScreenshotRequest != ScreenshotRequestNone && ClientCfg.ScreenShotFullDetail; @@ -1547,11 +1558,19 @@ bool mainLoop() s_ForceFullDetail.backup(); s_ForceFullDetail.set(); } + + // Render scene renderScene(); + if (fullDetail) { s_ForceFullDetail.restore(); } + if (ClientCfg.Bloom) + { + // apply bloom effect + CBloomEffect::getInstance().endBloom(); + } // for that frame and // tmp : display height grid diff --git a/code/ryzom/client/src/main_loop.h b/code/ryzom/client/src/main_loop.h index f2f650b3e..82137969a 100644 --- a/code/ryzom/client/src/main_loop.h +++ b/code/ryzom/client/src/main_loop.h @@ -30,7 +30,7 @@ bool mainLoop(); // render all void renderScene(); -void renderScene(bool forceFullDetail); +void renderScene(bool forceFullDetail, bool bloom); void setDefaultChatWindow(CChatWindow *defaultChatWindow); void updateDayNightCycleHour(); From 86870d66fba492835c081a84858a5c88cce86e68 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Thu, 4 Jul 2013 22:39:05 +0200 Subject: [PATCH 076/313] Pull weather updates out of scene render --- code/ryzom/client/src/main_loop.cpp | 129 +++++++++++++++------------- 1 file changed, 70 insertions(+), 59 deletions(-) diff --git a/code/ryzom/client/src/main_loop.cpp b/code/ryzom/client/src/main_loop.cpp index 8cd0aea63..95fd7086e 100644 --- a/code/ryzom/client/src/main_loop.cpp +++ b/code/ryzom/client/src/main_loop.cpp @@ -549,74 +549,82 @@ void renderScene(bool forceFullDetail, bool bloom) } } +// *************************************************************************************************************************** + +void updateWeather() +{ + H_AUTO_USE ( RZ_Client_Main_Loop_Sky_And_Weather ) + + //HeightGrid.update(Scene->getCam().getPos()); + + // update description of light cycle + updateLightDesc(); + + // server driven weather mgt + updateDBDrivenWeatherValue(); + + // Update the weather manager + updateWeatherManager(MainCam.getMatrix(), ContinentMngr.cur()); + + // compute thunder color + ThunderColor.modulateFromui(WeatherManager.getCurrWeatherState().ThunderColor, (uint) (256.f * WeatherManager.getThunderLevel())); + + // Update the lighting + LightCycleManager.setHour(DayNightCycleHour, WeatherManager, ThunderColor); + + #ifdef RENDER_CLOUDS + if (Filter3D[FilterCloud]) + { + H_AUTO_USE ( RZ_Client_Main_Loop_Update_Cloud_Scape ); + updateClouds(); + } + #endif + + ContinentMngr.getFogState(MainFog, LightCycleManager.getLightLevel(), LightCycleManager.getLightDesc().DuskRatio, LightCycleManager.getState(), View.viewPos(), MainFogState); + + // TODO: ZBuffer clear was originally before this, but should not be necessary normally. + // The anim function renders new clouds. Ensure this does not break. + // These are old-style nel clouds. + + #ifdef RENDER_CLOUDS + if (CloudScape != NULL && Filter3D[FilterCloud]) + { + H_AUTO_USE ( RZ_Client_Main_Loop_Anim_Cloud_Scape ); + + Driver->enableFog (false); + + // Force polygon mode to filled + NL3D::UDriver::TPolygonMode oldMode = Driver->getPolygonMode(); + Driver->setPolygonMode(NL3D::UDriver::Filled); + + CloudScape->anim (DT); // WARNING this function work with screen + + Driver->enableFog (true); + + // Reset backuped polygon mode + Driver->setPolygonMode(oldMode); + } + #endif +} + // *************************************************************************************************************************** // Render all scenes void renderScene() { + if (Driver->getPolygonMode() == UDriver::Filled) { - H_AUTO_USE ( RZ_Client_Main_Loop_Sky_And_Weather ) + Driver->clearZBuffer(); + } - //HeightGrid.update(Scene->getCam().getPos()); - - // update description of light cycle - updateLightDesc(); - - // server driven weather mgt - updateDBDrivenWeatherValue(); - - // Update the weather manager - updateWeatherManager(MainCam.getMatrix(), ContinentMngr.cur()); - - // compute thunder color - ThunderColor.modulateFromui(WeatherManager.getCurrWeatherState().ThunderColor, (uint) (256.f * WeatherManager.getThunderLevel())); - - // Update the lighting - LightCycleManager.setHour(DayNightCycleHour, WeatherManager, ThunderColor); - - #ifdef RENDER_CLOUDS - if (Filter3D[FilterCloud]) + // Sky is used to clear the frame buffer now, but if in line or point polygon mode, we should draw it + if (Driver->getPolygonMode() != UDriver::Filled) + { + if (!Driver->isLost()) { - H_AUTO_USE ( RZ_Client_Main_Loop_Update_Cloud_Scape ); - updateClouds(); - } - #endif - - - ContinentMngr.getFogState(MainFog, LightCycleManager.getLightLevel(), LightCycleManager.getLightDesc().DuskRatio, LightCycleManager.getState(), View.viewPos(), MainFogState); - - if (Driver->getPolygonMode() == UDriver::Filled) - { - Driver->clearZBuffer(); - } - - #ifdef RENDER_CLOUDS - if (CloudScape != NULL && Filter3D[FilterCloud]) - { - H_AUTO_USE ( RZ_Client_Main_Loop_Anim_Cloud_Scape ); - - Driver->enableFog (false); - - // Force polygon mode to filled - NL3D::UDriver::TPolygonMode oldMode = Driver->getPolygonMode(); - Driver->setPolygonMode(NL3D::UDriver::Filled); - - CloudScape->anim (DT); // WARNING this function work with screen - - Driver->enableFog (true); - - // Reset backuped polygon mode - Driver->setPolygonMode(oldMode); - } - #endif - // Sky is used to clear the frame buffer now, but if in line or point polygon mode, we should draw it - if (Driver->getPolygonMode() != UDriver::Filled) - { - if (!Driver->isLost()) - { - Driver->clearBuffers (CRGBA(127, 127, 127)); - } + Driver->clearBuffers (CRGBA(127, 127, 127)); } } + // Update Filter Flags Scene->enableElementRender(UScene::FilterAllMeshNoVP, Filter3D[FilterMeshNoVP]); Scene->enableElementRender(UScene::FilterAllMeshVP, Filter3D[FilterMeshVP]); @@ -1541,6 +1549,9 @@ bool mainLoop() Scene->updateWaterEnvMaps(TimeInSec - FirstTimeInSec); } #endif + + // TODO: Verify that moving this out of renderScene does not negatively impact oversize screenshots. + updateWeather(); if (ClientCfg.Bloom) { From 78276ab782c52819bb7f0f4736fff41282bd2970 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Thu, 4 Jul 2013 23:11:15 +0200 Subject: [PATCH 077/313] Make oversize screenshots be perfectly seamless --- .../src/interface_v3/action_handler_misc.cpp | 2 + code/ryzom/client/src/main_loop.cpp | 117 ++++++++++-------- code/ryzom/client/src/main_loop.h | 3 + 3 files changed, 69 insertions(+), 53 deletions(-) diff --git a/code/ryzom/client/src/interface_v3/action_handler_misc.cpp b/code/ryzom/client/src/interface_v3/action_handler_misc.cpp index 2d1fba931..7a628bb22 100644 --- a/code/ryzom/client/src/interface_v3/action_handler_misc.cpp +++ b/code/ryzom/client/src/interface_v3/action_handler_misc.cpp @@ -542,9 +542,11 @@ void renderSceneScreenShot (uint left, uint right, uint top, uint bottom, uint s CCameraBackup cbScene = setupCameraForScreenshot(*Scene, left, right, top, bottom, screenShotWidth, screenShotHeight); CCameraBackup cbCanopy = setupCameraForScreenshot(*SceneRoot, left, right, top, bottom, screenShotWidth, screenShotHeight); // sky setup are copied from main scene before rendering so no setup done here + commitCameraSky(); renderScene(ClientCfg.ScreenShotFullDetail, ClientCfg.Bloom); restoreCamera(*Scene, cbScene); restoreCamera(*SceneRoot, cbCanopy); + commitCameraSky(); } // *************************************************************************** diff --git a/code/ryzom/client/src/main_loop.cpp b/code/ryzom/client/src/main_loop.cpp index 95fd7086e..00a5fc092 100644 --- a/code/ryzom/client/src/main_loop.cpp +++ b/code/ryzom/client/src/main_loop.cpp @@ -352,6 +352,12 @@ void displaySceneProfiles(); // validate current dialogs (end them if the player is too far from its interlocutor) void validateDialogs(const CGameContextMenu &gcm); + +// *************************************************************************** + +enum TSkyMode { NoSky, OldSky, NewSky }; +static TSkyMode s_SkyMode = NoSky; + void preRenderNewSky () { CSky &sky = ContinentMngr.cur()->CurrentSky; @@ -362,29 +368,31 @@ void preRenderNewSky () cd.Hour = 12.f; } sky.setup(cd, SmoothedClientDate, WeatherManager.getWeatherValue(), MainFogState.FogColor, Scene->getSunDirection(), false); - // setup camera - CFrustum frust = MainCam.getFrustum(); - UCamera camSky = sky.getScene()->getCam(); - sky.getScene()->setViewport(Scene->getViewport()); - camSky.setTransformMode(UTransform::DirectMatrix); - // must have our own Far!!! - frust.Far= SkyCameraZFar; - camSky.setFrustum(frust); - CMatrix skyCameraMatrix; - skyCameraMatrix.identity(); - skyCameraMatrix= MainCam.getMatrix(); - skyCameraMatrix.setPos(CVector::Null); - camSky.setMatrix(skyCameraMatrix); } -//uint32 MainLoopCounter = 0; - +void commitCameraSky() +{ + if (s_SkyMode == NewSky) + { + CSky &sky = ContinentMngr.cur()->CurrentSky; + // setup camera + CFrustum frust = MainCam.getFrustum(); + UCamera camSky = sky.getScene()->getCam(); + sky.getScene()->setViewport(Scene->getViewport()); + camSky.setTransformMode(UTransform::DirectMatrix); + // must have our own Far!!! + frust.Far= SkyCameraZFar; + camSky.setFrustum(frust); + CMatrix skyCameraMatrix; + skyCameraMatrix.identity(); + skyCameraMatrix= MainCam.getMatrix(); + skyCameraMatrix.setPos(CVector::Null); + camSky.setMatrix(skyCameraMatrix); + } +} // *************************************************************************** -enum TSkyMode { NoSky, OldSky, NewSky }; - -// *************************************************************************** void beginRenderCanopyPart() { SceneRoot->beginPartRender(); @@ -403,17 +411,17 @@ void endRenderMainScenePart() Scene->endPartRender(true); } -void beginRenderSkyPart(TSkyMode skyMode) +void beginRenderSkyPart() { - if(skyMode == NewSky) + if (s_SkyMode == NewSky) { CSky &sky = ContinentMngr.cur()->CurrentSky; sky.getScene()->beginPartRender(); } } -void endRenderSkyPart(TSkyMode skyMode) +void endRenderSkyPart() { - if(skyMode == NewSky) + if (s_SkyMode == NewSky) { CSky &sky = ContinentMngr.cur()->CurrentSky; sky.getScene()->endPartRender(false); @@ -463,12 +471,12 @@ static void renderMainScenePart(UScene::TRenderPart renderPart) // *************************************************************************************************************************** // Render a part of the sky -static void renderSkyPart(UScene::TRenderPart renderPart, TSkyMode skyMode) +static void renderSkyPart(UScene::TRenderPart renderPart) { - nlassert(skyMode != NoSky); + nlassert(s_SkyMode != NoSky); Driver->setDepthRange(SKY_DEPTH_RANGE_START, 1.f); Driver->enableFog(false); - if (skyMode == NewSky) + if (s_SkyMode == NewSky) { CSky &sky = ContinentMngr.cur()->CurrentSky; sky.getScene()->renderPart(renderPart); @@ -605,6 +613,29 @@ void updateWeather() Driver->setPolygonMode(oldMode); } #endif + + // Update new sky + if (ContinentMngr.cur() && !ContinentMngr.cur()->Indoor) + { + if(Driver->getPolygonMode() == UDriver::Filled) + { + if (Filter3D[FilterSky]) + { + CSky &sky = ContinentMngr.cur()->CurrentSky; + if (sky.getScene()) + { + s_SkyMode = NewSky; + sky.getScene()->animate(TimeInSec-FirstTimeInSec); + // Setup the sky camera + preRenderNewSky(); + } + else + { + s_SkyMode = OldSky; + } + } + } + } } // *************************************************************************************************************************** @@ -638,36 +669,13 @@ void renderScene() if(Scene_Profile) Scene->profileNextRender(); - TSkyMode skyMode = NoSky; - if (ContinentMngr.cur() && !ContinentMngr.cur()->Indoor) - { - if(Driver->getPolygonMode() == UDriver::Filled) - { - if (Filter3D[FilterSky]) - { - CSky &sky = ContinentMngr.cur()->CurrentSky; - if (sky.getScene()) - { - skyMode = NewSky; - sky.getScene()->animate(TimeInSec-FirstTimeInSec); - // Setup the sky camera - preRenderNewSky(); - } - else - { - skyMode = OldSky; - } - } - } - } - // initialisation of polygons renderer CLandscapePolyDrawer::getInstance().beginRenderLandscapePolyPart(); // Start Part Rendering beginRenderCanopyPart(); beginRenderMainScenePart(); - beginRenderSkyPart(skyMode); + beginRenderSkyPart(); // Render part // WARNING: always must begin rendering with at least UScene::RenderOpaque, // else dynamic shadows won't work @@ -677,12 +685,12 @@ void renderScene() // render of polygons on landscape CLandscapePolyDrawer::getInstance().renderLandscapePolyPart(); - if (skyMode != NoSky) renderSkyPart((UScene::TRenderPart) (UScene::RenderOpaque | UScene::RenderTransparent), skyMode); + if (s_SkyMode != NoSky) renderSkyPart((UScene::TRenderPart) (UScene::RenderOpaque | UScene::RenderTransparent)); renderCanopyPart((UScene::TRenderPart) (UScene::RenderTransparent | UScene::RenderFlare)); renderMainScenePart((UScene::TRenderPart) (UScene::RenderTransparent | UScene::RenderFlare)); - if (skyMode == NewSky) renderSkyPart(UScene::RenderFlare, skyMode); + if (s_SkyMode == NewSky) renderSkyPart(UScene::RenderFlare); // End Part Rendering - endRenderSkyPart(skyMode); + endRenderSkyPart(); endRenderMainScenePart(); endRenderCanopyPart(); @@ -1550,7 +1558,7 @@ bool mainLoop() } #endif - // TODO: Verify that moving this out of renderScene does not negatively impact oversize screenshots. + // Update weather updateWeather(); if (ClientCfg.Bloom) @@ -1569,6 +1577,9 @@ bool mainLoop() s_ForceFullDetail.backup(); s_ForceFullDetail.set(); } + + // Commit camera changes to the sky camera + commitCameraSky(); // Render scene renderScene(); diff --git a/code/ryzom/client/src/main_loop.h b/code/ryzom/client/src/main_loop.h index 82137969a..b44ad764a 100644 --- a/code/ryzom/client/src/main_loop.h +++ b/code/ryzom/client/src/main_loop.h @@ -33,6 +33,9 @@ void renderScene(); void renderScene(bool forceFullDetail, bool bloom); void setDefaultChatWindow(CChatWindow *defaultChatWindow); +// Commit sky scene camera for rendering +void commitCameraSky(); + void updateDayNightCycleHour(); From 32e3da752fe3de6ec6bfb4cbf0b10040adc3f425 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Thu, 4 Jul 2013 23:41:46 +0200 Subject: [PATCH 078/313] Some more cleanup --- code/ryzom/client/src/main_loop.cpp | 95 ++++++++++++++++------------- 1 file changed, 52 insertions(+), 43 deletions(-) diff --git a/code/ryzom/client/src/main_loop.cpp b/code/ryzom/client/src/main_loop.cpp index 00a5fc092..6925533a8 100644 --- a/code/ryzom/client/src/main_loop.cpp +++ b/code/ryzom/client/src/main_loop.cpp @@ -559,6 +559,48 @@ void renderScene(bool forceFullDetail, bool bloom) // *************************************************************************************************************************** +void updateWaterEnvMap() +{ + #ifdef USE_WATER_ENV_MAP + if (WaterEnvMapRefCount > 0) // water env map needed + { + if (!WaterEnvMap) + { + CSky &sky = ContinentMngr.cur()->CurrentSky; + if (sky.getScene()) + { + nlassert(WaterEnvMapSkyCam.empty()); + WaterEnvMapSkyCam = sky.getScene()->createCamera(); // deleted in unselect + nlassert(WaterEnvMapCanopyCam.empty()); + WaterEnvMapCanopyCam = SceneRoot->createCamera(); // deleted in unselect + // Create water env map if not already created + WaterEnvMap = Driver->createWaterEnvMap(); + if(WaterEnvMap) + { + WaterEnvMap->init(128, 256, ClientCfg.WaterEnvMapUpdateTime); + WaterEnvMap->setWaterEnvMapRenderCallback(&WaterEnvMapRdr); + Scene->setWaterEnvMap(WaterEnvMap); + } + } + } + WaterEnvMapRdr.CurrDate = SmoothedClientDate; + WaterEnvMapRdr.AnimationDate = SmoothedClientDate; + if (ClientCfg.R2EDEnabled && R2::getEditor().getFixedLighting()) + { + WaterEnvMapRdr.CurrDate.Hour = 12.f; + } + WaterEnvMapRdr.CurrFogColor = MainFogState.FogColor; + WaterEnvMapRdr.CurrTime = TimeInSec - FirstTimeInSec; + WaterEnvMapRdr.CurrWeather = WeatherManager.getWeatherValue(); + CSky &sky = ContinentMngr.cur()->CurrentSky; + WaterEnvMap->setAlpha(sky.getWaterEnvMapAlpha()); + Scene->updateWaterEnvMaps(TimeInSec - FirstTimeInSec); + } + #endif +} + +// *************************************************************************************************************************** + void updateWeather() { H_AUTO_USE ( RZ_Client_Main_Loop_Sky_And_Weather ) @@ -999,6 +1041,14 @@ bool mainLoop() while( !UserEntity->permanentDeath() && !game_exit ) { + // If an action handler execute. NB: MUST BE DONE BEFORE ANY THING ELSE PROFILE CRASH!!!!!!!!!!!!!!!!! + testLaunchProfile(); + + // Test and may run a VBLock profile (only once) + testLaunchProfileVBLock(); + + // Start Bench + H_AUTO_USE ( RZ_Client_Main_Loop ) if (isBGDownloadEnabled()) { @@ -1073,11 +1123,6 @@ bool mainLoop() } { - // If an action handler execute. NB: MUST BE DONE BEFORE ANY THING ELSE PROFILE CRASH!!!!!!!!!!!!!!!!! - testLaunchProfile(); - - // Test and may run a VBLock profile (only once) - testLaunchProfileVBLock(); // Stop the Outgame music, with fade effect outgameFader.fade(); @@ -1085,10 +1130,6 @@ bool mainLoop() // update quit feature updateGameQuitting(); - // Start Bench - H_AUTO_USE ( RZ_Client_Main_Loop ) - - // update module manager NLNET::IModuleManager::getInstance().updateModules(); @@ -1523,40 +1564,8 @@ bool mainLoop() // Render if(Render) { - #ifdef USE_WATER_ENV_MAP - if (WaterEnvMapRefCount > 0) // water env map needed - { - if (!WaterEnvMap) - { - CSky &sky = ContinentMngr.cur()->CurrentSky; - if (sky.getScene()) - { - WaterEnvMapSkyCam = sky.getScene()->createCamera(); // deleted in unselect - WaterEnvMapCanopyCam = SceneRoot->createCamera(); // deleted in unselect - // Create water env map if not already created - WaterEnvMap = Driver->createWaterEnvMap(); - if(WaterEnvMap) - { - WaterEnvMap->init(128, 256, ClientCfg.WaterEnvMapUpdateTime); - WaterEnvMap->setWaterEnvMapRenderCallback(&WaterEnvMapRdr); - Scene->setWaterEnvMap(WaterEnvMap); - } - } - } - WaterEnvMapRdr.CurrDate = SmoothedClientDate; - WaterEnvMapRdr.AnimationDate = SmoothedClientDate; - if (ClientCfg.R2EDEnabled && R2::getEditor().getFixedLighting()) - { - WaterEnvMapRdr.CurrDate.Hour = 12.f; - } - WaterEnvMapRdr.CurrFogColor = MainFogState.FogColor; - WaterEnvMapRdr.CurrTime = TimeInSec - FirstTimeInSec; - WaterEnvMapRdr.CurrWeather = WeatherManager.getWeatherValue(); - CSky &sky = ContinentMngr.cur()->CurrentSky; - WaterEnvMap->setAlpha(sky.getWaterEnvMapAlpha()); - Scene->updateWaterEnvMaps(TimeInSec - FirstTimeInSec); - } - #endif + // Update water env map (happens when continent changed etc) + updateWaterEnvMap(); // Update weather updateWeather(); From 5e5cfba0356b0c71d9abad1ec443d7dc0602f0e2 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Fri, 5 Jul 2013 00:17:09 +0200 Subject: [PATCH 079/313] Some necessary changes to camera setting, re #43 --- .../src/interface_v3/action_handler_misc.cpp | 4 +- code/ryzom/client/src/main_loop.cpp | 79 +++++++++++++------ code/ryzom/client/src/main_loop.h | 2 +- 3 files changed, 60 insertions(+), 25 deletions(-) diff --git a/code/ryzom/client/src/interface_v3/action_handler_misc.cpp b/code/ryzom/client/src/interface_v3/action_handler_misc.cpp index 7a628bb22..779d58fc4 100644 --- a/code/ryzom/client/src/interface_v3/action_handler_misc.cpp +++ b/code/ryzom/client/src/interface_v3/action_handler_misc.cpp @@ -541,12 +541,12 @@ void renderSceneScreenShot (uint left, uint right, uint top, uint bottom, uint s { CCameraBackup cbScene = setupCameraForScreenshot(*Scene, left, right, top, bottom, screenShotWidth, screenShotHeight); CCameraBackup cbCanopy = setupCameraForScreenshot(*SceneRoot, left, right, top, bottom, screenShotWidth, screenShotHeight); + commitCamera(); // sky setup are copied from main scene before rendering so no setup done here - commitCameraSky(); renderScene(ClientCfg.ScreenShotFullDetail, ClientCfg.Bloom); restoreCamera(*Scene, cbScene); restoreCamera(*SceneRoot, cbCanopy); - commitCameraSky(); + commitCamera(); } // *************************************************************************** diff --git a/code/ryzom/client/src/main_loop.cpp b/code/ryzom/client/src/main_loop.cpp index 6925533a8..38752f77d 100644 --- a/code/ryzom/client/src/main_loop.cpp +++ b/code/ryzom/client/src/main_loop.cpp @@ -370,8 +370,9 @@ void preRenderNewSky () sky.setup(cd, SmoothedClientDate, WeatherManager.getWeatherValue(), MainFogState.FogColor, Scene->getSunDirection(), false); } -void commitCameraSky() +void commitCamera() { + // Set the sky camera if (s_SkyMode == NewSky) { CSky &sky = ContinentMngr.cur()->CurrentSky; @@ -389,6 +390,17 @@ void commitCameraSky() skyCameraMatrix.setPos(CVector::Null); camSky.setMatrix(skyCameraMatrix); } + + // Set The Root Camera + UCamera camRoot = SceneRoot->getCam(); + if(!camRoot.empty()) + { + // Update Camera Position/Rotation. + //camRoot.setPos(View.currentViewPos()); + //camRoot.setRotQuat(View.currentViewQuat()); + camRoot.setTransformMode(UTransformable::DirectMatrix); // FIXME + camRoot.setMatrix(MainCam.getMatrix()); + } } // *************************************************************************** @@ -439,14 +451,6 @@ static void renderCanopyPart(UScene::TRenderPart renderPart) ContinentMngr.getFogState(CanopyFog, LightCycleManager.getLightLevel(), LightCycleManager.getLightDesc().DuskRatio, LightCycleManager.getState(), View.viewPos(), RootFogState); RootFogState.setupInDriver(*Driver); - // Set The Root Camera - UCamera camRoot = SceneRoot->getCam(); - if(!camRoot.empty()) - { - // Update Camera Position/Rotation. - camRoot.setPos(View.currentViewPos()); - camRoot.setRotQuat(View.currentViewQuat()); - } // Render the root scene SceneRoot->renderPart(renderPart); } @@ -1364,9 +1368,17 @@ bool mainLoop() // Update Camera Position/Orientation. CVector currViewPos = View.currentViewPos(); + MainCam.setTransformMode(UTransformable::RotQuat); MainCam.setPos(currViewPos); MainCam.setRotQuat(View.currentViewQuat()); - if (StereoDisplay) StereoDisplay->updateCamera(0, &MainCam); + if (StereoDisplay) + { + StereoDisplay->updateCamera(0, &MainCam); + if (SceneRoot) + { + StereoDisplay->updateCamera(1, &SceneRoot->getCam()); + } + } // see if camera is below water (useful for sort order) if (ContinentMngr.cur()) @@ -1457,9 +1469,6 @@ bool mainLoop() } - ////////////////////////// - // RENDER THE FRAME 3D // - ////////////////////////// if (!ClientCfg.Light) { CClientDate newDate(RT.getRyzomDay(), DayNightCycleHour); @@ -1546,9 +1555,6 @@ bool mainLoop() } } - // Set the matrix in 3D Mode. - Driver->setMatrixMode3D(MainCam); - // R2ED pre render update if (ClientCfg.R2EDEnabled) @@ -1556,11 +1562,8 @@ bool mainLoop() R2::getEditor().updateBeforeRender(); } - - // Position the camera to prepare the render if (!ClientCfg.Light) { - // Render if(Render) { @@ -1569,7 +1572,42 @@ bool mainLoop() // Update weather updateWeather(); + } + } + + ////////////////////////// + // RENDER THE FRAME 3D // + ////////////////////////// + if (StereoDisplay) + { + // modify cameras for stereo display + const CViewport &vp = StereoDisplay->getCurrentViewport(); + Driver->setViewport(vp); + nlassert(Scene); + Scene->setViewport(vp); + if (SceneRoot) + { + SceneRoot->setViewport(vp); + } + MainCam.setTransformMode(UTransformable::DirectMatrix); + StereoDisplay->getCurrentMatrix(0, &MainCam); + StereoDisplay->getCurrentFrustum(0, &MainCam); + if (SceneRoot) + { + // matrix updated during commitCamera from maincam + StereoDisplay->getCurrentFrustum(1, &SceneRoot->getCam()); + } + } + + // Commit camera changes + commitCamera(); + + if (!ClientCfg.Light) + { + // Render + if(Render) + { if (ClientCfg.Bloom) { // set bloom parameters before applying bloom effect @@ -1586,9 +1624,6 @@ bool mainLoop() s_ForceFullDetail.backup(); s_ForceFullDetail.set(); } - - // Commit camera changes to the sky camera - commitCameraSky(); // Render scene renderScene(); diff --git a/code/ryzom/client/src/main_loop.h b/code/ryzom/client/src/main_loop.h index b44ad764a..21f64d37e 100644 --- a/code/ryzom/client/src/main_loop.h +++ b/code/ryzom/client/src/main_loop.h @@ -34,7 +34,7 @@ void renderScene(bool forceFullDetail, bool bloom); void setDefaultChatWindow(CChatWindow *defaultChatWindow); // Commit sky scene camera for rendering -void commitCameraSky(); +void commitCamera(); void updateDayNightCycleHour(); From 59edf6ea138028fc41678ccde6baab7415a68a53 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Fri, 5 Jul 2013 00:47:17 +0200 Subject: [PATCH 080/313] Move some more updates out of the render code, see #43 --- code/ryzom/client/src/main_loop.cpp | 93 +++++++++++++++-------------- 1 file changed, 49 insertions(+), 44 deletions(-) diff --git a/code/ryzom/client/src/main_loop.cpp b/code/ryzom/client/src/main_loop.cpp index 38752f77d..e9f6312e8 100644 --- a/code/ryzom/client/src/main_loop.cpp +++ b/code/ryzom/client/src/main_loop.cpp @@ -1575,9 +1575,9 @@ bool mainLoop() } } - ////////////////////////// - // RENDER THE FRAME 3D // - ////////////////////////// + /////////////////// + // SETUP CAMERAS // + /////////////////// if (StereoDisplay) { @@ -1602,6 +1602,10 @@ bool mainLoop() // Commit camera changes commitCamera(); + + ////////////////////////// + // RENDER THE FRAME 3D // + ////////////////////////// if (!ClientCfg.Light) { @@ -1711,17 +1715,6 @@ bool mainLoop() // Display some things not in the scene like the name, the entity path, etc. EntitiesMngr.updatePostRender(); - // R2ED pre render update - if (ClientCfg.R2EDEnabled) - { - // IMPORTANT : this should be called after CEntitiesMngr::updatePostRender() because - // entity may be added / removed there ! - R2::getEditor().updateAfterRender(); - } - - // Update FXs (remove them). - FXMngr.update(); - // Render the stat graphs if needed { H_AUTO_USE ( RZ_Client_Main_Loop_Debug ) @@ -1738,6 +1731,7 @@ bool mainLoop() Driver->drawQuad(0, 0, 1, 1, ThunderColor); // TODO : boris : add sound here ! + // Needs more explosions } // Update the contextual menu @@ -1827,39 +1821,50 @@ bool mainLoop() Driver->drawBitmap(x/(float)ClientCfg.Width, y/(float)ClientCfg.Height, width/(float)ClientCfg.Width, height/(float)ClientCfg.Height, *LogoBitmaps[i]); } } - - // FPS - { - static TTicks oldTick = CTime::getPerformanceTime(); - TTicks newTick = CTime::getPerformanceTime(); - double deltaTime = CTime::ticksToSecond (newTick-oldTick); - oldTick = newTick; - smoothFPS.addValue((float)deltaTime); - moreSmoothFPS.addValue((float)deltaTime); - deltaTime = smoothFPS.getSmoothValue (); - if (deltaTime > 0.0) - { - CCDBNodeLeaf*pNL = NLGUI::CDBManager::getInstance()->getDbProp("UI:VARIABLES:FPS"); - pNL->setValue64((sint64)(1.f/deltaTime)); - } - } - - // Detect disconnection / server down: display information text - // but keep the rendering so that the player can remember where he is - // and what he was doing. He can't move because the connection quality returns false. - - if ((connectionState == CNetworkConnection::Disconnect) && (lastConnectionState != CNetworkConnection::Disconnect) && (!FarTP.isFarTPInProgress())) - { - UserControls.stopFreeLook(); // let the player click on Exit - pIMinstance->messageBoxWithHelp(CI18N::get("uiDisconnected")); - - // If we have started a Far TP sequence and are waiting for onServerQuitOK() - // from the EGS, resume the sequence because the EGS is down and won't reply. - FarTP.onServerQuitOk(); - } } } + // FPS + { + static TTicks oldTick = CTime::getPerformanceTime(); + TTicks newTick = CTime::getPerformanceTime(); + double deltaTime = CTime::ticksToSecond (newTick-oldTick); + oldTick = newTick; + smoothFPS.addValue((float)deltaTime); + moreSmoothFPS.addValue((float)deltaTime); + deltaTime = smoothFPS.getSmoothValue (); + if (deltaTime > 0.0) + { + CCDBNodeLeaf*pNL = NLGUI::CDBManager::getInstance()->getDbProp("UI:VARIABLES:FPS"); + pNL->setValue64((sint64)(1.f/deltaTime)); + } + } + + // R2ED post render update + if (ClientCfg.R2EDEnabled) + { + // IMPORTANT : this should be called after CEntitiesMngr::updatePostRender() because + // entity may be added / removed there ! + R2::getEditor().updateAfterRender(); + } + + // Update FXs (remove them). + FXMngr.update(); + + // Detect disconnection / server down: display information text + // but keep the rendering so that the player can remember where he is + // and what he was doing. He can't move because the connection quality returns false. + + if ((connectionState == CNetworkConnection::Disconnect) && (lastConnectionState != CNetworkConnection::Disconnect) && (!FarTP.isFarTPInProgress())) + { + UserControls.stopFreeLook(); // let the player click on Exit + pIMinstance->messageBoxWithHelp(CI18N::get("uiDisconnected")); + + // If we have started a Far TP sequence and are waiting for onServerQuitOK() + // from the EGS, resume the sequence because the EGS is down and won't reply. + FarTP.onServerQuitOk(); + } + // Yoyo: MovieShooter. if(MovieShooterSaving) { From 541f75920b1efb58ede28926ea9033eb3fc67df9 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Fri, 5 Jul 2013 02:04:34 +0200 Subject: [PATCH 081/313] Init/release VR interfaces in ryzom client, ref #43 --- code/nel/include/nel/3d/stereo_debugger.h | 4 + code/nel/src/3d/stereo_debugger.cpp | 118 ++++++++++++++-------- code/nel/src/3d/stereo_ovr.cpp | 13 +-- code/ryzom/client/src/camera.cpp | 2 +- code/ryzom/client/src/client_cfg.cpp | 7 ++ code/ryzom/client/src/client_cfg.h | 5 + code/ryzom/client/src/global.cpp | 4 +- code/ryzom/client/src/global.h | 7 +- code/ryzom/client/src/init.cpp | 66 ++++++++++++ code/ryzom/client/src/main_loop.cpp | 2 +- code/ryzom/client/src/release.cpp | 10 ++ 11 files changed, 185 insertions(+), 53 deletions(-) diff --git a/code/nel/include/nel/3d/stereo_debugger.h b/code/nel/include/nel/3d/stereo_debugger.h index b94ffaf6b..b07a9630c 100644 --- a/code/nel/include/nel/3d/stereo_debugger.h +++ b/code/nel/include/nel/3d/stereo_debugger.h @@ -65,6 +65,10 @@ public: /// Sets driver and generates necessary render targets virtual void setDriver(NL3D::UDriver *driver); + void releaseTextures(); + void initTextures(); + void setTextures(); + void verifyTextures(); /// Gets the required screen resolution for this device virtual bool getScreenResolution(uint &width, uint &height); diff --git a/code/nel/src/3d/stereo_debugger.cpp b/code/nel/src/3d/stereo_debugger.cpp index c24bcdc93..036cb7e1b 100644 --- a/code/nel/src/3d/stereo_debugger.cpp +++ b/code/nel/src/3d/stereo_debugger.cpp @@ -96,21 +96,13 @@ CStereoDebugger::CStereoDebugger() : m_Driver(NULL), m_Stage(0), m_SubStage(0), CStereoDebugger::~CStereoDebugger() { + releaseTextures(); + if (!m_Mat.empty()) { - m_Mat.getObjectPtr()->setTexture(0, NULL); - m_Mat.getObjectPtr()->setTexture(1, NULL); m_Driver->deleteMaterial(m_Mat); } - delete m_LeftTexU; - m_LeftTexU = NULL; - m_LeftTex = NULL; // CSmartPtr - - delete m_RightTexU; - m_RightTexU = NULL; - m_RightTex = NULL; // CSmartPtr - delete m_PixelProgram; m_PixelProgram = NULL; @@ -143,33 +135,7 @@ void CStereoDebugger::setDriver(NL3D::UDriver *driver) { m_Driver = driver; - // todo: handle reso change! - - uint32 width, height; - driver->getWindowSize(width, height); - - m_LeftTex = new CTextureBloom(); - m_LeftTex->setRenderTarget(true); - m_LeftTex->setReleasable(false); - m_LeftTex->resize(width, height); - m_LeftTex->setFilterMode(ITexture::Linear, ITexture::LinearMipMapOff); - m_LeftTex->setWrapS(ITexture::Clamp); - m_LeftTex->setWrapT(ITexture::Clamp); - drvInternal->setupTexture(*m_LeftTex); - m_LeftTexU = new CTextureUser(m_LeftTex); - nlassert(!drvInternal->isTextureRectangle(m_LeftTex)); // not allowed - - m_RightTex = new CTextureBloom(); - m_RightTex->setRenderTarget(true); - m_RightTex->setReleasable(false); - m_RightTex->resize(width, height); - m_RightTex->setFilterMode(ITexture::Linear, ITexture::LinearMipMapOff); - m_RightTex->setWrapS(ITexture::Clamp); - m_RightTex->setWrapT(ITexture::Clamp); - drvInternal->setupTexture(*m_RightTex); - m_RightTexU = new CTextureUser(m_RightTex); - nlassert(!drvInternal->isTextureRectangle(m_RightTex)); // not allowed - + initTextures(); m_Mat = m_Driver->createMaterial(); m_Mat.initUnlit(); @@ -182,9 +148,8 @@ void CStereoDebugger::setDriver(NL3D::UDriver *driver) mat->setZWrite(false); mat->setZFunc(CMaterial::always); mat->setDoubleSided(true); - mat->setTexture(0, m_LeftTex); - mat->setTexture(1, m_RightTex); - + + setTextures(); m_QuadUV.V0 = CVector(0.f, 0.f, 0.5f); m_QuadUV.V1 = CVector(1.f, 0.f, 0.5f); @@ -198,6 +163,79 @@ void CStereoDebugger::setDriver(NL3D::UDriver *driver) } } +void CStereoDebugger::releaseTextures() +{ + if (!m_Mat.empty()) + { + m_Mat.getObjectPtr()->setTexture(0, NULL); + m_Mat.getObjectPtr()->setTexture(1, NULL); + m_Driver->deleteMaterial(m_Mat); + } + + delete m_LeftTexU; + m_LeftTexU = NULL; + m_LeftTex = NULL; // CSmartPtr + + delete m_RightTexU; + m_RightTexU = NULL; + m_RightTex = NULL; // CSmartPtr +} + +void CStereoDebugger::initTextures() +{ + uint32 width, height; + m_Driver->getWindowSize(width, height); + NL3D::IDriver *drvInternal = (static_cast(m_Driver))->getDriver(); + + m_LeftTex = new CTextureBloom(); + m_LeftTex->setRenderTarget(true); + m_LeftTex->setReleasable(false); + m_LeftTex->resize(width, height); + m_LeftTex->setFilterMode(ITexture::Linear, ITexture::LinearMipMapOff); + m_LeftTex->setWrapS(ITexture::Clamp); + m_LeftTex->setWrapT(ITexture::Clamp); + drvInternal->setupTexture(*m_LeftTex); + m_LeftTexU = new CTextureUser(m_LeftTex); + nlassert(!drvInternal->isTextureRectangle(m_LeftTex)); // not allowed + + m_RightTex = new CTextureBloom(); + m_RightTex->setRenderTarget(true); + m_RightTex->setReleasable(false); + m_RightTex->resize(width, height); + m_RightTex->setFilterMode(ITexture::Linear, ITexture::LinearMipMapOff); + m_RightTex->setWrapS(ITexture::Clamp); + m_RightTex->setWrapT(ITexture::Clamp); + drvInternal->setupTexture(*m_RightTex); + m_RightTexU = new CTextureUser(m_RightTex); + nlassert(!drvInternal->isTextureRectangle(m_RightTex)); // not allowed +} + +void CStereoDebugger::setTextures() +{ + NL3D::CMaterial *mat = m_Mat.getObjectPtr(); + mat->setTexture(0, m_LeftTex); + mat->setTexture(1, m_RightTex); +} + +void CStereoDebugger::verifyTextures() +{ + if (m_Driver) + { + uint32 width, height; + m_Driver->getWindowSize(width, height); + if (m_LeftTex->getWidth() != width + || m_RightTex->getWidth() != width + || m_LeftTex->getHeight() != height + || m_RightTex->getHeight() != height) + { + nldebug("Rebuild textures"); + releaseTextures(); + initTextures(); + setTextures(); + } + } +} + /// Gets the required screen resolution for this device bool CStereoDebugger::getScreenResolution(uint &width, uint &height) { diff --git a/code/nel/src/3d/stereo_ovr.cpp b/code/nel/src/3d/stereo_ovr.cpp index e58e82892..36f48c7e3 100644 --- a/code/nel/src/3d/stereo_ovr.cpp +++ b/code/nel/src/3d/stereo_ovr.cpp @@ -233,11 +233,6 @@ CStereoOVR::~CStereoOVR() void CStereoOVR::setDriver(NL3D::UDriver *driver) { - // Do not allow weird stuff. - uint32 width, height; - driver->getWindowSize(width, height); - nlassert(width == m_DevicePtr->HMDInfo.HResolution); - nlassert(height == m_DevicePtr->HMDInfo.VResolution); nlassert(!m_PixelProgram); NL3D::IDriver *drvInternal = (static_cast(driver))->getDriver(); @@ -264,7 +259,7 @@ void CStereoOVR::setDriver(NL3D::UDriver *driver) m_BarrelTex = new CTextureBloom(); // lol bloom m_BarrelTex->setRenderTarget(true); m_BarrelTex->setReleasable(false); - m_BarrelTex->resize(width, height); + m_BarrelTex->resize(m_DevicePtr->HMDInfo.HResolution, m_DevicePtr->HMDInfo.VResolution); m_BarrelTex->setFilterMode(ITexture::Linear, ITexture::LinearMipMapOff); m_BarrelTex->setWrapS(ITexture::Clamp); m_BarrelTex->setWrapT(ITexture::Clamp); @@ -358,6 +353,12 @@ void CStereoOVR::updateCamera(uint cid, const NL3D::UCamera *camera) bool CStereoOVR::nextPass() { + // Do not allow weird stuff. + uint32 width, height; + m_Driver->getWindowSize(width, height); + nlassert(width == m_DevicePtr->HMDInfo.HResolution); + nlassert(height == m_DevicePtr->HMDInfo.VResolution); + switch (m_Stage) { case 0: diff --git a/code/ryzom/client/src/camera.cpp b/code/ryzom/client/src/camera.cpp index 66aac6f76..c409f9e58 100644 --- a/code/ryzom/client/src/camera.cpp +++ b/code/ryzom/client/src/camera.cpp @@ -17,7 +17,7 @@ #include #include "camera.h" -#include +#include #include "global.h" #include "misc.h" diff --git a/code/ryzom/client/src/client_cfg.cpp b/code/ryzom/client/src/client_cfg.cpp index 5bbd90e01..67016de37 100644 --- a/code/ryzom/client/src/client_cfg.cpp +++ b/code/ryzom/client/src/client_cfg.cpp @@ -302,6 +302,10 @@ CClientConfig::CClientConfig() Contrast = 0.f; // Default Monitor Contrast. Luminosity = 0.f; // Default Monitor Luminosity. Gamma = 0.f; // Default Monitor Gamma. + + VREnable = false; + VRDisplayDevice = "Auto"; + VRDisplayDeviceId = ""; Local = false; // Default is Net Mode. FSHost = ""; // Default Host. @@ -847,6 +851,9 @@ void CClientConfig::setValues() else cfgWarning ("Default value used for 'Driver3D' !!!"); + READ_BOOL_FV(VREnable) + READ_STRING_FV(VRDisplayDevice) + READ_STRING_FV(VRDisplayDeviceId) //////////// // INPUTS // diff --git a/code/ryzom/client/src/client_cfg.h b/code/ryzom/client/src/client_cfg.h index c76cfaf36..44ddf3891 100644 --- a/code/ryzom/client/src/client_cfg.h +++ b/code/ryzom/client/src/client_cfg.h @@ -146,6 +146,11 @@ struct CClientConfig /// Monitor Gamma [-1 ~ 1], default 0 float Gamma; + // VR + bool VREnable; + std::string VRDisplayDevice; + std::string VRDisplayDeviceId; + /// Client in Local mode or not. bool Local; /// Host. diff --git a/code/ryzom/client/src/global.cpp b/code/ryzom/client/src/global.cpp index bb60f5c04..2e3ba875a 100644 --- a/code/ryzom/client/src/global.cpp +++ b/code/ryzom/client/src/global.cpp @@ -26,8 +26,8 @@ using namespace NLMISC; // *************************************************************************** // Main System NL3D::UDriver *Driver = 0; // The main 3D Driver -NL3D::CStereoOVR *StereoDisplay = NULL; // Stereo display -NL3D::CStereoOVR *StereoHMD = NULL; // Head mount display +NL3D::IStereoDisplay *StereoDisplay = NULL; // Stereo display +NL3D::IStereoHMD *StereoHMD = NULL; // Head mount display CSoundManager *SoundMngr = 0; // the sound manager NL3D::UMaterial GenericMat; // Generic Material NL3D::UTextContext *TextContext = 0; // Context for all the text in the client. diff --git a/code/ryzom/client/src/global.h b/code/ryzom/client/src/global.h index 6ec3db7ec..9e5a294ae 100644 --- a/code/ryzom/client/src/global.h +++ b/code/ryzom/client/src/global.h @@ -40,7 +40,8 @@ namespace NL3D class UMaterial; class UTextContext; class UWaterEnvMap; - class CStereoOVR; + class IStereoDisplay; + class IStereoHMD; } class CEntityAnimationManager; @@ -78,8 +79,8 @@ const float ExtraZoneLoadingVision = 100.f; // *************************************************************************** // Main System extern NL3D::UDriver *Driver; // The main 3D Driver -extern NL3D::CStereoOVR *StereoDisplay; // Stereo display -extern NL3D::CStereoOVR *StereoHMD; +extern NL3D::IStereoDisplay *StereoDisplay; // Stereo display +extern NL3D::IStereoHMD *StereoHMD; // Head mount display extern CSoundManager *SoundMngr; // the sound manager extern NL3D::UMaterial GenericMat; // Generic Material extern NL3D::UTextContext *TextContext; // Context for all the text in the client. diff --git a/code/ryzom/client/src/init.cpp b/code/ryzom/client/src/init.cpp index 9d515fabd..4c5cd1eca 100644 --- a/code/ryzom/client/src/init.cpp +++ b/code/ryzom/client/src/init.cpp @@ -39,6 +39,7 @@ #include "nel/3d/u_driver.h" #include "nel/3d/u_text_context.h" #include "nel/3d/u_shape_bank.h" +#include "nel/3d/stereo_hmd.h" // Net. #include "nel/net/email.h" // Ligo. @@ -46,6 +47,7 @@ // Std. #include +#include // Game Share #include "game_share/ryzom_version.h" // Client @@ -792,6 +794,59 @@ void prelogInit() // Check driver version checkDriverVersion(); + // Initialize the VR devices (even more important than the most important part of the client) + nmsg = "Initializing VR devices..."; + ProgressBar.newMessage ( ClientCfg.buildLoadingString(nmsg) ); + if (ClientCfg.VREnable) + { + nldebug("VR [C]: Enabled"); + std::vector devices; + IStereoDisplay::listDevices(devices); + for (std::vector::iterator it(devices.begin()), end(devices.end()); it != end; ++it) + { + std::stringstream name; + name << std::string("[") << it->Serial << "] [" << IStereoDisplay::getLibraryName(it->Library) << " - " << it->Manufacturer << " - " << it->ProductName << "]"; + nlinfo("VR [C]: Stereo Display: %s", name.str().c_str()); + } + CStereoDeviceInfo *deviceInfo = NULL; + if (ClientCfg.VRDisplayDevice == std::string("Auto") + && devices.begin() != devices.end()) + { + deviceInfo = &devices[0]; + } + else + { + for (std::vector::iterator it(devices.begin()), end(devices.end()); it != end; ++it) + { + std::stringstream name; + name << IStereoDisplay::getLibraryName(it->Library) << " - " << it->Manufacturer << " - " << it->ProductName; + if (name.str() == ClientCfg.VRDisplayDevice) + deviceInfo = &(*it); + if (ClientCfg.VRDisplayDeviceId == it->Serial) + break; + } + } + if (deviceInfo) + { + nlinfo("VR [C]: Create VR stereo display device"); + StereoDisplay = IStereoDisplay::createDevice(*deviceInfo); + if (StereoDisplay) + { + if (deviceInfo->Class == CStereoDeviceInfo::StereoHMD) + { + nlinfo("VR [C]: Stereo display device is a HMD"); + StereoHMD = static_cast(StereoDisplay); + } + } + } + } + else + { + nldebug("VR [C]: NOT Enabled"); + } + IStereoDisplay::releaseUnusedLibraries(); + + // Create the driver (most important part of the client). nmsg = "Creating 3d driver..."; ProgressBar.newMessage ( ClientCfg.buildLoadingString(nmsg) ); @@ -860,6 +915,11 @@ void prelogInit() Driver->setSwapVBLInterval(1); else Driver->setSwapVBLInterval(0); + + if (StereoDisplay) + { + // override mode TODO + } // Set the mode of the window. if (!Driver->setDisplay (mode, false)) @@ -1102,6 +1162,12 @@ void prelogInit() // init bloom effect CBloomEffect::getInstance().init(driver != UDriver::Direct3d); + + if (StereoDisplay) + { + // Init stereo display resources + StereoDisplay->setDriver(Driver); + } nlinfo ("PROFILE: %d seconds for prelogInit", (uint32)(ryzomGetLocalTime ()-initStart)/1000); diff --git a/code/ryzom/client/src/main_loop.cpp b/code/ryzom/client/src/main_loop.cpp index e9f6312e8..ce42b3d8e 100644 --- a/code/ryzom/client/src/main_loop.cpp +++ b/code/ryzom/client/src/main_loop.cpp @@ -42,7 +42,7 @@ #include "nel/3d/u_material.h" #include "nel/3d/u_instance_material.h" #include "nel/3d/u_cloud_scape.h" -#include "nel/3d/stereo_ovr.h" +#include "nel/3d/stereo_hmd.h" // game share #include "game_share/brick_types.h" #include "game_share/light_cycle.h" diff --git a/code/ryzom/client/src/release.cpp b/code/ryzom/client/src/release.cpp index 0c975aebe..eea5c6ef9 100644 --- a/code/ryzom/client/src/release.cpp +++ b/code/ryzom/client/src/release.cpp @@ -35,6 +35,7 @@ #include "nel/3d/u_scene.h" #include "nel/3d/u_visual_collision_manager.h" #include "nel/3d/u_shape_bank.h" +#include "nel/3d/stereo_hmd.h" // Client #include "global.h" #include "release.h" @@ -559,6 +560,15 @@ void release() CEntityAnimationManager::delInstance(); EAM= NULL; + nldebug("VR [C]: VR Shutting down"); + if (StereoDisplay) + { + delete StereoDisplay; + StereoDisplay = NULL; + StereoHMD = NULL; + } + IStereoDisplay::releaseAllLibraries(); + // Delete the driver. if(Driver) { From c3f6f5b8ffb5b838f0ead3ab87c52c7e0a4b305f Mon Sep 17 00:00:00 2001 From: kaetemi Date: Fri, 5 Jul 2013 02:38:56 +0200 Subject: [PATCH 082/313] Put stereo render loop inside ryzom client main loop, see #43 --- code/nel/src/3d/stereo_ovr.cpp | 13 +- code/ryzom/client/src/main_loop.cpp | 985 +++++++++++++++------------- 2 files changed, 536 insertions(+), 462 deletions(-) diff --git a/code/nel/src/3d/stereo_ovr.cpp b/code/nel/src/3d/stereo_ovr.cpp index 36f48c7e3..e7e31d557 100644 --- a/code/nel/src/3d/stereo_ovr.cpp +++ b/code/nel/src/3d/stereo_ovr.cpp @@ -438,8 +438,17 @@ void CStereoOVR::getCurrentMatrix(uint cid, NL3D::UCamera *camera) const CMatrix translate; if (m_Stage % 2) translate.translate(CVector((m_DevicePtr->HMDInfo.InterpupillaryDistance * m_Scale) * -0.5f, 0.f, 0.f)); else translate.translate(CVector((m_DevicePtr->HMDInfo.InterpupillaryDistance * m_Scale) * 0.5f, 0.f, 0.f)); - camera->setTransformMode(NL3D::UTransformable::DirectMatrix); - camera->setMatrix(m_CameraMatrix[cid] * translate); + CMatrix mat = m_CameraMatrix[cid] * translate; + if (camera->getTransformMode() == NL3D::UTransformable::RotQuat) + { + camera->setPos(mat.getPos()); + camera->setRotQuat(mat.getRot()); + } + else + { + // camera->setTransformMode(NL3D::UTransformable::DirectMatrix); + camera->setMatrix(mat); + } } bool CStereoOVR::wantClear() diff --git a/code/ryzom/client/src/main_loop.cpp b/code/ryzom/client/src/main_loop.cpp index ce42b3d8e..095236ef0 100644 --- a/code/ryzom/client/src/main_loop.cpp +++ b/code/ryzom/client/src/main_loop.cpp @@ -398,8 +398,8 @@ void commitCamera() // Update Camera Position/Rotation. //camRoot.setPos(View.currentViewPos()); //camRoot.setRotQuat(View.currentViewQuat()); - camRoot.setTransformMode(UTransformable::DirectMatrix); // FIXME - camRoot.setMatrix(MainCam.getMatrix()); + camRoot.setPos(MainCam.getPos()); + camRoot.setRotQuat(MainCam.getRotQuat()); } } @@ -533,6 +533,30 @@ private: }; static CForceFullDetail s_ForceFullDetail; +void clearBuffers() +{ + if (Render) + { + if (Driver->getPolygonMode() == UDriver::Filled) + { + Driver->clearZBuffer(); + } + + // Sky is used to clear the frame buffer now, but if in line or point polygon mode, we should draw it + if (Driver->getPolygonMode() != UDriver::Filled) + { + if (!Driver->isLost()) + { + Driver->clearBuffers (CRGBA(127, 127, 127)); + } + } + } + else + { + Driver->clearBuffers(ClientCfg.BGColor); + } +} + void renderScene(bool forceFullDetail, bool bloom) { if (bloom) @@ -548,6 +572,7 @@ void renderScene(bool forceFullDetail, bool bloom) s_ForceFullDetail.backup(); s_ForceFullDetail.set(); } + clearBuffers(); renderScene(); if (forceFullDetail) { @@ -688,20 +713,6 @@ void updateWeather() // Render all scenes void renderScene() { - if (Driver->getPolygonMode() == UDriver::Filled) - { - Driver->clearZBuffer(); - } - - // Sky is used to clear the frame buffer now, but if in line or point polygon mode, we should draw it - if (Driver->getPolygonMode() != UDriver::Filled) - { - if (!Driver->isLost()) - { - Driver->clearBuffers (CRGBA(127, 127, 127)); - } - } - // Update Filter Flags Scene->enableElementRender(UScene::FilterAllMeshNoVP, Filter3D[FilterMeshNoVP]); Scene->enableElementRender(UScene::FilterAllMeshVP, Filter3D[FilterMeshVP]); @@ -1371,6 +1382,10 @@ bool mainLoop() MainCam.setTransformMode(UTransformable::RotQuat); MainCam.setPos(currViewPos); MainCam.setRotQuat(View.currentViewQuat()); + if (StereoHMD) + { + // ... + } if (StereoDisplay) { StereoDisplay->updateCamera(0, &MainCam); @@ -1575,498 +1590,548 @@ bool mainLoop() } } - /////////////////// - // SETUP CAMERAS // - /////////////////// - - if (StereoDisplay) + uint i = 0; + uint bloomStage = 0; + while ((!StereoDisplay && i == 0) || (StereoDisplay && StereoDisplay->nextPass())) { - // modify cameras for stereo display - const CViewport &vp = StereoDisplay->getCurrentViewport(); - Driver->setViewport(vp); - nlassert(Scene); - Scene->setViewport(vp); - if (SceneRoot) - { - SceneRoot->setViewport(vp); - } - MainCam.setTransformMode(UTransformable::DirectMatrix); - StereoDisplay->getCurrentMatrix(0, &MainCam); - StereoDisplay->getCurrentFrustum(0, &MainCam); - if (SceneRoot) - { - // matrix updated during commitCamera from maincam - StereoDisplay->getCurrentFrustum(1, &SceneRoot->getCam()); - } - } + ++i; + /////////////////// + // SETUP CAMERAS // + /////////////////// - // Commit camera changes - commitCamera(); - - ////////////////////////// - // RENDER THE FRAME 3D // - ////////////////////////// - - if (!ClientCfg.Light) - { - // Render - if(Render) + if (StereoDisplay) { - if (ClientCfg.Bloom) + // modify cameras for stereo display + const CViewport &vp = StereoDisplay->getCurrentViewport(); + Driver->setViewport(vp); + nlassert(Scene); + Scene->setViewport(vp); + if (SceneRoot) { - // set bloom parameters before applying bloom effect - CBloomEffect::getInstance().setSquareBloom(ClientCfg.SquareBloom); - CBloomEffect::getInstance().setDensityBloom((uint8)ClientCfg.DensityBloom); - // init bloom - CBloomEffect::getInstance().initBloom(); + SceneRoot->setViewport(vp); } - // nb : force full detail if a screenshot is asked - // todo : move outside render code - bool fullDetail = ScreenshotRequest != ScreenshotRequestNone && ClientCfg.ScreenShotFullDetail; - if (fullDetail) + //MainCam.setTransformMode(UTransformable::DirectMatrix); + StereoDisplay->getCurrentMatrix(0, &MainCam); + StereoDisplay->getCurrentFrustum(0, &MainCam); + if (SceneRoot) { - s_ForceFullDetail.backup(); - s_ForceFullDetail.set(); + // matrix updated during commitCamera from maincam + StereoDisplay->getCurrentFrustum(1, &SceneRoot->getCam()); } + } + + // Commit camera changes + commitCamera(); + + ////////////////////////// + // RENDER THE FRAME 3D // + ////////////////////////// + + if (StereoDisplay) + { + StereoDisplay->beginRenderTarget(); + } - // Render scene - renderScene(); - - if (fullDetail) + if (!StereoDisplay || StereoDisplay->wantClear()) + { + if(Render) { - s_ForceFullDetail.restore(); - } - if (ClientCfg.Bloom) - { - // apply bloom effect - CBloomEffect::getInstance().endBloom(); + if (ClientCfg.Bloom) + { + nlassert(bloomStage == 0); + // set bloom parameters before applying bloom effect + CBloomEffect::getInstance().setSquareBloom(ClientCfg.SquareBloom); + CBloomEffect::getInstance().setDensityBloom((uint8)ClientCfg.DensityBloom); + // start bloom effect (just before the first scene element render) + CBloomEffect::instance().initBloom(); + bloomStage = 1; + } } - // for that frame and - // tmp : display height grid - //static volatile bool displayHeightGrid = true; - /*if (displayHeightGrid) + // Clear buffers + clearBuffers(); + } + + if (!StereoDisplay || StereoDisplay->wantScene()) + { + if (!ClientCfg.Light) { - HeightGrid.display(*Driver); - }*/ - // display results? - if(Scene_Profile) - { - displaySceneProfiles(); - Scene_Profile= false; + // Render + if(Render) + { + // nb : force full detail if a screenshot is asked + // todo : move outside render code + bool fullDetail = ScreenshotRequest != ScreenshotRequestNone && ClientCfg.ScreenShotFullDetail; + if (fullDetail) + { + s_ForceFullDetail.backup(); + s_ForceFullDetail.set(); + } + + // Render scene + renderScene(); + + if (fullDetail) + { + s_ForceFullDetail.restore(); + } + } } - // Render the primitives + } + + if (!StereoDisplay || StereoDisplay->wantInterface3D()) + { + if (!ClientCfg.Light) + { + // Render + if (Render) + { + if (ClientCfg.Bloom && bloomStage == 1) + { + // End the actual bloom effect visible in the scene. + if (StereoDisplay) Driver->setViewport(NL3D::CViewport()); + CBloomEffect::instance().endBloom(); + if (StereoDisplay) Driver->setViewport(StereoDisplay->getCurrentViewport()); + bloomStage = 2; + } + + // for that frame and + // tmp : display height grid + //static volatile bool displayHeightGrid = true; + /*if (displayHeightGrid) + { + HeightGrid.display(*Driver); + }*/ + // display results? + if(Scene_Profile) + { + displaySceneProfiles(); + Scene_Profile= false; + } + // Render the primitives + { + H_AUTO_USE ( RZ_Client_Main_Loop_Debug ) + PrimFiles.display (*Driver); + } + } /* if (Render) */ + + // Draw Extra 3D Objects + Driver->setMatrixMode3D(MainCam); + Driver->setModelMatrix(CMatrix::Identity); + + // Display PACS borders. + if (PACSBorders) + { + H_AUTO_USE ( RZ_Client_Main_Loop_Debug ) + displayPACSBorders(); + displayPACSPrimitive(); + } + + // display Sound box + if (SoundBox) + { + H_AUTO_USE ( RZ_Client_Main_Loop_Debug ) + displaySoundBox(); + } + + // display Debug of Clusters + if (DebugClusters) + { + H_AUTO_USE ( RZ_Client_Main_Loop_Debug ) + displayDebugClusters(); + } + } /* if (!ClientCfg.Light) */ + else + { + // static UTextureFile *backgroundBitmap = NULL; + // if (backgroundBitmap == NULL) + // backgroundBitmap = Driver->createTextureFile("temp_background.tga"); + // Driver->setMatrixMode2D11(); + // Driver->drawBitmap (0.f, 0.f, 1024.f/1024.f, 1024.f/768.f, (UTexture&)*backgroundBitmap); + // Driver->setMatrixMode3D(MainCam); + + Driver->clearBuffers(CRGBA (0,0,0,0)); + displayPACSBorders(); + displayPACSPrimitive(); + } + + if (!ClientCfg.Light && !Landscape) + { + displayPACSBorders(); + } + + // Display some things not in the scene like the name, the entity path, etc. + EntitiesMngr.updatePostRender(); + + // Render the stat graphs if needed { H_AUTO_USE ( RZ_Client_Main_Loop_Debug ) - PrimFiles.display (*Driver); + CGraph::render (ShowInfos); } - } - else + + } /* if (!StereoDisplay || StereoDisplay->wantInterface3D()) */ + + if (!StereoDisplay || StereoDisplay->wantInterface2D()) { - Driver->clearBuffers(ClientCfg.BGColor); - } + // Render in 2D Mode to display 2D Interfaces and 2D texts. + Driver->setMatrixMode2D11(); - // Draw Extra 3D Objects - Driver->setMatrixMode3D(MainCam); - Driver->setModelMatrix(CMatrix::Identity); - - // Display PACS borders. - if (PACSBorders) - { - H_AUTO_USE ( RZ_Client_Main_Loop_Debug ) - displayPACSBorders(); - displayPACSPrimitive(); - } - - // display Sound box - if (SoundBox) - { - H_AUTO_USE ( RZ_Client_Main_Loop_Debug ) - displaySoundBox(); - } - - // display Debug of Clusters - if (DebugClusters) - { - H_AUTO_USE ( RZ_Client_Main_Loop_Debug ) - displayDebugClusters(); - } - - } - else - { -// static UTextureFile *backgroundBitmap = NULL; -// if (backgroundBitmap == NULL) -// backgroundBitmap = Driver->createTextureFile("temp_background.tga"); -// Driver->setMatrixMode2D11(); -// Driver->drawBitmap (0.f, 0.f, 1024.f/1024.f, 1024.f/768.f, (UTexture&)*backgroundBitmap); -// Driver->setMatrixMode3D(MainCam); - - Driver->clearBuffers(CRGBA (0,0,0,0)); - displayPACSBorders(); - displayPACSPrimitive(); - } - - if (!ClientCfg.Light && !Landscape) - { - displayPACSBorders(); - } - - // Display some things not in the scene like the name, the entity path, etc. - EntitiesMngr.updatePostRender(); - - // Render the stat graphs if needed - { - H_AUTO_USE ( RZ_Client_Main_Loop_Debug ) - CGraph::render (ShowInfos); - } - - // Render in 2D Mode to display 2D Interfaces and 2D texts. - Driver->setMatrixMode2D11(); - - // draw a big quad to represent thunder strokes - if (Render && WeatherManager.getThunderLevel() != 0.f) - { - H_AUTO_USE ( RZ_Client_Main_Loop_Render_Thunder ) - Driver->drawQuad(0, 0, 1, 1, ThunderColor); - - // TODO : boris : add sound here ! - // Needs more explosions - } - - // Update the contextual menu - { - H_AUTO_USE ( RZ_Client_Main_Loop_Interface ) - - // Update the game cursor. - ContextCur.check(); - GameContextMenu.update(); - - // validate dialogs - validateDialogs(GameContextMenu); - - // Display interface v3 - Driver->enableFog (false); - if (!Driver->isLost()) - { - if(ShowInterface) - pIMinstance->updateFrameViews (MainCam); - if(DebugUIView) - pIMinstance->displayUIViewBBoxs(DebugUIFilter); - if(DebugUICtrl) - pIMinstance->displayUICtrlBBoxs(DebugUIFilter); - if(DebugUIGroup) - pIMinstance->displayUIGroupBBoxs(DebugUIFilter); - } - - // special case in OpenGL : all scene has been display in render target, - // now, final texture is display with a quad - if(!ClientCfg.Light && ClientCfg.Bloom) - CBloomEffect::getInstance().endInterfacesDisplayBloom(); - } - - { - H_AUTO_USE ( RZ_Client_Main_Loop_Debug ) - if (!Driver->isLost()) - { - // If show information is Active. - if(ShowInfos == 1) - displayDebug(); - - // If show information is Active. - if(ShowInfos == 2) - displayNetDebug(); - - // If show information is Active. - if(ShowInfos == 4) - displayDebugFps(); - - // If show information is Active. - if(ShowInfos == 5) - displayDebugUIUnderMouse(); - - // If show information is Active. - displayStreamingDebug(); - - // If Show Help is active -> Display an help. - if(ShowHelp) - displayHelp(); - - // Yoyo: indicate profiling state - if( Profiling ) - displaySpecialTextProgress("Profiling"); - - // Display frame rate - - // Create a shadow when displaying a text. - TextContext->setShaded(true); - // Set the font size. - TextContext->setFontSize(10); - // Set the text color - TextContext->setColor(CRGBA(255,255,255)); - - // temporary values for conversions - float x, y, width, height; - - for(uint i = 0; i < ClientCfg.Logos.size(); i++) + // draw a big quad to represent thunder strokes + if (Render && WeatherManager.getThunderLevel() != 0.f) { - std::vector res; - explode(ClientCfg.Logos[i],std::string(":"), res); - if(res.size()==9 && idrawQuad(0, 0, 1, 1, ThunderColor); + + // TODO : boris : add sound here ! + // Needs more explosions + } + + // Update the contextual menu + { + H_AUTO_USE ( RZ_Client_Main_Loop_Interface ) + + // Update the game cursor. + ContextCur.check(); + GameContextMenu.update(); + + // validate dialogs + validateDialogs(GameContextMenu); + + // Display interface v3 + Driver->enableFog (false); + if (!Driver->isLost()) { - fromString(res[5], x); - fromString(res[6], y); - fromString(res[7], width); - fromString(res[8], height); - Driver->drawBitmap(x/(float)ClientCfg.Width, y/(float)ClientCfg.Height, width/(float)ClientCfg.Width, height/(float)ClientCfg.Height, *LogoBitmaps[i]); + if(ShowInterface) + pIMinstance->updateFrameViews (MainCam); + if(DebugUIView) + pIMinstance->displayUIViewBBoxs(DebugUIFilter); + if(DebugUICtrl) + pIMinstance->displayUICtrlBBoxs(DebugUIFilter); + if(DebugUIGroup) + pIMinstance->displayUIGroupBBoxs(DebugUIFilter); + } + + // special case in OpenGL : all scene has been display in render target, + // now, final texture is display with a quad + if (!ClientCfg.Light && ClientCfg.Bloom && Render && bloomStage == 2) + { + // End bloom effect system after drawing the 3d interface (z buffer related). + if (StereoDisplay) Driver->setViewport(NL3D::CViewport()); + CBloomEffect::instance().endInterfacesDisplayBloom(); + if (StereoDisplay) Driver->setViewport(StereoDisplay->getCurrentViewport()); + bloomStage = 0; } } - } - } - // FPS - { - static TTicks oldTick = CTime::getPerformanceTime(); - TTicks newTick = CTime::getPerformanceTime(); - double deltaTime = CTime::ticksToSecond (newTick-oldTick); - oldTick = newTick; - smoothFPS.addValue((float)deltaTime); - moreSmoothFPS.addValue((float)deltaTime); - deltaTime = smoothFPS.getSmoothValue (); - if (deltaTime > 0.0) - { - CCDBNodeLeaf*pNL = NLGUI::CDBManager::getInstance()->getDbProp("UI:VARIABLES:FPS"); - pNL->setValue64((sint64)(1.f/deltaTime)); - } - } - - // R2ED post render update - if (ClientCfg.R2EDEnabled) - { - // IMPORTANT : this should be called after CEntitiesMngr::updatePostRender() because - // entity may be added / removed there ! - R2::getEditor().updateAfterRender(); - } - - // Update FXs (remove them). - FXMngr.update(); - - // Detect disconnection / server down: display information text - // but keep the rendering so that the player can remember where he is - // and what he was doing. He can't move because the connection quality returns false. - - if ((connectionState == CNetworkConnection::Disconnect) && (lastConnectionState != CNetworkConnection::Disconnect) && (!FarTP.isFarTPInProgress())) - { - UserControls.stopFreeLook(); // let the player click on Exit - pIMinstance->messageBoxWithHelp(CI18N::get("uiDisconnected")); - - // If we have started a Far TP sequence and are waiting for onServerQuitOK() - // from the EGS, resume the sequence because the EGS is down and won't reply. - FarTP.onServerQuitOk(); - } - - // Yoyo: MovieShooter. - if(MovieShooterSaving) - { - H_AUTO_USE ( RZ_Client_Main_Loop_Debug ) - - // Add the buffer frame to the movie. - if(!MovieShooter.addFrame(TimeInSec, Driver)) - { - // Fail to add the frame => abort. - endMovieShooting(); - } - else - { - // Ok, just add a display. - displaySpecialTextProgress("MovieShooting"); - } - } - - if (isRecordingCamera()) - { - displaySpecialTextProgress("CameraRecording"); - } - - // Temp for weather test - if (ClientCfg.ManualWeatherSetup && ContinentMngr.cur() && ContinentMngr.cur()->WeatherFunction) - { - H_AUTO_USE ( RZ_Client_Main_Loop_Debug ) - static float displayHourDelta = 0.04f; // static for edition during debug.. - - // Display weather function - if (DisplayWeatherFunction) - { - uint64 currDay = RT.getRyzomDay(); - float currHour = DayNightCycleHour; - float singleHourDelta = fmodf(currHour, 1.f); - uint32 wndWidth, wndHeight; - Driver->getWindowSize(wndWidth, wndHeight); - Driver->setMatrixMode2D(CFrustum(0, 800, 600, 0, 0, 1, false)); - const float lineHeight = 100.f; - - // draw the weather function - for(uint x = 0; x < wndWidth; ++x) { - float weatherValue; - if(ContinentMngr.cur()) - weatherValue = ::getBlendedWeather(currDay, currHour, *WeatherFunctionParams, ContinentMngr.cur()->WeatherFunction); + H_AUTO_USE ( RZ_Client_Main_Loop_Debug ) + if (!Driver->isLost()) + { + // If show information is Active. + if(ShowInfos == 1) + displayDebug(); + + // If show information is Active. + if(ShowInfos == 2) + displayNetDebug(); + + // If show information is Active. + if(ShowInfos == 4) + displayDebugFps(); + + // If show information is Active. + if(ShowInfos == 5) + displayDebugUIUnderMouse(); + + // If show information is Active. + displayStreamingDebug(); + + // If Show Help is active -> Display an help. + if(ShowHelp) + displayHelp(); + + // Yoyo: indicate profiling state + if( Profiling ) + displaySpecialTextProgress("Profiling"); + + // Display frame rate + + // Create a shadow when displaying a text. + TextContext->setShaded(true); + // Set the font size. + TextContext->setFontSize(10); + // Set the text color + TextContext->setColor(CRGBA(255,255,255)); + + // temporary values for conversions + float x, y, width, height; + + for(uint i = 0; i < ClientCfg.Logos.size(); i++) + { + std::vector res; + explode(ClientCfg.Logos[i],std::string(":"), res); + if(res.size()==9 && idrawBitmap(x/(float)ClientCfg.Width, y/(float)ClientCfg.Height, width/(float)ClientCfg.Width, height/(float)ClientCfg.Height, *LogoBitmaps[i]); + } + } + } + } + + // FPS + { + static TTicks oldTick = CTime::getPerformanceTime(); + TTicks newTick = CTime::getPerformanceTime(); + double deltaTime = CTime::ticksToSecond (newTick-oldTick); + oldTick = newTick; + smoothFPS.addValue((float)deltaTime); + moreSmoothFPS.addValue((float)deltaTime); + deltaTime = smoothFPS.getSmoothValue (); + if (deltaTime > 0.0) + { + CCDBNodeLeaf*pNL = NLGUI::CDBManager::getInstance()->getDbProp("UI:VARIABLES:FPS"); + pNL->setValue64((sint64)(1.f/deltaTime)); + } + } + + // R2ED post render update + if (ClientCfg.R2EDEnabled) + { + // IMPORTANT : this should be called after CEntitiesMngr::updatePostRender() because + // entity may be added / removed there ! + R2::getEditor().updateAfterRender(); + } + + // Update FXs (remove them). + FXMngr.update(); + + // Detect disconnection / server down: display information text + // but keep the rendering so that the player can remember where he is + // and what he was doing. He can't move because the connection quality returns false. + + if ((connectionState == CNetworkConnection::Disconnect) && (lastConnectionState != CNetworkConnection::Disconnect) && (!FarTP.isFarTPInProgress())) + { + UserControls.stopFreeLook(); // let the player click on Exit + pIMinstance->messageBoxWithHelp(CI18N::get("uiDisconnected")); + + // If we have started a Far TP sequence and are waiting for onServerQuitOK() + // from the EGS, resume the sequence because the EGS is down and won't reply. + FarTP.onServerQuitOk(); + } + + // Yoyo: MovieShooter. + if(MovieShooterSaving) + { + H_AUTO_USE ( RZ_Client_Main_Loop_Debug ) + + // Add the buffer frame to the movie. + if(!MovieShooter.addFrame(TimeInSec, Driver)) + { + // Fail to add the frame => abort. + endMovieShooting(); + } else - weatherValue = ::getBlendedWeather(currDay, currHour, *WeatherFunctionParams, 0); - - NLMISC::clamp(weatherValue, 0.f, 1.f); - CRGBA seasonToColor[EGSPD::CSeason::Invalid] = { - CRGBA::Green, - CRGBA::Yellow, - CRGBA::Red, - CRGBA::Blue - }; - Driver->drawLine((float) x, 0.f, (float) x, lineHeight * weatherValue, seasonToColor[CRyzomTime::getSeasonByDay((uint32)currDay)]); - currHour += displayHourDelta; - if (currHour >= 24.f) - { - ++currDay; - currHour -= 24.f; - } - singleHourDelta += displayHourDelta; - if (singleHourDelta >= 1.f) - { - singleHourDelta -= 1.f; - Driver->drawLine((float) x, 100.f, (float) x, 130, CRGBA::Red); + // Ok, just add a display. + displaySpecialTextProgress("MovieShooting"); } } - if(ContinentMngr.cur()) + if (isRecordingCamera()) { - // draw lines for current weather setups - uint numWeatherSetups = ContinentMngr.cur()->WeatherFunction[CurrSeason].getNumWeatherSetups(); - for (uint y = 0; y < numWeatherSetups; ++y) + displaySpecialTextProgress("CameraRecording"); + } + + // Temp for weather test + if (ClientCfg.ManualWeatherSetup && ContinentMngr.cur() && ContinentMngr.cur()->WeatherFunction) + { + H_AUTO_USE ( RZ_Client_Main_Loop_Debug ) + static float displayHourDelta = 0.04f; // static for edition during debug.. + + // Display weather function + if (DisplayWeatherFunction) { - float py = lineHeight * (y / (float) numWeatherSetups); - Driver->drawLine(0.f, py, 800.f, py, CRGBA::Magenta); + uint64 currDay = RT.getRyzomDay(); + float currHour = DayNightCycleHour; + float singleHourDelta = fmodf(currHour, 1.f); + uint32 wndWidth, wndHeight; + Driver->getWindowSize(wndWidth, wndHeight); + Driver->setMatrixMode2D(CFrustum(0, 800, 600, 0, 0, 1, false)); + const float lineHeight = 100.f; + + // draw the weather function + for(uint x = 0; x < wndWidth; ++x) + { + float weatherValue; + if(ContinentMngr.cur()) + weatherValue = ::getBlendedWeather(currDay, currHour, *WeatherFunctionParams, ContinentMngr.cur()->WeatherFunction); + else + weatherValue = ::getBlendedWeather(currDay, currHour, *WeatherFunctionParams, 0); + + NLMISC::clamp(weatherValue, 0.f, 1.f); + CRGBA seasonToColor[EGSPD::CSeason::Invalid] = + { + CRGBA::Green, + CRGBA::Yellow, + CRGBA::Red, + CRGBA::Blue + }; + Driver->drawLine((float) x, 0.f, (float) x, lineHeight * weatherValue, seasonToColor[CRyzomTime::getSeasonByDay((uint32)currDay)]); + currHour += displayHourDelta; + if (currHour >= 24.f) + { + ++currDay; + currHour -= 24.f; + } + singleHourDelta += displayHourDelta; + if (singleHourDelta >= 1.f) + { + singleHourDelta -= 1.f; + Driver->drawLine((float) x, 100.f, (float) x, 130, CRGBA::Red); + } + } + + if(ContinentMngr.cur()) + { + // draw lines for current weather setups + uint numWeatherSetups = ContinentMngr.cur()->WeatherFunction[CurrSeason].getNumWeatherSetups(); + for (uint y = 0; y < numWeatherSetups; ++y) + { + float py = lineHeight * (y / (float) numWeatherSetups); + Driver->drawLine(0.f, py, 800.f, py, CRGBA::Magenta); + } + } + } + + // Ctrl+ & Ctrl- change the weather value + if (Actions.valide ("inc_time")) + { + ManualWeatherValue += DT * 0.04f; + } + if (Actions.valide ("dec_time")) + { + ManualWeatherValue -= DT * 0.04f; + } + NLMISC::clamp(ManualWeatherValue, 0.f, 1.f); + + if (ForcedDayNightCycleHour < 0) // if time is forced then can't change it manually ... + { + // Ctrl-K increase hour + if (Actions.valide ("inc_hour")) + { + RT.increaseTickOffset( (uint32)(2000 * displayHourDelta) ); + RT.updateRyzomClock(NetMngr.getCurrentServerTick(), ryzomGetLocalTime() * 0.001); + } + + // Ctrl-L decrease hour + if (Actions.valide ("dec_hour")) + { + RT.decreaseTickOffset( (uint32)(2000 * displayHourDelta) ); + RT.updateRyzomClock(NetMngr.getCurrentServerTick(), ryzomGetLocalTime() * 0.001); + CTimedFXManager::getInstance().setDate(CClientDate(RT.getRyzomDay(), (float) RT.getRyzomTime())); + if (IGCallbacks) + { + IGCallbacks->changeSeason(); // the season doesn't change, but this force fxs to be recreated + } + } + } + + // Ctrl-M generate statistics in a file + /* + if (Actions.valide ("weather_stats")) + { + // Only usable if there is a continent loaded. + if(ContinentMngr.cur()) + CPredictWeather::generateWeatherStats("weather_stats.csv", WeatherFunctionParams, ContinentMngr.cur()->WeatherFunction); + }*/ + + // Ctrl-B decrease display factor + if (Actions.valide ("dec_display_factor")) + { + displayHourDelta *= 0.90f; + } + // Ctrl-J increase display factor + if (Actions.valide ("inc_display_factor")) + { + displayHourDelta *= 1.1f; + displayHourDelta = std::min(1000.f, displayHourDelta); } } - } - // Ctrl+ & Ctrl- change the weather value - if (Actions.valide ("inc_time")) - { - ManualWeatherValue += DT * 0.04f; - } - if (Actions.valide ("dec_time")) - { - ManualWeatherValue -= DT * 0.04f; - } - NLMISC::clamp(ManualWeatherValue, 0.f, 1.f); - - if (ForcedDayNightCycleHour < 0) // if time is forced then can't change it manually ... - { - // Ctrl-K increase hour - if (Actions.valide ("inc_hour")) + // Ctrl-AltGR-Z show timed FXs + if (ShowTimedFX) { - RT.increaseTickOffset( (uint32)(2000 * displayHourDelta) ); - RT.updateRyzomClock(NetMngr.getCurrentServerTick(), ryzomGetLocalTime() * 0.001); - } - - // Ctrl-L decrease hour - if (Actions.valide ("dec_hour")) - { - RT.decreaseTickOffset( (uint32)(2000 * displayHourDelta) ); - RT.updateRyzomClock(NetMngr.getCurrentServerTick(), ryzomGetLocalTime() * 0.001); - CTimedFXManager::getInstance().setDate(CClientDate(RT.getRyzomDay(), (float) RT.getRyzomTime())); - if (IGCallbacks) + if (!Driver->isLost()) { - IGCallbacks->changeSeason(); // the season doesn't change, but this force fxs to be recreated + CTimedFXManager::getInstance().displayFXBoxes(ShowTimedFXMode); } } - } - // Ctrl-M generate statistics in a file - /* - if (Actions.valide ("weather_stats")) - { - // Only usable if there is a continent loaded. - if(ContinentMngr.cur()) - CPredictWeather::generateWeatherStats("weather_stats.csv", WeatherFunctionParams, ContinentMngr.cur()->WeatherFunction); - }*/ + #if !FINAL_VERSION + CVector2f camPos(Scene->getCam().getPos().x, Scene->getCam().getPos().y); + if (!ClientCfg.Light) + { + if (DisplayMicroLifeZones) + { + CMicroLifeManager::getInstance().renderMLZones(camPos); + } + } + if (DisplayWaterMap) + { + if (ContinentMngr.cur()) + { + ContinentMngr.cur()->WaterMap.render(camPos); + } + } + #endif - // Ctrl-B decrease display factor - if (Actions.valide ("dec_display_factor")) - { - displayHourDelta *= 0.90f; - } - // Ctrl-J increase display factor - if (Actions.valide ("inc_display_factor")) - { - displayHourDelta *= 1.1f; - displayHourDelta = std::min(1000.f, displayHourDelta); - } - } + #ifdef NL_DEBUG + if (!ClientCfg.Light) + { + if (DisplayMicroLifeActiveTiles) + { + CMicroLifeManager::getInstance().renderActiveTiles(); + } + } + #endif + // tmp : debug of ground fxs + //TestGroundFX.displayFXBoxes(); - // Ctrl-AltGR-Z show timed FXs - if (ShowTimedFX) - { - if (!Driver->isLost()) - { - CTimedFXManager::getInstance().displayFXBoxes(ShowTimedFXMode); - } - } - -#if !FINAL_VERSION - CVector2f camPos(Scene->getCam().getPos().x, Scene->getCam().getPos().y); - if (!ClientCfg.Light) - { - if (DisplayMicroLifeZones) + // Temp for sound debug { - CMicroLifeManager::getInstance().renderMLZones(camPos); - } - } - if (DisplayWaterMap) - { - if (ContinentMngr.cur()) - { - ContinentMngr.cur()->WaterMap.render(camPos); - } - } + H_AUTO_USE ( RZ_Client_Main_Loop_Debug ) + + if (SoundMngr != 0) + { + static bool drawSound = false; + static float camHeigh = 150.0f; + + #if FINAL_VERSION + if (ClientCfg.ExtendedCommands) #endif + if (Actions.valide ("draw_sound")) + drawSound = !drawSound; - #ifdef NL_DEBUG - if (!ClientCfg.Light) - { - if (DisplayMicroLifeActiveTiles) - { - CMicroLifeManager::getInstance().renderActiveTiles(); + if (Actions.valide ("inc_camera_height")) + camHeigh -= 10.0f; + if (Actions.valide ("dec_camera_height")) + camHeigh += 10.0f; + + if (drawSound) + SoundMngr->drawSounds(camHeigh); + } } - } - #endif - // tmp : debug of ground fxs - //TestGroundFX.displayFXBoxes(); + } /* if (!StereoDisplay || StereoDisplay->wantInterface2D()) */ - // Temp for sound debug - { - H_AUTO_USE ( RZ_Client_Main_Loop_Debug ) - - if (SoundMngr != 0) + if (StereoDisplay) { - static bool drawSound = false; - static float camHeigh = 150.0f; - -#if FINAL_VERSION - if (ClientCfg.ExtendedCommands) -#endif - if (Actions.valide ("draw_sound")) - drawSound = !drawSound; - - if (Actions.valide ("inc_camera_height")) - camHeigh -= 10.0f; - if (Actions.valide ("dec_camera_height")) - camHeigh += 10.0f; - - if (drawSound) - SoundMngr->drawSounds(camHeigh); + StereoDisplay->endRenderTarget(); } - } + } /* stereo pass */ // Draw to screen. static CQuat MainCamOri; From cb9e5762db743b74c716275820ec0646a428a94b Mon Sep 17 00:00:00 2001 From: kaetemi Date: Fri, 5 Jul 2013 03:42:38 +0200 Subject: [PATCH 083/313] Don't render to texture when in wireframe mode, re #43 --- code/nel/src/3d/stereo_debugger.cpp | 59 ++++++++----- code/nel/src/3d/stereo_ovr.cpp | 123 ++++++++++++++++------------ code/ryzom/client/src/main_loop.cpp | 11 +-- 3 files changed, 114 insertions(+), 79 deletions(-) diff --git a/code/nel/src/3d/stereo_debugger.cpp b/code/nel/src/3d/stereo_debugger.cpp index 036cb7e1b..9255fa812 100644 --- a/code/nel/src/3d/stereo_debugger.cpp +++ b/code/nel/src/3d/stereo_debugger.cpp @@ -257,20 +257,41 @@ void CStereoDebugger::getClippingFrustum(uint cid, NL3D::UCamera *camera) const /// Is there a next pass bool CStereoDebugger::nextPass() { - switch (m_Stage) + if (m_Driver->getPolygonMode() == UDriver::Filled) { - case 0: - ++m_Stage; - m_SubStage = 0; - return true; - case 1: - ++m_Stage; - m_SubStage = 0; - return true; - case 2: - m_Stage = 0; - m_SubStage = 0; - return false; + switch (m_Stage) + { + case 0: + ++m_Stage; + m_SubStage = 0; + return true; + case 1: + ++m_Stage; + m_SubStage = 0; + return true; + case 2: + ++m_Stage; + m_SubStage = 0; + return true; + case 3: + m_Stage = 0; + m_SubStage = 0; + return false; + } + } + else + { + switch (m_Stage) + { + case 0: + ++m_Stage; + m_SubStage = 0; + return true; + case 1: + m_Stage = 0; + m_SubStage = 0; + return false; + } } } @@ -303,34 +324,34 @@ void CStereoDebugger::getCurrentMatrix(uint cid, NL3D::UCamera *camera) const bool CStereoDebugger::wantClear() { m_SubStage = 1; - return true; + return m_Stage != 3; } /// The 3D scene bool CStereoDebugger::wantScene() { m_SubStage = 2; - return true; + return m_Stage != 3; } /// Interface within the 3D scene bool CStereoDebugger::wantInterface3D() { m_SubStage = 3; - return true; + return m_Stage == 3; } /// 2D Interface bool CStereoDebugger::wantInterface2D() { m_SubStage = 4; - return true; + return m_Stage == 3; } /// Returns true if a new render target was set, always fase if not using render targets bool CStereoDebugger::beginRenderTarget() { - if (m_Driver) + if (m_Stage != 3 && m_Driver && (m_Driver->getPolygonMode() == UDriver::Filled)) { if (m_Stage % 2) static_cast(m_Driver)->setRenderTarget(*m_RightTexU, 0, 0, 0, 0); else static_cast(m_Driver)->setRenderTarget(*m_LeftTexU, 0, 0, 0, 0); @@ -342,7 +363,7 @@ bool CStereoDebugger::beginRenderTarget() /// Returns true if a render target was fully drawn, always false if not using render targets bool CStereoDebugger::endRenderTarget() { - if (m_Driver) + if (m_Stage != 3 && m_Driver && (m_Driver->getPolygonMode() == UDriver::Filled)) { CTextureUser cu; (static_cast(m_Driver))->setRenderTarget(cu); diff --git a/code/nel/src/3d/stereo_ovr.cpp b/code/nel/src/3d/stereo_ovr.cpp index e7e31d557..2b91e1836 100644 --- a/code/nel/src/3d/stereo_ovr.cpp +++ b/code/nel/src/3d/stereo_ovr.cpp @@ -359,54 +359,71 @@ bool CStereoOVR::nextPass() nlassert(width == m_DevicePtr->HMDInfo.HResolution); nlassert(height == m_DevicePtr->HMDInfo.VResolution); - switch (m_Stage) + if (m_Driver->getPolygonMode() == UDriver::Filled) { - case 0: - ++m_Stage; - m_SubStage = 0; - // stage 1: - // (initBloom) - // clear buffer - // draw scene left - return true; - case 1: - ++m_Stage; - m_SubStage = 0; - // stage 2: - // draw scene right - return true; - case 2: - ++m_Stage; - m_SubStage = 0; - // stage 3: - // (endBloom) - // draw interface 3d left - return true; - case 3: - ++m_Stage; - m_SubStage = 0; - // stage 4: - // draw interface 3d right - return true; - case 4: - ++m_Stage; - m_SubStage = 0; - // stage 5: - // (endInterfacesDisplayBloom) - // draw interface 2d left - return true; - case 5: - ++m_Stage; - m_SubStage = 0; - // stage 6: - // draw interface 2d right - return true; - case 6: - m_Stage = 0; - m_SubStage = 0; - // present - m_OrientationCached = false; - return false; + switch (m_Stage) + { + case 0: + ++m_Stage; + m_SubStage = 0; + // stage 1: + // (initBloom) + // clear buffer + // draw scene left + return true; + case 1: + ++m_Stage; + m_SubStage = 0; + // stage 2: + // draw scene right + return true; + case 2: + ++m_Stage; + m_SubStage = 0; + // stage 3: + // (endBloom) + // draw interface 3d left + return true; + case 3: + ++m_Stage; + m_SubStage = 0; + // stage 4: + // draw interface 3d right + return true; + case 4: + ++m_Stage; + m_SubStage = 0; + // stage 5: + // (endInterfacesDisplayBloom) + // draw interface 2d left + return true; + case 5: + ++m_Stage; + m_SubStage = 0; + // stage 6: + // draw interface 2d right + return true; + case 6: + m_Stage = 0; + m_SubStage = 0; + // present + m_OrientationCached = false; + return false; + } + } + else + { + switch (m_Stage) + { + case 0: + ++m_Stage; + m_SubStage = 0; + return true; + case 1: + m_Stage = 0; + m_SubStage = 0; + return false; + } } nlerror("Invalid stage"); m_Stage = 0; @@ -459,7 +476,7 @@ bool CStereoOVR::wantClear() m_SubStage = 1; return true; } - return false; + return m_Driver->getPolygonMode() != UDriver::Filled; } bool CStereoOVR::wantScene() @@ -471,7 +488,7 @@ bool CStereoOVR::wantScene() m_SubStage = 2; return true; } - return false; + return m_Driver->getPolygonMode() != UDriver::Filled; } bool CStereoOVR::wantInterface3D() @@ -483,7 +500,7 @@ bool CStereoOVR::wantInterface3D() m_SubStage = 3; return true; } - return false; + return m_Driver->getPolygonMode() != UDriver::Filled; } bool CStereoOVR::wantInterface2D() @@ -495,7 +512,7 @@ bool CStereoOVR::wantInterface2D() m_SubStage = 4; return true; } - return false; + return m_Driver->getPolygonMode() != UDriver::Filled; } @@ -504,7 +521,7 @@ bool CStereoOVR::beginRenderTarget() { // render target always set before driver clear // nlassert(m_SubStage <= 1); - if (m_Driver && m_Stage == 1) + if (m_Driver && m_Stage == 1 && (m_Driver->getPolygonMode() == UDriver::Filled)) { static_cast(m_Driver)->setRenderTarget(*m_BarrelTexU, 0, 0, 0, 0); return true; @@ -517,7 +534,7 @@ bool CStereoOVR::endRenderTarget() { // after rendering of course // nlassert(m_SubStage > 1); - if (m_Driver && m_Stage == 6) // set to 4 to turn off distortion of 2d gui + if (m_Driver && m_Stage == 6 && (m_Driver->getPolygonMode() == UDriver::Filled)) // set to 4 to turn off distortion of 2d gui { CTextureUser cu; (static_cast(m_Driver))->setRenderTarget(cu); diff --git a/code/ryzom/client/src/main_loop.cpp b/code/ryzom/client/src/main_loop.cpp index 095236ef0..7b7c0bd9c 100644 --- a/code/ryzom/client/src/main_loop.cpp +++ b/code/ryzom/client/src/main_loop.cpp @@ -1627,14 +1627,11 @@ bool mainLoop() // RENDER THE FRAME 3D // ////////////////////////// - if (StereoDisplay) - { - StereoDisplay->beginRenderTarget(); - } + bool stereoRenderTarget = (StereoDisplay != NULL) && StereoDisplay->beginRenderTarget(); if (!StereoDisplay || StereoDisplay->wantClear()) { - if(Render) + if (Render) { if (ClientCfg.Bloom) { @@ -1777,14 +1774,14 @@ bool mainLoop() Driver->setMatrixMode2D11(); // draw a big quad to represent thunder strokes - if (Render && WeatherManager.getThunderLevel() != 0.f) + /*if (Render && WeatherManager.getThunderLevel() != 0.f) { H_AUTO_USE ( RZ_Client_Main_Loop_Render_Thunder ) Driver->drawQuad(0, 0, 1, 1, ThunderColor); // TODO : boris : add sound here ! // Needs more explosions - } + }*/ // Update the contextual menu { From a1ded7183ae128faa91f52573142ea317e6905ac Mon Sep 17 00:00:00 2001 From: kaetemi Date: Fri, 5 Jul 2013 04:09:53 +0200 Subject: [PATCH 084/313] Handle head orientation in ryzom client, ref #43 --- code/ryzom/client/src/main_loop.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/code/ryzom/client/src/main_loop.cpp b/code/ryzom/client/src/main_loop.cpp index 7b7c0bd9c..c76a6964c 100644 --- a/code/ryzom/client/src/main_loop.cpp +++ b/code/ryzom/client/src/main_loop.cpp @@ -1384,7 +1384,15 @@ bool mainLoop() MainCam.setRotQuat(View.currentViewQuat()); if (StereoHMD) { - // ... + NLMISC::CQuat hmdOrient = StereoHMD->getOrientation(); + NLMISC::CMatrix camMatrix = MainCam.getMatrix(); + NLMISC::CMatrix hmdMatrix; + hmdMatrix.setRot(hmdOrient); + NLMISC::CMatrix posMatrix; // minimal head modeling, will be changed in the future + posMatrix.translate(StereoHMD->getEyePosition()); + NLMISC::CMatrix mat = ((camMatrix * hmdMatrix) * posMatrix); + MainCam.setPos(mat.getPos()); + MainCam.setRotQuat(mat.getRot()); } if (StereoDisplay) { From 67ebf920ec3de6f5d26e7e373c6b11d87d45b4b5 Mon Sep 17 00:00:00 2001 From: Michael Witrant Date: Sat, 6 Jul 2013 19:33:07 +0200 Subject: [PATCH 085/313] Added LibOVR finder and Linux support, ref #43 --- code/CMakeModules/FindLibOVR.cmake | 60 ++++++++++++++++++++++++++++++ code/nel/CMakeLists.txt | 2 + code/nel/src/3d/CMakeLists.txt | 6 ++- 3 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 code/CMakeModules/FindLibOVR.cmake diff --git a/code/CMakeModules/FindLibOVR.cmake b/code/CMakeModules/FindLibOVR.cmake new file mode 100644 index 000000000..0ef4e1f47 --- /dev/null +++ b/code/CMakeModules/FindLibOVR.cmake @@ -0,0 +1,60 @@ +# - Locate LibOVR library +# This module defines +# LIBOVR_LIBRARIES, the libraries to link against +# LIBOVR_FOUND, if false, do not try to link to LIBOVR +# LIBOVR_INCLUDE_DIR, where to find headers. + +IF(LIBOVR_LIBRARIES AND LIBOVR_INCLUDE_DIR) + # in cache already + SET(LIBOVR_FIND_QUIETLY TRUE) +ENDIF(LIBOVR_LIBRARIES AND LIBOVR_INCLUDE_DIR) + +FIND_PATH(LIBOVR_INCLUDE_DIR + OVR.h + PATHS + $ENV{LIBOVR_DIR}/Include + /usr/local/include + /usr/include + /sw/include + /opt/local/include + /opt/csw/include + /opt/include +) + +IF(TARGET_CPU STREQUAL "x86_64") + SET(LIBOVR_LIBRARY_BUILD_PATH "Lib/Linux/Release/x86_64") +ELSE(TARGET_CPU STREQUAL "x86_64") + SET(LIBOVR_LIBRARY_BUILD_PATH "Lib/Linux/Release/i386") +ENDIF(TARGET_CPU STREQUAL "x86_64") + +FIND_LIBRARY(LIBOVR_LIBRARY + NAMES ovr + PATHS + $ENV{LIBOVR_DIR}/${LIBOVR_LIBRARY_BUILD_PATH} + /usr/local/lib + /usr/lib + /usr/local/X11R6/lib + /usr/X11R6/lib + /sw/lib + /opt/local/lib + /opt/csw/lib + /opt/lib + /usr/freeware/lib64 +) + +IF(LIBOVR_LIBRARY AND LIBOVR_INCLUDE_DIR) + IF(NOT LIBOVR_FIND_QUIETLY) + MESSAGE(STATUS "Found LibOVR: ${LIBOVR_LIBRARY}") + ENDIF(NOT LIBOVR_FIND_QUIETLY) + SET(LIBOVR_FOUND "YES") + SET(LIBOVR_DEFINITIONS "-DHAVE_LIBOVR") + IF(UNIX) + SET(LIBOVR_LIBRARIES ${LIBOVR_LIBRARY} X11 Xinerama udev pthread) + ELSE(UNIX) + SET(LIBOVR_LIBRARIES ${LIBOVR_LIBRARY}) + ENDIF(UNIX) +ELSE(LIBOVR_LIBRARY AND LIBOVR_INCLUDE_DIR) + IF(NOT LIBOVR_FIND_QUIETLY) + MESSAGE(STATUS "Warning: Unable to find LibOVR!") + ENDIF(NOT LIBOVR_FIND_QUIETLY) +ENDIF(LIBOVR_LIBRARY AND LIBOVR_INCLUDE_DIR) diff --git a/code/nel/CMakeLists.txt b/code/nel/CMakeLists.txt index 745278cd7..1ac54f332 100644 --- a/code/nel/CMakeLists.txt +++ b/code/nel/CMakeLists.txt @@ -41,6 +41,8 @@ IF(WITH_GTK) FIND_PACKAGE(GTK2) ENDIF(WITH_GTK) +FIND_PACKAGE(LibOVR) + IF(WITH_INSTALL_LIBRARIES) IF(UNIX) SET(prefix ${CMAKE_INSTALL_PREFIX}) diff --git a/code/nel/src/3d/CMakeLists.txt b/code/nel/src/3d/CMakeLists.txt index ffdc876a9..b92f58dba 100644 --- a/code/nel/src/3d/CMakeLists.txt +++ b/code/nel/src/3d/CMakeLists.txt @@ -701,9 +701,9 @@ SOURCE_GROUP(Stereo FILES NL_TARGET_LIB(nel3d ${HEADERS} ${SRC}) -INCLUDE_DIRECTORIES(${LIBXML2_INCLUDE_DIR} ${FREETYPE_INCLUDE_DIRS}) +INCLUDE_DIRECTORIES(${LIBXML2_INCLUDE_DIR} ${FREETYPE_INCLUDE_DIRS} ${LIBOVR_INCLUDE_DIR}) -TARGET_LINK_LIBRARIES(nel3d nelmisc ${FREETYPE_LIBRARY}) +TARGET_LINK_LIBRARIES(nel3d nelmisc ${FREETYPE_LIBRARY} ${LIBOVR_LIBRARIES}) SET_TARGET_PROPERTIES(nel3d PROPERTIES LINK_INTERFACE_LIBRARIES "") NL_DEFAULT_PROPS(nel3d "NeL, Library: NeL 3D") NL_ADD_RUNTIME_FLAGS(nel3d) @@ -713,6 +713,8 @@ NL_ADD_LIB_SUFFIX(nel3d) ADD_DEFINITIONS(${LIBXML2_DEFINITIONS}) +ADD_DEFINITIONS(${LIBOVR_DEFINITIONS}) + IF(WITH_PCH) ADD_NATIVE_PRECOMPILED_HEADER(nel3d ${CMAKE_CURRENT_SOURCE_DIR}/std3d.h ${CMAKE_CURRENT_SOURCE_DIR}/std3d.cpp) ENDIF(WITH_PCH) From f3936225734ea33332158a2d2106e6b33a966fc1 Mon Sep 17 00:00:00 2001 From: Michael Witrant Date: Sat, 6 Jul 2013 19:34:28 +0200 Subject: [PATCH 086/313] Fixed Linux build failure because a temporary pointer was passed, ref #43 --- code/ryzom/client/src/main_loop.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/code/ryzom/client/src/main_loop.cpp b/code/ryzom/client/src/main_loop.cpp index c76a6964c..b273984ff 100644 --- a/code/ryzom/client/src/main_loop.cpp +++ b/code/ryzom/client/src/main_loop.cpp @@ -1399,7 +1399,8 @@ bool mainLoop() StereoDisplay->updateCamera(0, &MainCam); if (SceneRoot) { - StereoDisplay->updateCamera(1, &SceneRoot->getCam()); + UCamera cam = SceneRoot->getCam(); + StereoDisplay->updateCamera(1, &cam); } } @@ -1624,7 +1625,8 @@ bool mainLoop() if (SceneRoot) { // matrix updated during commitCamera from maincam - StereoDisplay->getCurrentFrustum(1, &SceneRoot->getCam()); + UCamera cam = SceneRoot->getCam(); + StereoDisplay->getCurrentFrustum(1, &cam); } } From ed8e0edfd0555a030939f596a036e6bd1f299583 Mon Sep 17 00:00:00 2001 From: Michael Witrant Date: Sat, 6 Jul 2013 19:55:08 +0200 Subject: [PATCH 087/313] Added Win32 and Apple LibOVR build paths --- code/CMakeModules/FindLibOVR.cmake | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/code/CMakeModules/FindLibOVR.cmake b/code/CMakeModules/FindLibOVR.cmake index 0ef4e1f47..1403a4b2c 100644 --- a/code/CMakeModules/FindLibOVR.cmake +++ b/code/CMakeModules/FindLibOVR.cmake @@ -21,11 +21,21 @@ FIND_PATH(LIBOVR_INCLUDE_DIR /opt/include ) -IF(TARGET_CPU STREQUAL "x86_64") - SET(LIBOVR_LIBRARY_BUILD_PATH "Lib/Linux/Release/x86_64") -ELSE(TARGET_CPU STREQUAL "x86_64") - SET(LIBOVR_LIBRARY_BUILD_PATH "Lib/Linux/Release/i386") -ENDIF(TARGET_CPU STREQUAL "x86_64") +IF(UNIX) + IF(TARGET_X64) + SET(LIBOVR_LIBRARY_BUILD_PATH "Lib/Linux/Release/x86_64") + ELSE(TARGET_X64) + SET(LIBOVR_LIBRARY_BUILD_PATH "Lib/Linux/Release/i386") + ENDIF(TARGET_X64) +ELSEIF(APPLE) + SET(LIBOVR_LIBRARY_BUILD_PATH "Lib/MacOS/Release") +ELSEIF(WIN32) + IF(TARGET_X64) + SET(LIBOVR_LIBRARY_BUILD_PATH "Lib/x64") + ELSE(TARGET_X64) + SET(LIBOVR_LIBRARY_BUILD_PATH "Lib/Win32") + ENDIF(TARGET_X64) +ENDIF(UNIX) FIND_LIBRARY(LIBOVR_LIBRARY NAMES ovr From 69c194f57fb85a157ad5caa5eb580a2425685ee0 Mon Sep 17 00:00:00 2001 From: Michael Witrant Date: Sat, 6 Jul 2013 20:05:19 +0200 Subject: [PATCH 088/313] LibOVR is optional, and is disabled by default --- code/CMakeModules/nel.cmake | 2 ++ code/nel/CMakeLists.txt | 4 +++- code/nel/include/nel/3d/stereo_ovr.h | 5 +++++ code/nel/src/3d/stereo_display.cpp | 6 ++++++ code/nel/src/3d/stereo_ovr.cpp | 4 ++++ 5 files changed, 20 insertions(+), 1 deletion(-) diff --git a/code/CMakeModules/nel.cmake b/code/CMakeModules/nel.cmake index 4308d0b44..f3897cce0 100644 --- a/code/CMakeModules/nel.cmake +++ b/code/CMakeModules/nel.cmake @@ -324,6 +324,8 @@ MACRO(NL_SETUP_NEL_DEFAULT_OPTIONS) OPTION(WITH_NEL_MAXPLUGIN "Build NeL 3dsMax Plugin" OFF) OPTION(WITH_NEL_SAMPLES "Build NeL Samples" ON ) OPTION(WITH_NEL_TESTS "Build NeL Unit Tests" ON ) + + OPTION(WITH_LIBOVR "With LibOVR support" OFF) ENDMACRO(NL_SETUP_NEL_DEFAULT_OPTIONS) MACRO(NL_SETUP_NELNS_DEFAULT_OPTIONS) diff --git a/code/nel/CMakeLists.txt b/code/nel/CMakeLists.txt index 1ac54f332..07474abcd 100644 --- a/code/nel/CMakeLists.txt +++ b/code/nel/CMakeLists.txt @@ -41,7 +41,9 @@ IF(WITH_GTK) FIND_PACKAGE(GTK2) ENDIF(WITH_GTK) -FIND_PACKAGE(LibOVR) +IF(WITH_LIBOVR) + FIND_PACKAGE(LibOVR) +ENDIF(WITH_LIBOVR) IF(WITH_INSTALL_LIBRARIES) IF(UNIX) diff --git a/code/nel/include/nel/3d/stereo_ovr.h b/code/nel/include/nel/3d/stereo_ovr.h index 3b417b871..e8a67a45a 100644 --- a/code/nel/include/nel/3d/stereo_ovr.h +++ b/code/nel/include/nel/3d/stereo_ovr.h @@ -43,6 +43,9 @@ #ifndef NL3D_STEREO_OVR_H #define NL3D_STEREO_OVR_H + +#ifdef WITH_LIBOVR + #include // STL includes @@ -166,6 +169,8 @@ private: } /* namespace NL3D */ +#endif /* WITH_LIBOVR */ + #endif /* #ifndef NL3D_STEREO_OVR_H */ /* end of file */ diff --git a/code/nel/src/3d/stereo_display.cpp b/code/nel/src/3d/stereo_display.cpp index d2a8fd932..57f2722eb 100644 --- a/code/nel/src/3d/stereo_display.cpp +++ b/code/nel/src/3d/stereo_display.cpp @@ -75,7 +75,9 @@ const char *IStereoDisplay::getLibraryName(CStereoDeviceInfo::TStereoDeviceLibra void IStereoDisplay::listDevices(std::vector &devicesOut) { +#ifdef WITH_LIBOVR CStereoOVR::listDevices(devicesOut); +#endif #if !FINAL_VERSION CStereoDebugger::listDevices(devicesOut); #endif @@ -88,13 +90,17 @@ IStereoDisplay *IStereoDisplay::createDevice(const CStereoDeviceInfo &deviceInfo void IStereoDisplay::releaseUnusedLibraries() { +#ifdef WITH_LIBOVR if (!CStereoOVR::isLibraryInUse()) CStereoOVR::releaseLibrary(); +#endif } void IStereoDisplay::releaseAllLibraries() { +#ifdef WITH_LIBOVR CStereoOVR::releaseLibrary(); +#endif } } /* namespace NL3D */ diff --git a/code/nel/src/3d/stereo_ovr.cpp b/code/nel/src/3d/stereo_ovr.cpp index 2b91e1836..3c6fb88e2 100644 --- a/code/nel/src/3d/stereo_ovr.cpp +++ b/code/nel/src/3d/stereo_ovr.cpp @@ -41,6 +41,8 @@ * so, delete this exception statement from your version. */ +#ifdef WITH_LIBOVR + #include #include @@ -736,4 +738,6 @@ bool CStereoOVR::isDeviceCreated() } /* namespace NL3D */ +#endif /* WITH_LIBOVR */ + /* end of file */ From 59dc1a6c1648fe4908abab8851b7ebee7ece6667 Mon Sep 17 00:00:00 2001 From: Michael Witrant Date: Sat, 6 Jul 2013 20:40:42 +0200 Subject: [PATCH 089/313] Fixed definition tests for optional LibOVR --- code/nel/include/nel/3d/stereo_ovr.h | 4 ++-- code/nel/src/3d/stereo_display.cpp | 6 +++--- code/nel/src/3d/stereo_ovr.cpp | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/code/nel/include/nel/3d/stereo_ovr.h b/code/nel/include/nel/3d/stereo_ovr.h index e8a67a45a..c2dccf930 100644 --- a/code/nel/include/nel/3d/stereo_ovr.h +++ b/code/nel/include/nel/3d/stereo_ovr.h @@ -44,7 +44,7 @@ #ifndef NL3D_STEREO_OVR_H #define NL3D_STEREO_OVR_H -#ifdef WITH_LIBOVR +#ifdef HAVE_LIBOVR #include @@ -169,7 +169,7 @@ private: } /* namespace NL3D */ -#endif /* WITH_LIBOVR */ +#endif /* HAVE_LIBOVR */ #endif /* #ifndef NL3D_STEREO_OVR_H */ diff --git a/code/nel/src/3d/stereo_display.cpp b/code/nel/src/3d/stereo_display.cpp index 57f2722eb..0f6d0cbfb 100644 --- a/code/nel/src/3d/stereo_display.cpp +++ b/code/nel/src/3d/stereo_display.cpp @@ -75,7 +75,7 @@ const char *IStereoDisplay::getLibraryName(CStereoDeviceInfo::TStereoDeviceLibra void IStereoDisplay::listDevices(std::vector &devicesOut) { -#ifdef WITH_LIBOVR +#ifdef HAVE_LIBOVR CStereoOVR::listDevices(devicesOut); #endif #if !FINAL_VERSION @@ -90,7 +90,7 @@ IStereoDisplay *IStereoDisplay::createDevice(const CStereoDeviceInfo &deviceInfo void IStereoDisplay::releaseUnusedLibraries() { -#ifdef WITH_LIBOVR +#ifdef HAVE_LIBOVR if (!CStereoOVR::isLibraryInUse()) CStereoOVR::releaseLibrary(); #endif @@ -98,7 +98,7 @@ void IStereoDisplay::releaseUnusedLibraries() void IStereoDisplay::releaseAllLibraries() { -#ifdef WITH_LIBOVR +#ifdef HAVE_LIBOVR CStereoOVR::releaseLibrary(); #endif } diff --git a/code/nel/src/3d/stereo_ovr.cpp b/code/nel/src/3d/stereo_ovr.cpp index 3c6fb88e2..a57a0ffcb 100644 --- a/code/nel/src/3d/stereo_ovr.cpp +++ b/code/nel/src/3d/stereo_ovr.cpp @@ -41,7 +41,7 @@ * so, delete this exception statement from your version. */ -#ifdef WITH_LIBOVR +#ifdef HAVE_LIBOVR #include #include @@ -738,6 +738,6 @@ bool CStereoOVR::isDeviceCreated() } /* namespace NL3D */ -#endif /* WITH_LIBOVR */ +#endif /* HAVE_LIBOVR */ /* end of file */ From cb53258f8cb2d262affc40a13513bdc1d60dbfb5 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Sat, 6 Jul 2013 21:58:26 +0200 Subject: [PATCH 090/313] Fix bad includes in snowballs, re #43 --- code/snowballs2/client/src/commands.cpp | 2 +- code/snowballs2/client/src/compass.cpp | 2 +- code/snowballs2/client/src/snowballs_client.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/code/snowballs2/client/src/commands.cpp b/code/snowballs2/client/src/commands.cpp index 6d9b23085..41faeab1e 100644 --- a/code/snowballs2/client/src/commands.cpp +++ b/code/snowballs2/client/src/commands.cpp @@ -35,7 +35,7 @@ #include #include #include -#include +#include #include "network.h" #include "snowballs_client.h" diff --git a/code/snowballs2/client/src/compass.cpp b/code/snowballs2/client/src/compass.cpp index 9cc6fac57..27e0e27f1 100644 --- a/code/snowballs2/client/src/compass.cpp +++ b/code/snowballs2/client/src/compass.cpp @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include "mouse_listener.h" #include "camera.h" diff --git a/code/snowballs2/client/src/snowballs_client.cpp b/code/snowballs2/client/src/snowballs_client.cpp index 6c3630a8b..b401d80bc 100644 --- a/code/snowballs2/client/src/snowballs_client.cpp +++ b/code/snowballs2/client/src/snowballs_client.cpp @@ -51,7 +51,7 @@ #if SBCLIENT_DEV_STEREO # include #endif /* #if SBCLIENT_DEV_STEREO */ -#include +#include // Project includes #include "pacs.h" From 1ed7e4fcffda21c45ff591473e82c96a218ea3f3 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Thu, 1 Aug 2013 10:11:11 +0200 Subject: [PATCH 091/313] Force link --- code/nel/src/gui/widget_manager.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/code/nel/src/gui/widget_manager.cpp b/code/nel/src/gui/widget_manager.cpp index 16b946159..7cfe26a3e 100644 --- a/code/nel/src/gui/widget_manager.cpp +++ b/code/nel/src/gui/widget_manager.cpp @@ -3183,6 +3183,8 @@ namespace NLGUI CWidgetManager::CWidgetManager() { + LinkHack(); + CStringShared::createStringMapper(); CReflectableRegister::registerClasses(); From bbe4ea4ba8408c846620390cecbe4089ddfd0144 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Thu, 1 Aug 2013 22:47:46 +0200 Subject: [PATCH 092/313] Add some basic prediction to CCDBNodeBranch::find --- code/nel/include/nel/misc/cdb_branch.h | 6 ++++++ code/nel/src/misc/cdb_branch.cpp | 23 +++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/code/nel/include/nel/misc/cdb_branch.h b/code/nel/include/nel/misc/cdb_branch.h index 11adbac0d..f8a979499 100644 --- a/code/nel/include/nel/misc/cdb_branch.h +++ b/code/nel/include/nel/misc/cdb_branch.h @@ -21,6 +21,8 @@ #include "cdb.h" +#define NL_CDB_OPTIMIZE_PREDICT 1 + namespace NLMISC{ /** @@ -247,6 +249,10 @@ protected: /// called by clear void removeAllBranchObserver(); + +#if NL_CDB_OPTIMIZE_PREDICT + CRefPtr _PredictNode; +#endif }; } diff --git a/code/nel/src/misc/cdb_branch.cpp b/code/nel/src/misc/cdb_branch.cpp index 998ea8cd3..627c1f38b 100644 --- a/code/nel/src/misc/cdb_branch.cpp +++ b/code/nel/src/misc/cdb_branch.cpp @@ -232,6 +232,9 @@ void CCDBNodeBranch::attachChild( ICDBNode * node, string nodeName ) //nldebug ( "CDB: Attaching node" ); _NodesByName.push_back( node ); _Sorted = false; +#if NL_CDB_OPTIMIZE_PREDICT + _PredictNode = node; +#endif } } // attachChild // @@ -799,6 +802,18 @@ public: //----------------------------------------------- ICDBNode *CCDBNodeBranch::find(const std::string &nodeName) { +#if NL_CDB_OPTIMIZE_PREDICT + ICDBNode *predictNode = _PredictNode; + if (predictNode) + { + if (predictNode->getParent() == this + && *predictNode->getName() == nodeName) + { + return predictNode; + } + } +#endif + if (!_Sorted) { _Sorted = true; @@ -812,7 +827,15 @@ ICDBNode *CCDBNodeBranch::find(const std::string &nodeName) else { if (*(*it)->getName() == nodeName) + { +#if NL_CDB_OPTIMIZE_PREDICT + ICDBNode *node = *it; + _PredictNode = node; + return node; +#else return *it; +#endif + } else return NULL; } From d0a4f5ed7555b7cead92b7af0a8d6ebc87b65946 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Sat, 3 Aug 2013 21:29:47 +0200 Subject: [PATCH 093/313] Fix warning --- code/nel/src/3d/stereo_debugger.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/code/nel/src/3d/stereo_debugger.cpp b/code/nel/src/3d/stereo_debugger.cpp index 9255fa812..6be97d42a 100644 --- a/code/nel/src/3d/stereo_debugger.cpp +++ b/code/nel/src/3d/stereo_debugger.cpp @@ -293,6 +293,7 @@ bool CStereoDebugger::nextPass() return false; } } + return false; } /// Gets the current viewport From a2f91ce484d916289784437baa6323633ac39df1 Mon Sep 17 00:00:00 2001 From: kervala Date: Mon, 5 Aug 2013 10:30:40 +0200 Subject: [PATCH 094/313] Changed: Tabulations to spaces --- code/nel/tools/3d/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/nel/tools/3d/CMakeLists.txt b/code/nel/tools/3d/CMakeLists.txt index 441a83800..7f4ed6b8c 100644 --- a/code/nel/tools/3d/CMakeLists.txt +++ b/code/nel/tools/3d/CMakeLists.txt @@ -24,7 +24,7 @@ IF(WITH_NEL_TOOLS) shapes_exporter tga_cut tga_resize - shape2obj + shape2obj zone_check_bind zone_dump zviewer) From e74913f589559e09de96eca8494598bb5accc696 Mon Sep 17 00:00:00 2001 From: kervala Date: Mon, 5 Aug 2013 10:31:23 +0200 Subject: [PATCH 095/313] Fixed: X64 not detected with CMake 2.8.11 --- code/CMakeModules/nel.cmake | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/code/CMakeModules/nel.cmake b/code/CMakeModules/nel.cmake index 834a5da5b..c70d6acde 100644 --- a/code/CMakeModules/nel.cmake +++ b/code/CMakeModules/nel.cmake @@ -387,11 +387,11 @@ MACRO(NL_SETUP_BUILD) SET(HOST_CPU ${CMAKE_HOST_SYSTEM_PROCESSOR}) - IF(HOST_CPU MATCHES "amd64") + IF(HOST_CPU MATCHES "amd64|AMD64") SET(HOST_CPU "x86_64") ELSEIF(HOST_CPU MATCHES "i.86") SET(HOST_CPU "x86") - ENDIF(HOST_CPU MATCHES "amd64") + ENDIF(HOST_CPU MATCHES "amd64|AMD64") # Determine target CPU @@ -400,11 +400,11 @@ MACRO(NL_SETUP_BUILD) SET(TARGET_CPU ${CMAKE_SYSTEM_PROCESSOR}) ENDIF(NOT TARGET_CPU) - IF(TARGET_CPU MATCHES "amd64") + IF(TARGET_CPU MATCHES "amd64|AMD64") SET(TARGET_CPU "x86_64") ELSEIF(TARGET_CPU MATCHES "i.86") SET(TARGET_CPU "x86") - ENDIF(TARGET_CPU MATCHES "amd64") + ENDIF(TARGET_CPU MATCHES "amd64|AMD64") IF(${CMAKE_CXX_COMPILER_ID} MATCHES "Clang") SET(CLANG ON) From f9b70173ce0a3ffba6bff8c9c0e9b0d353e4bcfc Mon Sep 17 00:00:00 2001 From: kervala Date: Mon, 5 Aug 2013 10:32:17 +0200 Subject: [PATCH 096/313] Fixed: PCH with Qt 5 and CMake 2.8.11 --- code/CMakeModules/PCHSupport.cmake | 52 ++++++++++++++++++++++-------- 1 file changed, 39 insertions(+), 13 deletions(-) diff --git a/code/CMakeModules/PCHSupport.cmake b/code/CMakeModules/PCHSupport.cmake index 042f6b0cc..bcca9d877 100644 --- a/code/CMakeModules/PCHSupport.cmake +++ b/code/CMakeModules/PCHSupport.cmake @@ -10,7 +10,6 @@ IF(MSVC) SET(PCHSupport_FOUND TRUE) - SET(_PCH_include_prefix "/I") ELSE(MSVC) IF(CMAKE_COMPILER_IS_GNUCXX) EXEC_PROGRAM(${CMAKE_CXX_COMPILER} @@ -26,16 +25,14 @@ ELSE(MSVC) # TODO: make tests for other compilers than GCC SET(PCHSupport_FOUND TRUE) ENDIF(CMAKE_COMPILER_IS_GNUCXX) - - SET(_PCH_include_prefix "-I") ENDIF(MSVC) # Set PCH_FLAGS for common flags, PCH_ARCH_XXX_FLAGS for specific archs flags and PCH_ARCHS for archs MACRO(PCH_SET_COMPILE_FLAGS _target) SET(PCH_FLAGS) - SET(PCH_ARCHS) + SET(PCH_ARCHS) + SET(_FLAGS) - LIST(APPEND _FLAGS ${CMAKE_CXX_FLAGS}) STRING(TOUPPER "${CMAKE_BUILD_TYPE}" _UPPER_BUILD) @@ -46,24 +43,33 @@ MACRO(PCH_SET_COMPILE_FLAGS _target) IF(${_targetType} STREQUAL SHARED_LIBRARY OR ${_targetType} STREQUAL MODULE_LIBRARY) LIST(APPEND _FLAGS " -fPIC") ENDIF(${_targetType} STREQUAL SHARED_LIBRARY OR ${_targetType} STREQUAL MODULE_LIBRARY) + + GET_TARGET_PROPERTY(_pic ${_target} POSITION_INDEPENDENT_CODE) + IF(_pic) + LIST(APPEND _FLAGS " -fPIE") + ENDIF(_pic) ENDIF(NOT MSVC) GET_DIRECTORY_PROPERTY(DIRINC INCLUDE_DIRECTORIES) FOREACH(item ${DIRINC}) - LIST(APPEND _FLAGS " ${_PCH_include_prefix}\"${item}\"") + LIST(APPEND _FLAGS " -I\"${item}\"") ENDFOREACH(item) # Required for CMake 2.6 SET(GLOBAL_DEFINITIONS) GET_DIRECTORY_PROPERTY(DEFINITIONS COMPILE_DEFINITIONS) - FOREACH(item ${DEFINITIONS}) - LIST(APPEND GLOBAL_DEFINITIONS " -D${item}") - ENDFOREACH(item) + IF(DEFINITIONS) + FOREACH(item ${DEFINITIONS}) + LIST(APPEND GLOBAL_DEFINITIONS " -D${item}") + ENDFOREACH(item) + ENDIF(DEFINITIONS) GET_DIRECTORY_PROPERTY(DEFINITIONS COMPILE_DEFINITIONS_${_UPPER_BUILD}) - FOREACH(item ${DEFINITIONS}) - LIST(APPEND GLOBAL_DEFINITIONS " -D${item}") - ENDFOREACH(item) + IF(DEFINITIONS) + FOREACH(item ${DEFINITIONS}) + LIST(APPEND GLOBAL_DEFINITIONS " -D${item}") + ENDFOREACH(item) + ENDIF(DEFINITIONS) GET_TARGET_PROPERTY(oldProps ${_target} COMPILE_FLAGS) IF(oldProps) @@ -75,6 +81,27 @@ MACRO(PCH_SET_COMPILE_FLAGS _target) LIST(APPEND _FLAGS " ${oldPropsBuild}") ENDIF(oldPropsBuild) + GET_TARGET_PROPERTY(DIRINC ${_target} INCLUDE_DIRECTORIES) + IF(DIRINC) + FOREACH(item ${DIRINC}) + LIST(APPEND _FLAGS " -I\"${item}\"") + ENDFOREACH(item) + ENDIF(DIRINC) + + GET_TARGET_PROPERTY(DEFINITIONS ${_target} COMPILE_DEFINITIONS) + IF(DEFINITIONS) + FOREACH(item ${DEFINITIONS}) + LIST(APPEND GLOBAL_DEFINITIONS " -D${item}") + ENDFOREACH(item) + ENDIF(DEFINITIONS) + + GET_TARGET_PROPERTY(DEFINITIONS ${_target} COMPILE_DEFINITIONS_${_UPPER_BUILD}) + IF(DEFINITIONS) + FOREACH(item ${DEFINITIONS}) + LIST(APPEND GLOBAL_DEFINITIONS " -D${item}") + ENDFOREACH(item) + ENDIF(DEFINITIONS) + GET_DIRECTORY_PROPERTY(_directory_flags DEFINITIONS) GET_DIRECTORY_PROPERTY(_directory_definitions DIRECTORY ${CMAKE_SOURCE_DIR} DEFINITIONS) LIST(APPEND _FLAGS " ${GLOBAL_DEFINITIONS}") @@ -90,7 +117,6 @@ MACRO(PCH_SET_COMPILE_FLAGS _target) SEPARATE_ARGUMENTS(_FLAGS) ENDIF(MSVC) - IF(CLANG) # Determining all architectures and get common flags SET(_ARCH_NEXT) From 33115f61b261d8dc78f1d67c9cfcf1808887b6be Mon Sep 17 00:00:00 2001 From: kervala Date: Mon, 5 Aug 2013 10:34:38 +0200 Subject: [PATCH 097/313] Changed: Minor formatting --- code/CMakeModules/FindDirectXSDK.cmake | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/code/CMakeModules/FindDirectXSDK.cmake b/code/CMakeModules/FindDirectXSDK.cmake index 9947778db..dc6753a8b 100644 --- a/code/CMakeModules/FindDirectXSDK.cmake +++ b/code/CMakeModules/FindDirectXSDK.cmake @@ -6,8 +6,8 @@ # DXSDK_FOUND - True if MAX SDK found. IF(DXSDK_DIR) - # Already in cache, be silent - SET(DXSDK_FIND_QUIETLY TRUE) + # Already in cache, be silent + SET(DXSDK_FIND_QUIETLY TRUE) ENDIF(DXSDK_DIR) FIND_PATH(DXSDK_DIR @@ -26,10 +26,10 @@ FIND_PATH(DXSDK_DIR MACRO(FIND_DXSDK_LIBRARY MYLIBRARY MYLIBRARYNAME) FIND_LIBRARY(${MYLIBRARY} - NAMES ${MYLIBRARYNAME} - PATHS - "${DXSDK_LIBRARY_DIR}" - ) + NAMES ${MYLIBRARYNAME} + PATHS + "${DXSDK_LIBRARY_DIR}" + ) ENDMACRO(FIND_DXSDK_LIBRARY MYLIBRARY MYLIBRARYNAME) IF(DXSDK_DIR) From e169978d135754c832b0c3456e11a417c8adf856 Mon Sep 17 00:00:00 2001 From: Quitta Date: Fri, 16 Aug 2013 03:40:59 +0200 Subject: [PATCH 098/313] add email related stuff to support groups --- .../ams_lib/autoload/support_group.php | 53 ++++++++++++++++-- .../tools/server/ryzom_ams/www/config.php | 16 ++++-- .../ryzom_ams/www/html/func/add_sgroup.php | 7 ++- .../ryzom_ams/www/html/inc/sgroup_list.php | 2 +- .../server/ryzom_ams/www/html/sql/install.php | 4 ++ .../ryzom_ams/www/html/sql/ticketsql.sql | 4 ++ .../www/html/sql/ticketsystemmodel.mwb | Bin 16833 -> 17249 bytes .../www/html/templates/sgroup_list.tpl | 38 +++++++++++++ 8 files changed, 113 insertions(+), 11 deletions(-) diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/support_group.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/support_group.php index 4ee3e79fe..e1cc3347f 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/support_group.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/support_group.php @@ -5,6 +5,10 @@ class Support_Group{ private $sGroupId; private $name; private $tag; + private $groupEmail; + private $iMAP_MailServer; + private $iMAP_Username; + private $iMAP_Password; ////////////////////////////////////////////Functions//////////////////////////////////////////////////// @@ -35,15 +39,21 @@ class Support_Group{ } //wrapper for creating a support group - public static function createSupportGroup( $name, $tag) { + public static function createSupportGroup( $name, $tag, $groupemail, $imap_mailserver, $imap_username, $imap_password) { if(strlen($name) < 21 && strlen($name) > 4 &&strlen($tag) < 8 && strlen($tag) > 1 ){ $notExists = self::supportGroup_EntryNotExists($name, $tag); if ( $notExists == "SUCCESS" ){ $sGroup = new self(); - $sGroup->setName($name); - $sGroup->setTag($tag); + $values = array('Name' => $name, 'Tag' => $tag, 'GroupEmail' => $groupemail, 'IMAP_MailServer' => $imap_mailserver, 'IMAP_Username' => $imap_username, 'IMAP_Password' => $imap_password); + $sGroup->setName($values['Name']); + $sGroup->setTag($values['Tag']); + $sGroup->setGroupEmail($values['GroupEmail']); + $sGroup->setIMAP_MailServer($values['IMAP_MailServer']); + $sGroup->setIMAP_Username($values['IMAP_Username']); + $sGroup->setIMAP_Password($values['IMAP_Password']); $sGroup->create(); + return "SUCCESS"; }else{ //return NAME_TAKEN or TAG_TAKEN @@ -199,12 +209,16 @@ class Support_Group{ $this->setSGroupId($values['SGroupId']); $this->setName($values['Name']); $this->setTag($values['Tag']); + $this->setGroupEmail($values['GroupEmail']); + $this->setIMAP_MailServer($values['IMAP_MailServer']); + $this->setIMAP_Username($values['IMAP_Username']); + $this->setIMAP_Password($values['IMAP_Password']); } public function create() { $dbl = new DBLayer("lib"); - $query = "INSERT INTO support_group (Name, Tag) VALUES (:name, :tag)"; - $values = Array('name' => $this->name, 'tag' => $this->tag); + $query = "INSERT INTO support_group (Name, Tag, GroupEmail, IMAP_MailServer, IMAP_Username, IMAP_Password) VALUES (:name, :tag, :groupemail, :imap_mailserver, :imap_username, :imap_password)"; + $values = Array('name' => $this->getName(), 'tag' => $this->getTag(), 'groupemail' => $this->getGroupEmail(), 'imap_mailserver' => $this->getIMAP_MailServer(), 'imap_username' => $this->getIMAP_Username(), 'imap_password' => $this->getIMAP_Password()); $dbl->execute($query, $values); } @@ -247,6 +261,21 @@ class Support_Group{ return $this->tag; } + public function getGroupEmail(){ + return $this->groupEmail; + } + + public function getIMAP_MailServer(){ + return $this->iMAP_MailServer; + } + + public function getIMAP_Username(){ + return $this->iMAP_Username; + } + + public function getIMAP_Password(){ + return $this->iMap_Password; + } ////////////////////////////////////////////Setters//////////////////////////////////////////////////// public function setSGroupId($id){ @@ -261,5 +290,19 @@ class Support_Group{ $this->tag = $t; } + public function setGroupEmail($ge){ + $this->groupEmail = $ge; + } + public function setIMAP_MailServer($ms){ + $this->iMAP_MailServer = $ms; + } + + public function setIMAP_Username($u){ + $this->iMAP_Username = $u; + } + + public function setIMAP_Password($p){ + $this->iMap_Password = $p; + } } \ No newline at end of file diff --git a/code/ryzom/tools/server/ryzom_ams/www/config.php b/code/ryzom/tools/server/ryzom_ams/www/config.php index 82c010e58..4112b9f32 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/config.php +++ b/code/ryzom/tools/server/ryzom_ams/www/config.php @@ -57,13 +57,21 @@ $CREATE_RING = true ; $AMS_LIB = dirname( dirname( __FILE__ ) ) . '/ams_lib'; $AMS_TRANS = $AMS_LIB . '/translations'; $AMS_CACHEDIR = $AMS_LIB . '/cache'; - -$DEFAULT_LANGUAGE = 'en'; - $SITEBASE = dirname( __FILE__ ) . '/html/' ; +//defines the default language +$DEFAULT_LANGUAGE = 'en'; + +//defines if logging actions should happen or not. $TICKET_LOGGING = true; -$TICKET_MAILING_SUPPORT = true; + +//defines the time format display $TIME_FORMAT = "m-d-Y H:i:s"; + +//defines which ingame layout template should be used $INGAME_LAYOUT = "basic"; + +//Defines mailing related stuff +$SUPPORT_GROUP_IMAP_CRYPTKEY = "azerty"; +$TICKET_MAILING_SUPPORT = true; $MAIL_DIR = "/tmp"; diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/func/add_sgroup.php b/code/ryzom/tools/server/ryzom_ams/www/html/func/add_sgroup.php index 9fc59d917..4a5ff8452 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/func/add_sgroup.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/func/add_sgroup.php @@ -8,8 +8,13 @@ function add_sgroup(){ $name = filter_var($_POST['Name'],FILTER_SANITIZE_STRING); $inner_tag = filter_var($_POST['Tag'], FILTER_SANITIZE_STRING); $tag = "[" . $inner_tag . "]"; + $inner_tag = filter_var($_POST['Tag'], FILTER_SANITIZE_STRING); + $groupemail = filter_var($_POST['GroupEmail'], FILTER_SANITIZE_STRING); + $imap_mailserver = filter_var($_POST['IMAP_MailServer'], FILTER_SANITIZE_STRING); + $imap_username = filter_var($_POST['IMAP_Username'], FILTER_SANITIZE_STRING); + $imap_password = filter_var($_POST['IMAP_Password'], FILTER_SANITIZE_STRING); - $result['RESULT_OF_ADDING'] = Support_Group::createSupportGroup($name, $tag); + $result['RESULT_OF_ADDING'] = Support_Group::createSupportGroup($name, $tag, $groupemail, $imap_mailserver, $imap_username, $imap_password); $result['permission'] = $_SESSION['ticket_user']->getPermission(); $result['no_visible_elements'] = 'FALSE'; $result['username'] = $_SESSION['user']; diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/inc/sgroup_list.php b/code/ryzom/tools/server/ryzom_ams/www/html/inc/sgroup_list.php index 3d380c2d2..f9c2ac150 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/inc/sgroup_list.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/inc/sgroup_list.php @@ -14,7 +14,7 @@ function sgroup_list(){ if(Ticket_User::isAdmin($_SESSION['ticket_user'])){ $result['isAdmin'] = "TRUE"; } - $result['grouplist'] = Gui_Elements::make_table(Support_Group::getGroups(), Array("getSGroupId","getName","getTag"), Array("sGroupId","name","tag")); + $result['grouplist'] = Gui_Elements::make_table(Support_Group::getGroups(), Array("getSGroupId","getName","getTag","getGroupEmail"), Array("sGroupId","name","tag","groupemail")); return $result; }else{ //ERROR: No access! diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/sql/install.php b/code/ryzom/tools/server/ryzom_ams/www/html/sql/install.php index cf8a514e4..6a49cf8bd 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/sql/install.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/sql/install.php @@ -286,6 +286,10 @@ `SGroupId` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT , `Name` VARCHAR(22) NOT NULL , `Tag` VARCHAR(7) NOT NULL , + `GroupEmail` VARCHAR(45) NULL , + `IMAP_MailServer` VARCHAR(60) NULL , + `IMAP_Username` VARCHAR(45) NULL , + `IMAP_Password` VARCHAR(45) NULL , PRIMARY KEY (`SGroupId`) , UNIQUE INDEX `Name_UNIQUE` (`Name` ASC) , UNIQUE INDEX `Tag_UNIQUE` (`Tag` ASC) ) diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/sql/ticketsql.sql b/code/ryzom/tools/server/ryzom_ams/www/html/sql/ticketsql.sql index e7864e628..905f8f693 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/sql/ticketsql.sql +++ b/code/ryzom/tools/server/ryzom_ams/www/html/sql/ticketsql.sql @@ -241,6 +241,10 @@ CREATE TABLE IF NOT EXISTS `mydb`.`support_group` ( `SGroupId` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT , `Name` VARCHAR(22) NOT NULL , `Tag` VARCHAR(7) NOT NULL , + `GroupEmail` VARCHAR(45) NULL , + `IMAP_MailServer` VARCHAR(60) NULL , + `IMAP_Username` VARCHAR(45) NULL , + `IMAP_Password` VARCHAR(45) NULL , PRIMARY KEY (`SGroupId`) , UNIQUE INDEX `Name_UNIQUE` (`Name` ASC) , UNIQUE INDEX `Tag_UNIQUE` (`Tag` ASC) ) diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/sql/ticketsystemmodel.mwb b/code/ryzom/tools/server/ryzom_ams/www/html/sql/ticketsystemmodel.mwb index baf11d3359d5b56ab84791b5294de39ccc4c5434..cc8aec0c235f77bdc1681d83396bac44250bb3fb 100644 GIT binary patch delta 16999 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 diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/templates/sgroup_list.tpl b/code/ryzom/tools/server/ryzom_ams/www/html/templates/sgroup_list.tpl index 57ab6e01f..a139aec34 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/templates/sgroup_list.tpl +++ b/code/ryzom/tools/server/ryzom_ams/www/html/templates/sgroup_list.tpl @@ -19,6 +19,7 @@ ID Name Tag + Email {if isset($isAdmin) && $isAdmin eq 'TRUE'}Action{/if} @@ -28,6 +29,7 @@ {$group.sGroupId} {$group.name} {$group.tag} + {$group.groupemail} {if isset($isAdmin) && $isAdmin eq 'TRUE'} Delete{/if} {/foreach} @@ -71,6 +73,42 @@ +
+ +
+
+ +
+
+
+ +
+ +
+
+ +
+
+
+ +
+ +
+
+ +
+
+
+ +
+ +
+
+ +
+
+
+
From 141f40835f0a347ac01d45143e81e57ddd3ef549 Mon Sep 17 00:00:00 2001 From: Quitta Date: Fri, 16 Aug 2013 05:37:38 +0200 Subject: [PATCH 099/313] Functionality to modify mailsettings of a support group. --- .../ams_lib/autoload/support_group.php | 6 +- .../ryzom_ams/ams_lib/autoload/users.php | 2 +- .../ryzom_ams/ams_lib/translations/en.ini | 3 + .../ryzom_ams/ams_lib/translations/fr.ini | 4 + .../www/html/func/modify_email_of_sgroup.php | 49 +++++++++++ .../ryzom_ams/www/html/inc/show_sgroup.php | 4 +- .../www/html/templates/show_sgroup.tpl | 81 ++++++++++++++++++- 7 files changed, 142 insertions(+), 7 deletions(-) create mode 100644 code/ryzom/tools/server/ryzom_ams/www/html/func/modify_email_of_sgroup.php diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/support_group.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/support_group.php index e1cc3347f..b44e1552e 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/support_group.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/support_group.php @@ -12,7 +12,7 @@ class Support_Group{ ////////////////////////////////////////////Functions//////////////////////////////////////////////////// - //return all groups + //return group public static function getGroup($id) { $dbl = new DBLayer("lib"); $statement = $dbl->execute("SELECT * FROM support_group WHERE SGroupId = :id", array('id' => $id)); @@ -234,8 +234,8 @@ class Support_Group{ //update private data to DB. public function update(){ $dbl = new DBLayer("lib"); - $query = "UPDATE `support_group` SET `Name` = :name, `Tag` = :tag WHERE `SGroupId` = :id"; - $values = Array('id' => $this->getSGroupId(), 'name' => $this->getName(), 'tag' => $this->getTag() ); + $query = "UPDATE `support_group` SET `Name` = :name, `Tag` = :tag, `GroupEmail` = :groupemail, `IMAP_MailServer` = :mailserver, `IMAP_Username` = :username, `IMAP_Password` = :password WHERE `SGroupId` = :id"; + $values = Array('id' => $this->getSGroupId(), 'name' => $this->getName(), 'tag' => $this->getTag(), 'groupemail' => $this->getGroupEmail(), 'mailserver' => $this->getIMAP_MailServer(), 'username' => $this->getIMAP_Username(), 'password' => $this->getIMAP_Password() ); $statement = $dbl->execute($query, $values); } diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/users.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/users.php index d5ba0963a..8d07283e6 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/users.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/users.php @@ -192,7 +192,7 @@ class Users{ * @takes $email * @return true or false depending on if its a valid email format. */ - private function validEmail( $email ){ + public function validEmail( $email ){ $isValid = true; $atIndex = strrpos( $email, "@" ); if ( is_bool( $atIndex ) && !$atIndex ){ diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/translations/en.ini b/code/ryzom/tools/server/ryzom_ams/ams_lib/translations/en.ini index 90619bcfc..02de8c019 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/translations/en.ini +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/translations/en.ini @@ -53,6 +53,9 @@ user_already_added = "The user is already part of the group!" group_not_existing = "The group doesn't exist!" user_not_existing = "The user doesn't seem to exist" not_mod_or_admin = "You can only add Moderators or Admins!" +modify_mail_of_group_success = "The Support Group's email settings have been modified!" +email_not_valid = "The group email address is invalid!" +no_password_given = "There was no password filled in!" [sgroup_list] group_success = "The group has been created!" diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/translations/fr.ini b/code/ryzom/tools/server/ryzom_ams/ams_lib/translations/fr.ini index 9127fd0a3..e28bc311a 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/translations/fr.ini +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/translations/fr.ini @@ -49,6 +49,10 @@ user_already_added = "cet user est deja membre de la groupe!" group_not_existing = "cet Groupe n' existe pas!" user_not_existing = "cet user n'existe pas" not_mod_or_admin = "C'est possible d'ajoute seulement des mods et admins!" +modify_mail_of_group_success = "Les parametres de messagerie du Groupe d'appui ont ete modifies!" +email_not_valid = "L'adresse email de groupe est invalide!" +no_password_given = "Il'n y a pas une passport!" + [sgroup_list] group_success = "le group est cree!" diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/func/modify_email_of_sgroup.php b/code/ryzom/tools/server/ryzom_ams/www/html/func/modify_email_of_sgroup.php new file mode 100644 index 000000000..ff028864b --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/www/html/func/modify_email_of_sgroup.php @@ -0,0 +1,49 @@ +setGroupEmail($groupemail); + $group->setIMAP_MailServer(filter_var($_POST['IMAP_MailServer'],FILTER_SANITIZE_STRING)); + $group->setIMAP_Username(filter_var($_POST['IMAP_Username'],FILTER_SANITIZE_STRING)); + $group->setIMAP_Password($password); + $group->update(); + $result['RESULT_OF_MODIFYING'] = "SUCCESS"; + }else{ + $result['RESULT_OF_MODIFYING'] = "NO_PASSWORD"; + } + }else{ + $result['RESULT_OF_MODIFYING'] = "EMAIL_NOT_VALID"; + } + + $result['permission'] = $_SESSION['ticket_user']->getPermission(); + $result['no_visible_elements'] = 'FALSE'; + $result['username'] = $_SESSION['user']; + global $SITEBASE; + require_once($SITEBASE . 'inc/show_sgroup.php'); + $result= array_merge($result, show_sgroup()); + helpers :: loadtemplate( 'show_sgroup', $result); + exit; + + }else{ + //ERROR: No access! + $_SESSION['error_code'] = "403"; + header("Location: index.php?page=error"); + exit; + } + }else{ + //ERROR: not logged in! + header("Location: index.php"); + exit; + } + +} \ No newline at end of file diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/inc/show_sgroup.php b/code/ryzom/tools/server/ryzom_ams/www/html/inc/show_sgroup.php index f4f5edd64..1c9ab406f 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/inc/show_sgroup.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/inc/show_sgroup.php @@ -21,8 +21,10 @@ function show_sgroup(){ } $group = Support_Group::getGroup($result['target_id']); - $result['groupsname'] = $group->getName(); + $result['groupemail'] = $group->getGroupEmail(); + $result['imap_mailserver'] = $group->getIMAP_MailServer(); + $result['imap_username'] = $group->getIMAP_Username(); $result['userlist'] = Gui_Elements::make_table(Support_Group::getAllUsersOfSupportGroup($result['target_id']), Array("getTUserId","getPermission","getExternId"), Array("tUserId","permission","externId")); $i = 0; foreach( $result['userlist'] as $user){ diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/templates/show_sgroup.tpl b/code/ryzom/tools/server/ryzom_ams/www/html/templates/show_sgroup.tpl index 15c1d7b80..cd6913a2f 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/templates/show_sgroup.tpl +++ b/code/ryzom/tools/server/ryzom_ams/www/html/templates/show_sgroup.tpl @@ -10,7 +10,7 @@
- All members of the {$groupsname} Support Group + {$groupsname} Support Group Members @@ -49,7 +49,7 @@ - Add a user to the group '{$groupsname}' + Add user to '{$groupsname}'
@@ -95,6 +95,83 @@
+
+

Modify Email Settings

+
+ + +
+
+
+
+ + + + Mail settings of '{$groupsname}' + +
+ +
+
+ +
+
+
+ +
+ +
+
+ +
+
+
+ +
+ +
+
+ +
+
+
+ +
+ +
+
+ +
+
+
+ + + + +
+ +
+ +
+
+ + {if isset($RESULT_OF_MODIFYING) and $RESULT_OF_MODIFYING eq "SUCCESS"} +
+ {$modify_mail_of_group_success} +
+ {else if isset($RESULT_OF_MODIFYING) and $RESULT_OF_MODIFYING eq "EMAIL_NOT_VALID"} +
+ {$email_not_valid} +
+ {else if isset($RESULT_OF_MODIFYING) and $RESULT_OF_MODIFYING eq "NO_PASSWORD"} +
+ {$no_password_given} +
+ {/if} + + +
+
{/if} From 420454f7f388aca8b4a8b8f7cf2d305ee1933c1a Mon Sep 17 00:00:00 2001 From: Quitta Date: Fri, 16 Aug 2013 21:43:39 +0200 Subject: [PATCH 100/313] sending mail with supportgroup ID is possible --- .../ams_lib/autoload/mail_handler.php | 36 +++++++++--------- .../tools/server/ryzom_ams/www/config.php | 4 +- .../server/ryzom_ams/www/html/sql/install.php | 16 ++++---- .../ryzom_ams/www/html/sql/ticketsql.sql | 14 +++---- .../www/html/sql/ticketsystemmodel.mwb | Bin 17249 -> 17315 bytes 5 files changed, 36 insertions(+), 34 deletions(-) diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php index 4c5e14caf..67c53b301 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php @@ -58,11 +58,14 @@ class Mail_Handler{ //the main function function cron() { global $cfg; - $inbox_username = $cfg['mail']['username']; + $default_groupemail = $cfg['mail']['default_groupemail']; + $default_groupname = $cfg['mail']['default_groupname']; + /*$inbox_username = $cfg['mail']['username']; $inbox_password = $cfg['mail']['password']; $inbox_host = $cfg['mail']['host']; $oms_reply_to = "Ryzom Ticketing Support "; - global $MAIL_DIR; + global $MAIL_DIR;*/ + // Deliver new mail echo("mail cron\n"); @@ -70,15 +73,14 @@ class Mail_Handler{ //creates child process $pid = self::mail_fork(); $pidfile = '/tmp/ams_cron_email_pid'; - - //INFO: if $pid = - //-1: "Could not fork!\n"; - // 0: "In child!\n"; - //>0: "In parent!\n"; if($pid) { // We're the parent process, do nothing! + //INFO: if $pid = + //-1: "Could not fork!\n"; + // 0: "In child!\n"; + //>0: "In parent!\n"; } else { //make db connection here because the children have to make the connection. @@ -86,6 +88,7 @@ class Mail_Handler{ //if $pidfile doesn't exist yet, then start sending the mails that are in the db. if(!file_exists($pidfile)) { + //create the file and write the child processes id in it! $pid = getmypid(); $file = fopen($pidfile, 'w'); @@ -106,18 +109,16 @@ class Mail_Handler{ $email['Recipient'] = Ticket_User::get_email_by_user_id($email['UserId']); } - //create sending email adres based on the $sender id - if($email['Sender'] != 0) { - $username = Ticket_User::get_username_from_id($email['Sender']); - $from = "$username <$username@$inbox_host>"; + //create sending email adres based on the $sender id which refers to the department id + if($email['Sender'] == NULL) { + $from = $default_groupname ." <".$default_groupemail.">"; } else { - $from = $oms_reply_to; + $group = Support_Group::getGroup($email['Sender']); + $from = $group->getName()." <".$group->getGroupEmail().">"; } + $headers = "From: $from\r\n" . "Message-ID: " . $message_id ; - print("recip: " . $email['Recipient']); - print("subj: " .$email['Subject']); - print("body: " . $email['Body']); - print("headers: " . $headers); + if(mail($email['Recipient'], $email['Subject'], $email['Body'], $headers)) { $status = "DELIVERED"; echo("Emailed {$email['Recipient']}\n"); @@ -127,13 +128,12 @@ class Mail_Handler{ } //change the status of the emails. $this->db->execute('update email set Status = ?, MessageId = ?, Attempts = Attempts + 1 where MailId = ?', array($status, $message_id, $email['MailId'])); - //db_exec('update email set status = ?, message_id = ?, attempts = attempts + 1 where id_email = ?', array($status, $message_id, $email['id_email'])); + } unlink($pidfile); } // Check mail - //$mailbox = imap_open("{localhost:110/pop3/novalidate-cert}INBOX", $inbox_username, $inbox_password); $mbox = imap_open($cfg['mail']['server'], $inbox_username, $inbox_password) or die('Cannot connect to mail server: ' . imap_last_error()); $message_count = imap_num_msg($mbox); diff --git a/code/ryzom/tools/server/ryzom_ams/www/config.php b/code/ryzom/tools/server/ryzom_ams/www/config.php index 4112b9f32..611d96e76 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/config.php +++ b/code/ryzom/tools/server/ryzom_ams/www/config.php @@ -31,9 +31,11 @@ $cfg['db']['ring']['name'] = 'ring_open'; $cfg['db']['ring']['user'] = 'shard'; $cfg['db']['ring']['pass'] = ''; +$cfg['mail']['default_groupemail'] = 'support@ryzomcore.com'; +$cfg['mail']['default_groupname'] = 'Ryzomcore Support'; $cfg['mail']['username'] = 'amsryzom@gmail.com'; $cfg['mail']['password'] = 'lol123bol'; -$cfg['mail']['host'] = 'ryzomcore.com'; +//$cfg['mail']['host'] = 'ryzomcore.com'; // To connect to an IMAP server running on port 143 on the local machine, // do the following: $mbox = imap_open("{localhost:143}INBOX", "user_id", "password"); diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/sql/install.php b/code/ryzom/tools/server/ryzom_ams/www/html/sql/install.php index 6a49cf8bd..acfdb3987 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/sql/install.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/sql/install.php @@ -353,19 +353,14 @@ `Body` VARCHAR(400) NULL , `Status` VARCHAR(45) NULL , `Attempts` VARCHAR(45) NULL DEFAULT 0 , - `Sender` INT(10) UNSIGNED NOT NULL , `UserId` INT(10) UNSIGNED NOT NULL , - `MessageId` VARCHAR(45) NOT NULL , + `MessageId` VARCHAR(45) NULL , `TicketId` INT UNSIGNED NOT NULL , + `Sender` INT(10) UNSIGNED NULL , PRIMARY KEY (`MailId`) , - INDEX `fk_email_ticket_user1` (`Sender` ASC) , INDEX `fk_email_ticket_user2` (`UserId` ASC) , INDEX `fk_email_ticket1` (`TicketId` ASC) , - CONSTRAINT `fk_email_ticket_user1` - FOREIGN KEY (`Sender` ) - REFERENCES `" . $cfg['db']['lib']['name'] ."`.`ticket_user` (`TUserId` ) - ON DELETE NO ACTION - ON UPDATE NO ACTION, + INDEX `fk_email_support_group1` (`Sender` ASC) , CONSTRAINT `fk_email_ticket_user2` FOREIGN KEY (`UserId` ) REFERENCES `" . $cfg['db']['lib']['name'] ."`.`ticket_user` (`TUserId` ) @@ -375,6 +370,11 @@ FOREIGN KEY (`TicketId` ) REFERENCES `" . $cfg['db']['lib']['name'] ."`.`ticket` (`TId` ) ON DELETE NO ACTION + ON UPDATE NO ACTION, + CONSTRAINT `fk_email_support_group1` + FOREIGN KEY (`Sender` ) + REFERENCES `" . $cfg['db']['lib']['name'] ."`.`support_group` (`SGroupId` ) + ON DELETE NO ACTION ON UPDATE NO ACTION) ENGINE = InnoDB; diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/sql/ticketsql.sql b/code/ryzom/tools/server/ryzom_ams/www/html/sql/ticketsql.sql index 905f8f693..66059fa8d 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/sql/ticketsql.sql +++ b/code/ryzom/tools/server/ryzom_ams/www/html/sql/ticketsql.sql @@ -309,19 +309,14 @@ CREATE TABLE IF NOT EXISTS `mydb`.`email` ( `Body` VARCHAR(400) NULL , `Status` VARCHAR(45) NULL , `Attempts` VARCHAR(45) NULL DEFAULT 0 , - `Sender` INT(10) UNSIGNED NOT NULL , `UserId` INT(10) UNSIGNED NOT NULL , `MessageId` VARCHAR(45) NOT NULL , `TicketId` INT UNSIGNED NOT NULL , + `Sender` INT(10) UNSIGNED NULL , PRIMARY KEY (`MailId`) , - INDEX `fk_email_ticket_user1` (`Sender` ASC) , INDEX `fk_email_ticket_user2` (`UserId` ASC) , INDEX `fk_email_ticket1` (`TicketId` ASC) , - CONSTRAINT `fk_email_ticket_user1` - FOREIGN KEY (`Sender` ) - REFERENCES `mydb`.`ticket_user` (`TUserId` ) - ON DELETE NO ACTION - ON UPDATE NO ACTION, + INDEX `fk_email_support_group1` (`Sender` ASC) , CONSTRAINT `fk_email_ticket_user2` FOREIGN KEY (`UserId` ) REFERENCES `mydb`.`ticket_user` (`TUserId` ) @@ -331,6 +326,11 @@ CREATE TABLE IF NOT EXISTS `mydb`.`email` ( FOREIGN KEY (`TicketId` ) REFERENCES `mydb`.`ticket` (`TId` ) ON DELETE NO ACTION + ON UPDATE NO ACTION, + CONSTRAINT `fk_email_support_group1` + FOREIGN KEY (`Sender` ) + REFERENCES `mydb`.`support_group` (`SGroupId` ) + ON DELETE NO ACTION ON UPDATE NO ACTION) ENGINE = InnoDB; diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/sql/ticketsystemmodel.mwb b/code/ryzom/tools/server/ryzom_ams/www/html/sql/ticketsystemmodel.mwb index cc8aec0c235f77bdc1681d83396bac44250bb3fb..47b014bc951ad98d658f45a87503af9edf562903 100644 GIT binary patch delta 9481 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 Date: Fri, 16 Aug 2013 22:24:11 +0200 Subject: [PATCH 101/313] Hanle multiple mailaccounts --- .../ams_lib/autoload/mail_handler.php | 52 +++++++++++-------- .../tools/server/ryzom_ams/www/config.php | 13 +++-- 2 files changed, 36 insertions(+), 29 deletions(-) diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php index 67c53b301..19869abde 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php @@ -60,8 +60,7 @@ class Mail_Handler{ global $cfg; $default_groupemail = $cfg['mail']['default_groupemail']; $default_groupname = $cfg['mail']['default_groupname']; - /*$inbox_username = $cfg['mail']['username']; - $inbox_password = $cfg['mail']['password']; + /* $inbox_host = $cfg['mail']['host']; $oms_reply_to = "Ryzom Ticketing Support "; global $MAIL_DIR;*/ @@ -133,29 +132,38 @@ class Mail_Handler{ unlink($pidfile); } // Check mail + $sGroups = Support_Group::getGroups(); + $defaultGroup = new Support_Group(); + $defaultGroup->setGroupEmail($default_groupemail); + $defaultGroup->setIMAP_MailServer($cfg['mail']['default_mailserver']); + $defaultGroup->setIMAP_Username($cfg['mail']['default_username']); + $defaultGroup->setIMAP_Password($cfg['mail']['default_password']); + + $sGroups[] = $defaultGroup; - $mbox = imap_open($cfg['mail']['server'], $inbox_username, $inbox_password) or die('Cannot connect to mail server: ' . imap_last_error()); - $message_count = imap_num_msg($mbox); - - for ($i = 1; $i <= $message_count; ++$i) { - - //return task ID - self::incoming_mail_handler($mbox, $i); - $tid = 1; //self::ams_create_email($from, $subject, $txt, $html, $to, $from); - - if($tid) { - //TODO: base file on Ticket + reply id - /* $file = fopen($MAIL_DIR."/mail/".$tid, 'w'); - fwrite($file, $entire_email); - fclose($file); */ + foreach($sGroups as $group){ + $mbox = imap_open($group->getIMAP_MailServer(), $group->getIMAP_Username(), $group->getIMAP_Password()) or die('Cannot connect to mail server: ' . imap_last_error()); + $message_count = imap_num_msg($mbox); + + for ($i = 1; $i <= $message_count; ++$i) { + + //return task ID + self::incoming_mail_handler($mbox, $i); + $tid = 1; //self::ams_create_email($from, $subject, $txt, $html, $to, $from); + + if($tid) { + //TODO: base file on Ticket + reply id + /* $file = fopen($MAIL_DIR."/mail/".$tid, 'w'); + fwrite($file, $entire_email); + fclose($file); */ + } + //mark message $i of $mbox for deletion! + imap_delete($mbox, $i); } - //mark message $i of $mbox for deletion! - imap_delete($mbox, $i); + //delete marked messages + imap_expunge($mbox); + imap_close($mbox); } - //delete marked messages - imap_expunge($mbox); - imap_close($mbox); - } } diff --git a/code/ryzom/tools/server/ryzom_ams/www/config.php b/code/ryzom/tools/server/ryzom_ams/www/config.php index 611d96e76..13e6e37bb 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/config.php +++ b/code/ryzom/tools/server/ryzom_ams/www/config.php @@ -31,12 +31,6 @@ $cfg['db']['ring']['name'] = 'ring_open'; $cfg['db']['ring']['user'] = 'shard'; $cfg['db']['ring']['pass'] = ''; -$cfg['mail']['default_groupemail'] = 'support@ryzomcore.com'; -$cfg['mail']['default_groupname'] = 'Ryzomcore Support'; -$cfg['mail']['username'] = 'amsryzom@gmail.com'; -$cfg['mail']['password'] = 'lol123bol'; -//$cfg['mail']['host'] = 'ryzomcore.com'; - // To connect to an IMAP server running on port 143 on the local machine, // do the following: $mbox = imap_open("{localhost:143}INBOX", "user_id", "password"); // POP3 server on port 110: $mbox = imap_open ("{localhost:110/pop3}INBOX", "user_id", "password"); @@ -47,7 +41,12 @@ $cfg['mail']['password'] = 'lol123bol'; // NNTP server on port 119 use: $nntp = imap_open ("{localhost:119/nntp}comp.test", "", ""); // To connect to a remote server replace "localhost" with the name or the IP address of the server you want to connect to. //$cfg['mail']['server'] = '{localhost:110/pop3/novalidate-cert}INBOX'; -$cfg['mail']['server']= '{imap.gmail.com:993/imap/ssl}INBOX'; +$cfg['mail']['default_mailserver']= '{imap.gmail.com:993/imap/ssl}INBOX'; +$cfg['mail']['default_groupemail'] = 'support@ryzomcore.com'; +$cfg['mail']['default_groupname'] = 'Ryzomcore Support'; +$cfg['mail']['default_username'] = 'amsryzom@gmail.com'; +$cfg['mail']['default_password'] = 'lol123bol'; + //----------------------------------------------------------------------------------------- // If true= the server will add automatically unknown user in the database // (in nel.user= nel.permission= ring.ring_user and ring.characters From 993973441a63f38a96b56e3d20e847aa3eca9016 Mon Sep 17 00:00:00 2001 From: Quitta Date: Sat, 17 Aug 2013 03:06:22 +0200 Subject: [PATCH 102/313] creating new tickets over email works --- .../ams_lib/autoload/mail_handler.php | 73 ++++++++++++------ .../ryzom_ams/ams_lib/autoload/ticket.php | 11 ++- .../ams_lib/autoload/ticket_user.php | 8 +- .../tools/server/ryzom_ams/www/config.php | 1 + .../ryzom_ams/www/html/autoload/webusers.php | 6 +- .../server/ryzom_ams/www/html/sql/install.php | 4 +- .../ryzom_ams/www/html/sql/ticketsql.sql | 4 +- .../www/html/sql/ticketsystemmodel.mwb | Bin 17315 -> 17276 bytes 8 files changed, 72 insertions(+), 35 deletions(-) diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php index 19869abde..ff629e9ee 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php @@ -42,7 +42,7 @@ class Mail_Handler{ } - public static function send_mail($recipient, $subject, $body, $ticket_id = 0, $from = 1) { + public static function send_mail($recipient, $subject, $body, $ticket_id = 0, $from = NULL) { if(is_numeric($recipient)) { $id_user = $recipient; $recipient = NULL; @@ -134,6 +134,7 @@ class Mail_Handler{ // Check mail $sGroups = Support_Group::getGroups(); $defaultGroup = new Support_Group(); + $defaultGroup->setSGroupId(0); $defaultGroup->setGroupEmail($default_groupemail); $defaultGroup->setIMAP_MailServer($cfg['mail']['default_mailserver']); $defaultGroup->setIMAP_Username($cfg['mail']['default_username']); @@ -148,7 +149,7 @@ class Mail_Handler{ for ($i = 1; $i <= $message_count; ++$i) { //return task ID - self::incoming_mail_handler($mbox, $i); + self::incoming_mail_handler($mbox, $i,$group); $tid = 1; //self::ams_create_email($from, $subject, $txt, $html, $to, $from); if($tid) { @@ -182,19 +183,36 @@ class Mail_Handler{ function get_ticket_id_from_subject($subject){ $startpos = strpos($subject, "[Ticket #"); - $tempString = substr($subject, $startpos+9); - $endpos = strpos($tempString, "]"); - $ticket_id = substr($tempString, 0, $endpos); + if($startpos){ + $tempString = substr($subject, $startpos+9); + $endpos = strpos($tempString, "]"); + if($endpos){ + $ticket_id = substr($tempString, 0, $endpos); + }else{ + $ticket_id = 0; + } + }else{ + $ticket_id = 0; + } return $ticket_id; } - function incoming_mail_handler($mbox,$i){ + function incoming_mail_handler($mbox,$i,$group){ $header = imap_header($mbox, $i); $subject = self::decode_utf8($header->subject); + $entire_email = imap_fetchheader($mbox, $i) . imap_body($mbox, $i); + $subject = self::decode_utf8($header->subject); + $to = $header->to[0]->mailbox; + $from = $header->from[0]->mailbox . '@' . $header->from[0]->host; + $txt = self::get_part($mbox, $i, "TEXT/PLAIN"); + //$html = self::get_part($mbox, $i, "TEXT/HTML"); - print_r($header); + //get the id out of the email address of the person sending the email. + if($from !== NULL && !is_numeric($from)){ + $from = Ticket_User::get_id_from_email($from); + } //get ticket_id out of the message-id or else out of the subject line $ticket_id = 0; @@ -209,16 +227,9 @@ class Mail_Handler{ $ticket_id = self::get_ticket_id_from_subject($subject); } - //if ticket id is found + //if ticket id is found, that means it is a reply on an existing ticket if($ticket_id){ - $entire_email = imap_fetchheader($mbox, $i) . imap_body($mbox, $i); - $subject = self::decode_utf8($header->subject); - $to = $header->to[0]->mailbox; - $from = $header->from[0]->mailbox . '@' . $header->from[0]->host; - $txt = self::get_part($mbox, $i, "TEXT/PLAIN"); - //$html = self::get_part($mbox, $i, "TEXT/HTML"); - //use the line ---------- Ticket # to make a distincton between the old message and the reply $endpos = strpos($txt, ">---------- Ticket #"); if($endpos){ @@ -230,18 +241,32 @@ class Mail_Handler{ } } + //if email is sent from an existing email address in the db (else it will give an error while loading the user object) + if($from != "FALSE"){ + $user = new Ticket_User(); + $user->load_With_TUserId($from); + $ticket = new Ticket(); + $ticket->load_With_TId($ticket_id); + + //if user has access to it! + if((Ticket_User::isMod($user) or ($ticket->getAuthor() == $user->getTUserId())) and $txt != ""){ + Ticket::createReply($txt, $user->getTUserId(), $ticket->getTId(), 0); + } + } - //get the id out of the email address of the person sending the email. - if($from !== NULL && !is_numeric($from)) $from = Ticket_User::get_id_from_email($from); + }else{ - $user = new Ticket_User(); - $user->load_With_TUserId($from); - $ticket = new Ticket(); - $ticket->load_With_TId($ticket_id); + //if ticket_id isn't found, create a new ticket! - //if user has access to it! - if((Ticket_User::isMod($user) or ($ticket->getAuthor() == $user->getTUserId())) and $txt != ""){ - Ticket::createReply($txt, $user->getTUserId(), $ticket->getTId(), 0); + //if an existing email address mailed the ticket + if($from != "FALSE"){ + + $newTicketId = Ticket::create_Ticket($subject, $txt,1, $from, $from); + + //if not default group, then forward it! + if($group->getSGroupId()){ + Ticket::forwardTicket(0, $newTicketId, $group->getSGroupId()); + } } } diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket.php index a9bceacd3..cba0d1211 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket.php @@ -197,12 +197,15 @@ class Ticket{ public static function forwardTicket($user_id, $ticket_id, $group_id){ if(self::ticketExists($ticket_id)){ if(isset($group_id) && $group_id != ""){ - //unassign the ticket incase the ticket is assined to yourself - self::unAssignTicket($user_id, $ticket_id); //forward the ticket $returnvalue = Forwarded::forwardTicket($group_id, $ticket_id); - //make a log entry of this action - Ticket_Log::createLogEntry( $ticket_id, $user_id, 8, $group_id); + + if($user_id != 0){ + //unassign the ticket incase the ticket is assined to yourself + self::unAssignTicket($user_id, $ticket_id); + //make a log entry of this action + Ticket_Log::createLogEntry( $ticket_id, $user_id, 8, $group_id); + } return $returnvalue; }else{ return "INVALID_SGROUP"; diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_user.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_user.php index fe3529925..6772dd216 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_user.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_user.php @@ -98,8 +98,12 @@ class Ticket_User{ public static function get_id_from_email($email){ $webUserId = WebUsers::getIdFromEmail($email); - $user = Ticket_User::constr_ExternId($webUserId); - return $user->getTUserId(); + if($webUserId != "FALSE"){ + $user = Ticket_User::constr_ExternId($webUserId); + return $user->getTUserId(); + }else{ + return "FALSE"; + } } diff --git a/code/ryzom/tools/server/ryzom_ams/www/config.php b/code/ryzom/tools/server/ryzom_ams/www/config.php index 13e6e37bb..8cac58fe0 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/config.php +++ b/code/ryzom/tools/server/ryzom_ams/www/config.php @@ -46,6 +46,7 @@ $cfg['mail']['default_groupemail'] = 'support@ryzomcore.com'; $cfg['mail']['default_groupname'] = 'Ryzomcore Support'; $cfg['mail']['default_username'] = 'amsryzom@gmail.com'; $cfg['mail']['default_password'] = 'lol123bol'; +$cfg['mail']['host'] = "ryzomcore.com"; //----------------------------------------------------------------------------------------- // If true= the server will add automatically unknown user in the database diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/autoload/webusers.php b/code/ryzom/tools/server/ryzom_ams/www/html/autoload/webusers.php index 014864e2a..58856a5ce 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/autoload/webusers.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/autoload/webusers.php @@ -81,7 +81,11 @@ class WebUsers extends Users{ $dbw = new DBLayer("web"); $statement = $dbw->execute("SELECT * FROM ams_user WHERE Email=:email", array('email' => $email)); $row = $statement->fetch(); - return $row['UId']; + if($row !== "FALSE"){ + return $row['UId']; + }else{ + return "FALSE"; + } } public function getUId(){ diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/sql/install.php b/code/ryzom/tools/server/ryzom_ams/www/html/sql/install.php index acfdb3987..c593ec4ad 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/sql/install.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/sql/install.php @@ -273,7 +273,7 @@ ON UPDATE NO ACTION) ENGINE = InnoDB; - INSERT IGNORE INTO `" . $cfg['db']['lib']['name'] ."`.`ticket_category` (`Name`) VALUES ('Hacking'),('Ingame-Bug'),('Website-Bug'),('Installation'); + INSERT IGNORE INTO `" . $cfg['db']['lib']['name'] ."`.`ticket_category` (`Name`) VALUES ('Uncategorized'),('Hacking'),('Ingame-Bug'),('Website-Bug'),('Installation'); @@ -374,7 +374,7 @@ CONSTRAINT `fk_email_support_group1` FOREIGN KEY (`Sender` ) REFERENCES `" . $cfg['db']['lib']['name'] ."`.`support_group` (`SGroupId` ) - ON DELETE NO ACTION + ON DELETE CASCADE ON UPDATE NO ACTION) ENGINE = InnoDB; diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/sql/ticketsql.sql b/code/ryzom/tools/server/ryzom_ams/www/html/sql/ticketsql.sql index 66059fa8d..eb95f792b 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/sql/ticketsql.sql +++ b/code/ryzom/tools/server/ryzom_ams/www/html/sql/ticketsql.sql @@ -310,7 +310,7 @@ CREATE TABLE IF NOT EXISTS `mydb`.`email` ( `Status` VARCHAR(45) NULL , `Attempts` VARCHAR(45) NULL DEFAULT 0 , `UserId` INT(10) UNSIGNED NOT NULL , - `MessageId` VARCHAR(45) NOT NULL , + `MessageId` VARCHAR(45) NULL , `TicketId` INT UNSIGNED NOT NULL , `Sender` INT(10) UNSIGNED NULL , PRIMARY KEY (`MailId`) , @@ -330,7 +330,7 @@ CREATE TABLE IF NOT EXISTS `mydb`.`email` ( CONSTRAINT `fk_email_support_group1` FOREIGN KEY (`Sender` ) REFERENCES `mydb`.`support_group` (`SGroupId` ) - ON DELETE NO ACTION + ON DELETE CASCADE ON UPDATE NO ACTION) ENGINE = InnoDB; diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/sql/ticketsystemmodel.mwb b/code/ryzom/tools/server/ryzom_ams/www/html/sql/ticketsystemmodel.mwb index 47b014bc951ad98d658f45a87503af9edf562903..4e9f85fa4ef274ed3dd0c290afe464d27e8a1168 100644 GIT binary patch literal 17276 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 DUUp*< literal 17315 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 B$?X6D From b765b0133d25dc71bbad01e414a58e23ec3c3bd9 Mon Sep 17 00:00:00 2001 From: Quitta Date: Sat, 17 Aug 2013 19:07:00 +0200 Subject: [PATCH 103/313] made 3 options for incomming mail: reply on ticket, new ticket and nothing related to that. When it's nothing related to that the mail will stay in the mailbox untill it get's manually read and deleted! --- .../ams_lib/autoload/mail_handler.php | 44 +++++++++++-------- .../tools/server/ryzom_ams/www/config.php | 2 +- .../ryzom_ams/www/html/autoload/webusers.php | 4 +- 3 files changed, 30 insertions(+), 20 deletions(-) diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php index ff629e9ee..baa0a852f 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php @@ -149,17 +149,18 @@ class Mail_Handler{ for ($i = 1; $i <= $message_count; ++$i) { //return task ID - self::incoming_mail_handler($mbox, $i,$group); - $tid = 1; //self::ams_create_email($from, $subject, $txt, $html, $to, $from); + $tid = self::incoming_mail_handler($mbox, $i,$group); if($tid) { - //TODO: base file on Ticket + reply id - /* $file = fopen($MAIL_DIR."/mail/".$tid, 'w'); + //TODO: base file on Ticket + timestamp + $file = fopen($MAIL_DIR."/mail/ticket".$tid.".".time(), 'w'); fwrite($file, $entire_email); - fclose($file); */ + fclose($file); + + //mark message $i of $mbox for deletion! + imap_delete($mbox, $i); } - //mark message $i of $mbox for deletion! - imap_delete($mbox, $i); + } //delete marked messages imap_expunge($mbox); @@ -252,23 +253,30 @@ class Mail_Handler{ if((Ticket_User::isMod($user) or ($ticket->getAuthor() == $user->getTUserId())) and $txt != ""){ Ticket::createReply($txt, $user->getTUserId(), $ticket->getTId(), 0); } + } + print("\n Email found that is a reply to a ticket!\n"); + return $ticket_id; - }else{ + }else if($from != "FALSE"){ //if ticket_id isn't found, create a new ticket! - //if an existing email address mailed the ticket - if($from != "FALSE"){ - - $newTicketId = Ticket::create_Ticket($subject, $txt,1, $from, $from); - - //if not default group, then forward it! - if($group->getSGroupId()){ - Ticket::forwardTicket(0, $newTicketId, $group->getSGroupId()); - } - } + $newTicketId = Ticket::create_Ticket($subject, $txt,1, $from, $from); + + //if not default group, then forward it! + if($group->getSGroupId()){ + Ticket::forwardTicket(0, $newTicketId, $group->getSGroupId()); + } + print("\n Email found that is a new ticket!\n"); + return $newTicketId; + + + }else{ + //if it's a email that has nothing to do with ticketing, return 0; + print("\n Email found that isn't a reply or new ticket!\n"); + return 0; } } diff --git a/code/ryzom/tools/server/ryzom_ams/www/config.php b/code/ryzom/tools/server/ryzom_ams/www/config.php index 8cac58fe0..247b2a961 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/config.php +++ b/code/ryzom/tools/server/ryzom_ams/www/config.php @@ -42,7 +42,7 @@ $cfg['db']['ring']['pass'] = ''; // To connect to a remote server replace "localhost" with the name or the IP address of the server you want to connect to. //$cfg['mail']['server'] = '{localhost:110/pop3/novalidate-cert}INBOX'; $cfg['mail']['default_mailserver']= '{imap.gmail.com:993/imap/ssl}INBOX'; -$cfg['mail']['default_groupemail'] = 'support@ryzomcore.com'; +$cfg['mail']['default_groupemail'] = 'amsryzom@gmail.com'; $cfg['mail']['default_groupname'] = 'Ryzomcore Support'; $cfg['mail']['default_username'] = 'amsryzom@gmail.com'; $cfg['mail']['default_password'] = 'lol123bol'; diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/autoload/webusers.php b/code/ryzom/tools/server/ryzom_ams/www/html/autoload/webusers.php index 58856a5ce..88c9cc062 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/autoload/webusers.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/autoload/webusers.php @@ -81,7 +81,9 @@ class WebUsers extends Users{ $dbw = new DBLayer("web"); $statement = $dbw->execute("SELECT * FROM ams_user WHERE Email=:email", array('email' => $email)); $row = $statement->fetch(); - if($row !== "FALSE"){ + if(!empty($row)){ + print "shouldn't be here!" . $row . "seee"; + return $row['UId']; }else{ return "FALSE"; From 2a7f13c8d8516a03d4cade1d9cf8aa9bf73304bb Mon Sep 17 00:00:00 2001 From: Quitta Date: Sun, 18 Aug 2013 02:17:02 +0200 Subject: [PATCH 104/313] response is being sent from the correct support group atm --- .../ams_lib/autoload/mail_handler.php | 105 +++--------------- .../ryzom_ams/ams_lib/autoload/ticket.php | 16 ++- .../ryzom_ams/www/html/autoload/webusers.php | 2 - 3 files changed, 28 insertions(+), 95 deletions(-) diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php index baa0a852f..b65e04c70 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php @@ -12,30 +12,29 @@ class Mail_Handler{ } - public static function send_ticketing_mail($ticketObj, $content, $type, $author) { + public static function send_ticketing_mail($ticketObj, $content, $type, $sendingGroupId = 0) { global $TICKET_MAILING_SUPPORT; if($TICKET_MAILING_SUPPORT){ - $txt = ""; - $subject = ""; + //$txt = ""; + //$subject = ""; + if($sendingGroupId == 0){ + //if it is not forwarded (==public == which returns 0) then make it NULL which is needed to be placed in the DB. + $sendingGroupId = NULL; + } if($type == "REPLY"){ $txt = "---------- Ticket #". $ticketObj->getTId() . " ----------\n You received a new reply on your ticket: " . $ticketObj->getTitle() . "\n --------------------\n\n"; $subject = "New reply on [Ticket #" . $ticketObj->getTId() ."]"; $endTxt = "\n\n----------\nYou can reply on this message to answer directly on the ticket!"; $txt = $txt . $content . $endTxt; - self::send_mail($ticketObj->getAuthor(),$subject,$txt, $ticketObj->getTId(),$author); + self::send_mail($ticketObj->getAuthor(),$subject,$txt, $ticketObj->getTId(),$sendingGroupId); }else if($type == "NEW"){ $txt = "---------- Ticket #". $ticketObj->getTId() . " ----------\n Your ticket: " . $ticketObj->getTitle() . " is newly created"; - if($ticketObj->getAuthor() != $author){ - $txt = $txt . " by " . Ticket_User::get_username_from_id($author); - }else{ - $author = $ticketObj->getAuthor(); - } $txt = $txt . "\n --------------------\n\n"; $subject = "New ticket created [Ticket #" . $ticketObj->getTId() ."]"; $endTxt = "\n\n----------\nYou can reply on this message to answer directly on the ticket!"; $txt = $txt . $content . $endTxt; - self::send_mail($ticketObj->getAuthor(),$subject,$txt, $ticketObj->getTId()); + self::send_mail($ticketObj->getAuthor(),$subject,$txt, $ticketObj->getTId(), $sendingGroupId); } } @@ -62,8 +61,8 @@ class Mail_Handler{ $default_groupname = $cfg['mail']['default_groupname']; /* $inbox_host = $cfg['mail']['host']; - $oms_reply_to = "Ryzom Ticketing Support "; - global $MAIL_DIR;*/ + $oms_reply_to = "Ryzom Ticketing Support ";*/ + global $MAIL_DIR; // Deliver new mail @@ -154,7 +153,7 @@ class Mail_Handler{ if($tid) { //TODO: base file on Ticket + timestamp $file = fopen($MAIL_DIR."/mail/ticket".$tid.".".time(), 'w'); - fwrite($file, $entire_email); + fwrite($file, imap_fetchheader($mbox, $i) . imap_body($mbox, $i)); fclose($file); //mark message $i of $mbox for deletion! @@ -255,7 +254,7 @@ class Mail_Handler{ } } - print("\n Email found that is a reply to a ticket!\n"); + print("\n Email found that is a reply to a ticket at:".$group->getGroupEmail()); return $ticket_id; }else if($from != "FALSE"){ @@ -263,91 +262,21 @@ class Mail_Handler{ //if ticket_id isn't found, create a new ticket! //if an existing email address mailed the ticket - $newTicketId = Ticket::create_Ticket($subject, $txt,1, $from, $from); + //if not default group, then forward it by giving the $group->getSGroupId's param + $newTicketId = Ticket::create_Ticket($subject, $txt,1, $from, $from, $group->getSGroupId()); - //if not default group, then forward it! - if($group->getSGroupId()){ - Ticket::forwardTicket(0, $newTicketId, $group->getSGroupId()); - } - print("\n Email found that is a new ticket!\n"); + print("\n Email regarding new ticket found at:".$group->getGroupEmail()); return $newTicketId; }else{ //if it's a email that has nothing to do with ticketing, return 0; - print("\n Email found that isn't a reply or new ticket!\n"); + print("\n Email found that isn't a reply or new ticket, at:".$group->getGroupEmail()); return 0; } } - /*function ams_create_email($from, $subject, $body, $html, $recipient = 0, $sender = NULL) { - - //TODO: - if($recipient == 0 && !is_string($recipient)) { - global $user; - $recipient = $user->uid; - } - - if($sender !== NULL && !is_numeric($sender)) $sender = self::get_id_from_username($sender); - if(!is_numeric($recipient)) $recipient = self::get_id_from_username($recipient); - - $message = array( - 'creator' => $sender, - 'owner' => $recipient, - 'type' => 'email', - 'summary' => $subject, - 'data' => array ( - 'subject' => $subject, - 'body' => $body, - 'html' => $html, - 'sender' => oms_get_username_from_id($sender), - 'from' => $from, - 'recipient' => oms_get_username_from_id($recipient), - 'time' => time(), - ), - ); - - //TO ASK: - oms_task_create($message); - oms_task_index($message, array('subject', 'body', 'sender', 'recipient')); - //--------------------------- - return $message['id_task']; - }*/ - - - - /*function oms_get_email($id) { - - $message = oms_task_load($id); - if($message) { - oms_prepare_email($message); - return $message; - } else { - return FALSE; - } - - }*/ - - - - /*function oms_prepare_email(&$message) { - - $data = $message['data']; - $data['id_message'] = $message['id_task']; - $data['read'] = ($message['status'] != 'NEW' && $message['status'] != 'UNREAD'); - $message = $data; - - }*/ - - - - /*function oms_email_mark_read($mid) { - - db_exec("update task set status = 'READ' where id_task = ? and type = 'email' and module = 'email'", array($mid)); - - }*/ - function decode_utf8($str) { diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket.php index cba0d1211..db03aab97 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket.php @@ -81,9 +81,9 @@ class Ticket{ /*FUNCTION: create_Ticket() * creates a ticket + first initial reply and fills in the content of it! - * + * for_support_group defines to which support group the ticket has to be forwarded */ - public static function create_Ticket( $title, $content, $category, $author, $real_author) { + public static function create_Ticket( $title, $content, $category, $author, $real_author, $for_support_group = 0) { $ticket = new Ticket(); $values = array("Title" => $title, "Status"=> 1, "Queue"=> 0, "Ticket_Category" => $category, "Author" => $author, "Priority" => 0); @@ -96,8 +96,14 @@ class Ticket{ }else{ Ticket_Log::createLogEntry( $ticket_id, $real_author, 2, $author); } - Ticket_Reply::createReply($content, $author, $ticket_id, 0, $author); - Mail_Handler::send_ticketing_mail($ticket, $content, "NEW", $real_author); + Ticket_Reply::createReply($content, $author, $ticket_id, 0, $author); + + //send email that new ticket has been created + if($for_support_group){ + Ticket::forwardTicket(0, $ticket_id, $for_support_group); + } + + Mail_Handler::send_ticketing_mail($ticket, $content, "NEW", $real_author, $ticket->getForwardedGroupId()); return $ticket_id; } @@ -160,7 +166,7 @@ class Ticket{ //notify ticket author that a new reply is added! if($ticket->getAuthor() != $author){ - Mail_Handler::send_ticketing_mail($ticket, $content, "REPLY", $author); + Mail_Handler::send_ticketing_mail($ticket, $content, "REPLY", $ticket->getForwardedGroupId()); } diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/autoload/webusers.php b/code/ryzom/tools/server/ryzom_ams/www/html/autoload/webusers.php index 88c9cc062..3f49e5e88 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/autoload/webusers.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/autoload/webusers.php @@ -82,8 +82,6 @@ class WebUsers extends Users{ $statement = $dbw->execute("SELECT * FROM ams_user WHERE Email=:email", array('email' => $email)); $row = $statement->fetch(); if(!empty($row)){ - print "shouldn't be here!" . $row . "seee"; - return $row['UId']; }else{ return "FALSE"; From 83d4a89e46b2167022db60d720a6462075005ea1 Mon Sep 17 00:00:00 2001 From: Quitta Date: Sun, 18 Aug 2013 02:49:58 +0200 Subject: [PATCH 105/313] fixed some nasty errors + made it show the entire replied message --- .../server/ryzom_ams/ams_lib/autoload/mail_handler.php | 4 ++-- .../tools/server/ryzom_ams/ams_lib/autoload/ticket.php | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php index b65e04c70..cb101bfec 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php @@ -231,7 +231,7 @@ class Mail_Handler{ if($ticket_id){ //use the line ---------- Ticket # to make a distincton between the old message and the reply - $endpos = strpos($txt, ">---------- Ticket #"); + /*$endpos = strpos($txt, ">---------- Ticket #"); if($endpos){ $txt = substr($txt, 0, $endpos); }else{ @@ -239,7 +239,7 @@ class Mail_Handler{ if($endpos){ $txt = substr($txt, 0, $endpos); } - } + }*/ //if email is sent from an existing email address in the db (else it will give an error while loading the user object) if($from != "FALSE"){ diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket.php index db03aab97..1d57ae8b2 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket.php @@ -84,7 +84,7 @@ class Ticket{ * for_support_group defines to which support group the ticket has to be forwarded */ public static function create_Ticket( $title, $content, $category, $author, $real_author, $for_support_group = 0) { - + $ticket = new Ticket(); $values = array("Title" => $title, "Status"=> 1, "Queue"=> 0, "Ticket_Category" => $category, "Author" => $author, "Priority" => 0); $ticket->set($values); @@ -102,8 +102,8 @@ class Ticket{ if($for_support_group){ Ticket::forwardTicket(0, $ticket_id, $for_support_group); } - - Mail_Handler::send_ticketing_mail($ticket, $content, "NEW", $real_author, $ticket->getForwardedGroupId()); + + Mail_Handler::send_ticketing_mail($ticket, $content, "NEW", $ticket->getForwardedGroupId()); return $ticket_id; } From e0d9ff8628c70e9dc249f707c35c8bd177e0b457 Mon Sep 17 00:00:00 2001 From: Quitta Date: Sun, 18 Aug 2013 03:39:35 +0200 Subject: [PATCH 106/313] fixed dirty error in queue's that didn't allow me to assing/unassign after creating a queue --- .../ams_lib/autoload/gui_elements.php | 42 ++++++++++--------- .../ams_lib/autoload/ticket_queue_handler.php | 20 +++++---- .../ryzom_ams/www/html/inc/show_queue.php | 15 +++++-- .../www/html/templates/show_queue.tpl | 6 +-- 4 files changed, 48 insertions(+), 35 deletions(-) diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/gui_elements.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/gui_elements.php index cb932afe5..e9748fc04 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/gui_elements.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/gui_elements.php @@ -5,31 +5,33 @@ class Gui_Elements{ public static function make_table( $inputList, $funcArray ,$fieldArray){ $i = 0; $result = Array(); - foreach($inputList as $element){ - $j = 0; - foreach($funcArray as $function){ - $fnames = explode('->', $function); - $intermediate_result = NULL; - foreach($fnames as $fname) { - if(substr($fname, -2) == "()") { - $fname = substr($fname, 0, strlen($fname)-2); - if($intermediate_result == NULL) { - $intermediate_result = $element->$fname(); + if(!empty($inputList)){ + foreach($inputList as $element){ + $j = 0; + foreach($funcArray as $function){ + $fnames = explode('->', $function); + $intermediate_result = NULL; + foreach($fnames as $fname) { + if(substr($fname, -2) == "()") { + $fname = substr($fname, 0, strlen($fname)-2); + if($intermediate_result == NULL) { + $intermediate_result = $element->$fname(); + } else { + $intermediate_result = $intermediate_result->$fname(); + } } else { - $intermediate_result = $intermediate_result->$fname(); - } - } else { - if($intermediate_result == NULL) { - $intermediate_result = $element->$fname(); - } else { - $intermediate_result = $intermediate_result->$fname(); + if($intermediate_result == NULL) { + $intermediate_result = $element->$fname(); + } else { + $intermediate_result = $intermediate_result->$fname(); + } } } + $result[$i][$fieldArray[$j]] = $intermediate_result; + $j++; } - $result[$i][$fieldArray[$j]] = $intermediate_result; - $j++; + $i++; } - $i++; } return $result; } diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_queue_handler.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_queue_handler.php index 8930a0651..116473c07 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_queue_handler.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_queue_handler.php @@ -33,15 +33,19 @@ class Ticket_Queue_Handler{ default: return "ERROR"; } + $this->pagination = new Pagination($this->queue->getQuery(),"lib",10,"Ticket",$this->queue->getParams()); - foreach( $this->pagination->getElements() as $element ){ - $catInstance = new Ticket_Category(); - $catInstance->load_With_TCategoryId($element->getTicket_Category()); - $element->setTicket_Category($catInstance); - - $userInstance = new Ticket_User(); - $userInstance->load_With_TUserId($element->getAuthor()); - $element->setAuthor($userInstance); + $elemArray = $this->pagination->getElements(); + if(!empty($elemArray)){ + foreach( $elemArray as $element ){ + $catInstance = new Ticket_Category(); + $catInstance->load_With_TCategoryId($element->getTicket_Category()); + $element->setTicket_Category($catInstance); + + $userInstance = new Ticket_User(); + $userInstance->load_With_TUserId($element->getAuthor()); + $element->setAuthor($userInstance); + } } return $this->pagination->getElements(); diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/inc/show_queue.php b/code/ryzom/tools/server/ryzom_ams/www/html/inc/show_queue.php index 0d057e7e4..513c5f52f 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/inc/show_queue.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/inc/show_queue.php @@ -16,6 +16,12 @@ function show_queue(){ //Pagination Base Links $result['pagination_base_link'] = "index.php?page=show_queue&get=".$result['queue_view'] ; + //form url to keep the getters constant + $result['getURL'] = "index.php?page=show_queue&get=" . $result['queue_view']; + if(isset($_GET['pagenum'])){ + $result['getURL'] = $result['getURL'] . "&pagenum=".$_GET['pagenum']; + } + if(isset($_GET['get']) && ($_GET['get'] == "create") && isset($_GET['userid']) && isset($_GET['groupid']) && isset($_GET['what']) && isset($_GET['how']) && isset($_GET['who'])){ $userid = filter_var($_GET['userid'], FILTER_SANITIZE_NUMBER_INT); $groupid = filter_var($_GET['groupid'], FILTER_SANITIZE_NUMBER_INT); @@ -29,6 +35,9 @@ function show_queue(){ $result['prev_created_what'] = $what; $result['prev_created_how'] = $how; $result['prev_created_who'] = $who; + + $result['getURL'] = $result['getURL'] . "&userid=".$userid."&groupid=".$groupid."&what=".$what."&how=".$how."&who=".$who; + } //if an action is set @@ -57,10 +66,8 @@ function show_queue(){ $result['prev_created_what'] = $what; $result['prev_created_how'] = $how; $result['prev_created_who'] = $who; - - - - + $result['getURL'] = $result['getURL'] . "&userid=".$userid."&groupid=".$groupid."&what=".$what."&how=".$how."&who=".$who; + break; } diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/templates/show_queue.tpl b/code/ryzom/tools/server/ryzom_ams/www/html/templates/show_queue.tpl index 179767589..6cb2069fa 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/templates/show_queue.tpl +++ b/code/ryzom/tools/server/ryzom_ams/www/html/templates/show_queue.tpl @@ -34,7 +34,7 @@ @@ -108,13 +108,13 @@

+ {if isset($isAdmin) && $isAdmin eq 'TRUE'}{/if} @@ -94,6 +125,7 @@ + {if isset($isAdmin) && $isAdmin eq 'TRUE'}{/if} {/foreach} From bce43b641ed7acdafb2a4b01fbda368baf7fc068 Mon Sep 17 00:00:00 2001 From: Quitta Date: Sat, 24 Aug 2013 17:19:46 +0200 Subject: [PATCH 112/313] Inagme layout for modifying group email stuff is fixed --- .../ams_lib/ingame_templates/sgroup_list.tpl | 12 ++-- .../ams_lib/ingame_templates/show_sgroup.tpl | 58 +++++++++++++++++++ 2 files changed, 64 insertions(+), 6 deletions(-) diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/sgroup_list.tpl b/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/sgroup_list.tpl index a0c41f577..322323d03 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/sgroup_list.tpl +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/sgroup_list.tpl @@ -42,17 +42,17 @@
{if $ticket.assigned eq 0} -
+
{else if $ticket.assigned eq $user_id} -
+ From ba0e8e2d511ed94f7c57850706bbaa69b1b176d7 Mon Sep 17 00:00:00 2001 From: Quitta Date: Mon, 19 Aug 2013 00:02:55 +0200 Subject: [PATCH 107/313] Users can specify if they want to receive or dont want to receive tickt updates! --- .../ams_lib/autoload/mail_handler.php | 40 ++++++++++------ .../tools/server/ryzom_ams/www/config.php | 10 ++-- .../ryzom_ams/www/html/autoload/webusers.php | 30 ++++++++++-- .../www/html/func/change_receivemail.php | 48 +++++++++++++++++++ .../ryzom_ams/www/html/inc/settings.php | 19 ++++---- .../server/ryzom_ams/www/html/sql/install.php | 1 + .../ryzom_ams/www/html/templates/settings.tpl | 35 ++++++++++++++ 7 files changed, 152 insertions(+), 31 deletions(-) create mode 100644 code/ryzom/tools/server/ryzom_ams/www/html/func/change_receivemail.php diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php index cb101bfec..12553eb69 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php @@ -21,22 +21,32 @@ class Mail_Handler{ //if it is not forwarded (==public == which returns 0) then make it NULL which is needed to be placed in the DB. $sendingGroupId = NULL; } - if($type == "REPLY"){ - $txt = "---------- Ticket #". $ticketObj->getTId() . " ----------\n You received a new reply on your ticket: " . $ticketObj->getTitle() . - "\n --------------------\n\n"; - $subject = "New reply on [Ticket #" . $ticketObj->getTId() ."]"; - $endTxt = "\n\n----------\nYou can reply on this message to answer directly on the ticket!"; - $txt = $txt . $content . $endTxt; - self::send_mail($ticketObj->getAuthor(),$subject,$txt, $ticketObj->getTId(),$sendingGroupId); - }else if($type == "NEW"){ - $txt = "---------- Ticket #". $ticketObj->getTId() . " ----------\n Your ticket: " . $ticketObj->getTitle() . " is newly created"; - $txt = $txt . "\n --------------------\n\n"; - $subject = "New ticket created [Ticket #" . $ticketObj->getTId() ."]"; - $endTxt = "\n\n----------\nYou can reply on this message to answer directly on the ticket!"; - $txt = $txt . $content . $endTxt; - self::send_mail($ticketObj->getAuthor(),$subject,$txt, $ticketObj->getTId(), $sendingGroupId); - } + $author = $ticketObj->getAuthor(); + $webUser = new WebUsers($author); + //if the author of the ticket wants to receive mail, then send it! + if($webUser->getReceiveMail()){ + + switch($type){ + case "REPLY": + $txt = "---------- Ticket #". $ticketObj->getTId() . " ----------\n You received a new reply on your ticket: " . $ticketObj->getTitle() . + "\n --------------------\n\n"; + $subject = "New reply on [Ticket #" . $ticketObj->getTId() ."]"; + $endTxt = "\n\n----------\nYou can reply on this message to answer directly on the ticket!"; + $txt = $txt . $content . $endTxt; + self::send_mail($author,$subject,$txt, $ticketObj->getTId(),$sendingGroupId); + break; + + case "NEW": + $txt = "---------- Ticket #". $ticketObj->getTId() . " ----------\n Your ticket: " . $ticketObj->getTitle() . " is newly created"; + $txt = $txt . "\n --------------------\n\n"; + $subject = "New ticket created [Ticket #" . $ticketObj->getTId() ."]"; + $endTxt = "\n\n----------\nYou can reply on this message to answer directly on the ticket!"; + $txt = $txt . $content . $endTxt; + self::send_mail($author,$subject,$txt, $ticketObj->getTId(), $sendingGroupId); + break; + } + } } } diff --git a/code/ryzom/tools/server/ryzom_ams/www/config.php b/code/ryzom/tools/server/ryzom_ams/www/config.php index 247b2a961..d4b99a41c 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/config.php +++ b/code/ryzom/tools/server/ryzom_ams/www/config.php @@ -48,6 +48,11 @@ $cfg['mail']['default_username'] = 'amsryzom@gmail.com'; $cfg['mail']['default_password'] = 'lol123bol'; $cfg['mail']['host'] = "ryzomcore.com"; +//Defines mailing related stuff +$SUPPORT_GROUP_IMAP_CRYPTKEY = "azerty"; +$TICKET_MAILING_SUPPORT = true; +$MAIL_DIR = "/tmp"; + //----------------------------------------------------------------------------------------- // If true= the server will add automatically unknown user in the database // (in nel.user= nel.permission= ring.ring_user and ring.characters @@ -73,7 +78,4 @@ $TIME_FORMAT = "m-d-Y H:i:s"; //defines which ingame layout template should be used $INGAME_LAYOUT = "basic"; -//Defines mailing related stuff -$SUPPORT_GROUP_IMAP_CRYPTKEY = "azerty"; -$TICKET_MAILING_SUPPORT = true; -$MAIL_DIR = "/tmp"; + diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/autoload/webusers.php b/code/ryzom/tools/server/ryzom_ams/www/html/autoload/webusers.php index 3f49e5e88..d819c89c5 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/autoload/webusers.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/autoload/webusers.php @@ -9,6 +9,7 @@ class WebUsers extends Users{ private $lastname; private $gender; private $country; + private $receiveMail; function __construct($UId = 0) { $this->uId = $UId; @@ -22,6 +23,7 @@ class WebUsers extends Users{ $this->lastname = $values['LastName']; $this->gender = $values['Gender']; $this->country = $values['Country']; + $this->receiveMail = $values['ReceiveMail']; } /** @@ -114,16 +116,26 @@ class WebUsers extends Users{ public function getInfo(){ $dbw = new DBLayer("web"); - if(! (isset($this->firstname) && isset($this->lastname) && isset($this->gender) && isset($this->country) ) || - $this->firstname == "" || $this->lastname == "" || $this->gender == "" || $this->country == ""){ + if(! (isset($this->firstname) && isset($this->lastname) && isset($this->gender) && isset($this->country) && isset($this->receiveMail) ) || + $this->firstname == "" || $this->lastname == "" || $this->gender == "" || $this->country == "" || $this->receiveMail == ""){ $statement = $dbw->execute("SELECT * FROM ams_user WHERE UId=:id", array('id' => $this->uId)); $row = $statement->fetch(); $this->set($row); } - $result = Array('FirstName' => $this->firstname, 'LastName' => $this->lastname, 'Gender' => $this->gender, 'Country' => $this->country); + $result = Array('FirstName' => $this->firstname, 'LastName' => $this->lastname, 'Gender' => $this->gender, 'Country' => $this->country, 'ReceiveMail' => $this->receiveMail); return $result; } + public function getReceiveMail(){ + $dbw = new DBLayer("web"); + if(! isset($this->receiveMail) || $this->receiveMail == ""){ + $statement = $dbw->execute("SELECT * FROM ams_user WHERE UId=:id", array('id' => $this->uId)); + $row = $statement->fetch(); + $this->set($row); + } + return $this->receiveMail; + } + public function isLoggedIn(){ if(isset($_SESSION['user'])){ return true; @@ -159,6 +171,18 @@ class WebUsers extends Users{ return $reply; } + public static function setReceiveMail($user, $receivemail){ + $values = Array('user' => $user, 'receivemail' => $receivemail); + try { + //make connection with and put into shard db + $dbw = new DBLayer("web"); + $dbw->execute("UPDATE ams_user SET ReceiveMail = :receivemail WHERE UId = :user ",$values); + } + catch (PDOException $e) { + //ERROR: the web DB is offline + } + } + public function getUsers(){ $dbl = new DBLayer("web"); $data = $dbl->executeWithoutParams("SELECT * FROM ams_user"); diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/func/change_receivemail.php b/code/ryzom/tools/server/ryzom_ams/www/html/func/change_receivemail.php new file mode 100644 index 000000000..85cbec965 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/www/html/func/change_receivemail.php @@ -0,0 +1,48 @@ +getInfo(); $result['target_id'] = $_SESSION['id']; - $result['current_mail'] = $webUser->getEmail(); - - //Sanitize Data - $result['current_mail'] = filter_var($result['current_mail'], FILTER_SANITIZE_EMAIL); - //$result['Login'] = filter_var($result['Login'], FILTER_SANITIZE_STRING); - $result['FirstName'] = filter_var($result['FirstName'], FILTER_SANITIZE_STRING); - $result['LastName'] = filter_var($result['LastName'], FILTER_SANITIZE_STRING); - $result['Country'] = filter_var($result['Country'], FILTER_SANITIZE_STRING); - $result['Gender'] = filter_var($result['Gender'], FILTER_SANITIZE_NUMBER_INT); + $result['current_mail'] = $webUser->getEmail(); + } + //Sanitize Data + $result['current_mail'] = filter_var($result['current_mail'], FILTER_SANITIZE_EMAIL); + //$result['Login'] = filter_var($result['Login'], FILTER_SANITIZE_STRING); + $result['FirstName'] = filter_var($result['FirstName'], FILTER_SANITIZE_STRING); + $result['LastName'] = filter_var($result['LastName'], FILTER_SANITIZE_STRING); + $result['Country'] = filter_var($result['Country'], FILTER_SANITIZE_STRING); + $result['Gender'] = filter_var($result['Gender'], FILTER_SANITIZE_NUMBER_INT); + $result['ReceiveMail'] = filter_var($result['ReceiveMail'], FILTER_SANITIZE_NUMBER_INT); $result['country_array'] = getCountryArray(); return $result; }else{ diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/sql/install.php b/code/ryzom/tools/server/ryzom_ams/www/html/sql/install.php index c593ec4ad..f72e0c8bf 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/sql/install.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/sql/install.php @@ -25,6 +25,7 @@ `LastName` varchar(255) NOT NULL DEFAULT '', `Gender` tinyint(1) unsigned NOT NULL DEFAULT '0', `Country` char(2) NOT NULL DEFAULT '', + `ReceiveMail` int(1) NOT NULL DEFAULT 1, PRIMARY KEY (`UId`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1 COMMENT='contains all users information for ryzom_ams'; diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/templates/settings.tpl b/code/ryzom/tools/server/ryzom_ams/www/html/templates/settings.tpl index 0a7aa263c..599427946 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/templates/settings.tpl +++ b/code/ryzom/tools/server/ryzom_ams/www/html/templates/settings.tpl @@ -101,6 +101,7 @@ + {if isset($SUCCESS_MAIL) and $SUCCESS_MAIL eq "OK"}
The email has been changed! @@ -124,6 +125,40 @@
+
+

Ticket updates

+
+ + +
+
+
+
+
+ Ticket-Update Mail Settings + +
+ +
+ +
+ +
+ + + +
+ +
+ +
+
+
+
+
From 72a0c8303c63e9ab61d241f0b67001b3b175d6df Mon Sep 17 00:00:00 2001 From: Quitta Date: Mon, 19 Aug 2013 20:22:01 +0200 Subject: [PATCH 108/313] Added encryption by using the openSSL functionality to encrypt the email passwords in the db --- .../ams_lib/autoload/mail_handler.php | 51 +++++++++++------- .../ryzom_ams/ams_lib/autoload/mycrypt.php | 53 +++++++++++++++++++ .../ams_lib/autoload/support_group.php | 7 ++- .../tools/server/ryzom_ams/www/config.php | 4 ++ .../www/html/func/modify_email_of_sgroup.php | 7 ++- .../server/ryzom_ams/www/html/sql/install.php | 2 +- 6 files changed, 101 insertions(+), 23 deletions(-) create mode 100644 code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mycrypt.php diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php index 12553eb69..f509de729 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php @@ -142,38 +142,49 @@ class Mail_Handler{ } // Check mail $sGroups = Support_Group::getGroups(); + + //decrypt passwords in the db! + $crypter = new MyCrypt($cfg['crypt']); + foreach($sGroups as $group){ + $group->setIMAP_Password($crypter->decrypt($cfg['mail']['default_password'])); + } + $defaultGroup = new Support_Group(); $defaultGroup->setSGroupId(0); $defaultGroup->setGroupEmail($default_groupemail); $defaultGroup->setIMAP_MailServer($cfg['mail']['default_mailserver']); $defaultGroup->setIMAP_Username($cfg['mail']['default_username']); $defaultGroup->setIMAP_Password($cfg['mail']['default_password']); - + + //add default group to the list $sGroups[] = $defaultGroup; foreach($sGroups as $group){ - $mbox = imap_open($group->getIMAP_MailServer(), $group->getIMAP_Username(), $group->getIMAP_Password()) or die('Cannot connect to mail server: ' . imap_last_error()); - $message_count = imap_num_msg($mbox); - - for ($i = 1; $i <= $message_count; ++$i) { - - //return task ID - $tid = self::incoming_mail_handler($mbox, $i,$group); - - if($tid) { - //TODO: base file on Ticket + timestamp - $file = fopen($MAIL_DIR."/mail/ticket".$tid.".".time(), 'w'); - fwrite($file, imap_fetchheader($mbox, $i) . imap_body($mbox, $i)); - fclose($file); + //check if group has mailing stuff filled in! + if($group->getGroupEmail() != "" && $group->getIMAP_MailServer() != "" && $group->getIMAP_Username() != "" && $group->getIMAP_Password() != "") + $mbox = imap_open($group->getIMAP_MailServer(), $group->getIMAP_Username(), $group->getIMAP_Password()) or die('Cannot connect to mail server: ' . imap_last_error()); + $message_count = imap_num_msg($mbox); + + for ($i = 1; $i <= $message_count; ++$i) { - //mark message $i of $mbox for deletion! - imap_delete($mbox, $i); + //return task ID + $tid = self::incoming_mail_handler($mbox, $i,$group); + + if($tid) { + //TODO: base file on Ticket + timestamp + $file = fopen($MAIL_DIR."/mail/ticket".$tid.".".time(), 'w'); + fwrite($file, imap_fetchheader($mbox, $i) . imap_body($mbox, $i)); + fclose($file); + + //mark message $i of $mbox for deletion! + imap_delete($mbox, $i); + } + } - + //delete marked messages + imap_expunge($mbox); + imap_close($mbox); } - //delete marked messages - imap_expunge($mbox); - imap_close($mbox); } } diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mycrypt.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mycrypt.php new file mode 100644 index 000000000..2a90f21a8 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mycrypt.php @@ -0,0 +1,53 @@ +config = $cryptinfo; + } + + + public function encrypt($data) { + + self::check_methods($this->config['enc_method'], $this->config['hash_method']); + $iv = self::hashIV($this->config['key'], $this->config['hash_method'], openssl_cipher_iv_length($this->config['enc_method'])); + $infostr = sprintf('$%s$%s$', $this->config['enc_method'], $this->config['hash_method']); + return $infostr . openssl_encrypt($data, $this->config['enc_method'], $this->config['key'], false, $iv); + } + + public function decrypt($edata) { + $e_arr = explode('$', $edata); + if( count($e_arr) != 4 ) { + Throw new Exception('Given data is missing crucial sections.'); + } + $this->config['enc_method'] = $e_arr[1]; + $this->config['hash_method'] = $e_arr[2]; + self::check_methods($this->config['enc_method'], $this->config['hash_method']); + $iv = self::hashIV($this->config['key'], $this->config['hash_method'], openssl_cipher_iv_length($this->config['enc_method'])); + return openssl_decrypt($e_arr[3], $this->config['enc_method'], $this->config['key'], false, $iv); + } + + private static function hashIV($key, $method, $iv_size) { + $myhash = hash($method, $key, TRUE); + while( strlen($myhash) < $iv_size ) { + $myhash .= hash($method, $myhash, TRUE); + } + return substr($myhash, 0, $iv_size); + } + + private static function check_methods($enc, $hash) { + + if( ! function_exists('openssl_encrypt') ) { + Throw new Exception('openssl_encrypt() not supported.'); + } else if( ! in_array($enc, openssl_get_cipher_methods()) ) { + Throw new Exception('Encryption method ' . $enc . ' not supported.'); + } else if( ! in_array(strtolower($hash), hash_algos()) ) { + Throw new Exception('Hashing method ' . $hash . ' not supported.'); + } + } + + + +} \ No newline at end of file diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/support_group.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/support_group.php index b44e1552e..6975cb183 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/support_group.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/support_group.php @@ -51,7 +51,12 @@ class Support_Group{ $sGroup->setGroupEmail($values['GroupEmail']); $sGroup->setIMAP_MailServer($values['IMAP_MailServer']); $sGroup->setIMAP_Username($values['IMAP_Username']); - $sGroup->setIMAP_Password($values['IMAP_Password']); + + //encrypt password! + global $cfg; + $crypter = new MyCrypt($cfg['crypt']); + $enc_password = $crypter->encrypt($values['IMAP_Password']); + $sGroup->setIMAP_Password($enc_password); $sGroup->create(); return "SUCCESS"; diff --git a/code/ryzom/tools/server/ryzom_ams/www/config.php b/code/ryzom/tools/server/ryzom_ams/www/config.php index d4b99a41c..c57d71746 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/config.php +++ b/code/ryzom/tools/server/ryzom_ams/www/config.php @@ -53,6 +53,10 @@ $SUPPORT_GROUP_IMAP_CRYPTKEY = "azerty"; $TICKET_MAILING_SUPPORT = true; $MAIL_DIR = "/tmp"; +$cfg['crypt']['key'] = 'Sup3rS3cr3tStuff'; +$cfg['crypt']['enc_method'] = 'AES-256-CBC'; +$cfg['crypt']['hash_method'] = "SHA512"; + //----------------------------------------------------------------------------------------- // If true= the server will add automatically unknown user in the database // (in nel.user= nel.permission= ring.ring_user and ring.characters diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/func/modify_email_of_sgroup.php b/code/ryzom/tools/server/ryzom_ams/www/html/func/modify_email_of_sgroup.php index ff028864b..a21b19eff 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/func/modify_email_of_sgroup.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/func/modify_email_of_sgroup.php @@ -15,7 +15,12 @@ function modify_email_of_sgroup(){ $group->setGroupEmail($groupemail); $group->setIMAP_MailServer(filter_var($_POST['IMAP_MailServer'],FILTER_SANITIZE_STRING)); $group->setIMAP_Username(filter_var($_POST['IMAP_Username'],FILTER_SANITIZE_STRING)); - $group->setIMAP_Password($password); + + //encrypt password! + global $cfg; + $crypter = new MyCrypt($cfg['crypt']); + $enc_password = $crypter->encrypt($password); + $group->setIMAP_Password($enc_password); $group->update(); $result['RESULT_OF_MODIFYING'] = "SUCCESS"; }else{ diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/sql/install.php b/code/ryzom/tools/server/ryzom_ams/www/html/sql/install.php index f72e0c8bf..2c0ffb6d5 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/sql/install.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/sql/install.php @@ -290,7 +290,7 @@ `GroupEmail` VARCHAR(45) NULL , `IMAP_MailServer` VARCHAR(60) NULL , `IMAP_Username` VARCHAR(45) NULL , - `IMAP_Password` VARCHAR(45) NULL , + `IMAP_Password` VARCHAR(90) NULL , PRIMARY KEY (`SGroupId`) , UNIQUE INDEX `Name_UNIQUE` (`Name` ASC) , UNIQUE INDEX `Tag_UNIQUE` (`Tag` ASC) ) From 78aa1074afd4188885bb72bb49b069251dcf0657 Mon Sep 17 00:00:00 2001 From: Quitta Date: Tue, 20 Aug 2013 02:08:12 +0200 Subject: [PATCH 109/313] fixing a lot of mail bug problems --- .../ryzom_ams/ams_lib/autoload/helpers.php | 8 +- .../ams_lib/autoload/mail_handler.php | 95 ++++++++++++------- .../ryzom_ams/ams_lib/translations/en.ini | 2 +- .../ryzom_ams/ams_lib/translations/fr.ini | 2 +- .../tools/server/ryzom_ams/www/config.php | 2 +- .../www/html/func/modify_email_of_sgroup.php | 29 +++--- .../www/html/templates/show_sgroup.tpl | 2 +- 7 files changed, 85 insertions(+), 55 deletions(-) diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/helpers.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/helpers.php index 233f44510..27f2fd80f 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/helpers.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/helpers.php @@ -115,9 +115,13 @@ class Helpers{ //Time output function for handling the time display function. - static public function outputTime($time){ + static public function outputTime($time, $str = 1){ global $TIME_FORMAT; - return date($TIME_FORMAT,strtotime($time)); + if($str){ + return date($TIME_FORMAT,strtotime($time)); + }else{ + return date($TIME_FORMAT,$time); + } } static public function check_login_ingame(){ diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php index f509de729..df2045b07 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php @@ -12,14 +12,14 @@ class Mail_Handler{ } - public static function send_ticketing_mail($ticketObj, $content, $type, $sendingGroupId = 0) { + public static function send_ticketing_mail($ticketObj, $content, $type, $sendingId = 0) { global $TICKET_MAILING_SUPPORT; if($TICKET_MAILING_SUPPORT){ //$txt = ""; //$subject = ""; - if($sendingGroupId == 0){ + if($sendingId == 0){ //if it is not forwarded (==public == which returns 0) then make it NULL which is needed to be placed in the DB. - $sendingGroupId = NULL; + $sendingId = NULL; } $author = $ticketObj->getAuthor(); $webUser = new WebUsers($author); @@ -34,7 +34,7 @@ class Mail_Handler{ $subject = "New reply on [Ticket #" . $ticketObj->getTId() ."]"; $endTxt = "\n\n----------\nYou can reply on this message to answer directly on the ticket!"; $txt = $txt . $content . $endTxt; - self::send_mail($author,$subject,$txt, $ticketObj->getTId(),$sendingGroupId); + self::send_mail($author,$subject,$txt, $ticketObj->getTId(),$sendingId); break; case "NEW": @@ -43,7 +43,7 @@ class Mail_Handler{ $subject = "New ticket created [Ticket #" . $ticketObj->getTId() ."]"; $endTxt = "\n\n----------\nYou can reply on this message to answer directly on the ticket!"; $txt = $txt . $content . $endTxt; - self::send_mail($author,$subject,$txt, $ticketObj->getTId(), $sendingGroupId); + self::send_mail($author,$subject,$txt, $ticketObj->getTId(), $sendingId); break; } } @@ -52,10 +52,12 @@ class Mail_Handler{ public static function send_mail($recipient, $subject, $body, $ticket_id = 0, $from = NULL) { + $id_user = NULL; if(is_numeric($recipient)) { $id_user = $recipient; $recipient = NULL; } + $query = "INSERT INTO email (Recipient,Subject,Body,Status,Attempts,Sender,UserId,MessageId,TicketId) VALUES (:recipient, :subject, :body, :status, :attempts, :sender, :id_user, :messageId, :ticketId)"; $values = array('recipient' => $recipient, 'subject' => $subject, 'body' => $body, 'status' => 'NEW', 'attempts'=> 0, 'sender' => $from,'id_user' => $id_user, 'messageId' => 0, 'ticketId'=> $ticket_id); $db = new DBLayer("lib"); @@ -74,9 +76,8 @@ class Mail_Handler{ $oms_reply_to = "Ryzom Ticketing Support ";*/ global $MAIL_DIR; - - // Deliver new mail - echo("mail cron\n"); + echo("\n========================================================\n"); + echo("mailing cron Job started at: ". Helpers::outputTime(time(),0) . "\n"); //creates child process $pid = self::mail_fork(); @@ -91,6 +92,7 @@ class Mail_Handler{ //>0: "In parent!\n"; } else { + //deliver new mail //make db connection here because the children have to make the connection. $this->db = new DBLayer("lib"); @@ -112,7 +114,6 @@ class Mail_Handler{ $message_id = self::new_message_id($email['TicketId']); //if recipient isn't given, then use the email of the id_user instead! - echo("Emailing {$email['Recipient']}\n"); if(!$email['Recipient']) { $email['Recipient'] = Ticket_User::get_email_by_user_id($email['UserId']); } @@ -146,7 +147,7 @@ class Mail_Handler{ //decrypt passwords in the db! $crypter = new MyCrypt($cfg['crypt']); foreach($sGroups as $group){ - $group->setIMAP_Password($crypter->decrypt($cfg['mail']['default_password'])); + $group->setIMAP_Password($crypter->decrypt($group->getIMAP_Password())); } $defaultGroup = new Support_Group(); @@ -161,18 +162,19 @@ class Mail_Handler{ foreach($sGroups as $group){ //check if group has mailing stuff filled in! - if($group->getGroupEmail() != "" && $group->getIMAP_MailServer() != "" && $group->getIMAP_Username() != "" && $group->getIMAP_Password() != "") + if($group->getGroupEmail() != "" && $group->getIMAP_MailServer() != "" && $group->getIMAP_Username() != "" && $group->getIMAP_Password() != ""){ $mbox = imap_open($group->getIMAP_MailServer(), $group->getIMAP_Username(), $group->getIMAP_Password()) or die('Cannot connect to mail server: ' . imap_last_error()); $message_count = imap_num_msg($mbox); for ($i = 1; $i <= $message_count; ++$i) { //return task ID - $tid = self::incoming_mail_handler($mbox, $i,$group); + $tkey = self::incoming_mail_handler($mbox, $i,$group); - if($tid) { + if($tkey) { //TODO: base file on Ticket + timestamp - $file = fopen($MAIL_DIR."/mail/ticket".$tid.".".time(), 'w'); + $file = fopen($MAIL_DIR."/ticket".$tkey, 'w'); + print("Email was written to ".$MAIL_DIR."/ticket".$tkey."\n"); fwrite($file, imap_fetchheader($mbox, $i) . imap_body($mbox, $i)); fclose($file); @@ -186,7 +188,10 @@ class Mail_Handler{ imap_close($mbox); } } + print("\nChild Cron job finished at ". Helpers::outputTime(time(),0) . "\n"); + echo("========================================================\n"); } + } @@ -226,7 +231,8 @@ class Mail_Handler{ $entire_email = imap_fetchheader($mbox, $i) . imap_body($mbox, $i); $subject = self::decode_utf8($header->subject); $to = $header->to[0]->mailbox; - $from = $header->from[0]->mailbox . '@' . $header->from[0]->host; + $from = $header->from[0]->mailbox . '@' . $header->from[0]->host; + $fromEmail = $header->from[0]->mailbox . '@' . $header->from[0]->host; $txt = self::get_part($mbox, $i, "TEXT/PLAIN"); //$html = self::get_part($mbox, $i, "TEXT/HTML"); @@ -251,32 +257,52 @@ class Mail_Handler{ //if ticket id is found, that means it is a reply on an existing ticket if($ticket_id){ - //use the line ---------- Ticket # to make a distincton between the old message and the reply - /*$endpos = strpos($txt, ">---------- Ticket #"); - if($endpos){ - $txt = substr($txt, 0, $endpos); - }else{ - $endpos = strpos($txt, "---------- Ticket #"); - if($endpos){ - $txt = substr($txt, 0, $endpos); - } - }*/ + $ticket = new Ticket(); + $ticket->load_With_TId($ticket_id); //if email is sent from an existing email address in the db (else it will give an error while loading the user object) if($from != "FALSE"){ $user = new Ticket_User(); $user->load_With_TUserId($from); - $ticket = new Ticket(); - $ticket->load_With_TId($ticket_id); + //if user has access to it! if((Ticket_User::isMod($user) or ($ticket->getAuthor() == $user->getTUserId())) and $txt != ""){ - Ticket::createReply($txt, $user->getTUserId(), $ticket->getTId(), 0); + Ticket::createReply($txt, $user->getTUserId(), $ticket->getTId(), 0); + print("Email found that is a reply to a ticket at:".$group->getGroupEmail()."\n"); + }else{ + //if user has no access to it + //Warn real ticket owner + person that send the mail + $subject_warnAuthor = "Someone tried to reply to your ticket: [Ticket #" . $ticket->getTId() ."]"; + $body_warnAuthor = "Someone tried to reply at your ticket: " . $ticket->getTitle() ."by sending an email from ".$fromEmail."! Please use the email address matching to your account if you want to auto reply!\n\n + If ". $fromEmail. " isn't one of your email addresses, please contact us by replying to this ticket!" ; + Mail_Handler::send_mail($ticket->getAuthor(), $subject_warnAuthor , $body_warnAuthor, $ticket->getTId(), NULL); + + $subject_warnSender = "You tried to reply to someone elses ticket!"; + $body_warnSender = "It seems you tried to reply to someone elses ticket, please use the matching email address to that account!\n\n + This action is notified to the real ticket owner!" ; + Mail_Handler::send_mail($from, $subject_warnSender , $body_warnSender, $ticket->getTId(), NULL); + + print("Email found that was a reply to a ticket, though send by another user to ".$group->getGroupEmail()."\n"); } + }else{ + //if a reply to a ticket is being sent by a non-user! + //Warn real ticket owner + person that send the mail + $subject_warnAuthor = "Someone tried to reply to your ticket: [Ticket #" . $ticket->getTId() ."]"; + $body_warnAuthor = "Someone tried to reply at your ticket:' " . $ticket->getTitle() ."' by sending an email from ".$fromEmail." ! Please use the email address matching to your account if you want to auto reply!\n\n + If ". $fromEmail. " isn't one of your email addresses, please contact us by replying to this ticket!" ; + Mail_Handler::send_mail($ticket->getAuthor(), $subject_warnAuthor , $body_warnAuthor, $ticket->getTId(), NULL); + + $subject_warnSender = "You tried to reply to someone's ticket!"; + $body_warnSender = "It seems you tried to reply to someone's ticket, However this email address isn't linked to any account, please use the matching email address to that account!\n\n + This action is notified to the real ticket owner!" ; + Mail_Handler::send_mail($fromEmail, $subject_warnSender , $body_warnSender, $ticket->getTId(), NULL); + print("Email found that was a reply to a ticket, though send by an unknown email address to ".$group->getGroupEmail()."\n"); + } - print("\n Email found that is a reply to a ticket at:".$group->getGroupEmail()); - return $ticket_id; + + return $ticket_id .".".time(); }else if($from != "FALSE"){ @@ -286,13 +312,13 @@ class Mail_Handler{ //if not default group, then forward it by giving the $group->getSGroupId's param $newTicketId = Ticket::create_Ticket($subject, $txt,1, $from, $from, $group->getSGroupId()); - print("\n Email regarding new ticket found at:".$group->getGroupEmail()); - return $newTicketId; + print("Email regarding new ticket found at:".$group->getGroupEmail()."\n"); + return $newTicketId .".".time(); }else{ //if it's a email that has nothing to do with ticketing, return 0; - print("\n Email found that isn't a reply or new ticket, at:".$group->getGroupEmail()); + print("Email found that isn't a reply or new ticket, at:".$group->getGroupEmail()."\n"); return 0; } @@ -366,4 +392,5 @@ class Mail_Handler{ } // END OF FUNCTION -} \ No newline at end of file +} + diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/translations/en.ini b/code/ryzom/tools/server/ryzom_ams/ams_lib/translations/en.ini index 02de8c019..d9eaad035 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/translations/en.ini +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/translations/en.ini @@ -55,7 +55,7 @@ user_not_existing = "The user doesn't seem to exist" not_mod_or_admin = "You can only add Moderators or Admins!" modify_mail_of_group_success = "The Support Group's email settings have been modified!" email_not_valid = "The group email address is invalid!" -no_password_given = "There was no password filled in!" +no_password_given = "Be aware that there was no password filled in, so the password is empty atm!" [sgroup_list] group_success = "The group has been created!" diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/translations/fr.ini b/code/ryzom/tools/server/ryzom_ams/ams_lib/translations/fr.ini index e28bc311a..93904d6a0 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/translations/fr.ini +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/translations/fr.ini @@ -51,7 +51,7 @@ user_not_existing = "cet user n'existe pas" not_mod_or_admin = "C'est possible d'ajoute seulement des mods et admins!" modify_mail_of_group_success = "Les parametres de messagerie du Groupe d'appui ont ete modifies!" email_not_valid = "L'adresse email de groupe est invalide!" -no_password_given = "Il'n y a pas une passport!" +no_password_given = "Soyez conscient qu'il n'y avait aucun mot de passe remplie, de sorte que le mot de passe est atm vide!" [sgroup_list] diff --git a/code/ryzom/tools/server/ryzom_ams/www/config.php b/code/ryzom/tools/server/ryzom_ams/www/config.php index c57d71746..0f43110c4 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/config.php +++ b/code/ryzom/tools/server/ryzom_ams/www/config.php @@ -51,7 +51,7 @@ $cfg['mail']['host'] = "ryzomcore.com"; //Defines mailing related stuff $SUPPORT_GROUP_IMAP_CRYPTKEY = "azerty"; $TICKET_MAILING_SUPPORT = true; -$MAIL_DIR = "/tmp"; +$MAIL_DIR = "/tmp/mail"; $cfg['crypt']['key'] = 'Sup3rS3cr3tStuff'; $cfg['crypt']['enc_method'] = 'AES-256-CBC'; diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/func/modify_email_of_sgroup.php b/code/ryzom/tools/server/ryzom_ams/www/html/func/modify_email_of_sgroup.php index a21b19eff..bf842feff 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/func/modify_email_of_sgroup.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/func/modify_email_of_sgroup.php @@ -9,21 +9,20 @@ function modify_email_of_sgroup(){ $sgroupid = filter_var($_POST['target_id'],FILTER_SANITIZE_NUMBER_INT); $group = Support_Group::getGroup($sgroupid); $groupemail = filter_var($_POST['GroupEmail'],FILTER_SANITIZE_STRING); - if(Users::validEmail($groupemail)){ - $password = filter_var($_POST['IMAP_Password'],FILTER_SANITIZE_STRING); - if($password != ""){ - $group->setGroupEmail($groupemail); - $group->setIMAP_MailServer(filter_var($_POST['IMAP_MailServer'],FILTER_SANITIZE_STRING)); - $group->setIMAP_Username(filter_var($_POST['IMAP_Username'],FILTER_SANITIZE_STRING)); - - //encrypt password! - global $cfg; - $crypter = new MyCrypt($cfg['crypt']); - $enc_password = $crypter->encrypt($password); - $group->setIMAP_Password($enc_password); - $group->update(); - $result['RESULT_OF_MODIFYING'] = "SUCCESS"; - }else{ + if(Users::validEmail($groupemail) || $groupemail == ""){ + $password = filter_var($_POST['IMAP_Password'],FILTER_SANITIZE_STRING); + $group->setGroupEmail($groupemail); + $group->setIMAP_MailServer(filter_var($_POST['IMAP_MailServer'],FILTER_SANITIZE_STRING)); + $group->setIMAP_Username(filter_var($_POST['IMAP_Username'],FILTER_SANITIZE_STRING)); + + //encrypt password! + global $cfg; + $crypter = new MyCrypt($cfg['crypt']); + $enc_password = $crypter->encrypt($password); + $group->setIMAP_Password($enc_password); + $group->update(); + $result['RESULT_OF_MODIFYING'] = "SUCCESS"; + if($password == ""){ $result['RESULT_OF_MODIFYING'] = "NO_PASSWORD"; } }else{ diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/templates/show_sgroup.tpl b/code/ryzom/tools/server/ryzom_ams/www/html/templates/show_sgroup.tpl index cd6913a2f..921b0edd1 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/templates/show_sgroup.tpl +++ b/code/ryzom/tools/server/ryzom_ams/www/html/templates/show_sgroup.tpl @@ -164,7 +164,7 @@ {$email_not_valid}
{else if isset($RESULT_OF_MODIFYING) and $RESULT_OF_MODIFYING eq "NO_PASSWORD"} -
+
{$no_password_given}
{/if} From 2cf3441ab8436bb4cee3612e6e430c534776a741 Mon Sep 17 00:00:00 2001 From: Quitta Date: Tue, 20 Aug 2013 04:33:23 +0200 Subject: [PATCH 110/313] put mail cron related debug info into a log file specified in config.php --- .../ams_lib/autoload/mail_handler.php | 35 ++++++++++++------- .../tools/server/ryzom_ams/www/config.php | 4 +++ 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php index df2045b07..460e2b9b4 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php @@ -69,15 +69,15 @@ class Mail_Handler{ //the main function function cron() { global $cfg; + global $MAIL_LOG_PATH; $default_groupemail = $cfg['mail']['default_groupemail']; $default_groupname = $cfg['mail']['default_groupname']; /* $inbox_host = $cfg['mail']['host']; $oms_reply_to = "Ryzom Ticketing Support ";*/ global $MAIL_DIR; - - echo("\n========================================================\n"); - echo("mailing cron Job started at: ". Helpers::outputTime(time(),0) . "\n"); + error_log("========================================================\n", 3, $MAIL_LOG_PATH); + error_log("mailing cron Job started at: ". Helpers::outputTime(time(),0) . "\n", 3, $MAIL_LOG_PATH); //creates child process $pid = self::mail_fork(); @@ -130,10 +130,10 @@ class Mail_Handler{ if(mail($email['Recipient'], $email['Subject'], $email['Body'], $headers)) { $status = "DELIVERED"; - echo("Emailed {$email['Recipient']}\n"); + error_log("Emailed {$email['Recipient']}\n", 3, $MAIL_LOG_PATH); } else { $status = "FAILED"; - echo("Email to {$email['Recipient']} failed\n"); + error_log("Email to {$email['Recipient']} failed\n", 3, $MAIL_LOG_PATH); } //change the status of the emails. $this->db->execute('update email set Status = ?, MessageId = ?, Attempts = Attempts + 1 where MailId = ?', array($status, $message_id, $email['MailId'])); @@ -174,7 +174,7 @@ class Mail_Handler{ if($tkey) { //TODO: base file on Ticket + timestamp $file = fopen($MAIL_DIR."/ticket".$tkey, 'w'); - print("Email was written to ".$MAIL_DIR."/ticket".$tkey."\n"); + error_log("Email was written to ".$MAIL_DIR."/ticket".$tkey."\n", 3, $MAIL_LOG_PATH); fwrite($file, imap_fetchheader($mbox, $i) . imap_body($mbox, $i)); fclose($file); @@ -188,8 +188,8 @@ class Mail_Handler{ imap_close($mbox); } } - print("\nChild Cron job finished at ". Helpers::outputTime(time(),0) . "\n"); - echo("========================================================\n"); + error_log("Child Cron job finished at ". Helpers::outputTime(time(),0) . "\n", 3, $MAIL_LOG_PATH); + error_log("========================================================\n", 3, $MAIL_LOG_PATH); } @@ -226,6 +226,8 @@ class Mail_Handler{ function incoming_mail_handler($mbox,$i,$group){ + global $MAIL_LOG_PATH; + $header = imap_header($mbox, $i); $subject = self::decode_utf8($header->subject); $entire_email = imap_fetchheader($mbox, $i) . imap_body($mbox, $i); @@ -262,14 +264,17 @@ class Mail_Handler{ //if email is sent from an existing email address in the db (else it will give an error while loading the user object) if($from != "FALSE"){ + $user = new Ticket_User(); $user->load_With_TUserId($from); //if user has access to it! if((Ticket_User::isMod($user) or ($ticket->getAuthor() == $user->getTUserId())) and $txt != ""){ + Ticket::createReply($txt, $user->getTUserId(), $ticket->getTId(), 0); - print("Email found that is a reply to a ticket at:".$group->getGroupEmail()."\n"); + error_log("Email found that is a reply to a ticket at:".$group->getGroupEmail()."\n", 3, $MAIL_LOG_PATH); + }else{ //if user has no access to it //Warn real ticket owner + person that send the mail @@ -283,10 +288,12 @@ class Mail_Handler{ This action is notified to the real ticket owner!" ; Mail_Handler::send_mail($from, $subject_warnSender , $body_warnSender, $ticket->getTId(), NULL); - print("Email found that was a reply to a ticket, though send by another user to ".$group->getGroupEmail()."\n"); + error_log("Email found that was a reply to a ticket, though send by another user to ".$group->getGroupEmail()."\n", 3, $MAIL_LOG_PATH); + } }else{ + //if a reply to a ticket is being sent by a non-user! //Warn real ticket owner + person that send the mail $subject_warnAuthor = "Someone tried to reply to your ticket: [Ticket #" . $ticket->getTId() ."]"; @@ -298,7 +305,8 @@ class Mail_Handler{ $body_warnSender = "It seems you tried to reply to someone's ticket, However this email address isn't linked to any account, please use the matching email address to that account!\n\n This action is notified to the real ticket owner!" ; Mail_Handler::send_mail($fromEmail, $subject_warnSender , $body_warnSender, $ticket->getTId(), NULL); - print("Email found that was a reply to a ticket, though send by an unknown email address to ".$group->getGroupEmail()."\n"); + + error_log("Email found that was a reply to a ticket, though send by an unknown email address to ".$group->getGroupEmail()."\n", 3, $MAIL_LOG_PATH); } @@ -312,13 +320,14 @@ class Mail_Handler{ //if not default group, then forward it by giving the $group->getSGroupId's param $newTicketId = Ticket::create_Ticket($subject, $txt,1, $from, $from, $group->getSGroupId()); - print("Email regarding new ticket found at:".$group->getGroupEmail()."\n"); + error_log("Email regarding new ticket found at:".$group->getGroupEmail()."\n", 3, $MAIL_LOG_PATH); + return $newTicketId .".".time(); }else{ //if it's a email that has nothing to do with ticketing, return 0; - print("Email found that isn't a reply or new ticket, at:".$group->getGroupEmail()."\n"); + error_log("Email found that isn't a reply or new ticket, at:".$group->getGroupEmail()."\n", 3, $MAIL_LOG_PATH); return 0; } diff --git a/code/ryzom/tools/server/ryzom_ams/www/config.php b/code/ryzom/tools/server/ryzom_ams/www/config.php index 0f43110c4..7d8f202d7 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/config.php +++ b/code/ryzom/tools/server/ryzom_ams/www/config.php @@ -51,8 +51,12 @@ $cfg['mail']['host'] = "ryzomcore.com"; //Defines mailing related stuff $SUPPORT_GROUP_IMAP_CRYPTKEY = "azerty"; $TICKET_MAILING_SUPPORT = true; + +//You have to create this dir at first! $MAIL_DIR = "/tmp/mail"; +$MAIL_LOG_PATH = "/tmp/mail/cron_mail.log"; + $cfg['crypt']['key'] = 'Sup3rS3cr3tStuff'; $cfg['crypt']['enc_method'] = 'AES-256-CBC'; $cfg['crypt']['hash_method'] = "SHA512"; From 59ae5ce380c4669ff8ec8baed6182597e2c34c94 Mon Sep 17 00:00:00 2001 From: Quitta Date: Sat, 24 Aug 2013 15:56:16 +0200 Subject: [PATCH 111/313] Fixed sgroup_liist ingame layout.. --- .../ams_lib/ingame_templates/sgroup_list.tpl | 48 +++++++++++++++---- 1 file changed, 40 insertions(+), 8 deletions(-) diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/sgroup_list.tpl b/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/sgroup_list.tpl index 6279d8fe4..a0c41f577 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/sgroup_list.tpl +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/sgroup_list.tpl @@ -38,14 +38,44 @@
- - - - - - - - + + + +
Group name:
Group Tag:
+ + + + + + + + + + + + + + + +
Group name:
Group Tag:
Group EmailAddress:
+
+ + + + + + + + + + + + + + + +
IMAP MailServer IP:
IMAP Username:
IMAP Password:
+

@@ -86,6 +116,7 @@
ID Name TagEmailAction
{$group.sGroupId} {$group.name} {$group.tag}{$group.groupemail}Delete
- + - + - +
Group name:
Group Tag:
Group EmailAddress:
@@ -61,17 +61,17 @@ - + - + - +
IMAP MailServer IP:
IMAP Username:
IMAP Password:
diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/show_sgroup.tpl b/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/show_sgroup.tpl index 388cf8e32..7888996ed 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/show_sgroup.tpl +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/show_sgroup.tpl @@ -102,6 +102,64 @@ + + + +
+ + +
+

Mail settings

+ + + + + + + + + + + + + + + + + + + + + + + +
Group Email:
IMAP Mail Server:
IMAP Username:
IMAP Password:
+ + + + +

+ +

+ + {if isset($RESULT_OF_MODIFYING) and $RESULT_OF_MODIFYING eq "SUCCESS"} +

+ {$modify_mail_of_group_success} +

+ {else if isset($RESULT_OF_MODIFYING) and $RESULT_OF_MODIFYING eq "EMAIL_NOT_VALID"} +

+ {$email_not_valid} +

+ {else if isset($RESULT_OF_MODIFYING) and $RESULT_OF_MODIFYING eq "NO_PASSWORD"} +

+ {$no_password_given} +

+ {/if} + + +
+
+ From 845719fd29773418066e1674389fd9786a6e8e04 Mon Sep 17 00:00:00 2001 From: Quitta Date: Sun, 25 Aug 2013 01:22:24 +0200 Subject: [PATCH 113/313] receive messages setting is added to the ingame layout --- .../ams_lib/ingame_templates/settings.tpl | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/settings.tpl b/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/settings.tpl index c8f67140c..048675a9e 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/settings.tpl +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/settings.tpl @@ -129,7 +129,7 @@ New Email: - + {if isset($EMAIL_ERROR) and $EMAIL_ERROR eq "TRUE"}{$EMAIL}{/if} @@ -201,6 +201,36 @@

+ + + + + + + + From e9f3a560a8199a83195cb949db17073c5ccdcbc2 Mon Sep 17 00:00:00 2001 From: Quitta Date: Sun, 25 Aug 2013 06:27:44 +0200 Subject: [PATCH 114/313] rewrote language handling + language is now stored in the db and the value is based on the users language flag clicked (or default) or the ingame language --- .../ryzom_ams/ams_lib/autoload/helpers.php | 58 +++++++++++++++++-- .../ryzom_ams/www/html/autoload/webusers.php | 45 ++++++++++++++ .../ryzom_ams/www/html/func/add_user.php | 5 +- .../server/ryzom_ams/www/html/func/login.php | 2 + .../tools/server/ryzom_ams/www/html/index.php | 2 +- .../server/ryzom_ams/www/html/sql/install.php | 6 +- .../ryzom_ams/www/html/templates/layout.tpl | 18 ++++-- 7 files changed, 121 insertions(+), 15 deletions(-) diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/helpers.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/helpers.php index 27f2fd80f..935d1974b 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/helpers.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/helpers.php @@ -1,13 +1,19 @@ assign( $key, $value ); } - + $variables = Helpers::handle_language(); foreach ( $variables[$template] as $key => $value ){ $smarty -> assign( $key, $value ); @@ -87,7 +93,49 @@ class Helpers{ global $DEFAULT_LANGUAGE; global $AMS_TRANS; - //if language get param is given = set cookie + //if user wants to change the language + if(isset($_GET['Language']) && isset($_GET['setLang'])){ + //The ingame client sometimes sends full words, derive those! + switch($_GET['Language']){ + + case "English": + $lang = "en"; + break; + + case "French": + $lang = "fr"; + break; + + default: + $lang = $_GET['Language']; + } + //if the file exists en the setLang = true + if( file_exists( $AMS_TRANS . '/' . $lang . '.ini' ) && $_GET['setLang'] == "true"){ + //set a cookie & session var and incase logged in write it to the db! + setcookie( 'Language', $lang , time() + 60*60*24*30 ); + $_SESSION['Language'] = $lang; + if(WebUsers::isLoggedIn()){ + WebUsers::setLanguage($_SESSION['id'],$lang); + } + }else{ + $_SESSION['Language'] = $DEFAULT_LANGUAGE; + } + }else{ + //if the session var is not set yet + if(!isset($_SESSION['Language'])){ + //check if a cookie already exists for it + if ( isset( $_COOKIE['Language'] ) ) { + $_SESSION['Language'] = $_COOKIE['Language']; + //else use the default language + }else{ + $_SESSION['Language'] = $DEFAULT_LANGUAGE; + } + } + } + + return parse_ini_file( $AMS_TRANS . '/' . $_SESSION['Language'] . '.ini', true ); + + /*/if language get param is given = set cookie //else if no get param is given and a cookie is set, use that language, else use default. if ( isset( $_GET['language'] ) ) { //check if the language is supported @@ -110,7 +158,7 @@ class Helpers{ } } - return parse_ini_file( $AMS_TRANS . '/' . $language . '.ini', true ); + return parse_ini_file( $AMS_TRANS . '/' . $language . '.ini', true );*/ } diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/autoload/webusers.php b/code/ryzom/tools/server/ryzom_ams/www/html/autoload/webusers.php index d819c89c5..2311916fb 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/autoload/webusers.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/autoload/webusers.php @@ -10,6 +10,7 @@ class WebUsers extends Users{ private $gender; private $country; private $receiveMail; + private $language; function __construct($UId = 0) { $this->uId = $UId; @@ -24,6 +25,7 @@ class WebUsers extends Users{ $this->gender = $values['Gender']; $this->country = $values['Country']; $this->receiveMail = $values['ReceiveMail']; + $this->language = $values['Language']; } /** @@ -136,6 +138,16 @@ class WebUsers extends Users{ return $this->receiveMail; } + public function getLanguage(){ + $dbw = new DBLayer("web"); + if(! isset($this->language) || $this->language == ""){ + $statement = $dbw->execute("SELECT * FROM ams_user WHERE UId=:id", array('id' => $this->uId)); + $row = $statement->fetch(); + $this->set($row); + } + return $this->language; + } + public function isLoggedIn(){ if(isset($_SESSION['user'])){ return true; @@ -183,6 +195,18 @@ class WebUsers extends Users{ } } + public static function setLanguage($user, $language){ + $values = Array('user' => $user, 'language' => $language); + try { + //make connection with and put into shard db + $dbw = new DBLayer("web"); + $dbw->execute("UPDATE ams_user SET Language = :language WHERE UId = :user ",$values); + } + catch (PDOException $e) { + //ERROR: the web DB is offline + } + } + public function getUsers(){ $dbl = new DBLayer("web"); $data = $dbl->executeWithoutParams("SELECT * FROM ams_user"); @@ -193,4 +217,25 @@ class WebUsers extends Users{ return "SELECT * FROM ams_user"; } + public static function createWebuser($name, $pass, $mail){ + + //register account with the correct language (check if cookie is already set)! + if ( isset( $_COOKIE['Language'] ) ) { + $lang = $_COOKIE['Language']; + }else{ + global $DEFAULT_LANGUAGE; + $lang = $DEFAULT_LANGUAGE; + } + + $values = Array('name' => $name, 'pass' => $pass, 'mail' => $mail, 'lang' => $lang); + + try { + $dbw = new DBLayer("web"); + return $dbw->executeReturnId("INSERT INTO ams_user (Login, Password, Email, Language) VALUES (:name, :pass, :mail, :lang)",$values); + } + catch (PDOException $e) { + //ERROR: the web DB is offline + } + } + } \ No newline at end of file diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/func/add_user.php b/code/ryzom/tools/server/ryzom_ams/www/html/func/add_user.php index 7e81b2751..75f04cc82 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/func/add_user.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/func/add_user.php @@ -47,9 +47,8 @@ function write_user($newUser){ ); try{ - //make connection with web db and put it in there - $dbw = new DBLayer("web"); - $user_id = $dbw->executeReturnId("INSERT INTO ams_user (Login, Password, Email) VALUES (:name, :pass, :mail)",$params); + //make new webuser + createWebuser($params['name'], $params['pass'], $params['mail']); //Create the user on the shard + in case shard is offline put copy of query in query db //returns: ok, shardoffline or liboffline diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/func/login.php b/code/ryzom/tools/server/ryzom_ams/www/html/func/login.php index 49ef2a435..da3b29478 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/func/login.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/func/login.php @@ -11,6 +11,8 @@ function login(){ $_SESSION['user'] = $username; $_SESSION['id'] = $result['UId']; $_SESSION['ticket_user'] = Ticket_User::constr_ExternId($result['UId']); + $user = new WebUsers($_SESSION['id']); + $_SESSION['Language'] = $user->getLanguage(); //go back to the index page. header( 'Location: index.php' ); diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/index.php b/code/ryzom/tools/server/ryzom_ams/www/html/index.php index b14cbb40c..6ce06739e 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/index.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/index.php @@ -59,5 +59,5 @@ if($page == 'error'){ $return['permission'] = 0; $return['no_visible_elements'] = 'FALSE'; } -//print_r($return); + helpers :: loadTemplate( $page , $return ); diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/sql/install.php b/code/ryzom/tools/server/ryzom_ams/www/html/sql/install.php index 2c0ffb6d5..0bf6c640c 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/sql/install.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/sql/install.php @@ -26,6 +26,7 @@ `Gender` tinyint(1) unsigned NOT NULL DEFAULT '0', `Country` char(2) NOT NULL DEFAULT '', `ReceiveMail` int(1) NOT NULL DEFAULT 1, + `Language` varchar(3) DEFAULT NULL, PRIMARY KEY (`UId`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1 COMMENT='contains all users information for ryzom_ams'; @@ -391,11 +392,12 @@ 'name' => "admin", 'pass' => $hashpass, 'mail' => "admin@admin.com", - 'permission' => 3 + 'permission' => 3, + 'lang' => "en" ); try{ $dbw = new DBLayer("web"); - $user_id = $dbw->executeReturnId("INSERT INTO ams_user (Login, Password, Email, Permission) VALUES (:name, :pass, :mail, :permission)",$params); + $user_id = $dbw->executeReturnId("INSERT INTO ams_user (Login, Password, Email, Permission, Language) VALUES (:name, :pass, :mail, :permission, :lang)",$params); Users::createUser($params, $user_id); $dbl = new DBLayer("lib"); $dbl->execute("UPDATE ticket_user SET Permission = 3 WHERE TUserId = :user_id",array('user_id' => $user_id)); diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/templates/layout.tpl b/code/ryzom/tools/server/ryzom_ams/www/html/templates/layout.tpl index f8fb5078c..fa97211d7 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/templates/layout.tpl +++ b/code/ryzom/tools/server/ryzom_ams/www/html/templates/layout.tpl @@ -103,8 +103,8 @@ {/if}
- - + +
@@ -141,8 +141,8 @@ {if isset($no_visible_elements) and $no_visible_elements eq "TRUE"}
- - + +
{/if} @@ -181,6 +181,16 @@ + From d147cc681c5b0d5876a4362df29e8d16b7e5843f Mon Sep 17 00:00:00 2001 From: Quitta Date: Sun, 25 Aug 2013 17:49:01 +0200 Subject: [PATCH 115/313] #mailing languages setup --- .../ryzom_ams/ams_lib/autoload/helpers.php | 24 ------- .../ams_lib/autoload/mail_handler.php | 62 ++++++++++++------- .../ryzom_ams/ams_lib/autoload/ticket.php | 4 +- 3 files changed, 40 insertions(+), 50 deletions(-) diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/helpers.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/helpers.php index 935d1974b..68f0e7917 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/helpers.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/helpers.php @@ -135,30 +135,6 @@ class Helpers{ return parse_ini_file( $AMS_TRANS . '/' . $_SESSION['Language'] . '.ini', true ); - /*/if language get param is given = set cookie - //else if no get param is given and a cookie is set, use that language, else use default. - if ( isset( $_GET['language'] ) ) { - //check if the language is supported - if ( file_exists( $AMS_TRANS . '/' . $_GET['language'] . '.ini' ) ){ - //if it's supported, set cookie! - setcookie( 'language',$_GET['language'], time() + 60*60*24*30 ); - $language = $_GET['language']; - }else{ - //the language is not supported, use the default. - $language = $DEFAULT_LANGUAGE; - } - }else{ - //if no get param is given, check if a cookie value for language is set - if ( isset( $_COOKIE['language'] ) ) { - $language = $_COOKIE['language']; - } - //else use the default - else{ - $language = $DEFAULT_LANGUAGE; - } - } - - return parse_ini_file( $AMS_TRANS . '/' . $language . '.ini', true );*/ } diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php index 460e2b9b4..9ff499fc7 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php @@ -12,40 +12,50 @@ class Mail_Handler{ } - public static function send_ticketing_mail($ticketObj, $content, $type, $sendingId = 0) { + public static function send_ticketing_mail($receiver, $ticketObj, $content, $type, $sendingId = 0) { global $TICKET_MAILING_SUPPORT; if($TICKET_MAILING_SUPPORT){ - //$txt = ""; - //$subject = ""; + if($sendingId == 0){ //if it is not forwarded (==public == which returns 0) then make it NULL which is needed to be placed in the DB. $sendingId = NULL; } - $author = $ticketObj->getAuthor(); - $webUser = new WebUsers($author); - //if the author of the ticket wants to receive mail, then send it! - if($webUser->getReceiveMail()){ - switch($type){ - case "REPLY": + switch($type){ + case "REPLY": + $webUser = new WebUsers($receiver); + if($webUser->getReceiveMail()){ $txt = "---------- Ticket #". $ticketObj->getTId() . " ----------\n You received a new reply on your ticket: " . $ticketObj->getTitle() . "\n --------------------\n\n"; $subject = "New reply on [Ticket #" . $ticketObj->getTId() ."]"; $endTxt = "\n\n----------\nYou can reply on this message to answer directly on the ticket!"; $txt = $txt . $content . $endTxt; - self::send_mail($author,$subject,$txt, $ticketObj->getTId(),$sendingId); - break; - - case "NEW": + self::send_mail($receiver,$subject,$txt, $ticketObj->getTId(),$sendingId); + } + break; + + case "NEW": + $webUser = new WebUsers($receiver); + if($webUser->getReceiveMail()){ $txt = "---------- Ticket #". $ticketObj->getTId() . " ----------\n Your ticket: " . $ticketObj->getTitle() . " is newly created"; $txt = $txt . "\n --------------------\n\n"; $subject = "New ticket created [Ticket #" . $ticketObj->getTId() ."]"; $endTxt = "\n\n----------\nYou can reply on this message to answer directly on the ticket!"; $txt = $txt . $content . $endTxt; - self::send_mail($author,$subject,$txt, $ticketObj->getTId(), $sendingId); - break; - } + self::send_mail($receiver,$subject,$txt, $ticketObj->getTId(), $sendingId); + } + break; + + case "WARNAUTHOR": + break; + + case "WARNSENDER": + break; + + case "WARNUNKNOWNSENDER": + break; + } } } @@ -278,15 +288,17 @@ class Mail_Handler{ }else{ //if user has no access to it //Warn real ticket owner + person that send the mail - $subject_warnAuthor = "Someone tried to reply to your ticket: [Ticket #" . $ticket->getTId() ."]"; + /*$subject_warnAuthor = "Someone tried to reply to your ticket: [Ticket #" . $ticket->getTId() ."]"; $body_warnAuthor = "Someone tried to reply at your ticket: " . $ticket->getTitle() ."by sending an email from ".$fromEmail."! Please use the email address matching to your account if you want to auto reply!\n\n If ". $fromEmail. " isn't one of your email addresses, please contact us by replying to this ticket!" ; - Mail_Handler::send_mail($ticket->getAuthor(), $subject_warnAuthor , $body_warnAuthor, $ticket->getTId(), NULL); + Mail_Handler::send_mail($ticket->getAuthor(), $subject_warnAuthor , $body_warnAuthor, $ticket->getTId(), NULL);*/ + Mail_Handler::send_ticketing_mail($ticket->getAuthor(),$ticket, NULL , "WARNAUTHOR" , NULL); - $subject_warnSender = "You tried to reply to someone elses ticket!"; + /*$subject_warnSender = "You tried to reply to someone elses ticket!"; $body_warnSender = "It seems you tried to reply to someone elses ticket, please use the matching email address to that account!\n\n This action is notified to the real ticket owner!" ; - Mail_Handler::send_mail($from, $subject_warnSender , $body_warnSender, $ticket->getTId(), NULL); + Mail_Handler::send_mail($from, $subject_warnSender , $body_warnSender, $ticket->getTId(), NULL);*/ + Mail_Handler::send_ticketing_mail($from ,$ticket, NULL , "WARNSENDER" , NULL); error_log("Email found that was a reply to a ticket, though send by another user to ".$group->getGroupEmail()."\n", 3, $MAIL_LOG_PATH); @@ -296,15 +308,17 @@ class Mail_Handler{ //if a reply to a ticket is being sent by a non-user! //Warn real ticket owner + person that send the mail - $subject_warnAuthor = "Someone tried to reply to your ticket: [Ticket #" . $ticket->getTId() ."]"; + /*$subject_warnAuthor = "Someone tried to reply to your ticket: [Ticket #" . $ticket->getTId() ."]"; $body_warnAuthor = "Someone tried to reply at your ticket:' " . $ticket->getTitle() ."' by sending an email from ".$fromEmail." ! Please use the email address matching to your account if you want to auto reply!\n\n If ". $fromEmail. " isn't one of your email addresses, please contact us by replying to this ticket!" ; - Mail_Handler::send_mail($ticket->getAuthor(), $subject_warnAuthor , $body_warnAuthor, $ticket->getTId(), NULL); + Mail_Handler::send_mail($ticket->getAuthor(), $subject_warnAuthor , $body_warnAuthor, $ticket->getTId(), NULL);*/ + Mail_Handler::send_ticketing_mail($ticket->getAuthor() ,$ticket, NULL , "WARNAUTHOR" , NULL); - $subject_warnSender = "You tried to reply to someone's ticket!"; + /*$subject_warnSender = "You tried to reply to someone's ticket!"; $body_warnSender = "It seems you tried to reply to someone's ticket, However this email address isn't linked to any account, please use the matching email address to that account!\n\n This action is notified to the real ticket owner!" ; - Mail_Handler::send_mail($fromEmail, $subject_warnSender , $body_warnSender, $ticket->getTId(), NULL); + Mail_Handler::send_mail($fromEmail, $subject_warnSender , $body_warnSender, $ticket->getTId(), NULL);*/ + Mail_Handler::send_ticketing_mail($fromEmail ,$ticket, NULL , "WARNUNKNOWNSENDER" , NULL); error_log("Email found that was a reply to a ticket, though send by an unknown email address to ".$group->getGroupEmail()."\n", 3, $MAIL_LOG_PATH); diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket.php index 1d57ae8b2..8ecd00b15 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket.php @@ -103,7 +103,7 @@ class Ticket{ Ticket::forwardTicket(0, $ticket_id, $for_support_group); } - Mail_Handler::send_ticketing_mail($ticket, $content, "NEW", $ticket->getForwardedGroupId()); + Mail_Handler::send_ticketing_mail($ticket->getAuthor(), $ticket, $content, "NEW", $ticket->getForwardedGroupId()); return $ticket_id; } @@ -166,7 +166,7 @@ class Ticket{ //notify ticket author that a new reply is added! if($ticket->getAuthor() != $author){ - Mail_Handler::send_ticketing_mail($ticket, $content, "REPLY", $ticket->getForwardedGroupId()); + Mail_Handler::send_ticketing_mail($ticket->getAuthor(), $ticket, $content, "REPLY", $ticket->getForwardedGroupId()); } From c6ce0a188afdbf41d4fc382f51646c077a01848e Mon Sep 17 00:00:00 2001 From: Quitta Date: Mon, 26 Aug 2013 05:37:55 +0200 Subject: [PATCH 116/313] mailing refactored --- .../ams_lib/autoload/mail_handler.php | 55 ++++++++----------- .../ryzom_ams/ams_lib/translations/en.ini | 43 +++++++++++++++ 2 files changed, 67 insertions(+), 31 deletions(-) diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php index 9ff499fc7..7bdf7fce7 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php @@ -21,39 +21,50 @@ class Mail_Handler{ $sendingId = NULL; } - + $variables = Helpers::handle_language(); + $mailText = array(); + foreach ( $variables['email'] as $key => $value ){ + $mailText[$key] = $value; + } + switch($type){ case "REPLY": $webUser = new WebUsers($receiver); - if($webUser->getReceiveMail()){ - $txt = "---------- Ticket #". $ticketObj->getTId() . " ----------\n You received a new reply on your ticket: " . $ticketObj->getTitle() . - "\n --------------------\n\n"; - $subject = "New reply on [Ticket #" . $ticketObj->getTId() ."]"; - $endTxt = "\n\n----------\nYou can reply on this message to answer directly on the ticket!"; - $txt = $txt . $content . $endTxt; + if($webUser->getReceiveMail()){ + $subject = $mailText['email_subject_new_reply'] . $ticketObj->getTId() ."]"; + $txt = $mailText['email_body_new_reply_1']. $ticketObj->getTId() . $mailText['email_body_new_reply_2'] . $ticketObj->getTitle() . + $mailText['email_body_new_reply_3'] . $content . $mailText['email_body_new_reply_4']; self::send_mail($receiver,$subject,$txt, $ticketObj->getTId(),$sendingId); } break; case "NEW": $webUser = new WebUsers($receiver); - if($webUser->getReceiveMail()){ - $txt = "---------- Ticket #". $ticketObj->getTId() . " ----------\n Your ticket: " . $ticketObj->getTitle() . " is newly created"; - $txt = $txt . "\n --------------------\n\n"; - $subject = "New ticket created [Ticket #" . $ticketObj->getTId() ."]"; - $endTxt = "\n\n----------\nYou can reply on this message to answer directly on the ticket!"; - $txt = $txt . $content . $endTxt; + if($webUser->getReceiveMail()){ + $subject = $mailText['email_subject_new_ticket'] . $ticketObj->getTId() ."]"; + $txt = $mailText['email_body_new_ticket_1'] . $ticketObj->getTId() . $mailText['email_body_new_ticket_2'] . $ticketObj->getTitle() . $mailText['email_body_new_ticket_3'] + . $content . $mailText['email_body_new_ticket_4']; self::send_mail($receiver,$subject,$txt, $ticketObj->getTId(), $sendingId); } break; case "WARNAUTHOR": + $subject = $mailText['email_subject_warn_author'] . $ticketObj->getTId() ."]"; + $txt = $mailText['email_body_warn_author_1'] . $ticket->getTitle() .$mailText['email_body_warn_author_2'].$fromEmail.$mailText['email_body_warn_author_3']. + $fromEmail. $mailText['email_body_warn_author_4'] ; + self::send_mail($receiver,$subject,$txt, $ticketObj->getTId(), $sendingId); break; case "WARNSENDER": + $subject = $mailText['email_subject_warn_sender']; + $txt = $mailText['email_body_warn_sender']; + self::send_mail($receiver,$subject,$txt, $ticketObj->getTId(), $sendingId); break; case "WARNUNKNOWNSENDER": + $subject = $mailText['email_subject_warn_unknown_sender']; + $txt = $mailText['email_body_warn_unknown_sender']; + self::send_mail($receiver,$subject,$txt, $ticketObj->getTId(), $sendingId); break; } @@ -288,16 +299,7 @@ class Mail_Handler{ }else{ //if user has no access to it //Warn real ticket owner + person that send the mail - /*$subject_warnAuthor = "Someone tried to reply to your ticket: [Ticket #" . $ticket->getTId() ."]"; - $body_warnAuthor = "Someone tried to reply at your ticket: " . $ticket->getTitle() ."by sending an email from ".$fromEmail."! Please use the email address matching to your account if you want to auto reply!\n\n - If ". $fromEmail. " isn't one of your email addresses, please contact us by replying to this ticket!" ; - Mail_Handler::send_mail($ticket->getAuthor(), $subject_warnAuthor , $body_warnAuthor, $ticket->getTId(), NULL);*/ Mail_Handler::send_ticketing_mail($ticket->getAuthor(),$ticket, NULL , "WARNAUTHOR" , NULL); - - /*$subject_warnSender = "You tried to reply to someone elses ticket!"; - $body_warnSender = "It seems you tried to reply to someone elses ticket, please use the matching email address to that account!\n\n - This action is notified to the real ticket owner!" ; - Mail_Handler::send_mail($from, $subject_warnSender , $body_warnSender, $ticket->getTId(), NULL);*/ Mail_Handler::send_ticketing_mail($from ,$ticket, NULL , "WARNSENDER" , NULL); error_log("Email found that was a reply to a ticket, though send by another user to ".$group->getGroupEmail()."\n", 3, $MAIL_LOG_PATH); @@ -308,16 +310,7 @@ class Mail_Handler{ //if a reply to a ticket is being sent by a non-user! //Warn real ticket owner + person that send the mail - /*$subject_warnAuthor = "Someone tried to reply to your ticket: [Ticket #" . $ticket->getTId() ."]"; - $body_warnAuthor = "Someone tried to reply at your ticket:' " . $ticket->getTitle() ."' by sending an email from ".$fromEmail." ! Please use the email address matching to your account if you want to auto reply!\n\n - If ". $fromEmail. " isn't one of your email addresses, please contact us by replying to this ticket!" ; - Mail_Handler::send_mail($ticket->getAuthor(), $subject_warnAuthor , $body_warnAuthor, $ticket->getTId(), NULL);*/ Mail_Handler::send_ticketing_mail($ticket->getAuthor() ,$ticket, NULL , "WARNAUTHOR" , NULL); - - /*$subject_warnSender = "You tried to reply to someone's ticket!"; - $body_warnSender = "It seems you tried to reply to someone's ticket, However this email address isn't linked to any account, please use the matching email address to that account!\n\n - This action is notified to the real ticket owner!" ; - Mail_Handler::send_mail($fromEmail, $subject_warnSender , $body_warnSender, $ticket->getTId(), NULL);*/ Mail_Handler::send_ticketing_mail($fromEmail ,$ticket, NULL , "WARNUNKNOWNSENDER" , NULL); error_log("Email found that was a reply to a ticket, though send by an unknown email address to ".$group->getGroupEmail()."\n", 3, $MAIL_LOG_PATH); diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/translations/en.ini b/code/ryzom/tools/server/ryzom_ams/ams_lib/translations/en.ini index d9eaad035..0aa20a29b 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/translations/en.ini +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/translations/en.ini @@ -137,3 +137,46 @@ tac_tag1= "YES, I agree to the " tac_tag2="terms of service" tac_message = "You must accept the Terms of Service." + +[email] +email_subject_new_reply = "New reply on [Ticket #" +email_body_new_reply_1 = "---------- Ticket #" +email_body_new_reply_2 = " ---------- +You received a new reply on your ticket: " +email_body_new_reply_3 = " +-------------------- +Reply Content: +" +email_body_new_reply_4 = " +-------------------- +You can reply on this message to answer directly on the ticket!" + +email_subject_new_ticket = "New ticket created [Ticket #" +email_body_new_ticket_1 = "---------- Ticket #" +email_body_new_ticket_2 = " ---------- +Your ticket: " +email_body_new_ticket_3 = " is newly created +---------- +" +email_body_new_ticket_4 = " +---------- +You can reply on this message to answer directly on the ticket!" + +email_subject_warn_author = "Someone tried to reply to your ticket: [Ticket #" +email_body_warn_author_1 = "Someone tried to reply at your ticket: " +email_body_warn_author_2 = " by sending an email from " +email_body_warn_author_3 = " ! Please use the email address matching to your account if you want to auto reply + +If " +email_body_warn_author_4 = " isn't one of your email addresses, please contact us by replying to this ticket!" + +email_subject_warn_sender = "You tried to reply to someone elses ticket!" +email_body_warn_sender = "It seems you tried to reply to someone elses ticket, please use the matching email address to that account! + +This action is notified to the real ticket owner!" + + +email_subject_warn_unknown_sender = "You tried to reply to someone's ticket!" +email_body_warn_unknown_sender = "It seems you tried to reply to someone's ticket, However this email address isn't linked to any account, please use the matching email address to that account! + +This action is notified to the real ticket owner!" \ No newline at end of file From dabf1da618a50d8a6c1de7638226146101369059 Mon Sep 17 00:00:00 2001 From: Quitta Date: Mon, 26 Aug 2013 11:34:51 +0200 Subject: [PATCH 117/313] added language support however, something is broken :/ --- .../ams_lib/autoload/mail_handler.php | 10 +++- .../ryzom_ams/ams_lib/translations/en.ini | 15 ++++- .../ryzom_ams/ams_lib/translations/fr.ini | 55 ++++++++++++++++++- 3 files changed, 75 insertions(+), 5 deletions(-) diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php index 7bdf7fce7..f88f97e69 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php @@ -21,7 +21,15 @@ class Mail_Handler{ $sendingId = NULL; } - $variables = Helpers::handle_language(); + global $AMS_TRANS; + if(is_numeric($receiver)){ + $webUser = new WebUsers($receiver); + $lang = $webUser->getLanguage(); + }else{ + global $DEFAULT_LANGUAGE; + $lang = $DEFAULT_LANGUAGE; + } + $variables = parse_ini_file( $AMS_TRANS . '/' . $lang . '.ini', true ); $mailText = array(); foreach ( $variables['email'] as $key => $value ){ $mailText[$key] = $value; diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/translations/en.ini b/code/ryzom/tools/server/ryzom_ams/ams_lib/translations/en.ini index 0aa20a29b..b22e8db98 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/translations/en.ini +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/translations/en.ini @@ -137,8 +137,9 @@ tac_tag1= "YES, I agree to the " tac_tag2="terms of service" tac_message = "You must accept the Terms of Service." - [email] + +;NEWREPLY========================================================================== email_subject_new_reply = "New reply on [Ticket #" email_body_new_reply_1 = "---------- Ticket #" email_body_new_reply_2 = " ---------- @@ -151,6 +152,8 @@ email_body_new_reply_4 = " -------------------- You can reply on this message to answer directly on the ticket!" +;NEWTICKET +;========================================================================== email_subject_new_ticket = "New ticket created [Ticket #" email_body_new_ticket_1 = "---------- Ticket #" email_body_new_ticket_2 = " ---------- @@ -162,6 +165,8 @@ email_body_new_ticket_4 = " ---------- You can reply on this message to answer directly on the ticket!" +;WARNAUTHOR +;========================================================================== email_subject_warn_author = "Someone tried to reply to your ticket: [Ticket #" email_body_warn_author_1 = "Someone tried to reply at your ticket: " email_body_warn_author_2 = " by sending an email from " @@ -170,13 +175,17 @@ email_body_warn_author_3 = " ! Please use the email address matching to your acc If " email_body_warn_author_4 = " isn't one of your email addresses, please contact us by replying to this ticket!" +;WARNSENDER +;========================================================================== email_subject_warn_sender = "You tried to reply to someone elses ticket!" email_body_warn_sender = "It seems you tried to reply to someone elses ticket, please use the matching email address to that account! This action is notified to the real ticket owner!" - +;WARNUNKNOWNENDER +;========================================================================== email_subject_warn_unknown_sender = "You tried to reply to someone's ticket!" email_body_warn_unknown_sender = "It seems you tried to reply to someone's ticket, However this email address isn't linked to any account, please use the matching email address to that account! -This action is notified to the real ticket owner!" \ No newline at end of file +This action is notified to the real ticket owner!" +;=========================================================================== \ No newline at end of file diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/translations/fr.ini b/code/ryzom/tools/server/ryzom_ams/ams_lib/translations/fr.ini index 93904d6a0..1e03d6862 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/translations/fr.ini +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/translations/fr.ini @@ -132,4 +132,57 @@ email_default = "email" tac_tag1 = "OUI, j'accepte les " tac_tag2 = "termes de service" -tac_message = "Vous devez accepter les Conditions d'utilisation." \ No newline at end of file +tac_message = "Vous devez accepter les Conditions d'utilisation." + +[email] + +;NEWREPLY========================================================================== +email_subject_new_reply = "Nouvelle message sur [Billet #" +email_body_new_reply_1 = "---------- Billet #" +email_body_new_reply_2 = " ---------- +Vous avez recu un nouvelle message sur: " +email_body_new_reply_3 = " +-------------------- +Repondre Contenu: +" +email_body_new_reply_4 = " +-------------------- +Vous ne pouvez pas repondre a ce message pour repondre directement sur le billet! " + +;NEWTICKET +;========================================================================== +email_subject_new_ticket = "nouvelle billet est cree [Billet #" +email_body_new_ticket_1 = "---------- Billet #" +email_body_new_ticket_2 = " ---------- +Votre Billet: " +email_body_new_ticket_3 = " est nouvelle cree +---------- +" +email_body_new_ticket_4 = " +---------- +Vous ne pouvez pas repondre a ce message pour repondre directement sur le billet!" + +;WARNAUTHOR +;========================================================================== +email_subject_warn_author = "Quelqu'un a essayé de répondre à votre billet: [Ticket #" +email_body_warn_author_1 = "Quelqu'un a essayé de répondre à votre billet: " +email_body_warn_author_2 = " en envoyant un courriel à partir de " +email_body_warn_author_3 = " ! Veuillez utiliser l'adresse e-mail correspondant à votre compte si vous souhaitez réponse automatique. + +Si " +email_body_warn_author_4 = " n'est pas l'une de vos adresses e-mail, s'il vous plaît contactez-nous en répondant à ce billet!" + +;WARNSENDER +;========================================================================== +email_subject_warn_sender = "Vous avez tenté de répondre à quelqu'un billet elses!" +email_body_warn_sender = "Il semble que vous avez essayé de répondre à quelqu'un billet elses, copiez l'adresse e-mail correspondant à ce compte! + +Cet acte est notifié au propriétaire du billet de vrai! " + +;WARNUNKNOWNENDER +;========================================================================== +email_subject_warn_unknown_sender = "Vous avez tenté de répondre à la billetterie de quelqu'un!" +email_body_warn_unknown_sender = "Il semble que vous avez essayé de répondre à la billetterie de quelqu'un, mais cette adresse e-mail n'est pas liée à un compte, veuillez utiliser l'adresse e-mail correspondant à ce compte! + +Cet acte est notifié au propriétaire du billet de vrai!" +;=========================================================================== \ No newline at end of file From dd116afff3c04ac309e68c105ef57f1615939e6d Mon Sep 17 00:00:00 2001 From: kervala Date: Mon, 26 Aug 2013 12:33:26 +0200 Subject: [PATCH 118/313] Fixed: Crash when trying to display a missing texture --- code/nel/src/gui/view_renderer.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/code/nel/src/gui/view_renderer.cpp b/code/nel/src/gui/view_renderer.cpp index c61d8fa51..6d3ef62e7 100644 --- a/code/nel/src/gui/view_renderer.cpp +++ b/code/nel/src/gui/view_renderer.cpp @@ -354,6 +354,9 @@ namespace NLGUI // start to draw at the reference corner getTextureSizeFromId (nTxId, txw, txh); + // to avoid a division by zero crash later + if (txw < 0 || txh < 0) return; + if (rot > 3) rot = 3; sint32 startX = x, startY = y; From d2a9cb90d0c51830ff839ab2260dd56f15fca7ff Mon Sep 17 00:00:00 2001 From: kervala Date: Mon, 26 Aug 2013 12:34:04 +0200 Subject: [PATCH 119/313] Changed: Formatting --- code/nel/src/3d/computed_string.cpp | 4 ++-- code/nel/src/misc/gtk_displayer.cpp | 6 ++++-- code/nel/src/net/email.cpp | 9 ++++++--- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/code/nel/src/3d/computed_string.cpp b/code/nel/src/3d/computed_string.cpp index 0c2cd48de..a4c400284 100644 --- a/code/nel/src/3d/computed_string.cpp +++ b/code/nel/src/3d/computed_string.cpp @@ -242,8 +242,8 @@ void CComputedString::render2DClip (IDriver& driver, CRenderStringBuffer &rdrBuf uint lastIndex = 0; for(uint i=0;i>24) == 0) { + if ((col>>24) == 0) + { GdkColor color; color.red = (col >> 8) & 0xFF00; color.green = col & 0xFF00; diff --git a/code/nel/src/net/email.cpp b/code/nel/src/net/email.cpp index 589e3aa8e..ae6f92477 100644 --- a/code/nel/src/net/email.cpp +++ b/code/nel/src/net/email.cpp @@ -56,7 +56,8 @@ static void uuencode (const char *s, const char *store, const int length) unsigned char *us = (unsigned char *)s; /* Transform the 3x8 bits to 4x6 bits, as required by base64. */ - for (i = 0; i < length; i += 3) { + for (i = 0; i < length; i += 3) + { *p++ = tbl[us[0] >> 2]; *p++ = tbl[((us[0] & 3) << 4) + (us[1] >> 4)]; *p++ = tbl[((us[1] & 0xf) << 2) + (us[2] >> 6)]; @@ -64,10 +65,12 @@ static void uuencode (const char *s, const char *store, const int length) us += 3; } /* Pad the result if necessary... */ - if (i == length + 1) { + if (i == length + 1) + { *(p - 1) = tbl[64]; } - else if (i == length + 2) { + else if (i == length + 2) + { *(p - 1) = *(p - 2) = tbl[64]; } /* ...and zero-terminate it. */ From d6c84830296450d18f5c9332bc3e1521b3865e6a Mon Sep 17 00:00:00 2001 From: Quitta Date: Mon, 26 Aug 2013 17:25:28 +0200 Subject: [PATCH 120/313] Finaly got it to working again.. --- .../ams_lib/autoload/mail_handler.php | 31 ++++++++++--------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php index f88f97e69..3a17412e4 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php @@ -12,13 +12,15 @@ class Mail_Handler{ } - public static function send_ticketing_mail($receiver, $ticketObj, $content, $type, $sendingId = 0) { + public static function send_ticketing_mail($receiver, $ticketObj, $content, $type, $sender = 0) { + global $MAIL_LOG_PATH; + error_log("Receiver: {$receiver}, content: {$content}, type: {$type}, SendingId: {$sender} \n", 3, $MAIL_LOG_PATH); global $TICKET_MAILING_SUPPORT; if($TICKET_MAILING_SUPPORT){ - if($sendingId == 0){ + if($sender == 0){ //if it is not forwarded (==public == which returns 0) then make it NULL which is needed to be placed in the DB. - $sendingId = NULL; + $sender = NULL; } global $AMS_TRANS; @@ -42,7 +44,7 @@ class Mail_Handler{ $subject = $mailText['email_subject_new_reply'] . $ticketObj->getTId() ."]"; $txt = $mailText['email_body_new_reply_1']. $ticketObj->getTId() . $mailText['email_body_new_reply_2'] . $ticketObj->getTitle() . $mailText['email_body_new_reply_3'] . $content . $mailText['email_body_new_reply_4']; - self::send_mail($receiver,$subject,$txt, $ticketObj->getTId(),$sendingId); + self::send_mail($receiver,$subject,$txt, $ticketObj->getTId(),$sender); } break; @@ -52,27 +54,30 @@ class Mail_Handler{ $subject = $mailText['email_subject_new_ticket'] . $ticketObj->getTId() ."]"; $txt = $mailText['email_body_new_ticket_1'] . $ticketObj->getTId() . $mailText['email_body_new_ticket_2'] . $ticketObj->getTitle() . $mailText['email_body_new_ticket_3'] . $content . $mailText['email_body_new_ticket_4']; - self::send_mail($receiver,$subject,$txt, $ticketObj->getTId(), $sendingId); + self::send_mail($receiver,$subject,$txt, $ticketObj->getTId(), $sender); } break; case "WARNAUTHOR": + if(is_numeric($sender)){ + $sender = Ticket_User::get_email_by_user_id($sender); + } $subject = $mailText['email_subject_warn_author'] . $ticketObj->getTId() ."]"; - $txt = $mailText['email_body_warn_author_1'] . $ticket->getTitle() .$mailText['email_body_warn_author_2'].$fromEmail.$mailText['email_body_warn_author_3']. - $fromEmail. $mailText['email_body_warn_author_4'] ; - self::send_mail($receiver,$subject,$txt, $ticketObj->getTId(), $sendingId); + $txt = $mailText['email_body_warn_author_1'] . $ticketObj->getTitle() .$mailText['email_body_warn_author_2'].$sender.$mailText['email_body_warn_author_3']. + $sender. $mailText['email_body_warn_author_4'] ; + self::send_mail($receiver,$subject,$txt, $ticketObj->getTId(), NULL); break; case "WARNSENDER": $subject = $mailText['email_subject_warn_sender']; $txt = $mailText['email_body_warn_sender']; - self::send_mail($receiver,$subject,$txt, $ticketObj->getTId(), $sendingId); + self::send_mail($receiver,$subject,$txt, $ticketObj->getTId(), NULL); break; case "WARNUNKNOWNSENDER": $subject = $mailText['email_subject_warn_unknown_sender']; $txt = $mailText['email_body_warn_unknown_sender']; - self::send_mail($receiver,$subject,$txt, $ticketObj->getTId(), $sendingId); + self::send_mail($receiver,$subject,$txt, $ticketObj->getTId(), NULL); break; } @@ -287,7 +292,6 @@ class Mail_Handler{ //if ticket id is found, that means it is a reply on an existing ticket if($ticket_id){ - $ticket = new Ticket(); $ticket->load_With_TId($ticket_id); @@ -296,7 +300,6 @@ class Mail_Handler{ $user = new Ticket_User(); $user->load_With_TUserId($from); - //if user has access to it! if((Ticket_User::isMod($user) or ($ticket->getAuthor() == $user->getTUserId())) and $txt != ""){ @@ -307,7 +310,7 @@ class Mail_Handler{ }else{ //if user has no access to it //Warn real ticket owner + person that send the mail - Mail_Handler::send_ticketing_mail($ticket->getAuthor(),$ticket, NULL , "WARNAUTHOR" , NULL); + Mail_Handler::send_ticketing_mail($ticket->getAuthor(),$ticket, NULL , "WARNAUTHOR" , $from); Mail_Handler::send_ticketing_mail($from ,$ticket, NULL , "WARNSENDER" , NULL); error_log("Email found that was a reply to a ticket, though send by another user to ".$group->getGroupEmail()."\n", 3, $MAIL_LOG_PATH); @@ -318,7 +321,7 @@ class Mail_Handler{ //if a reply to a ticket is being sent by a non-user! //Warn real ticket owner + person that send the mail - Mail_Handler::send_ticketing_mail($ticket->getAuthor() ,$ticket, NULL , "WARNAUTHOR" , NULL); + Mail_Handler::send_ticketing_mail($ticket->getAuthor() ,$ticket, NULL , "WARNAUTHOR" , $fromEmail); Mail_Handler::send_ticketing_mail($fromEmail ,$ticket, NULL , "WARNUNKNOWNSENDER" , NULL); error_log("Email found that was a reply to a ticket, though send by an unknown email address to ".$group->getGroupEmail()."\n", 3, $MAIL_LOG_PATH); From 420bb064c6e839286a568ba0e04c141bea6a1e0f Mon Sep 17 00:00:00 2001 From: Quitta Date: Tue, 27 Aug 2013 19:08:10 +0200 Subject: [PATCH 121/313] ticket_info class almost done --- .../ams_lib/autoload/ticket_info.php | 234 ++++++++++++++++++ .../ams_lib/ingame_templates/show_ticket.tpl | 1 + .../ingame_templates/show_ticket_info.tpl | 96 +++++++ .../ryzom_ams/ams_lib/translations/en.ini | 2 + .../ryzom_ams/ams_lib/translations/fr.ini | 2 + .../server/ryzom_ams/www/html/inc/login.php | 1 + .../www/html/inc/show_ticket_info.php | 52 ++++ .../ryzom_ams/www/html/sql/DBScheme.png | Bin 124474 -> 198382 bytes .../server/ryzom_ams/www/html/sql/install.php | 33 +++ .../ryzom_ams/www/html/sql/ticketsql.sql | 33 +++ .../www/html/sql/ticketsystemmodel.mwb | Bin 17276 -> 19027 bytes .../www/html/templates/show_ticket.tpl | 1 + .../www/html/templates/show_ticket_info.tpl | 97 ++++++++ 13 files changed, 552 insertions(+) create mode 100644 code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_info.php create mode 100644 code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/show_ticket_info.tpl create mode 100644 code/ryzom/tools/server/ryzom_ams/www/html/inc/show_ticket_info.php create mode 100644 code/ryzom/tools/server/ryzom_ams/www/html/templates/show_ticket_info.tpl diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_info.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_info.php new file mode 100644 index 000000000..b0be3e5ca --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_info.php @@ -0,0 +1,234 @@ + $ticket_id, 'author' => $author_id, 'query' => json_encode(array($action,$arg))); + $dbl->execute($query, $values); + } + + + //return constructed element based on TLogId + public static function constr_TInfoId( $id) { + $instance = new self(); + $instance->setTInfoId($id); + return $instance; + } + + + ////////////////////////////////////////////Methods//////////////////////////////////////////////////// + + public function __construct() { + } + + //set values + public function set($values) { + $this->setTInfoId($values['TInfoId']); + $this->setTicket($values['Ticket']); + $this->setShardId($values['ShardId']); + $this->setUser_Position($values['UserPosition']); + $this->setView_Position($values['ViewPosition']); + + $this->setClient_Version($values['ClientVersion']); + $this->setPatch_Version($values['PatchVersion']); + $this->setServer_Tick($values['ServerTick']); + $this->setConnect_State($values['ConnectState']); + $this->setLocal_Address($values['LocalAddress']); + + $this->setMemory($values['Memory']); + $this->setOS($values['OS']); + $this->setProcessor($values['Processor']); + $this->setCPUId($values['CPUID']); + $this->setCPU_Mask($values['CpuMask']); + $this->setHT($values['HT']); + $this->setNel3D($values['NeL3D']); + } + + //Load with tInfoId + public function load_With_TInfoId( $id) { + $dbl = new DBLayer("lib"); + $statement = $dbl->execute("SELECT * FROM ticket_info WHERE TInfoId=:id", array('id' => $id)); + $row = $statement->fetch(); + $this->set($row); + } + + //Load with ticket Id + public function load_With_TId( $id) { + $dbl = new DBLayer("lib"); + $statement = $dbl->execute("SELECT * FROM ticket_info WHERE Ticket=:id", array('id' => $id)); + $row = $statement->fetch(); + $this->set($row); + } + + + + ////////////////////////////////////////////Getters//////////////////////////////////////////////////// + + public function getTInfoId(){ + return $this->tInfoId; + } + + public function getTicket(){ + return $this->ticket; + } + + public function getShardId(){ + return $this->shardid; + } + + public function getUser_Position(){ + return $this->user_position; + } + + public function getView_Position(){ + return $this->view_position; + } + + public function getClient_Version(){ + return $this->client_version; + } + + public function getPatch_Version(){ + return $this->patch_version; + } + + public function getServer_Tick(){ + return $this->server_tick; + } + + public function getConnect_State(){ + return $this->connect_state; + } + + public function getLocal_Address(){ + return $this->local_address; + } + + public function getMemory(){ + return $this->memory; + } + + public function getOS(){ + return $this->os; + } + + public function getProcessor(){ + return $this->processor; + } + + + public function getCPUId(){ + return $this->cpu_id; + } + + public function getCPU_Mask(){ + return $this->cpu_mask; + } + + public function getHT(){ + return $this->ht; + } + + public function getNel3D(){ + return $this->nel3d; + } + + ////////////////////////////////////////////Setters//////////////////////////////////////////////////// + + public function setTInfoId($id){ + $this->tInfoId = $id; + } + + public function setTicket($t){ + $this->ticket = $t; + } + + public function setShardId($s){ + $this->shardid = $s; + } + + public function setUser_Position($u){ + $this->user_position = $u; + } + + public function setView_Position($v){ + $this->view_position = $v; + } + + public function setClient_Version($c){ + $this->client_version = $c; + } + + public function setPatch_Version($p){ + $this->patch_version = $p; + } + + public function setServer_Tick($s){ + $this->server_tick = $s; + } + + public function setConnect_State($c){ + $this->connect_state = $c; + } + + public function setLocal_Address($l){ + $this->local_address = $l; + } + + public function setMemory($m){ + $this->memory = $m; + } + + public function setOS($o){ + $this->os = $o; + } + + public function setProcessor($p){ + $this->processor = $p; + } + + + public function setCPUId($c){ + $this->cpu_id = $c; + } + + public function setCPU_Mask($c){ + $this->cpu_mask = $c; + } + + public function setHT($h){ + $this->ht = $h; + } + + public function setNel3D($n){ + $this->nel3d = $n; + } + +} \ No newline at end of file diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/show_ticket.tpl b/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/show_ticket.tpl index 5058a5776..377f57de4 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/show_ticket.tpl +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/show_ticket.tpl @@ -10,6 +10,7 @@
{if isset($isMod) and $isMod eq "TRUE"}{/if} +
+ +
+

Ticket-Update Mail Settings

+
+ + +
+ Receive ticket updates + + +
+ + + +

+ +

+
+
Show Ticket LogSend Other TicketShow Additional Info
diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/show_ticket_info.tpl b/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/show_ticket_info.tpl new file mode 100644 index 000000000..2516620c2 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/show_ticket_info.tpl @@ -0,0 +1,96 @@ +{block name=content} +
+
+ +
+
+ Additional Info + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Shard ID: {$shard_id}
User Position: {$user_position}
View Position: {$view_position}
client_version: {$client_version}
patch_version: {$patch_version}
memory: {$memory}
server_tick: {$server_tick}
connect_state: {$connect_state}
local_address: {$local_address}
os: {$os}
processor: {$processor}
cpu_id: {$cpu_id}
cpu_mask: {$cpu_mask}
ht: {$ht}
nel3d: {$nel3d}
+ +
+
+
+ +
+
+

Actions

+
+ + +
+
+
+
+ + Actions +
+ + +
+
+
+
+
+{/block} + diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/translations/en.ini b/code/ryzom/tools/server/ryzom_ams/ams_lib/translations/en.ini index b22e8db98..1af2c3e70 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/translations/en.ini +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/translations/en.ini @@ -65,6 +65,8 @@ group_size_error = "The name has to be between 4-20 chars and the tag between 2- [createticket] +[show_ticket_info] + [show_ticket_log] [show_reply] diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/translations/fr.ini b/code/ryzom/tools/server/ryzom_ams/ams_lib/translations/fr.ini index 1e03d6862..81f58af42 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/translations/fr.ini +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/translations/fr.ini @@ -62,6 +62,8 @@ group_size_error = "le nom doit etre 4-20 chars et le tag 2-4!" [createticket] +[show_ticket_info] + [show_reply] [show_ticket_log] diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/inc/login.php b/code/ryzom/tools/server/ryzom_ams/www/html/inc/login.php index 38a9ab5d8..745d48288 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/inc/login.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/inc/login.php @@ -2,6 +2,7 @@ function login(){ if ( helpers :: check_if_game_client () ){ + //check if you are logged in ingame. $result = Helpers::check_login_ingame(); if( $result != "FALSE"){ //handle successful login diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/inc/show_ticket_info.php b/code/ryzom/tools/server/ryzom_ams/www/html/inc/show_ticket_info.php new file mode 100644 index 000000000..e2ee00150 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/www/html/inc/show_ticket_info.php @@ -0,0 +1,52 @@ +load_With_TId($result['ticket_id']); + + if(($target_ticket->getAuthor() == $_SESSION['ticket_user']->getTUserId()) || Ticket_User::isMod($_SESSION['ticket_user'] )){ + $result['ticket_title'] = $target_ticket->getTitle(); + $result['ticket_author'] = $target_ticket->getAuthor(); + + + $result['shard_id'] = $_GET['ShardId']; + $result['user_position'] = $_GET['UserPosition']; + $result['view_position'] = $_GET['ViewPosition']; + $result['client_version'] = $_GET['ClientVersion']; + $result['patch_version'] = $_GET['PatchVersion']; + + + $result['server_tick'] = $_GET['ServerTick']; + $result['connect_state'] = $_GET['ConnectState']; + $result['local_address'] = $_GET['LocalAddress']; + $result['memory'] = $_GET['Memory']; + $result['os'] = $_GET['OS']; + $result['processor'] = $_GET['Processor']; + $result['cpu_id'] = $_GET['CPUID']; + $result['cpu_mask'] = $_GET['CpuMask']; + $result['ht'] = $_GET['HT']; + + $result['nel3d'] = $_GET['NeL3D']; + + if(Ticket_User::isMod($_SESSION['ticket_user'])){ + $result['isMod'] = "TRUE"; + } + return $result; + + }else{ + //ERROR: No access! + $_SESSION['error_code'] = "403"; + header("Location: index.php?page=error"); + exit; + } + }else{ + //ERROR: not logged in! + header("Location: index.php"); + exit; + } +} \ No newline at end of file diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/sql/DBScheme.png b/code/ryzom/tools/server/ryzom_ams/www/html/sql/DBScheme.png index 16bced7cc392b28920028d92ef7b51c69bafc5b4..ce876402b9e9173bcd9473a61bda2b4dbd331655 100644 GIT binary patch literal 198382 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%0Dl>P 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%0DUASr>$YOrlqS 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 literal 124474 zcma&N1ymeSv@KW!cL@Xy8l2EUgKKd2;O-V&8rKAZ1a}GU?jGFT-QC@PC-40?Gw=UD zGu5lts#RTmZ`HlW_CDwQke3xhMIt}~0031&{EH$0K<7ZdYzS{4SKukzAr}BR11YgD z!0X>nMoV5a0FVL_Uj&t1Q;wEhzGAE4!mqKgEULeYTLIhpVI$wzFjR5ls!!x?`)CI34{X6{d=RcHYOHdW@tqlfFm2H3z4f{`$h zFbb;_?s7~FgXQg&q->GZR`Wun7*n_3F@*B<$@<#3ZMRo>m3FOcx^jDT(N8>IPZ z6d~fFQ&*~e0A_%^kNnyLuhybp#>2#AA(s}5i$w-Mh4^e=i?Ul zc5KC?1k5h+tHs7(0L=^UAY=7+T|raNeS<3<*3u=%tJfK;Qr%k6bf*%Y5pVv^`*$PQ z1TX`Vu;LauT>aE_L1@4yhOwVFuMERJL9nQ(1VJk1Yg)}#vj^CLa+en$D+@Q4Jd!Lk z%0{!4UgjE~@ak>{e(_r8BWV|Hs6IE|{0JGou5Qe@>T>5o#SB5z%sr17HApZ#>T@GY^Z*u}se*;*zj?V=iN3^E?;P z9K;l`_lEHw{Rzr2#M+0t;)jcdZl2)w^iZ}x@{*dDcO`H zKdcm%hAD!hW5Zuki6%$2bCOXH$A;=>MrV`BWAOMT!_;tg;}=IJS<0`hwQUTuigqxC z_KrLL3X&R%%p{6fiGB=0oIV>}Ql4^^WDVpUV(COnRB2;8G_t@tH{%T~>OiE|&cd~q zSLUwVOGRj}GrE%uC#&6FfpTdpQ{Gwo&8F$@u;z8amq$P~73{oo(A|3tXLV>cq+c0f zjnBNe3SbIeX%kp6$c)B z96k?)Ck;gl^YnwO@VF1LnFQ>-qH#CkBN5P}NQWgmu+lgQ*x z=MF}Qh|#K76=e#__~$x?2F={h)vgXZLIJCsZ+jkE^JNmBE(hiE6d703G-^M4+)y)O z+>TyPMeXam4K?a|@yI%U<+L`)Pms?&)+Ua%oiv=x32c&;HKTC%GUMj75DkQLGPndW zaz6Of;$hZWjppRg2jlgXoxJi2K}mPS#^yzL*k5(1h?UBnQtPiQy;X5h1k3&UB{Kif z;H_mri>YTAJVZxe|LTZSw;3xdG%GUX+gYfts-(Cd_X33{LwV@pByYS5?($2mi=agm zYO)ja=CsR>P?9)KI@g_3dgn@2>y8a*-m!9_uJw1%^O~R^zp7=UkZ$XnuyHGHFUe$d zkmYA_r;}bcKe`|IKXs(?gk88Pr@#@tS!_5%;9V?z9@1T%Y7)i>u-40)Kpsc%D@QYn zn9YG0&*?L4JfZwjJdLbe$O0zis=s3;kRQ*;#E%UcANv_(GR-PAo3TPEPK+x+sm20i z^f$cHh?uYK*h0L6*6Di5b`#@Ks!Qo@E3q@S2$YXlu!JrrQZ@QE{v7UUi4G_x5}7of z)(l`Avkye_p~yfW<-qn)EHD9lRB#HR2$X;Nr~Qca4YOR?ga`D@4s%TnRQ}n|Ahg}T z3znlWX#uxI7baPtOrv5SD3qgIs4Ur_m8nS2W)YTTvM)Kq0sw*ljr~^A1bwmVPKnT$ zNay1+ivEbwv=^_w1KT*tW^twRlWX<1GX7Yhr(ZSj4tSoAwS0iwq><_kjLrMV0H5hC9&yj8qBkKF)y%U;UwO=v*Dj+kgSnJ1Y281w(!m1XTvb#yXGG3h zz_(s%fBk9@@+`#qAJEZ@d0ibCY7kDgS292|3^EtrjKUx@0DZ;-1H`i5t=>ZqtY+c) zU?qX;m3Ix8{6H0!R2B5A89V(i(EK9(I$>(0T~MjLD4%xYY)Bgvso@Kh=C$`s!F96F z6pPt1X8D2-fv@&%WWtLYYKBCo%G0qB2as9$9~&Ix=CQrBy=zXZa%y5>4Z=8R-k#SK zBya`SF{!q>!-cMCeAjik#$UDENh$GF8~I|iH9i9U?)Gq{clzgJQZFe)RNBAsLJQ@? zIhLBr4;aI8j^fy>PeK(Qtvb}FL6HSifAjiltvxdPrWRUT#e5;b%YM*3 z%KtgALHWBl7CntLt*ya6kh`Y=NEnn!Q15oR^K^fC2*%FWc*pS7=kE|O|7L*P!)E+_}@b}gO$=H@uS`M^;kYCG&D7}|~icwt- zVvvU-Kzfpe%=sgCkBXjONMRp4n6lD~-u0nQixJe+xx@Gl>&F*zas)q&J*f{^laV6h zf#*_BR$|(8rTV7b@uKstpq~n{^VuQVQ_bB6!3Ri>G+D2^(s=0HS1IE*@v8|t#P!TC znIq>@7x9qkrttpxjFec>JZOSN9PPAT&KVgQKKcn0?|ag{RORI(oh&QLp6{yg zVPRoMDRv$%R{Q5@(mYnX@bl~wn3Inj!*^3rAof}Q`Co)ZO=OqRNCRz#qwpkN5S6zL zriqn|Gqzm9hz5He=Y7<+vRc+a)y4hQ3yi7bzPr73slGj8HNXCisY^WSHlNRRyqALR zab&eD)N2(M%M{Dnf16NUczFM#YIJwng9EZeNhtsPfYAPZfihZqWzz2_YPVcwSv6k) z0gNxG$@-TdX_87=p4*MAVB8&v8Pr5OCeYy?F2&8g&3DP4T8GYSz5EpEMY_r*jti@X zJw&KWu7~5ttBbwQxMlgpDvAqs{Xx#XeCggf0KHBChP&}lsbm%=McUH5B z3@Y1B$Wrhyk&&**2N>fe&-+n9{Y~%hG=D-M!S#yLKd}huTnC^4l)V~u7M8aNFl@H| z3hxSF;e@aNA1s24>A=bjk1+XkIRN-}_2DU=rH7$JIm-Qk=MCg%i9i*v$X_NcYGoG> zmQNR&%*C#dV5KF%%?Kg$vp*0fK)QDBT7yLKmdoXvJC-x8;nFAizWVyt3PWAFbm_wY z|IZf>&P*}gL@j9|x3tF+86Ds|Ufx!4~*t8xOv#;aU>eEBOl`l+}oMy`4-l zQ_45&@25e|Y;rGqW*247QE|I3!wpLOG7nX1p<``H9m_Q9*%X~A&C2|!d#oSog^{Tn zBQd00wGKoSI&YIMl-gv4pET|GJ`R*qHQAN7 z(6=QdB+QimIykexaRpytu6Mj`LrXsYXHF^JxOBvbuXp8x$G4^|hm(^78n1>bE)Xr1 zg3ZphKJA}CbuN_~h^wNOO^c$R=5%+qxL6KY$Rt=}MxvNE#7v(pv5bEpMiwe-?=*lI zhjx}!gAD3T`vxB3vYQfl_qa5{I7)9aj*C!-2hUUP<}%Bde(QUG7ufxlN|xEN3U&G8 zN1?E>nzL^Wl9yUXeQPN;{@GNpqt`?-q0!{zgoD4d@Na4E&me9q6%h59Y*r>p2FyjhvM>x*lCo!?i0Ry**%(?_zm~#3x6 zE_fCwpw%C?t^7Ww37L`3*DdZEc9I^m5G~|`*>4YQ_>RgzW*LiIX_+c)5rRFtNe3JY zR7)#T>Cs>G)5W1R0jidcbu>+Pi|rEVNhP{1xt7yv>ddg{SQ|Ed4mx$tzoq?cows4= z<=n8}b|V*#YUcL+@6eGhc|H^bSU~Be8GCkKNd^?=c_4O|GYtC)4b!Nw1`8v)5;a6A zSbVKWqE%(4%W0HnR4Y>ET9gd^J_PMke8zqI#_!{MZ9idJdNQc?0TNLXeu8uj8b`C$ zLNUut=DHhb)i`H&Q{|-ghFfYKDk7>==dJTei9|i3G3x3XW1B=c&ASBUpZ!@Zvgy18 z)Ceb*w@*V9=51&%inUhAnJ3MrVsh!?QPl@@KRB_HKRTW8(6mdm4W+^&6BBFU>gv5^ zt#892nJR#nOP6Ii`qUV!Wc?Pf6ry4BiXkYY8v)g&=9%j4FE%cTqHu|zqQi4XxHxY$hDVoxcYHi5;i(D9{Le%#!o5A*mb=VB|XadD7A&RYufMAN%OtQ|*EH%RVaakG{I7?;GFFp-j zi0S8Vdvc`PzBn#026vizZ@jU4)sp*|NF3K&{J7n_`6W1Zk}4{Uwn^^;nQOj@kvdV3{d0|QX5syoc@XgK|Fo*VhieewQJ zd~}XSGP=7I8UKz|vtq3MXL95eIM}bvO&UrTr?kwKG)*E_1|Mr~Jxrkm@@LKxj#`4; z;!rVi)yS1Se~JpTXeGfWLHX%TGZfJlI7+<&KXaq5o4E2TPk9YGMizTxU`m(kv)X<_ z1M)^tfYB2!93c9C;WU78q9xWM1DKjuz%Md6(nVJ46 z7R5+%mu$^iE2ob&5tW_EM*lXD|ryugX&e&E%WQPiEwr$H9idDYKfHiRS3DvSwwfvkgzM z<+~t8^}@>cPon^4_9y(33Iz4{gb)Zf3wE`mx-2s|Ts(4c^fELh*C2s6Oxmmd@?qYQJE@7%0qm>hZMl(w3pcrfl6~L!obYdemoSv$STTfBdL5y zHbw*qVHSfuXHF00axlD&jys4%S6fTa+b3BQV_QO5AGZpZlhL=!zXZb3i|v__;Q|2j zO-Y0Ot=CZNG^U&0Twyd~-BD-#PAZ)GpQ^r2bcE54R+H;US(D?{G*Vi>?Bb*v@NEl) zGB-!tqYgOYf?-vIaYxf{KNdkH_&<@MdI;1TRtO@@_WciFh`=bp(moM8)x^WC#{E4a zz3JPpV{{VB&zQg4@zbwDmM>O|b9RCDG0TZR7)l`$boT}SkRiBEN0->SWiVkS6khb_ zTwUepdjL3W-+$XIGcBjXT5lzII9npRk)UkWp=3)7h!dp+$Q+l!kfw-+;hhY^(|{+! zDi6kcjJMOi{3&pO?|ew8+6YA*T1vKjl}BHuP^PabaAC^7mMOSho4%zPy(rl>n9)O! z;c8JVRqn~jZJeJ&xc7+adMmc~ems9#i_u)4T$SdIASg$D;wR`IW;_#t83qLH)q}>L z^-49%P)(cvVMT+`nZc3aNgK4M8~(C&MDu<7UnO}t6+VDZf>WOT3(IBS^_)R#7iB$xU4jfpi(;<)68Py3f6#=Q>OoUe3KgZA{%sAr zOT`_P5M0t9A5bAD6>rqqwZ6Cm>G>-N&SuVO|Yo zqJx~WOFozMHOGq?lxeL9pY0%i%9Vtj;^#H`R;~`ttIxgfM@z37r#iN>EXh6f`^D== zqUCh7v1p7!ZOZPeUyqcki<)Ry0*~rlS96Ku9}f?VOF<+8I8|f4V6X9P%O8iDApmw= zTTyjy>w0R~gErPdsJLoYHsuS&?2!)lROUhF5Eae;f5e?|IM?=P^B$|o1gq%+)gtYP z|4b?7Xqq`01KyFCr9I^_=^X(0P*h*pRZLB1G2h%pu*hl+>;yBOF%{Nnme{$v(tfls zvQRC7Y!u-v;IBrgmPSb*ic~aK&w4$pV(2YPfs!p+xa7YmtNp4AIxyyjiu=W_Gu)hV zZ7lVR`C%=_w>R3Cej|06B4~qX6LWdd)hO-GzI`3Ox_vHYWW~;-_eEc`wz_H~26deE zoN1s@Y*(2At*RhbZ2&uDZ)%E%Z*{DPij0ZrGgC!NRaHf%zIi|Q%T?7mPqh_qsCV^1Q~51xT`b997p)$38`rr8GV9e9Spmh7k0y zr)L`)6YYylVHFS4ovMD8WM107l8LqE%iUiswC@R%GVVmZN<<@&PG;3#dBG)^g`osP zA$szMn$zDPTA`9gnVOen7s!(}*L>Vd;i9A6d^l?JE~A;`khR27&t{a~NJqyzm$AO` z!jbuFIA8ri-}V0U2NVEE_O|OEhp+DbUTzF#{HV>@CE(@sdWso>>uD*>+V)axd%gK7 zS;yVjyn?=>DmprQteaAmob%>PUsZ=#d;Yfs0D141hvUM<2A6o$SNq}S2#u!2MyJ&0 z(bB{ge=Uah3*IhVp-`&tUUzX*o8Q0powa(jo!&(P&S9?Od2QY00sFH+;fVuI z<2e!kQQo{KsX8hk?_oM?{J3sin5319JaH-TVM>*gCph!aWyG4cpjz_R>S{&si|hRc z<{v{%a%9Ufir!`sbTuwY+P1>HX7eb#M0fZ)^Z6F<=~p-Sx?gyStyK!Y9&Btnj+=#b z?nmj{Wlo)R8?<`YJ9ijP_CJh+ zH7#heo7tIEl^|q%8Z&fRGK2;8;yFw5tKv-qofYf|z9Crc{-LO^)qYHTZ3u!=8CixQ zAoAmCyhMyTU7l6)cDeKCc1xO31ZYsll~K*~8(NYeh6>ceOh-nhtGhZjQi?%}P8v+w zKF@hE^8WemuC%rFAy#f};{N>e32XtnbsGa95rrm*oW)cRNsh&B&_Dj@qwmVlRR$6SG_UwL1(E~o@HJX$Hnp2_;Y!t zqbnrNr|UgsN7r%7_n!QweZHjLc-ga_4z6CkrB8+6DI?J*7XhQb!phTQ9lUjp6VsyG z)6GN>cB01O>@5w35UREl|9glZTsA}KC<>z=jhYy*|J-7G^k6-@#HW7Mjq)hQkAeiI zWS3C`$$Fp$zmcbd1X<;ULTmsm4CU6FK4T;TALMGS%r|QY`MA@P65mZwA!?xEPC59l zNExORJJRUg;Lay$1qDnMEiHNFzlTVRrsU{pv<{j_>r(F|dxkX#9!ht8)3{!WD)iAs z4w9sMl6t%W3=ewS>}b-6HUxBEjQ2rTV=wfelNo9#NmG_U-$7gLL8%6p{q`K;U!$SI z*wWh{E%7oK+=q!b^F{;LBpm!^N*a^6O}?8up>w&8K*LuU{vno0j%qm!+3efz0qZ;k zFgqe!jQ=(=2)8sJnr`J4eeyDGZ~o3gE&EjGVljxAMAp<UK$s4;GtF;1b#k6(Q-lx>Q+fFs1tQraQ{Xd;tbr=* zgncuu#P~=w(wZB4T;sIHm%VA%yX)c{oo$#-;x7}a z$gHj0RBBOx>~;b0H21eII4$E}g;eqI7ew$Vel3r`DV(+K2*yY5wn6T|*u zt@Fuz9DvlE$?^z8O*YY7q$H_FpFQVF@|OU{#@^SI1iaYD3?>$()u!E;UzmF4=KA^y z7BBDO{%0*fYX9=UInlzynX|js({>)avclGe9d}tRa_49CfoDW0D0#qD_~&tfpto)MePgma&)eNUeQUwMv<_IQLtoP)v7_*49F~qde8T z5#fI0n=b?^&J{3W(wnY^!WJ_#E}gG%RU{JYqYy86tZ!38la`h0ryABOVL+{^gZha3 zTRoIxh$>n~A}V{TJp&Rd9IbcWHS_cyS@F};LwGnA7M4tTF&fQlLiX2h-D=!T!7!(1 zxF19d;GWpVmsAbhGhK?D7nAI;DAI3NrX=5duqO3r1ix&I@H?IUCg!NOZ6>*OD;2sv zS|5GvjVS$X7oNbw#BtM|p;G)u~1d57z#oZDAHz|(0ovfu0AC_RMUSStyy0dMd zdfz^9D>OEGW0U$IRx6P_ChvcR{JMQ16jWQ23jpG!Q~iPm%KJW+IR8|dHiVg-^=SRb zDlNunf6K_s%*?`q4m*xtro$oK>GS=zZg=X7#0d=GNt^}?01AgPh`Tl37Waq#$N;dT zgvN^(<@)+-Xl`&;Pzb789sk-)$We910swK{OK=bvy+3~B zc#O{d@_dH{v}c6=9Pw^?#~Y{R#{Ot7q$OD~H?uq}`q~ZNiq%2LYK3QhjC=1t+g$Gf z_+ZA~+^sh>qdQ&C87()vIlMgGU-KdY^JTw+@*t+g5)E1^39j2DN+*J4>P@!hH~eY&3;Z|&GNTVM2j>=FO~TO<*W zp!V`&!i<%NIAV0SEs>2gyM5?m`H?>IY+sI>Wqc+0d-1bSF%;1Lr%6O&D%rp7^-zOK zJhMSe-U+RJ1tz>T*7O@i+b4(?5>#>FXd3%#ApY(VEcq2r#ruy80)4jO2Vzhyo*Uz@ z`r~{DZ=W1^oxZ67V{kRZp7>J_^&f<;&G>QWkKIz)i5FyT64&kTL*ZlLYM67NimHMZ zbF7csjz&`^(rGhx14^<|KgxtMhEMO5j1X++{77m$TU>thjyOA1osMqT@ML@4{T1}1 z$SbA!{;Kx!IqrI@A$e+rg@sQt?y}fX${$r{dPn`>tNk;bul&`>CByQH33^OlftXqz zF`F0BXvtT^5Kb0F8&sqS(zLZgKe%6jwYCD8;9OkgTA4 z{6T$MCS(XKt~aVuF)#&h`U}WyV~wEzGnEG9nkf+^I3J@JdmMpS+&$KsH5Eu65m0uc zMpPBScchj`sS!iiSIxppyC6`LFDmt!w!h=LR1NE*67tETzFBE-+ZZY}+-Bq&oU7Xz zSbk)BR2AFu)i07&;Ww0_b;ThII7p=fb4NdNr@l6EdB!$#Bo{Dn!I52g6=}9SlIO=t zF=0zs`fD_R{4_YH5iA1l8jx|dm13+W;A*&VQj=>OkMg0%$;*Bq3rkB;>(>aD{C{Px zjWOLjSm5eD#|FRFn{3oa-#c@GB5?0K*lwbM?-~%Vv_kmuW7_tgU-~uJYn>{wU6Ri3 z^|-xl5NcoW6)TupU+M9+nNsMO)uQX$YGFE)^;uye_>u~vIRr!KT1v?=I+i3e-!Y^U zOT7(;=UJ06K7Lu>0T&sPg~Y^!5k$D6#QI_0_uI_)<(CbQA4Qz_2P~#^Qc7Zyk``^{ z7NeBk$er(-jz){_{5PbsteSCqVumRcyBRH346cQp0zG5KUmd7^2ETXLQA{4Rm3Bba z!heDyZ6jyP95V`5PWib4%`JcYk)Wq5A6gNyWPJKG5 zu^SjF&Vz+h9jX0o!IO&m(G};C0aGXpTiYWZ>j$C=W0A7L-?XL=COS4!V9xCgq4&eQ zE1>cem2o0LXvkhOqoa_R{$?Xi)3EesHL0(Pu};$JjX&rQoGy{RdLCj1L8j}4=UdaC zR#i3N{b5-e?&T~Igcd}nLKi|cdCNuBiddN9xGW0W*fj!8cS1F0wD7Ts-tI~Q=$Vt{ zNIx~E(Mqy^lztB;d|~D5wgTPny8=Z@xxw3V6}p;P7&Y7umP^}HTAsL%hCV7?uWW`J z8?RB~!L{j6w%v8_*ZX4B5sBTY*@@lT8`0I9w|l)r0id#I@-cnC#nTuJEreg?-eo_D zqZt(M#h*VJ?#1ubGh!BN{n&cLOeOA8=Mf=M^yWq;#gp$~RyN4V9SuWhTCwha8M3w( zTbKGDp;hSx{}b6R`y8l2l*n7&-K)?MK8;jEDdzRVs#ecF9R@;SEI7-{{pHm9Cs}`A z+6q@I=rEdu-+8U?)Z=wrX!|$IdCQkyp^fPHr`Q#Y)E1WRg=zm~wv+TE3N+7>cF>JiyQ5uu?BV(7LFMZ5{A zaesVKY)r_cbJU+mrQv1jnCPaZO{9}!N|)!m`|p9j>`GAO9vk>)Iv=hd^)RAF-GRl5 z2S6&_^wTc2j+dt`b-k7%Nz?DwlS+(+(CwLXj*TT>Fk%KR}Ly38JY_XL?i zES)^U7y`^2;s~{p{$(|VAohog>RqHXVNDX^&HM9sUGL{Fw|RZ##v{xW6lv8appJXEvPq-Y{jG=Z*8>8@d&N zzcx=$uUZbsJ#(R++b&Ot{E|#=d$~XhA0E4wss($*DJ1`*2q;87)V>PAb&DJIWi_bs zqL2L1t(tXxbtUOCx759njDT;3guLC3dw7Y^+Iv+Mzw>|Y}9m>uz;=4x%b{S4tu)({h2u49p4+uBveK1bC?;~riXh(`^v26edUMkx?V1t z^_e{f2WHn?7o5+9XMHUg?Q%Jc}_Kf^^{#Mfl+c zHadsnO}~@m+7WrCgt@_dJ(0FRhp^rRe+p-}!h5Rarq+vb@_>~9f~h75z~sv}PRk*8*H5>b4I=ikXYBHs;^DuP z*~`;){5k*ZTm2L`Aw4pGn%DcfG_Jb<7<~Nn=2GStE%q%j#0pi;|M>ATGouI%ef+Ua zs$fzM^!|2!U=T^kkc(yZ&L4J>UD`%Jn;xxPHD=u5xv{I{$7@|486;Cu&pTPC_K;i8 z&voEWXObCl`}A947HhVS!HwxM!ja|JAubO@?yK-_;O+z(_qpm=%9{y_PCMAzYGNUB zMopFHuC9s?5`CrNAF)?$UZ4GN#uf;?p|>Sc{z2GV>hj{2=+ZU7$^YsCn$5WHb?{2T zwh)4%G4v+*bocqV1d--Y-JXbC_WpSJjC-Fd& zdpTctep1*>dcVRh@M1fkKRB}UWhT&gGtVYmk?V$FwStDzb$G$%OIIVU(rl_6g*CUd zdXtU)_qCPEQ5SV~DebCJ>*Y811BS*FpKyQD;mcfKfS@Qotzsk!VWuV}_3jcb%zgMk zwL?W!>*CDL&dTu7ZT`3AY7Ge%pQGHL4p|c-`P`$|M$-u53;Ouh%LymT1dX*ObVB_F zugUFZ;+7m3ALf_U=Mds#|7$1#%%;P#KQpQIVznz;GnFNG-4u>1HUjS61*M&}HY!df zMV`yI%%qhOAcV{0s?+|BF-+Xxml|u|?q?NYc(l*;c18iHIj)X3t;auk=`|0aFtiH% zfdO00L^ltUdDHWp6+$k_m7%H`qqBPtzE{+iSRFwEr-YhM?WICCl(gk%$IP2;u-eIo z`|gmMOsGGT;d?kSY3Y*N03-)F;2mYE(ZVAoV=`sUULNYOAM??8WxeXhaxuq6ljRQ4 zttM{b#Rqq#D#Od#4~w3)Ub6J36^nHq7Vh5XV#u*da$VF~cO7=Cedqf7qY++A2I;3k z`CWZAHt=3gEEf%Qr+==<4!ayceD1FLX-3PD__)Zdu;{W+%^bCljj5|J;Lr3ZkJ}b} z$QS?IA55ZU5xw<`@mh~&gw5*;-$|RR_UCx7f1WRMxdP((Xxj&~Ec2@8V&~@jB^38I zu;_mgf$u)igCe}_Kc*??@0}0{Tu*=Y+kRJ3T13S*9>hD=Uu;xE zTja9eie19gE;_gwWJSB06ed&zqkW@me&7H8X|sC7#(BlF^;5^7BbCXYnzsdE0*---W6<#RyU`$d) zC_ii+9uh9h&jvkRxHoc< zf=*1VG*JN{mBgEcU74b->S=oDHQ8H(m*f1BeWfhXH!aZA7QToou;X1f-yXY-wg?DB z!$|AgCE2#ll|ADq)2`o3i}^?{&N_jh*-sx{e)=@1@)WJVE)$^v0;an6$G3UIt#-I? zXwp_>T3t3e$9Vs2#AuDG!`}{Hm6lU}fiP;PQT5yGC`T7JI`WV>AqrA68QZ)5&BrHc zOWj`DQ)}vT84qLFN+!wc%6#T*+R$%JDK73hyxYNh>F7xIT4QQqyzbEa!K*8j*xRiJ zVe9F8f=s@209f0TxP$prNM6$}K&U5yhFv-Sml7%Dztx zJy;yApP4J|+p%w?!%L1QSoTBm!?`U{NUZ-2X`Z$CYDMYiy5L>h)p_SH%m;??Y9_NB zwVwPgO%3-;4sIJOpPW_|hQ9#v|A^N-k&6Zg398b<%{J|R&v8oa1V`$Fo#AAKTdZg$ zW)&W$^!%%GQ~)7-iZ@^62dSLW@ncctjT2j`*Sl1zo9eOf#E&7*kB1{rS%#wT?|0wQ zBl%PwvDsd(Co_#RN!-R?&V;{Grj?e9z_wR8?ow|$VME9vL#Gl*s8~}_sF0SEJss~K zqUPPgCff4`h<34F9?-n;^uH!PnU7_oI@+5N(Y?9^wg)f&Fr3kU(Ix;pSITm@QbfSg zgmb#{hQj$O!RJxKgN(-j$DZ!zi1&-!EsE}kKiiAV|3jSbn(DtJBS&Mst-_-U)<;4{ zig7vRGBt6>BaCl#AkvuU<#yUmyqPz~X>gOg&r(v0O&k6=!1&V$(%Ud?N4nMLX&0IG zh_DMgaNs}Ma%#orY%Q0t(}~$C1klvC-hW?8`#E=eyQ;4sq_%SJ)3DqQYI~}D znYD&TA2o1!c9&A!*C#{vsJ|+t0*!*%4M1gO2*`hb9I^qyXoj9mYgYwr%&R*D3 zKL}>zE!R70$5>?JTZ!6rF>LVsO!WSkRPpNQ_X+?1g9mKWviJ{@kWsUh9@oiyef}U1 zDm4hk#ccfxE|vje#9-C&>tr@RxCY_52!AOIOO}gyD6UbZRxK4Xfo-G>2C}Zu|7UJD zQ+o(Pnzfi*CvkdQ#LaknpHK|&UCIQj+l62o3^m9jj=>br4yjcDAps69mw3m%W3rOop&O8N?v<}21h?X$WvP1GmeQ&K?tj`s6$ zjN?IK+Ya~=5O8`0x78Ak8ehLxKH1p;*?ex7)bQlYuYaA@E|Q}dR(1K+@A!c5~=0tsdY0R@-Tz%6OBu*GS^;mP?A+=`Ue-n zJ&i;CAyy}u?z6etU_26}Ue&Os%*FEklq4^2>$jr-99-;~+U8>Fv5n+amyy%o6a_W8 zpS-S73uMwLF*Z!1oqHq1<-SWXVl9H}HGa?nYc<*qhdM*4%6~>?JEfxuo+DXn$~iHP zy+DgY@Ffyin~O(N+T#)% z=g&Y|T=C10#8)N|xah-HTQ*6w>|2f9n=s<*yga~XxTXHQx&JJ%lrkNjXSSnr$ZNrs z%b~q@JKR^&=5yq;0w^`unn--&I$3Sz<+7lW@N}W_t*N_X8qe$8^wuOik=ud`q5kK6 z$)g8V8w%6fhm0~)c^`;UwCWAD*68STZ`an_`+B`|>dknp)@YVSAiHWcEZTiMZ3ESb zoFBP3-H6M$Q#)P2Us=Zo<8t$RKE&ki+c(i}zI$AVXj6RJIK9{tff60gBLNp0sK=?~ zkR)%IOl*$kC0T@LA&KCMX@Ec0moY554gK+nRQW-t-9&pbVz-UxrkWLQIti)Dtn4eW z8CLs{?}+}mTzF^Iwiym-kdu{_)z{Ayy@bR(N#P$snAEaN7nwlCH7neo4J6=xv>`v#d zCO=?TvXR4P^r5qWF3wn8=A|=vy)No<8sw6_jeq{RbXjts?Cc~f{p4VW_0CLo`ZTO@ z9GfpJteF5mS@q6j^{MbqMH}v9gyt1fJ%rKAou8bUU0YPGQY5;uMy+g^mJLs((*t>0 zya(z07a4Wj*2GsCji7l-W~V{J}oo zZLfI|iR$)YK~A%AZmzM0pp}t4q5w}rIp)77z%AxQk`Ah~9Qe*Es+^GDSFx$~>=D5O zW}9kFY3Uq~9>hBNS9E;opjgRn1G|PMxMr0T0HOS3{$I@nkC z@%AP9>wJj4qR~DM(U$)C1=gTi9gV>Y&%yJu-lEH`92TVD{^vrOX~xx)@;(}mV0A9d zt3<2YwV#HH`RoZ2m&ZCe0OX#nhiS@fF(hy%mJfJXZ;j2QLN~|aQlY6+>v*3lsBH|X z9S0AcwNxo;Gggb2xW8-l^cp%iJNwd9kHh;IzJTv)pI?h?{bbR%q6wdDYE43Pk@q`f zaVc^lT#9|k?Ee0V{_`x%%tNApEfQHJ`Y}IGbrKkvhzr%;HMvueO;905{7ThUbaqep zMPl`$AY2Rpn`)RAg#@pdXy$v< zziVACC{THXP*(QLFogD=74kR&li+JEir=InF{a3>LII=4RUd=HiBUDjC#4DVG@^HH_iGPoW56n z(X>N3F1Bztolo{s0(Q1xyE8)FP}5{Wdbn;+?hLGsuXcL&rx7sK{iw#*n5R^BzB_l zg6=b)dRTAUW6#l@(AL79HXtoJ1(OfU3$QgIOZ>}Nyhg^7(n7vd!;>REgZX?poNrD# z!$CLs35|uHt#l0rxhr6M()ez$(12%G3gh$jok5l^^hu6zzL6eCHw9#)h4y%VNA^ek zKDEE7I!2*H$HLmMcUW39 z2dO*yG?~NDD7j=gOvmF`-kA&D2gCjLpfSxCLMF63$K>QtM)ZQ8S8@t^Za=?HQ=8LNy>>z4Osjr7r4XvRHu4{LLnu zyI=2bufcz}*KfmiIlGd@`s#Y__Zu!y{BVCQh1v9Q5tABYzmZC@B*4Z0($jKu#G>n6 zxooG>)!EeKzM95vbE|YL2{XOPn|^(#!{pwP$9^8luaTePPR_Uf2R-~zG@PFtzwW=# zciyEV7431ok#Lh?`e>V?4-QBORVH-G;6f|ffxqTC*Yn<$CxV-^sz6miiNE~ zT|`6#DJqMRfq{;0!h6^c*7)j%Wk)xtj(sCqH2Oy8K!y+#kiO}sJ!bD|u$J7j7Fm5X z{%=JgsyB__zM<5A$?y2{_T_JH7Rjs$6m!LgbA>1>dZp-SZTYX*c^Q}Qd54yGi|n3R zDMr;0QDGq^kE31l-C6>`CyQ}-Z=&?58oi!%>9GH$PD0gioET1vd`dvSg~Q*u?SIw+ zKsxjD&oRG$VeI(0m8*)Lo{Nk9u7`e~M-2VYhZGq(bXMl%4X|3^f1Kt2*g%z@GKUDPvflc~@1c7+i(t?X}s+8Yn>1w{UojJ z?u%&bd@SC#AM((D`zz#f<-g?R@|nzA{`kS^=C)nxIP;z*G2cr#l|E~?G1=R(`09;m z_i;8V#4?Pun5wO>Z-{>)Ozd0N-wwE)x7l5rSv7T`UjblxvvakkZtAn0 zV^utov~XT}Up5r0gk`cO8sFt_HX0t5Ip(`r<;L7Fe>%I(vohK3IN7WG=-P=l zGWLlXS$b(dee10!3hE+5>&4nq(5V_pcte|EB#uY|3NPWSr4%M$K4l2D*ceUqYvZ|E zyE&n}^x}OaH87^8i28)@O8U^ZBsh#B)y8@KHxKTeh8uDED124INleMcGOPcD4+}ok z#4R0xA`MvKEp{2FukN;Cj4<6hO8K*yDkVKsDt}>NNF}%3999{0%4Zd@A1D(qg`{Kr z)b@zO+XEM$lA`>d6_6v43dm3$9-$?BO9LH2LBU@__ebKdO%AOH;2s}xcXJ50>rU(u zm3T$EeZT4LhZg>@@x^9(m#k=+qkO&>H{jlLMMvWzweY(-_t9mlT6Cy6AkV`EUUlvn zDgS%45cW2O4mEn4G+3YK_(fjwq!^8CNnpD=stvoeh z8j{cH!7zWxQ>*<`QrdnJa*-J?GOu^BClMGH`H%)tka_4b51+1qDK)i3W~fy07kfxi zNU@rf&N5T_jyg^{R;OdzcE`4DyJOq7ZFg+j zcE`4pdAi?wXYSmYk27cGOV-ND*=MVE{pw$}RROYtmn+q8x{^7nuZ+*{xb8>M$+UO` zTusDvh@%9q#vkY0fx;^cMNfli6Jd6zMS&PGfNuJ8I`=uhC>*!MVsv1)Ov@lx{XA#y z7UoRsL)+4PQA@cr)!{y$i@|&yFENVAubuB5>6=@S|LN21V0)D5R9si~3Y<%;mwr{X-O-E08pi_5?dQ~}^|z`$ z-H5t5cAvDz2rM#S?EFV8F#HNk4Nvn!ir_#NMDjlLl3q7kaL5FDotiAk8Jxlc7H&=@5x?`{<9h}{zTX=p!YpSCd%K~z?EBr@ z)>@O*T9eazw2lV@=fBvX&v?n2r7rs6towJl16-c;<%uvbz<3Z5OQMbU+pV?h=PPshuV1Xsn=c50ZH^C&^%t!~c|PFVoDD8wux;;y zI@Lh{#?QYAwH~UMc?_yes)eH_DL6idROx+mz|CLL&W_*OT24-G1-Fqt{$HY{+Vt0d zuu!Vm)BdbG&DUO;?fZ?_&d036>;{?0#&-?;m7%n{8#G zj1c!E(XJgVKfJG>9~?ML?T5Pe&3lS6Q>xu5|3C22OqEOyO#FoqP>RBMqOET)UKB=p zfYNQM>L+-daY(=>|CobD?8hY~VLhqhKe#ZJH`kwq#s$!=la|cJ;`O>k$D@_77}LIG z!SBRG!HswHX16(i(%Dr4a7nm$*}q}`ip4@3fBT!hd&NBaDwWgRyERbworQ&*KGw-@ zL~o_n4^+vq!NIsFxY8<+OJcU-@kEK!Z-~~#Yy=djp;n(g>^nDH>Urk~Zm8v!vu{e> z1(vD#!XLBx0xV9WEACHC4!FwVS~^eEmH0<{KAWGH8yEG`5Sm|;G)=W^J#Ud(Jkvuw zy+uJO9gU=|+EeE0!F;+s?`E9{seb};6c(E;?@ue;&iy?!=eu;=D$G^vpP}BL9<8k& zr9#*czkGK*dAPo^UcEgEG=ZPuRhb*qDBm|1?~g8g~n_SYFsOGwI?jq)uio7 z^E?fbw43|DTGWDIm*!0<`+@)xEMs0q@qxwspKj;|nlo)==r06OQ3Ii&z@>o2$avr3 zE{%x^l9beXOGQ2gVv+o|^Z8t*zZ94wxn56cA-NoR+2K}~&WNsr+u!G4`s)2K$;cB{ z7T#xK++^ddz|!%E_w#2yI4megXpWChh|kxr%70K%zY*kohf+lZFYqV(fg6kWT^?Ed zr+~mOg2)CbmbJ0*c6=6Sf)z#auaD0$TZRwo4pZYv><)+PpZ6x`4{rB;W)A^pFJWqJ zSH}pRh<4?%4t@vTT$fQZW27GUgvE{%iBcw#yd2Fgq4+QWrc#MlAA-6E)YAk(&XI8ogAJ&%~^P z9Kdkg7OAw@$L`V@>&v3VTGdPE!P7>4VGcth~CD?IAop z?KRk&!?31Zek*$6_a%>)>hjGF!VhB}%gOGOZ=3z4JWj{f_l1$@<2u=Aa;)&)|<^Yqbf+0;ZY5-LAiHzPGBvH zg6C+y?}#CIfDN7`8m(22V$ITssd28jSUVE-mb^0Y;yGq7r`ua)P+z`oaDXm0^cT?E zX`>O;;-R%@F7?V`cK080lC!fga~avZsvGK4Wq)Y7sY$a+icUUMNW2cLIo~~~rREwq zmrngAa2tP2vhj3z(IX|ra&dPnXiL|$c4)r{Pmh)qd3W4Sf7a#-u7B(81CA>sXKHhT?sQXIgK&2p$3 zHA|~_aS8HYgFHm`!r9YBVbgc?MlbjJx#JH(DJQzrJ_ z!GF5fYV@imvALyVdGbsyq{ry4=X-pEa!S*7X6wMa^CD%m4l zVU|gX(uVe~4v%fT$8M3>tix%d7OCjnB_R&j?+LAEyN6kC{G7MeLwAi4s+c;0vrLuL zysx>z_mrO@rOLM(&YZLaW@%+y3|9RoZFLa~1Wi-bPmj(wE%<}0Zr)iX^QfO6eooy( z!SM<*H1N${)(V|Tz`;-b?fSHS8wa0pPAVjUd+f;3qDd3`GeO+wHEzQMrtYTywfZe3 z*E+eD!EHZ4hE|Nj3$5*NA+ONAAnf&V@AJLb|M4fVfep&`w9l*ecMM7r)M=You{U`I zPX)7R=ScUCQ_-@;$40?(r~H@bc^We#zk-~Cc1NWFi7rg7gB4}5s(>S2n%bxA#jd(sKJ{bvP4qh=l#okN{Oy9Zk&)OJ(0CGo zN-;6NhzKMQkjnY<*tiI67eN-A*WJsri0mdwhY;Svt!i{bEiW1a*vprjl975X8@wz- zrQ`_4%%LfJ?f12Tvf4dxHE(>y=oPg7;9ouSeFVOAvH16E%wm6_3_3cR{`BInk)veD zClFqh`xZ%B$9m#dwP00Q74bmV}oGBqWc9Z%Oo|!H>Yb(b+*Nu(YMpE zQFQwP%S63oN*7i(a6LLGP|2XiDHM{HKlxUkdNNH!ng8+~cd&D><*j)+Y^Kv2e?Dqs zGa2&UuN3Ciz`)D_VS+ZDMg_r}zpImyd_tmifVJ0DURzeS>Y83Z#;TFYH8VI;U3vm3O^SD~KjfMEKS756=xgpyt*BLA^|U{wl|IqM5f2t6cBzs}q~OpGb_}V$;LlS8}jNpz&LG7eo07-O7X@ zyCE_Qga(_L7qjguRAB0U0PPa!wWQt29cH0AC5!#c?pak-;iI5FEU{pYAykSbY67Zq zHan-i5H?syX2sS5OryFu(SVxT?CL5%)Y0aAvEJgabg6Nu_SsC|RHw#XsLiOf`RE^d<8<3x1T5K9qF{iFZNAuR z5ER93Lo^(AD{Oh(!9WZLIJp6ZmLO^f#~A-@&!q*DGs5esj>Iqy%q5#v+EPesYq}Ns zYgzM%=f#bTcc}|CqYUYhv<4O|mJ+bEz{Q=jIn*;0dR*j6n4zJeD>a<$J(~GTlw#xK zqVX?J$Pj~|;o&b%5D`NzqQrBDyW)kC5{*MBabF=Y)60(D-jG#zWJT1?Vu?` z>a{;6U+i(bcDQs!c|Nv_yEjVg={Lw)GC19w}Fgh_oK|3|Lu+ZP%-`kt_ z+-{k))cfZR@)Ha0mC+jmARDaEexazg3NEyNte_n=1u4o%YEpbaA=C*1hf<60liK0> zQ-t-6@xk_ad9dTjE0Jc#MkE_el-Jc4zqX?R7691XNT%;#qU6>w{`2Qt&q4?&A@WF2 z{g?qW=(+Vy)P@8c)i~^t$2Wx2C`zW?y{$f-MPC-7Dk$@GN((>$mfIW1?S5LT^B7$o zT5GfBXHc;_jGGS9Mp0+36^Ev#s;LC~&_$?H{p)S(NMLVEh#CJZ!;uO|RZ}}&(61(l zCHZk1tf0NH!R=lM7EB*))6r7q9S{q`oAC5D3y#Sv43XAku_>{JNVV_Vl0Avh&MeOB z=wI9Gf_C9zV$Pr(B(!fOxyFS!aegMO+2y6xhin=e%)_nGHQTf@J)Xwws@XX`?l&Qf zA0jH?U*9O158T^w1%sOmZ-1VaBYn}c)@SHnYTo-H6!a}Htqh*q8j2A6G6sZT(`n>l zFW{KRpmSo&+t>mCMdUY&9WOjHE5qx?)yZgIur6hypBQziE2Du-`>*1x({;A@_n(P- z0&=t6pkqPMj5Cj+D)Ev+-?FGrJ9TdtcgQJ_>S~#iT)xD1^TFb_MX6)fqw+Nh!P?r6 zEiKO7-gRMg^Xag~DS#3)GI)L|Y_v&Es0%Mjwywh8AFm>w_Y+n>DbJVGU*M^(5NT}g zF!I%(DwI`6aW(Fk_C>Y7MP|c#@}oKkk$!OhCNqDQuNQ}dt6*XD>iBM9dWmKz3_OyO zii_+@RS8+zm`jeCkEEHaYwm}{x>#$AF*3koAuZ6w7qt8)(vJ7KwGMy>_#_yvw{s3Z zpTg;!O-w$SjWLNr41@d;D-MxDx3Qu5*nd7u8tAy?wCVYCFM>_8AJskS$>n%c3?g=l zky`AvI{D&sdU5>`BpP5hH=NkDe)KsRH^%DC=OosF3)nm7tWkW_xCb2i~wg7IHGq+xD#ZDU;!C7+8iyuV+k$ta)mZLB$rot<;H38XCcYB$omZN%`ZuX~rjmvPbwLJC^E`m?E{A>#yRJb7G9?Ynhf8Fw zwe?4X1JdRTJ%6waDo+eK$%h6($QlihEHRm;G}?Cj`OST(?nqZ$Y;QCxpu_%VVhcmUh zf1X;ZWE;Wn;+F5vDf`&CSrlz18cb?(e9oOn9`t(5c=GMj^}anY7;M;G>@l9C9%&=x z!SSYw{}VzrT&6|pf}7TAB;h(x>g|Fnhqo*_#tis|v#N7<&hjV%0=c5Oweb5f*_ryS zhS#nuQW@e_D?)eq0W-n-UMT*($rEBnD0ps3G_5-5$P=-s6AS|W4!!YNtQWj07Yy+FX1ml+=2AJkdaYl}q1CI%nrLp{w3Kg7-Km zp1|SxW*G@`NHJ2FNXj5~dJL&@9{uV(VQ8?QD7c1vhr*=hndIq7;dEYR=TS1!9(eDK zSYsj_zll&C)}`Z;LfvdGmoha!5^Q3;;Tj#=l35xr^STuRv6}v!Fn6&S8d>{1F(ENA zlgn*(TOwa1vaYU<;YnVGKR*VYc&iESXl(a}qjC5L=!_neA01MvyRh(~`f7;fS=yL> zQ-LNsu{+=FhI(wYcSqdhmkyyKX!x_9HT%iaiD&;N!%T1hot7iZYvg#_fbQX`QC}Yj z-tv53^s-z+*_I)sM`qme73iGX1N*|+3jh!8Na#+VEHkPgg$Rxa97DvyOkE}Un8gxM zXp!r+1V}66Y++)1dDhtOcFb_vJyZh#%ESRab>8rxKB+BW*?^ovV)6lit=UY;myAWR zZvb%i{Yn?Z=@y1ZSv2TLf>>=!Sd-Vtd!)0rq*NlH7+c7fzlh4O$qpSrrnm|@B?9~4 zrNfPA)2KA2i*bO+MRj|I&m?vF*$9KRYE51feAMkjhivX??g+V~)Z0(FEc%@?xsF;$ zQCZzg8zdC$i-?+j~^&;UiJ z-s)`HjdaOhQg8P6?NIe)gqPj~I(Mgv?uu}WxZk0A=hxT0haEUFqXzVASI!K}ZL_qp>DrXBuBVujlsDA}G?#{1+HrATtcwKPLUEceSa-)BKP_VpolZn1pS3f#jdY$D}xA&+H zO5b#8+)HEFUi57$W4#7V>T`mLNbQI8Wf1v>>GAeL$xPK4-dW@eos<^$c|dF3`nW!-4j%w8x8B#6 zrWK!_l%~*E_i$Htpvy-tetS`_c2H$qRkes?C9GW=p|>(0L2d7ZL|^VOH(N(($|$N* zK2&hK*>Sv7trIU4^F74tJuP(Rj%=R6Om7zsEqouDCuTr@e`sJ)QJIU#=j(LN^Aua> zebIOgzPN|ivESA$+|if!vtB9Y^bJNMF~pqlSVc&_a=7xpF1*NcqCqk_D_t^-@K%2J=kiVq&wp zz+Vv4CP5Un@=J6Oqjgw_gZ{ha?$L*b+U#wPH?5zQqw@0|=Bx+j$p8cm?1E=ZL*<*hE&4HX4K72puzAYZm-6LR{W z!5=9lZg0!U0TCuN-KNxo%=G(u99U032v7h&&E%9r!;Fk47m|$7e>%eE3M(-B&C7Ko zva_jUn}sRZ6eeIOT3uRUU}8SMys&KUot|148k!Ciim&O{buae5zYo0~AcLNQ;Jn}!#QDPh4 z70E}gQ<=!j-cv`1Y+rz98(QIFLMM(msTfJWAGY$v3v{+|V$&RzLB0yGB+~MHthneU z!TU!$P%*WxD>vo$yw(`BPjgFyzen}ay*uta3&qNXygcQmzjPyDat z)|77faUTai?_f%eXW@EmOWxIgyz-*b9`ukL4GhcQT;q!wJ!Lk* zMW5!Op`eB*CzUA@0p0udV4&(1bC*beG&{x*?a%xL=txiiwrFcsN4MnO7n2 zV`D>bn%cC-CV}<#AVv1&yQ6Sar;ZQRNXIT!w8RyTXR;nSIC3nA8eHazC;;@OG6`c^ zkKf_kjXAHR2mt}1-SG<*015_{Dru}DdS|+wBAuUWR<`4os7^DdtJUa^Mkm0n*vDvZU~&q;_e=xoOD9>V>b5IRP?~I(W0nS*Rz$Ae z6*I3)su31EXE{!G^vpV> zK@Z0=-2xB1;H^C;d=$+9Q0hu?3KVzF z=Sy3tKFUc(2c;#gmdO?3QzHaU0=yOoLuCcM8S;CB#$VYrAXwmedYS-$go=`yMx$zX zOgub1VC{{U$f~G)IXh_V6ioDVck)G}X+*y<1;wN-ZkIB}(%P?IkIBDd*%A0_0F@74+*8k+7_4HLkIotW@hVFkHyQTxHw0-atASV zT3w)gnFxZ5+28mrPfGEfT2hQ`?;3-nP(b0)IV>Jo{5Kbx={Ip^F)#o*<kKpN#XS-VVL+RB!xXE_= z(h_z3gRN#rvyXZzu5rU>ecWXXj9sHuVOZlgXD%%yr19aubnT{t5t~oVQ^sub*$h}) zH*blMss0KEx8laefP*1;3?)sCD>DWFKzIEW^oytQq7~KD^K+Py=HHii*(ZEN zEX?N6LGKB0lrFAaTEKl%y=-5pH8~BR0uCAdF~wZoI=LBD0es4v z;tWw~OPd!+Q2q6g>0|XZ4ktjehTsbOOoME06|YUz9>$0GMNi*)2Zz0m?z$*5+jDB} z&BRSQwLr@;SAUjBqP_Dx^UsS4CJU{VI8I!8lFZ6$OL{#$(8!L{=VNv?u}0=@7J z|DjybpaAtX{_1bq!r$+X6tt%ZVqY|V4~|${R0dSN_kx|qYn9BsT^dYS%MEAP?4cm< z8uYo3bA(AgV%}PPd@!~VAL~RI9iS~w^=aa#P@F%acx6kSbf?t;iLLLEKHicO7<6M3 z|0N3W|6|SNGk@C2&EFL6{4U}i(~vhms7cg;M|-Fd6aumkle8h_u5e&$3X-9#e;{^1 zt@pF87kDNOZt7*KZ~?;w9Y~yNB0%b8>QBm~0|(uRmDBw95C5{^afjQ331W#PM<8eq z2@MU6in2alAO*UexypTaBs)rf8v|dGIO=!T2A`#EN%QD?!U&A^G3DL{<=>}Z#20a6}b;)Y>s$vvg!sO?NXXdw`FIlx{YydKwaTs8swIiLu zOSl&uKTt>cQsiY3P_)4ZZCf5adI~4O?|8g2ir(mdKTHF1nsPKt2?v-xp!0HPG@2 zm-gUgxY~&>xrS$#(|?#uGyFu(0Bma-pA{C$|B3!aD|iS=a7?m0c(M87YZ-_rYz$Tp zd}VA>slV)|f>p8t=L<*Bf2gA=ddKVWe}JR@&;ADi0_sY;o!Yv&_&HSj>Jn3h+tvgX zuf5y|1TW1`ZH&XoGM9(tpb!^J z?)0kT*PW_Nt#tw(HFRR#rdVH{7N>2#A^+O7Kt|$VR@_8{;qL39_W9U zB{wn7N3Ia zS$!@Gr~|qTsY2914tVkC8oN;_a0R?RIy=3_oj?u(x&4FJ*esSxmiYzEC3+`~=vqyF zY$V6N1gj*|y{WJk?y7nnAt;kN23`n>}9e%rsj;;(2y9s|1G;np#6pTkb6QZcFO z2QkfNk~R+<1svMP#cFyo40hFl;ZD|wr zlg5IxJcRN_+rK>tzCHAQa}Ho2fIDnfteTacSIX2oKb7;?&=a=Bo2|`S9p2sNuRrYeRZ%^>@rWcA$R@G#K0Aw%vjy&=3s*_7JWGFuL#ihws=lVe}d0#*y_)Lrn~CKd&fU7A2M(%x#RU+ zG5FxC5!+gdMxD(_7$f5Q#!w9zmE#{6u-A6i>5u&n;|bb~`@!PT)n9M_4agSa6Pb~$ z_^)0~nI`$>I8qBr1_gRBW}*TYowQV}rO`gG57J5fSV;~xH(8k8vtgAe^&XbR@8FLZ z?=DI^p#XrMZY{M>DPV%7inG4;a6_~!}C`DA=XZ|201nZ+iK#0B|+}>ye z9y)$7Jcfl$HZ~xwET^WVsH&>kwycXBLNcDbUUWtr7pfgUGZ2#6AZQSR0=i4K^6FP? z3^%DEICW^T@!;uPH74CWQ9g30S84$O6!>D-jnX=%9W`%TCI@?G^D2Qq*xOV@>de+} z;RAnp8So}Honj)EGb4^Ep5nCk?y&tvI`+@&Y#gB_0b75N*?BCd+!G^r6_;Guchb0Btj@b{C+5HCAEn$I5nfNMnZ6npd=$r4!hhjgM zAUWxijJ9R*FS?Yze#P2vqe`};J$}HQ{M<%CAOQ}<+u_}du9H@6D}&RL*UbVhrK^q9 zvb`1-61nNB9AS2f)Ss}({MYf1xA73Es$yu7@v|QTbIt~3*hS$7&SFDdj^Y92Ik)l)G8T(Im$$-KOT}o=k-9z1cJ$HwZ|U&Q~qUC&<)@7q*QFx zVZ~?=J!9a-LnXH19MRxrCx$O&IQr#0jmSQrwlkDJ8*m`E_0C0Rda({3^{EFlIpru4 z7s&nf|5x<*A{m7;wtw^a@k-0h&CSF#KU=qebG8EPc02^?1)rlR-JCsJHY&7oZb~f} zNDnr-kDIs3y^pNDvA&bh3Er=bPRYN~a3d(wnHSJTX>chVG1rt^o~^T0cr%P;hO4Sm z8ey_Gom<#R?xLAwHeD)z&Hq$znjDFLPGemQORT~C+#AjGy*!I`r?R;1ZiPJ3qt8QH zy$L`GhR5Zw-!(VNPH`|nf2jxYNjMCSk zY|1DZQ&89mpK>LVIvAB_@wv9v?Eo(=KAnbjoY888RFC=M_0mJ;P4i=M(4|9F=ClSW z8am=TT~bVOOSVh3cprm94dV|Vvy&4u()q-FW3=)oP*4EymKnUov$XYw`3$V2#^PjV zNHB4^G#8x-1{Ws>r^W+Q%9}7F*s&sG%h>Yp5Vg$oq$Dl=boN&DMuU~|O$j5T{gHT5 zTJ84boQ^=g@2v>1UOCFdO8QN}yGFQ2X!y{R((65WpFRpT;=v%Jyu7*#=9Hb!pRLaA z5|2zh&}X2kK3}n&r5jz@viDs(uFqdE7uf(Y;Mgw+Pt4clxqWyndMy#zPzneI0q9!J znVpgWmyiIT)u*i}%1j7SoTojGX-$Na@j;2*nLQy@37$v*0ID2>kn7Bv=W-HUNJdD- z=~BBiAnDplXpss6(L+MP5BU&%y?T+J48_4l;UA!52Ld{9Nl`EzBEzH-rh*2ta-na! z7VxWe4WOY>dU`YS5o9y}2%Wyt?(V!sVqDV_$>qj0Q)YIy_vgp^Oui^gU~dyP(8VbR z?<12{V^kveF8P}THfTq)TfD0T|LujB|=cL`CZA8mZw_xDyq)R^f5hTk$SENY2=Fu(uBn7i-P5=@SQR5c8ZjWyU}BB$Ja#Yo!OR)@)-GCbkWob^Hs0le5ikZ!j=0 zFxL>>_b97o#Zo~*fsl~Uk58exElc{K?Pp1&3bKYniy6JY-JRLBlIIR^C*{?5H(S}H zKVak47nM6v>7N%eImyk_BCB+nF!mhEkr~fO5ofsfTiI}~R0vL7pc=zwHa$8L^ zM^#Ebk5g)4XJ_A6PihR#=hK?+mn5i8@||8J{q6mUD8neJtVnizl7EUNDJdxufkE%_ zWNTosR}}>gbQpd?WLBE+2~+})(Ez~kNkh5HT{;~0_pvrRVwo=T|3PY4>-jShWPA5( z`SNAUfD_=S8vkx0=zI9eQnle}^G#Zc*;j?Ct-~8O$?r-C8Qg+3sukPYy1;7457OTJ zz&Q6809Vj2hz?l`{;Y!Q#$stjCFT38JjZ*1N_9X9x;b-YI3MkN>#qUFi!O~S2{_03 zVNzq{7X7`W(_3C%LS-Xs5AWiQoBN+7sJ=&?vRHv~FE1~zulc}113I4KCPOZ; z4yKY0Plc79MKGjBVZM$s^H4)IcDm8IY(grz)3lANexHR=S%7Y&8IO)EoZx2avTW7l zegjv41*qSNsYbqjv{i1-+4D*Z{p-fR?+Hmdvg`!n2%z@l-+Q@zNelntiU0n6!J+DSH-CF<_N8@Ic(Jw*+k? z%!n#PzcpMHAp=ZT&M$TBcW!WB?{1sWo;zEQs~Q5e0XeB(yezoIqcEnoMZdSQnsVy_ zaRzOLV5>lWhE*Z{j>mX8KDt0`m=s9c zSdio5zZ;=R?t`|o*9R5)-I6&u$rGQkK(X4ue zvdQB%&Y*2S@C5vf_o?4sjaIi>+H57Og1Qm}PsJNcby9)ltSKHwWBlhm@)M`>)n;1F zek;=t`RPr(dn{lo3=vUEt`4{mE5vaUAz}zT-z-zSVxv{?k$Fl%Se}N?RK5vHa-wj} z%3l@KG8@nOhE|WE_V|b#*|$n2B4SL|ixB2UOcUp9XEoe(j~(yP#`;Isw>zDaeOleu zX7utzF-mQ(E%@;i&zb_=OLvQgY^~8Q^se5A`6-hzOgtN#Cw-qS4bCRX z`k${m;qjh}I<#csE9;M2)lG(FBWT>=%#YRQs`k@X8;xgwGN;~IT@Ee;v?zU9eD_(r zxSp4Cxu|{qbJUm$#abklT5H(ucszaDdP$eZ@obF38)`t2|QiIE9 zs6W2Mqkl}eYu3HAn39U&;`B`h5r?mGj(L)(fC9Bjd8}VQRdNvz;f<#{($2Wv>dh0! zuPDmypr-QOQ>x*s@QcnXCHDrA9pppyX!B8DryUbmvjfTJ>+@PR!r@DGOM^|EQ$8gr ztE1=z5R?t?OC$;NGM={q_sOMv3+(?^ynsy9ln6-n;%Nu(KUjz`n6C@Pe%{TZBX)7cvzd#)^|9d$SWrobT{Y;!_7mup9c zeN|X>+p_k|auMee=XIaj9{p?KQwZ!5(BiVG3A-ko=H`?yfE)|`!&cSkt(pTjFs1GD z@D57;ztN8)KyGfXWaT%wYi9@5g1Fqoz~EpPr|;2`W689vD~!VvQsjwQQzLTn-v9}e z-->5v=ZT5ENJ!4L>{E4-0RW7k9avB>04%^qexb}xQe9ac_!M2c+gyKBIYoag(s^5d z{D`F$ZGGX3QYOLc;r=`1eBFDUQ-URXWO8fI-F_hZ zSzXWzxTaiteR)6FT*FWKGdEntB%3!0mp-M9wG!m2UxNX}1 z3;p4->kmZoUN5gKrm!=jL${sN+q!TiHu`|{PL6B%Gn-r=1-JZknjtgEY|+Va zSqOmD@yfn|W@Mz*hE`=jQKy1e75bzM1eRZKCo1B({-lW)~=?=JBSIg6CV>`mk?9z1Z zsk~M#=eG^IS$z@i>R&qYmL*qe{DQKAfk|ds65qYmd|8LOMp;rxk zd@iN4wDj}SJMJs`1P@;Ab8)RQwep~g!}JjPHNdZsi|%obahIl?$($RY?tEXL8~+Kn z8KnkmM|!HSFu2dX-8{5-4c;wXiGno)1FVp+ZgywWz799}yg1*#WLS+; zuQVG9Q}LSremE2}(DA$^wVPl$_9qp zIb+a;xpHgAk(C3>&7ERw%x0s=Ke&x~JqB5Ms)O65fSRD?x9-ia{DEY92O2M8P2T4@ ziN@B|2nFm#i9@{3RA*=D=T%}AD!loqVis~HS|BMuTRy;O&!w;hbVoqaZqYv5)5xy%G=bip&4EtgY2lNM#xN5hR5BXFmi1_^{eKEP}r)f9fm-Ob`CI7J!i! z6rk$)f5Tn?i0YO^PEz6yoC*)nb;(qKF3*l_*it5iM6gduKc|`4Bi#_dMq;BQ0cXAT zavqHDS{tfxEA|m`py?NDtd^T(?Ds7-`{x6M`@Wn}!}qg2u$|?^DG2WxqeU1PbH>TD z&aN9}ONPchx2+X-FR>R|ROjfk&+`Ep$QIAfQHJ{)2TTLkrDRd5tn**DS48FT>+o1# zbMTZl2dKj7x7e9q>aO2)up+!Mbjoo1{n6wKt<)|xmrB~%EtP1Yq;%~vi<)@_<4nDWi@XllQ$ z4EufAYqN)e0g;Yj`W|LIi$V%4z+u`Itfnabw=@*M_T#3$47zdn;JJ?(HoMy|6jT$y z^nsQ8>0BNHjrM^joB1GZ-{i#YNv;D5FxSTwO5j`LOL5HhijGXjU6>cbze7@*AE#aL zUX*rqOfYknXT{y49aI>`-nd@*o2S&n{Dg(qk+TB*NSPqzHZwvFUfK_{3}0Rk@;*#9 zD_7G!-=U(%3S4N&87wY5o8_9};&n4u*C#)47!v7>l27zLy0jt?T_7y6SgZ(*j`kkh zJ8sEfvlI9%a9t=tNFNm& zJk?%o6In4@I`kIkJROAzRkytXX%$k8=!p~T_20RmwphHkVwSuy?2Z|`@Z3Z<94P4# zWuVp>er>34b=0enQkeQgP4HOrRIe01=Oe)O(TkT`Jbla*Bn60=S^FWL#ZnzSkW(8Hm=K3Wu+b8G|-DKtuq1`W^yd z(6Po_KM(7B>aOLOrvH?zDXGz5+@0@W^sLfBUVaD)aA%0(!aeJ~VozGWl5cgDzv(>X z93AoR+!|ea@h;>(7Z-LqzrhNzk~6W=z1D6%DXOJAki#kZmd`E8=x8##44LqA0iw3r z&u<$ki#?MIEXR}{1FZ4?enTN0FkAKwtgExU$SiFU?cx`I|Gxc;M>26o z$26<%2rjeK+zocNTbs9sAVIi$AG5smXJ3RwrBGM4r`0@Le)-gvqGxX$lJ@N@4bM+8!rQTS7;FmEPnC+`gq!W&#UkK zey^gciV;=}Es;cZe1f<-5JxP5B@h`Nj?H43lAXN;Jij<`Xc;ixMINa!xpiT6jYT%M z@mh&kU&WWs9g9;^MsKt_y*`}3y?L}~O3NuS5}J){oT>nGXp>l8N=^-l&6cW*6Nlk6 zdG2HWm7jW3g5la9W-ccqfuiwW@Fp7ZKFx!#8r$|= z?f;zbp7Y(AyC#~<-h267&->GpoE#Amk(j7*ZIJ*11;s|W4f-S7t@M9&fZXW+Ox8P4 zw9BlW0Ss;x(O>Y&X;{yR4rr|}O9e@{&C+K8<}X4Sn8x?hY!2rgVCx3F9quDhMHd}Kv79(yy%BOg9pj_WZIF|2!^^bW01 zLGu%ya5!s?KT42P*xp?#!~OkvtjaO4|1CeY>Av#iXyG~)*6KUXd=eS0XjO-5!rM86 zp5OIx7mmond{ix&CcJ-XcJ50e0~~1kS$ZWwUBTXBKh4GKji#hI8RI!OSXj2<0Bw{) zH_u$M&^%}B1W#_?nTdxJh8-3I2*0 zVnn$u3JZ8 zx52(Kpu(%XGCw5{F>t zj}MpIV8j#@rog}oq{xQw9siv)7#bS-zCD^b!%gK|D8j6mI5=ly;c@0MMayIh{C2lQ04IvoDWKlCA4bt-(}|9D<0Az_>hN%NOUUa2zGGJ(G9`Wk%C6E426+B!}bnNGB zeM!H{>bT1w^yXtWw7YB?hzpo&OH2bR!Y+5|_#Z6|KA-^TQJs3T{bo1@4M=|~Peg|R zPT)SGIGM>@J{r3($I|Y6jZiJjk^wLw_Qz*c7wNtC$N=Qud+7_8;5=gMBwQ_F#;Z$U zg@olYm7r<|t`$KpACf9EfN=7LIcy)Py5n-YS|2CLg`8m`Z5=wfuH5qz48^gxfu_oa znZw$t`A=m{O9PeF_KGMr=;Go)pn`VRV_8X0gD6aYRn;U3^tlg9NsZ&|uf;d=Jhhh45EZkz$nHOr^2R9!eS|tt_j_ zd(wf-EBc%qRtIec#-l;QgF@7o!OL*zjhj&@-Q3^%5hZS=ugohn)7t zZL37@<@yt4y|_y*s@Eibcj}((L^!B2$T_s%>~jWB_SU?^Rk79H2(?us^XZDZ(Px?> zWpmEQNAsz!CI0#IXL}$T09s&R5N%uOt&Fn7J69;FY!;q~nrlM%xp(c`-DhD~@Jo*H zpwSq+T1cQ+VQ7*6r~4*qStCk=WAA!lV}+0vg-E*hD*n!+dL$5l#EJU>!BR`~v=u+pQq6X8$OBRGtg3VNG+Q z8j(|{R#)7>1<^4kPT${2)a6;p&qGvV1tms{<7wajG1QHN@MeOVkeZ6 zb9R?LMK+QdwMCSqmExq1_fEUJ1go*+#W2u9h_{v%nS49z&=OM^;BKg1(TKE!JAs*ErBc5g& zM;&J{I~PEl_M1!vJ?d<1+4OL_aE*HVMD@}+TO9d&_m~i=jqCZEise8BCB<(&C;cp2 z_aRpMl%A}OCah%#tMD(K@!(`?jVERJ*Kbm;4M;YluMdS)Jm7%i%zx^%t*F1ZHzzl+ zxL=8T1&cwqLVi+3yRA_MMGh;rb}%pmiGZuV9OCdyU){^EHwzKi*>&ObcKF!gBT3jH ztgy(mun!8B5+;dhHns+%M`4BV9oMhSy_eeq3a_?q0<@SwA=Y$r6hBWYIFzTqTwh#q`uO&~aZ0%#~T zF+3euMO*QwO}-?&$;HgdgaLdDabf)a*^eyQSjMpK?E2a*?dEmPOq7Aiy zXTChji&BJHLF9R`OiWB2R2v@(o@VpSb?`q1n8yR|sQAxvxZW1C zB?_vlssvZx0RUe(3O_a`26TVzOdAmRX%<<#VDTd1lQ}_zvSR~kk*eu`f&k`F8f4N{ z%1(*2y-||@9%X75nL_s$AGk(Kg3YI`EnEj(O<*Mbol^8e_ssQcQBk906a?rGzSCzU zEGsay`r2oY)W<~XkNZo{&>TO@SmGQdLijq9lqD=BbLVFt4Ok-Ww$Bd9wzdSydPOue z`lfYRWvv0md21^!IeEdjDj8`V>%_!&RPsZS{4yT$tpXh!w!ViGN ztb}>#DAS6}b1asHDe;jx^TZOEct~gD|B^bf-7w%rupViF?OtD@#bUo@DtIJKFV4-# zmQan1DPeQA!hrc(oNZc;RgKEf6w~pn@^mQI?w}^-v7UFD_-B^m#hBvTXjlSvo5Sb0WPJfapDcRC|>`r?MR~G1#f`D9<6FQ9T2Y07mKRF zR$sZd8}yK5`*ua6uzq@4>xK3?C_7uDNt|Tvj2cYq0|nYyNXl=$1U}DkL75ny z*@Rz?e!!+1Xr?IgKQ#Xn5j2XL_&ovBBrz1+dQ4C&NtJktiIz4k4TYTRBQ^=NScRl^ zhG)5A-r3PNpV9vqONt5#SWhl*Z@(}fMTS~>-QwJQt;{?CO)0!htow+GIJkg;x>#1Q z)IAvj(M!WiatxyhBEK{)YE?|7EE);sXX~JSG3>*XiY!6#672hm@&QjoJ{+Nd{-?|E z{Q$UP5@xJU(mglf1T**b-uT)0sE^%xyC6h02}~eyXuo{Mv{bqgBfLKeKv9|lWHlo^ zbdJSGkh#Ws$h}=%ukuh_xJUq0x4mfLlJOF(oNl{svfQPNHU8>yef?B-v8Z*E?0442w;T9p)+lTW(7Z zNU90ugYZv69|R5lMzXLV3Stcz$%FX_!d_PWk;J1R+ZbeXWS&0~gY}VSRP@qDlS9Kk zxRk+&@bg|w_ksoF+U8MspP`4RC$+AZr)RBt#k;Dk&9x4zyj70?M71xhb8N@2oak2S zlIgh^?58RME>~yvikf_as+o43fgiR|2b*o7M^ka5?1vwk1ANNaD$_233@-BXcz$un zv5w^?yKU=V$@{yKC0o|XqrRCL85vY&>72_4;v1?dGRg^BFyL^>Y(VCZ3v>6!jUp@4 zwu1cp{LX(%e*U%}eL>@^!<0ZJ0tlh~iAXuoJvXRgoldvC`R4XJ0v=i1*7Et&?Xe1a zEZ0|l9$}@q^FQ)wQKLSO7Xe!)hj5hM*9U@^;t(GWCrDTxyeU3m4uzlh4u?f~Pi)r- zQlLr!r)%){>}MoJ3_A)DUdqn$eVO1T3^Ry~U%F*Q# zp}lY@%hxj8k?wNw_`>RHy|HYVyszf7#ZdBH--Nz+BuFb$(r^b*6y^n{*|C@N;E`it z4foyRXD!#%);|44J(^dHfU>M~i*op*(xBMLiGVQvq(Y}zUvhK1)fX}H7FJSBG1~$j z^)yR-`v={Yi}UP1`~{-knM`B^(*6|hgA3&^>m{$ zyYNRIX1bfT{SmF+>*p~4izbahQhuLzjWosljC^KZd3nPRLk^a*i_?*0i?3fl5CwSVSUmj> zh66F`cl%qOB~J_uUEbf@bvG*)T`{03gdO=gS~{D*;m#hde)G2ZpWBQvn*1!M@nM{v z{|HSVUbaOcyG-7LEu-)z*(Mo5;S)yo6HQ1>t z@LqA!ynsjgz>?3g;^-(Su{5;ZQ{8iAs?0ZE4vs%AH{A;g>5;+&nzJE{x+%P`QAlN0 zQ;LX+cD|BfBd^eWA`?NvEV*M1Q@KvLP!(b4sj)w%oc;3ISZ8A+@cD^4~%`jU`u5)r54B5E+I z-HC8_86`|2=;JYmgL@0B;J1dOjx84wJrk6G?*3!L_vk+MX~7c@skv`{b6Y?HPB;To zXdwgsX-Y{8oh`TTW^W;jHi6E~wnM>AYdYWKE?h}u>f7O6r`O33?{@xqbm*84?bEZy zTKm180-e*x@VT83N-8JW>y&JsS3}JH)fC$Sa1hu1vhck_ZMWF~6gK_z5@9q()3Ek70b#ml*ZfqWoC_h4XPQWwSgR$GI5t&S0JRO36-&N<$0N422ITgdMu*VJ$LPzHJ?I#`ShDh0raWJd6ikHZ=ZZ5ifj5Cj*TVg~<;COptsB)Qr_og_XI?szScRP_Tf zU7FWhf7>MKeed$Mf|_XD_)^RDozRBWV8NW|^+=SDMoAxyh*)O)kAWVKIDV@cNHC3y zgym|Bvy+f+#Mjr-rC=?ApEiYx*w61-7`ZS+DeqpcDr4x$_monLLVTFEuaStE^3Bg| zV^rsWEP~f{!}f*w?8*!vH^O<{lQ_4uwdAmi=FIfmeeZSqq9`RtAQ_M9EQ;-paab@H zS9>s5)AUCyFXRO1a=ozxJ4v8^#H#m|Q)0_tg@)Ek$7U4Zz-y~T+bnW!#T`bZCBlI@6px>tX*Q@El(tvlMYx)fgiP~PmU zbZP`!@z$^JHvG=myi^aj?VL*kXDd;9`tF)c9T!t?P9(|XRJpnJvH2$xy!TFCcWSJL z$|@8+&VL$Zpfd*A-wuaHG3T-=(~yC&ZE!*LvP5)>RpOhltb_?!{Lr7;R6{Cqa&sF_ z6|zEOMUMb=+T@{xq+5DsY9vtYZ=>ucTQl4J$?;@^Up*jmhpRf8WYp=8?u|O!H>c+6 zck+@E>uVfO4+IPA?_O%86p2`9F1!8d(u~%;-A(x(wyEIU&3HbQ69OXc;5`c#u(f&N z9q5ait$Tz7G|~ll)MZfl-+7l&gs(4G&Ca=amYZK_NmzZVl%@pNGP$ zc!wtdsUkDHXtu}R^_}mvLpg$jNGDC?A{L45Jk`}`7-f#fkJ;PqHqKWlTtk(_QUp*p z};zIf2aRn)kfpMs;B52HUbcr*HoS(?eOC#e(Oi-yp$P$`H zIuSckKY)mA`697rr|<6?dIlDqiS!0${mdfUv-+o*j4?B~opEOH)5zy$v}L?H{M9Po|pPufMt~HDyeEu+;60U`oNH^HCzyEn{5ED`Xg- ze90#|(heZD49ztwB=x9CteznC<2PgLa027wo>2WF$ zD2^879~torq_1yW*$2vjTj%$J2Dv5QMfF;c=t0J$WoSHOe38WduF-TD>%I*sGZW5F zr{(r~^*oI{CzQ|k$&!lo+_z7n^=-j%7=Em1j`KhWAby?sZtj5A*VgVhK!^1oa9LAu z=uR&Dv4l{dZD!~ss<6{mlFnv7RTUFJFZTSAp!)XF=J(468PnEH%@A+Eon52j#)y^bG2p&3|Va)f5+7;0A^C>ZI7J-QhOCBo;7`kK!K1%VcEOl5^S4q& z!C8b)pLfwiO@gGU$?c_SV+RX>TLvovhq)yo0nIUvqWoY2-~*wEhfvU*hlp9MXAsw4 zuCa&+kuGtK2)lfiTlQ^@WC`qm4&O<>Gf+$2GfQGZzWnDM7WQ@HXBVuenS*)hZ4Z~( zUF+gbc#TD776INx{f(ZQo~~nl$?zb|%GST7O%_lE19jGy+0F>_AN^M+RyX6Z!w47l^FKlpK_g1>tt;VlA zu=`n;I%XAjZ1gr3znkf2LLM$cKjVEP{RscMfM63cU`zPZ{^26NDIyIa^UQR$qP`ny z(N$&B{5Y-tg4trTZ>s9y0jZ!S?(eYeb$9Y8g;b{rwc1|On+efJ$|@oz#_yYz5SK<} zq{ehKbNFYW7N`#HPXS>9EnpY}Dsm}U1RtXv%C_2lU)!Nj0+qxeMuKLW_~XLN)Od)g z5;>T>J^5$)JWkw^%7Y;7al=hh(PdQFkR0>4ei;sJV8?N{FPP%M;mxL$G294#X1u($ zA0}N8vfod#9XEe+4mNa5*Q0O!;d6WTEyeB>Fzob!y1gdGC%1x^E2C}HUKG0)zB6Xu zJVdv^`*>j)*#L+i=7S5n)||ACHU z#Xn>80Q|BHn9K}%rTahYx;j~X+3RWq0^p4%1mS;llHbs8-B*ibOd{2D3mCID$6%-Dy`oA$AX)f*zV_HPmR z>C>n4dM)J?N{omg&TKNn*l7_-2M_On04C_dc7(xN#$Ov7U3-tXuwPb5JB0-VKu7e` zp{$wbG-PDLbDxO&$~gV4e4lNNh|JM;j~f=$vncvDFQe$vOSwMol*^Z+EBTNJA|r?@ z-h3v2MxA*HlFVc>|J_Vnnuf#r5(vLGSo(A=gj`??4i2tN03Jw;5ME$`Nmc5>w_{}( zi(0l^A<=WK1C;TQf-%(t@^WfYS=C3BX4ozy-#y;`Bumc}ra%K-6rPmy<|JiNRjg!3*gU_^gu{Z%Sl zi1Lw?dFGj?|Ff5w?5WS%#N@a!0 zhlLtI6T@o%_o*~O`KHQ2Fv-rRaiGU;4d>fU>#$MaLtwScj;NLbJ&(gWv| z;zI*aXnZ5tEro{wqZlbJbcAAz2tq+$4;a`@V7rv%^>wE9Ke;_@G-3v~8$Sb>H_%iB zS7q?8xGW7!*3Pw6K2LJG0eGWba$sIa99krF+7`NJjxJ**-Poc1fGCcmdZS5=)-h6> zFbvbWTPnvNIXtaWwg)XVrBYqDKcu}%2J0h&x5taDVX)`{tZ#~ zjhhhvaLQ1$xn$YH;_^5>*~pQKvYBtTbs|_2$+xghc3xiG0#>i;VqCo~%k0{cX+Jpx zVNSHFLS17Omm#}y$dSB`RD2r%T7{h;At6h+g3_4O8<{^LnWs^XdZibV$9Xw&(pEGY z&C8#Uzw;d?3H>SbVM<@yw(BgvdQ41ly%%9=3p&$&6c1uv|6Twf91S0fe5Zb=IEAeR z%8t1jF7U7mW0Tzsi|px6?(e*nY%pLRiF-O;Vj=-u8WzhW)NPM zFLVSAQTHM8Q=;T_T}S2U*oWm8&5de`aClkQ%-hOpzW9yyL}CxkkGNrZFSm%1x+E%ne;qR%B zB5H_upad~p)0M+kVL~;x^(U z%kb6vMVcu5&fj0sVBh!i8?==&zM3(5W4bQN@T_iLd~ZQ64|3V4Od9V(b1QM`mVY|I)NjL)wqQ5=qb=Xp7j<;Brikqc@Q}L zw0h}-g#Bp^C0p}sc68Ln^ju{3t3R?d+74b&8Gfz0NEee%Q~BxX73Zz^V6p$D=VL+0U9@Elb-U-Yy(E)xrd%tS3)$6inlHQSWz>8lg3>w z%!N$!v5`&gb}#Ozexb8;5~T^}be_2xxcC8uzY277F?cHgQ|&~)dK{n8kVU)h8P9+k zZYJNK>T)32u>4ryk#(IEecfOnZuDMNumAab>c+GLos0UU?OtsocO%ar-I-EU`d!9O z^On53FIGnm@~YV;ZBx-}vJWq=*LwcRxX-DmEwhXNN zAiJHHs~7hdHq+_1rrw3`SgQJp!- z7W>5Pav2=u?Bnr3S3j9fK3R!=|H3B{Yq9RJKw?%ljJJX;GtbJUChzF`-lMi>{DHPT zMk9TLlYBh+@}`*skTDLf|4N8RWC)6ZDdbR%x!mjtLBe5f(ijn2R>_Xo@`j+p2T_{9 zZE5Rtk|3B_9@iEG*T#`@*eJ5--r@h=1Xf0}usI14 z4+5lraq9uBqaPI;j*@9?mwEEfpHNYO$Eq|%)-&+#uJ^a{W$_r#0ppb+az2uZ>MVhX zA$`iskhPb#Vx(bsoPIZM^vnH^VidNMPxlefw(sZ91R+gpTs7>DWsmza#zQ>#`!y5v zNN)7L-z>N-E6H3I7#gL4QCEDspY@par_}kb7nJCjjw5ODQDhU@#15x(ov+WE0zCq; zZ+mZ#l}$#4_a_5zJvX*p=A(hfYrnL44g5#c1;ZqoTx$H4@kFeIt~QrV6BH0#B7zfZ)lB=bBk`RI`IB1t1Ao4Dax2sVq(Nnw9(dvl}g4y;3>(~(>R z3KX}$QiN~mLv4Ifc}Q~xYqe+#X#o$6e?KV0pKcfQ7AiC#KqE`v+I+*_th9mQb)D+7PN0 z(Y|P_g+je#)ebo?`b>4MULl81b#8bJ+0arQE4ODGHM{=$<-bl5QUHkmqb-;HjNC3( zZ$N339>MWShn|_-d-)n0!xgpCOmCEzwDZKE^{bE`iwQT2u0x@U@SDd9oDB5``G`Tz zDyK#SPoCd(MqPGX@Li^sLmw1I`z6G98sGO8ymqsE*?rY@dqSd#qrkq?Czt2W6w3hw z3?~T*cn_nU-T4PNOn5vt={dnK;Mhg3UHfQwokU+O6Uy=hENO@-pWp^6RLY5RH9uVM z0=3uMag$KkIh3Joh8P%=p@RjE=3rQIFm$*DLq=)R6Df@HhF|>1y;Ap7XI0vYN2+ zI?k?6>S?_TpHJeiz)ZZ9AUqFPHJkNf&V$qA)ouh910l1qI03gO03)+<*0LAR+ro{| zyGcYIGn4BlnMv7=FwdlbTOko0TYmiALLqA-J^V7(WPM1*-QZd^{kx$|7a7#!Q8aEN zLsQ{D47X9^KJ~Ql_DkocX8HT;Es5FTQiG+IOexItVy?H^X$Gg0sFc*m#Dv^sF(Bnm z`(hRN?VAFcU6i~Qcp$5TbsGwpU!;8cZ-;NTQmRdj3a$*e<6o{1S}qI)7#Qe44`~KH zYH#Yu1quQ(-;YG5n%fNf!fVlz8T2|W=cs2RnnY#v56FerawV}8p^A2S^h=^+r_-mJ z8+pG!dZf;K-q=72c0vxj%}E9K>wNI@^P{7y1Z;XLBEiJsbikW{Y6kb~E6%KVa2CK+ z=(a~G=k>x`zo-y=Q1qHALj(b3U#TCZha#!l`MOEn5}m6tVodLlsOFX@3Yr&uobP$owPd}i4k+11+M;NU$G3ryn= z@e#@XKe6iZi$_rXIk0S06upmXRXR{bg^nPsG3kue1R7JSHG3LaB8{x=dYBWftk zq$D;vYv4c@6PxArDtfNVt3rVOuXqkEy5I1@CZtb7$(FBO@!#RNk59@Jr;dn>RCb|- znJ&mpG6g>MgUq;_2#JE}UNL?aMlcs6EA2<4y(=3&@6i}>va%l^1k&cG8})N#C~pB(@W<_b^u~7FS_x+zfiXvz3ffLrN1%QWJ$yolm z8)%T+USw&IBI)`{fh)3behVo*ZnmQ?5O;N2vgY-AR%YX7rJ`!{9_GI77t57Txi!ud zt1E3{20Gmp5AG6v?`-=W=y~A1R92dyRgU?hVrM~EmpUy!a+hG{*bpC`AxG6&q=@cy zqKOis<~@nx%oj^R8~eKBXX6gKD(IFtE(C-1u#h7fDCSMeZm_&!@-$4eitQ!f)}5?@ zX4w&NHlUj?nYkQH_s;z(pnS8ZKN6GbRJ}KOd!s;%AcyJvrfNjn?vG`3BO&ejPYyY&4}vvW1ur+Lfq8ga) zJ!g31>YkZj?OiP$kTcsD7F0AD67Fx&h-W)ZnVG3fjj}rbus?kiM_+deg-9ZssZ@|H zQZsP&1#d;7EA1O1)k_StH=$6?&(lF2vE(K! zlOX;zw6H)~Hlk2DHX&}fl!S^Lb>w5OVT<&m0>VP0lllJ2YM_|OYBOOy%ZY~L)~-(R z`=ax!V0it}cam1dx2YkScl@`{RJ>d#gFjYkme4_^l$#!!mge&2()8qBSZ+*=5BGo= znCG3ViDQLgU9T0j{`i*_6M}%rMwg5E!0DoOs-dCx*7cvt;`8*Im_v5>Poei^%L9lSc=(Q%ijj^9QDWo1PX1^+y z`h3?>bOa#mc}=UXr10&GmRu(z{duuaAv2#F+4Pka8Y%DoXzYc;+VF-T&9pW$@O1Tm zK~0Ut0@TJ>FZTg+Gkapo2&V?QL-!2k%|=d18&Z6)JrCV#ePUMsXS(PeVV#<$yH&T= zWgUj1)UvrE_ROKK(RP<{@8+0FF8L89xtce8cou`^^@&`LJyOrz_*k4_1A{3pd``A( z_=x2HGY=dV8KIe%!YD5W21;9eK zwAITj5{Wp&VOUAYNpzV(@kV7?sokK2oI++v`eca${5W}EIlRZi|8N=EuDWUrM4SwK zb;g+P-zvtoiIQVciFq84?c6jgbV0bFV zWmzzdHQL{gMud?qOs0(TIqnWFO+DYc)##(zO@!bBiF)``xt!6XonEi@$nj@0^QYc} zvGPYJgqr=mg~9C6?=x=q{NFNYTvr|>8KGwK3hjn_q%exK>TmjR%HHWpR}Q4^&o`<$ z!ipk{nIrRpII=`$IMG$q)dwl5Q8E86M+dX37X@=yUi?~!Vkc*YYpr$JbjhR+Zgboe zsD1A~Tq72SzKf|d0ys@OJWuRkL}HEe>|j+~h9Ez{vvd{4Yaac~SsOL6akKV_;kF2Ju)#Z9x)TbSiVxJANRRh9>0EpG z@V!`S-Qa+;;r4P$6}^|4-Pi!s-ALrd=sFlam=5@|Ud(Kt4|_wEHbcH^Q9s*Y@Gbkv z?$nZzvnH@PCcL%)un-jJyMT)D;^jmv`lGM6 zVnr+OUbq5DzMji5Uou?O`Z-j68%bCG-X{a`sBeq>{a3#R#U@sa*D~H;KehG53ffY& zK|jlJ&K-Uy5?K0LfvWmgkyO=^`ez_obH=Q9c5li|Qu1f%jTTvOnA$sx;W?kZb0{}K z+fkb%oaosgEPwsg?)=3CS^9&_YiFi?z06Mlozh?aQTZ{@jZb~OkZAugpyLwJY>XO{ z&bO>2IqUKlA?*>X*?YoU`6@>NRjNTrJJFeT$@3ZXt0a=%9<1&o0L@ zD^$OC9 z!S4F81l53cbp>~7Ui}l*Kg#sGSQe)P$ph0wY%7X;Q!}@1qSa2z#JbDBjk|lVaSV)} zS}4sa)haB9&8e*(J7j)o{*_Vq)?it~@E^=Wg8+f!Ntfjvc4c7!M570j`fz_p`}z6h zCrfd45%#~EuWI@27wd%6`jtcHm`ftPx}wgk6}ywhO0yA?V}u*gYW+Uhz=6=_qezY5 zr$E?zd}^1`KMhL{JDcFgO$PfvvZJ1q$r8}B1;WdV_Ri?%E5)9d963$TsyrQ0hU%1+ z9VR+lH@xqa`;66aq|3KR6iw3QTLa4MW(8v2D~5L4&;?`AV_1Q2bh=`TJdPPUA24)< za=6CtS(Ud-@IP9bvIz&!^jC@^An;~t&%O-SzpR-0h_cBC>AAD(;3C$z>Ey|>QteHz&j`Kem+BcLp%4~;b90~rX^S-fIc z#H}jUhBnq(FKS`@G5a$-2Cr-_xf_s2mb^ z{Y=!!{Fz37GkA1)RbDjg+JuJ}$T{q0t94`CHQa^!L_J4%T6Rtrd5utSFRyx9Z=|N9 z(<{@Ndhl>?F*XUYApIGs!9;~x)uRKwtSoKoyNa3vO)e~s1dWF9Aw_~MaUiRq%-Lt@ z*q{tZPFTmd1T=I(|47Kd0%J<9pDfNZo%KL1J=#}hdOijmw!2Tj%nejFgm80!U@ zU8#c%V}l?;#C@nZp!g9=y7VG2AV+2iqNQE_D;S6zV+5p*Fq@j2@W0qj5eR5`42&Yu zaOozMa#gV(_^|Ns?GD?b#j=^y6W_O+JF9Hfyu8eEV-iyi5;E8NfB#-yUheG;8hDO7 zM`0&uKK!hHv{vQz{(}x0QVYS>&T_5)VY~oQ2g-Y;VfzOCoroI-^j}yDy>hvn+~fG> z#t+;te^yMUYNPkxIJ8=h^>L(aUXIi^&R)5^p7c%|Jz~`eAWW7J2**274fs3j1ebV4 zvDsDw^@f|x~*?8Lj1=KA+p_tSqOx+t%q~aVSf$U}So+Uo=iFqWtLN1pOEP z-4w3nUaoF5GXsEljAL8~C(~Y!Spf=8$*c0yRt>@uSwl_-rms&g`P#SwFYaL>3(FiD zk6(X7c4AmYYmPdSD+eo;E|>OI6t#x_FW{=ov9hY_R`GCzx~w_dfI}gMAs?)_`fK){ zf+VIwE3661@0*3guizm6K{>z7EIIEiO{E~1JV}|eE6D$UGTCY&I~TLL=yb*Yy~FeT zk3`d@E7h~y4hUNGr1e*8*OtejClGsyt(6-WUTm`;Cs^<)QNw*R#9tC*f4a!SaIT z*SSp$z_Yk#1PH;i*S?6<&2g`L4!dCl`5U-V6%KvV*mz%iZ@zggf0!k)ImVP#*hhY%w=_^+#!ZPaH=ge^OdncIRThELxLk_F(gwYDxkc`qwb48vd>=Vd^Rg0@R3X=bx-_uEKR&+3zg#jL?QgTx|M0^vgkG>RMM5F_<=z!pIpfiztH=COME=A6#HHVCtbewdsByq zsHM);i{pYIqXk*dgQDtLF1}0d;ksI8g~A#f6fhAtM2epZ`omaO_nImt*Z6_&SEOc( zJ#n!!O2FyLpO$ZhAoXk5?S9W$rFxg{srfG!n}zOE7glOS$@Dsk1?YfP^8)Cyu&W3U z)gvxf9Z^mrh-}T4-m^I?7lNE&wKdycFbbIz@J&rDq}J(zPRmmZxRX-TU~NN}aT3(oD-gOB28)yB;}Cp4C(qC1M-(VNxSvyj?^ z1qsQsN^Dc$(+kExMmaoSOMrc^GnafMXG*#bN64iCuK$_&Ntr&SN%k-sA#CZaU@8aWwNZD9=qNCI%|t4&1v{qQ-o0F^bHzFbG%+|Q-lDBP11>~ApMBeU zjl-()8wxuO^T<#^-IEOV<69N?Uv=QTtUALxg_P8Y<|>*s6VV0|Y1EQV196a%<>zW$ z0Jpk%TGGhw`Fe-f`&&m>SJyOWxug#^JXH1uWtM6bp0ieMYlF9os_K9rZyD|&2lV=`#IFY`A?@Ts~ix|Aj-m2M5MXK0qTckk))4P%wsq9GSt_LyrL z5iy~oqF%|ii3EqW5K-FjeN%>?5tw29z$&9uzx72F0s(k7-u#NpK+L&ND+Bq(N-W=~+AUThP{?@ATcE2Nt`q;w|@wGCUM}2Wc{I!ij z6O%$Y5r)k*T>9(fdxzJ(YL#|sY;2AF=I`^a_pDzJ-W^J@gr$_C1>@A-dL~`FweM;W zJ*fPK4$#c7G<< z9qVk9a1U`=tZXJ1&=XxL_SSZ_xyErmf>=C0$bu6ITeHtgdxfya8V~4KW-*O zpEjW=`P|Xn+i1iTP*6;%hgaMKs(vsqFd#an;#I~v6qkC%D;M@ISOghz3R{MLLnOvi z!RKy!|L@`kWC`H0+p;PP|AkxwDHnFK*aL_W`WF_|0PV@Y|NT`!#CWRYVnM2YVJ-gJ zWV_b+pd<3A{nGSN^`^ME#%le3qDmatKSGtD{YQsh6E5w^Z}X7+#Z(F@g_+IedO z?7-$`N|k^dog(Y}uY?^Y&#{Cb;vfDDlQ#tDDgLu6b7nvl8Tv7_aQZV)lZN%7i_Gz` z|9h2Q4%J>;ZM)UgDsp&SITvz47^(!>#PsyE#gl=eqO2~VM4ZXS+QN)95PbK5{oO#D zAV5I>q+?HG{x)~shkhqaZ=)SQUP^E?v&NNoVo#+B^{{T2PP(ftL(LH@hRn1kfA?V$ zcz!O8%*kS#GSO%&b9woS+p+x<5`xh}__*4U;&vrFZ}gAB8gla< ztm&RKLX_yf;o4^g2meG5s^gU^aBq^abEdo*nE3-ZK!iv&$7UkMi;wEGnj@2UC``BcKa zi-S!rseuP-y-ljHzYmp0IeqtSIvnSQ{Qx5NX>2@i#`|&oZTuvCc#YfnM9)qFboDyK z1aWWvitMi6Id{0ZK_Ffz{UptTq=`jQu_hL6j{LvC&mu`ze47m&13_Nk9B`E(wq=FS zQOzEiy-g5d1(xI%C7z=OPt|h-{d(xupH`nI+S;;>eggGR62-|9lM}7gPXSJR$ikXPJ)x`rjJ)oU2xlQM$XU%O-|z=whcF zv&jIDoen-IrGG8y4B=oLUG15r(q(grY6#}RzKT>6WlhnB8hoaX!BS5)YDjX!g@ zjG~eUhmM@9=1q_nsI;aLd$bz7{3g{B^L(rdja6PHDvfliqp(?KDlXPy!CP2|8!t>W zO{%{*3;e~uF;omD@Z@+L3M)o~rNb#p&AxTdbV^WBk;xUghLS2qHSg;@kYLASL#G36 z8#*faf?ozO3AwGQi8|!5y{B&`QjzL!XVbNBXKeGbo$-jhVU#h&gy}D9X`$e_#zWAb zBbV>$pXOa46_8|THpnBgm>-ro(!*kzDHtR8vVhCkB;CE-LbH6I0&M8|_H9G&2F5${ zor&txsnXRtX0d1#|84F%%eo?bD#r3M%* z+bl<}V*)mbtKUuUJZ*~;D?Qf{YFfv?7k<>5Onac1v1q#0>VwZzJkRR+rmij(aBTF^ z_3K8iPX!)5xFhT#2b~A?Xr+vMt?<^-ngXrgQq!}A_%}}k~R>vgg@yyC}aeBKCIcXH8;OE^V zVzC=8y${zr-v{u*;d9kbfq)rcQ5m-<9-Tt>)ksG>n?TIZwV38CnuwgJ2dNVN|6%Mc zpz7$BZqZF31a}GU?jD@r?(XjH?h+&rJh;2NLkK~F2lq{Ich}c^=l*j3d*6Mp2V=0c z_i9D=#OHo>{BKK023`C{+8N=u2+YU z(}LFv&Gr8$xyNZYV811j7%|ssyF>Pv^WrB0v6<54Np6}Bc{}YWPnTZ4um4cK-5j%Sv!0aa`9=L zt>vnn-(>%o@2lSrNfV)IF;*=Bv2BoV{yu&bEj_&{1}3bF3p|jO*6$>>+5nd{WFEb- zeY(?)R}wDwz^$xWY|{_RM49=wVg6`T&*=28?!t5eAdG*IVB-`t+=qc7C3mxl=cyN1 z)834QL14j-%b_YNG#_4Al1&)+jv%`j#Zv4a-7L_*)c&yn(`7c#5efPtZeq=<!6fFUxDj9J+x#4EKH4dq~T+P}7Syh4k@_I+(i5cZL})>xJYq&jlhgL#c>K@@57Z z(`I*0A>1<04AyM7AJz6V2VP7LzVrwt>`3CieIv|U8dtby>r`a z*f8cZo@4KLIU~aYs@YDe%jQAomc!!;5*1B3avnxTQXAF%`ZQD!rmQ^#pv@@ z;=qHZt$K`ez?iR$%jpiwQxmOLMW~VmMZYI9N@Mb7m2Y}e#{9%Fc6!FRFj;(@+H?_A zyoFz5Mio4-I^}N0?!iR$y!bd@di~A@4PLo>%!FF zhqKq$#DR08^Sw>P)Z-oQcZND-PC+yGmMOpWW8<@7GzXbwNC*z1fEkF35y} z&Tq)O#0wAe#F4(CrFu~gq{xJ{#8Y+YY^ITz&MuKBO+G~6uurSWWuiH_nXPmKN~7hc zpp<2EUBPD~>DYwW5LNY|m3|_uP(2;4(oaBhpA@rQ`Oyy~=cFL5;U=5>W`0|4kf?q7 zLa7&nAde_I%fI1)0BId*E5U$tA}{0m@G{QX3Ut0nL8x!DX}S#$i+)E7uCnH_r(Gr5 z22e*&Ehr&)lW{&%uNHP{{=e%3{FFRv_xFP`?$g~(#;UYc&Y1|0_G92|Em|JUA4A44g&lnU)Ev6cef|v3JPJnQ=o(`It^u05V2Rc-7p|5APfyo@7~Bp$&&vj zo(CZKvXtn|pj@Gfg2+hNRGqe#RAtPHrD9##Kh=(R!swaquJ?uS@{RL8=Vup;!ts>t;VOCu-rkoA2#;2oc#< z5T}Vf%(M$3j5|A8IL$!nt4Y&#{*2M!G=&;>+;e}e-&O+tN|aI%gKdRxIo4!7TRx25 zPZaXSLod{5>Gm{YdwZKkPZ76gykK$WQLH}zYX$#L&75vz3CFHd&Aj-qo+9d6y6A1SmeO)*tpvi9fN zv$#Ze-n7|F{jbCgI2-R6R0Aj zl$gr3N?0u&glD9u4|?E1vJ47d=0P7D4C_9*?Xfj#Z73f=Xv$Bgvy*<_GI1-MZWAvjCLv1(vQR9Y%!2od(Dyu~oe7`w)qTZ3I zz0ATkt<{xkd&WyzU8`IXzij@py}RktiOJ_Tnt{W*q)p|Zcvzy+am70wJd`&YqXkl2 z@kcSL$a-LI@R=m2Iar2GQz!FyKK*vt7^CgLt32@$Av``?A6r8sn0UdKGor7o4&4hj z(_wLHx&9}6b)*Vj;sbI6l*sZ*Th+Y=P5L&ofA@V%qEv`~@@H;?FEvohChJM~a;^#X*-8$Yg)9*;<2@Eu*5ct4-N1o5b>DTg9Jn`}hMeXh(hx zqORBW%Iby|`!4Yi;%m5RXF}%2sM|Nz=pv^nGeCIc==V+S`|J2A6IzF0*bI3h0!3Uu|Ai{rtug1^%p@H{(hE!OMu{;H<#_*7g}Uh|_Kf zP%bXzfh%lYYW%ElAvyyCl&t}9ns{L0o&paNdAPjJS5OFL41Bo{3|lZe^9)e=1ndh6 zkb8{+x;j5E^HPVhq6P%y-v$XG zkmF!&Y)dRZXeB&bN38bKO4uOD-pG7-+gXhOKG^q7#pp9ip_3)Jn?G)hl~LMANm*?$ zn7TKgd?3u>PB*rtrKQ~D^Ec-=6pvPsJbBIMx?GVokGg!P4OO&Ywq7tqIA^VkUKRln z4*XGi?flC-XzN2YNQCrt)c4REEzn^!Ddq!!G=!>~D2#GECAdZ%^FQmVHkXLY6^Y~d zq-7EzSLEdFZS3t!D@F{N1$yML2W*MhJonwE>fQ^oVV-uMjP?>`{=KeR7OoM^%as8)gFYt)igQi?5-A zkyd^3>=C|rTRX$%$oj{T=78iC^IhRifB&`I{KTV3idZ5<8YwNe?c63XH;0^%C}Pla zVr^kjS)!4CF`W30swx$leCun2QP9zte@|>8@-^)9&0P6=*Z|kt84Y*ZJm+$s7V&?9 z3Y)wHdnSqrOl-ErYj>Y8g;LIXn*G+Gx>uItU%{M!N&uWd4!A(VLrvnG=>G1GUbEJ4 zq1Gr$kOiT@3Kt}wBhUpjM*8vvCRsd!IK9m3+Je|rik&yU7n|*l=lf9#^J}%Hay9xw z_aBq@)qU?7pbf{M9rYsMb$f1K$SCNJ0_IG}F6pquEuK3n`|F{UW-AIPh#c1Zvvh8} zoA&yla?&acP^az+ZpOspy0ZM#mSb1kT)i?XpBv@`e4Pw`CYe}m++qh1tXtXG8=uT< zmNYnQg8*`+%$gj`tJ~ca3?LpyZT$}u?SD`(sgL=3owD1C=8th?9Cq{mvZ8Js>U8_w zm#bCKUYX@n(-IRK_D5lsN5Oq&*O?ynkn*AxkqKb-%%(&b z)9ZLU-*{jBMAcpo@FeQ#&s^>CRqYsq8vrTNp6-`7wWw?jgUxCSEmGd=F=ZGt$0S25 z-g1yx(;m&zK}SqCYq0?V|5HxaV{G(>jkqp+xevyAlMZXScnj8_(<(5)QN|X~{f2uw`dHjIIV_7)F4;H+`_i1Qo z7;(DoSX|U1ltOZMPjUH#JvFs;F=&u=x5kErB$I7wv(4q}eOL2J9{JF<*5|Mk?%?C& z-)MLFxC7hc>(e>X{`~{@c}6?oqrXc%Tq~nW4xf#d)2p~~8Wt#!R-rv!vv_!o!==dl zq<^&XP=;QiQczFRVV&YDWUBp9z|m&yh_Y-aOjF~xZz%lyT>w#D!=TjxEC4(<>I-jD zt3WweaHTt~Z8@selEDDy+_X3tfG2P(8>O7=%1ml`CV?cfqp;%HAr#+Y)tg=MyM%IrJ5>$*9+Pj^sN$ht z!2mTb%ep-TO3|um_R)i+)m>v@XI7o zzS*6#XleiK!AAeMUZ{4u{k9&~pgsd8sL#^Oj9bjx`73hHAW3@rWKj9gSGjWP?#}zJ zt^~D+fR2WNSM|Gg{`YEiLW$3(5Ay-)0n9$RZVv_pbCv?HoAXIMl%Mb)xvydzhc{Td z^bD4pNhh1vjHW{U*H>_!C#sO6lTCh2qC&{aM@2<7SXsfPLk9}CJ&6hj(=ts=4}Bpg zdS$~hcrsh;#n1MuX%R$PcBSQTi5NVm=wQo%S{rUH3d!`A*9z#Ktduaov+v6=ZnTbq zn7SI%yJhm-JU^TC5XtPMJo23Uu?P)P#;^BI9;}|kbb347qW^xlAVbs}p;}d1Gj}Is z8+iEm?pm+;in7hywUevAwF2V!CN)L@uh093;vgYxgQup~7=UVEx z?)qEdATJB~qp~CM@EZa97^hpzzl@K&Cy~`p6TIR2@4bEf9e6t~E-p5$tdLFX6JcS^ zxViP@)2@00qyeCsp8p#D?2=&DcxP=6sn??dSLDUOPWNW<2j7JF!}#j2Qn)FXGg(I35~+ z%|@|yn(gqcbHN5glwG`1$IKxhKnS(G(Q=6Qo#Ng@w<8g@U z9+f!a+J`N ziB0FWBXb4u0jDFoY=71!_STF#TDvtJn`^G>r;nOwulmoAI*u2GuNf6h5bz*z!WfSZ9s1^Tq}YxON8ae#>l6Ap z^Zt@}b6et0rh@MO-~x;i3ffqcEv7CVx&zuYt0VHN*Y9+_*!;Ago`v_ePL54Sy_S#w zgvV=Od8`YLT3#lta!_d2@Io)KJ&iPOnR#C=&{FxjW%-*i1aryRW-u z=7P^LdHNlAZAv~8-Dl)1}xnGcDF1_A|69zJe2{>VecTf6th zqTfH?(IV1+k&^?&{SX!!6%|o1o7XHZD^IdC5!J_<_gaij_Q%0V&%0S7HR0t$c;qOS zP1=m9Bb(<3NTe2cn(dD|>N-MjRqa=`Kt3|kLP2U)*oP>GXCex}bMRN25792x z0&=K+EFi_*Y%jd*`FPw?adD#K&^0?}_qjBLEIl)8efE0y;ufG1X?S>r9Z5Q&RvA52 zGK@k!na2VOM4&1@UXG4Hx^s8tt=VNJ*$Q~JhN)yyWUjg|??Ew7e?vNo!u5FZ<_)?F z;KAM^7J|RaLn9&%?@&FP*1_;i^&$~S$7@}W6N0a{hvEO6tye~nV_R?d04-9+*f!tp zNkbTC)~YQojgWJH?D&R754v!qAM$VbCNz>zMG4izo!|EM_Oq#c^Hs^H!efQeUa5oI zXRVpVBP5|H!Z3LnIgC4cz@y_qKwHhNTt%hC9V^dWcz1_P1cqd$8W>B98~PI3fI+A> z#6KS_9ozF4_&U$0(GHlVX1JoaF1*}*y0S8Kv~&g3Y1G5iD7UHZx$Lo&)Z9E(dXO2OKguUY6uJTGSjgZnzDydz^CX39$r zArz#5pczo=wT|~il=PA?dQ+#E=2$F_kpBhfs1cq$+r%5x^skHjPPjCruQPfwx+2>@ zJuQ?rY54mGWKTYQ0}BhrR3AqEXQi>Bk)i5w0_L6Xr#nd-oikU#5-tAhUp=3%lVQtK z7Q;$3b^E*bUF4Hhfz;w+c(y-vvUK!Disy1zHp4gRqPTo)&lX3Kr3Ij54d9F)Z*ODb z;Aq`ez*xibykNfD6cDZgT`~#-9e$4B?tBAyc}b*hT%6IOL`zs%%O%$GH~$9gO)TH& zefth59X0q^^v@wj&A+(nL0^176`$x~1d`!{^dI?8)A2ugO^quA^AX^8LQ*mEs^r?K zr+J_AOlV#&-S1ycq_R{wQ7=^i+7H>>E|jFC#(+A!UWa@COtf}-?4Vz$_6OiBe6j$ zOu{g3^xr*Dt@+6Vr7NoZziNjmOO>nEC!nRtsyBree@u`= zJU9l`0LQq9l)(z&g{M?*tH|7XL&`OO>`Hv-H(JTD_vWJe~&dbiuw zV_;1Gns&ft0H)ncFAD<={o7V~vRPuyAs#nzYGbCB#>{l$R%=~#cFW#ZISMUN zMdiB#UAT<<;UY!RP*MN1IDnh&Q2}fB3iUR#07cX^JRC$P$1h{U^>xjhtgNhDT-Y#B zv*X6--Y2K8xLodL^2)-oOEZhpO5f`ObfhT|Db+V`i*DMps5#tj>(SMoFQWwbGx;d5 z_1jlw`j+*7y$jq)@)E8AH)r|)O>RVdSEh^bA1B#{CoPIv?JrMEn-@^6EC8iNmVMGt z#SbA~*m>9*ho-J<^W{;UpWg+kR{%jPA7M>5Nro+?%|!N`THNYd*54aXy?B$`Y!3JY z-@1yLR`2IA>JZ#|K%jSfBo!Htv$6^=7)5xmsJAm(5f51Qh4X5wbb=002?$n)8a%Cl zBkqbi^XB+Jnqg}Rpe@%@J1k(0|Ge4qJa^F#5ilmdBw(7@0ozLwx|Dz<{&c{6LN)`( zd`cRMGR45O-8{imQTQ+8FfI`etQteWoy!7h3f6A1w|_Bmh)jUVO9_7lzCIKCucbp9 zFI@Ova}7nflwsfSZU3zQ>>qee228=XB_fpP?WHD&WZ`&x4;3XXscBc8miNHwaw{SL}v zOOJ2MS2h>nE&bF}6Zjo!OG2gU7O!j`fnh;uAr)CHV7~Q3^f1-u_Zg+T+odt-w#RMM zpR1x?$0L~u0`C57R?!$+wB1+BN>|(tHJXM1gLFfAGfisJZG2=eNEMX}y^>JKQztos zD(bqb{1^X6E1v;v`(+8&^z134KxUlr$=jm1eXaNxlaI^rY+i&j99ser-|*V8hvfwo zm2}!e6sNWVyrC23WAOyHnlghygY6+dYt;<#Ox-#IA<7iTKbG;IpXco8T+k+eSG4kZ zXnVEC<@GLpDnDPltFuW%_6e*}VNbI+Wz!RL3ZxY`Sv=JzaISjXB+huu<4t{X+#h%u6hP{r@BanN zh&{odj};^{vJA2f_pmUlT?@RZOC^?`&>c_X*W)ycP9OWvaF_oZwtFFSRzAsUs}mx#|s zPyI?`+_ePI+13&;d8tT`Z)-fGUdQiiv9f)frF>yKsk%mCcLS+pURSJ+wnWp!*O2YPd2xu zHtY;+AC-tWmmPsWEFv%KP)FSN#Z-0D)?+Mfu)Y zHp%vJVSEFD=z~xdRPszgl6jGYupptR4}R-t9nYg}3{<>+>{Hp(hVW8OLkDVv9l{#F ze!G>$X?##qR27Tb|x3gGjc>pt;$`n zL7uUYc2mgxDXgHVjLXiB*YBm!hB6P`s{up72H@Cx-!+PY?*F+OSMY?WIOt+rzZ|U& z&o;D>58;4vN2;rJa4c?9di0mp2*ij^1l!Hdsrqw0j29>XG1ldMF#6BG~ecSm=)T(=T(NKV-0&Ry5F72~?zKZm46_K1@_k<4BMwUtlAE2y;e0NGm%a({vYjk?|r|N)L`;_-AUi@T??aqHWK_HZHKWqn>tF*SiU4j<9LxeB!0#7p;2dEvz z8MRg-pVzW$DMGj+-g}fMGa$Z8AwYPwG)2xZ=;RZNJ0W*oTk^bu^vQGHGaTbB081bI z^inNX6_nNSS-%|HwDVFpNPC>d4uH~F4*k`2J3$saaEE-fFR6y|K*|_lb2&c2=8klT zQ(Hl|(nj;$$+Q!Zu6n?T8TsKy#X8x^!90bW-=_J0`VJEwxo=*AHB9(^tp!|PU!N1X z?y~izAjId!bGptoG9e{ZD_xpd3xEb~A zuD@7cGtYAMs*-27lY2Okjx4&H&x_v*J{)He1U(3;?NCwe1Sdyigw@KI7F~KE9s78y zAD!e4j4dC|P3_cZf;3wlNwV!6tn6OK15vEa7J8x;ZA|Jk2h7d&pFddtuLw9D&mAI= zq5k~DYFZK^fywAj)QiA^22)!nZh%u=a|9=TU2>G4kWcUZxmfvBrb0T~onWbs$OVu{vuvB?ckH9q^lYu(){D7&^AWtTn<@$Uy+gyo5CK1C&7wkJ%k``vw%-^w?5FxN{=AXw z-4Y9fYFqB(hD>UBcI)?ic0GTmf78g`BH&c`OGaxg zzsDPXmmOJA(O*E5DIk#(Rfhc*-Buw0Zz6v825t+I8Fl8<{5h3m?>Au-zy|~B0f>0r zE30%uQLJU=YYYr}{QYJ_n(g7=9Gk;g#Sv@Mogl6?&w>Mp@#olY)xXEap3nM_r>CZZ zgM-(6PP=Z`d?pqb?}5&nEsc$~VO1^nl`b%O>VW@LYQ~~q*|Y=*=L4$!NVx3Ow6x{c zMnInopagaxUwS}6rA#$j zFrZs5BOK-6yCWZ@wH9!r)2UYi;v=Z2s2^QWtrHkCt>b{cV?Yp4)hiG684B>|JH6PL znQNiG^r zYGYphY7}r;d7r$HgNcy{c-W18LV0-H=H?QEKKa~K0(x#hQ%$_I-ZQ~;fYd)d2=={{ zyEZ@-CLr{m{NEkX>osfntlFVuVtNbxL@q_x0!cxaBXt^pgzmL3Qj*&fmE%EJGF1 zdce4@JXLz7t|DInWp^kAZ^Y+%x~7D%ee39FP2wE%!^cYl`M!Ul6qMJiXtqGb9?DAg za>Ff;%Z!xFb1)kz8s{}?cP1hU6oi$0pGW1TIGt zVUcb&YCwjbzAXbJDHl-!fG)`VP#h2theNXU?KJK;?Y<;>Ll`$avkW$?1*mmygZ1rBJ1ve!L;(wPb@zxUI>l;@$VM&i6Zp6&!ZhG2+#@({(Yp< zdRkJ+smc^$s!Bo4jgpiViU~*cQE+LpH-%s5-7i14b&y}Kv>~uzU!NN=rE;!rmhbY~ zDQiHc{3z~-2vVj|Sa6xMJUAjr(XH-ZU!0a5Kax@ek)5SA85#!`7=?hHwTgS`<{plb zpGmaC{W{+wE~Gc_nlQC`b?YI2<_f`1DX+OuQ=6otrf`^mCwg@yXKaeN>tz`=i~y(W zbVBeJjL|w-LdKY)8l3~69kX%;g+Cv`cP(`(u|y*#>& zt?!jL)Ovc9|$iuJC~^N$>tT>3p~6G-?F^ zrEZ3@bj+m~#WLxjm35S=%|RG6yC+G16liUUlPW6ZE+;EZ@5O|W4Y z+#tZf!tQYc61KbNoR#fGxIdZ+JB+Lxw6zqvVB8Q0{KHK`6&axVTo5RDLJ!hQ~ zNXm1!+b`2=+^8#^rS_t_NfSecHb|X7SYFBnfodX^Y`uzNaoP%a+k}S6Zl#C zNMtGbLdO50O_~4zI^OW&e*k9Hueyv(^uDU3wZ%@Lz5o&QBjr51m%evJ^=m%3q=5I@ zodqD`y!8ND)l4vjf{tFWgKMeDQQfq3(N+8#Q;vM4+rc7AcGT+~e#PbY^ewAT55S^M z#B3<^hDBrn9t*Ih!~++td90(~Y}=VXLmOVtFGdoL&_Gj*!v_lD2j7bn8$w@TH|nAO zD22jn_l~-I)#{#mWrE2N?TFQ(I&tCQTXr^v-@6r0ja&@OVDHQ5H}&Jsybftkz_UKA zyl@oC6a{_oWGz$^R%|w<|40ZTh#e9`?>aHha*{Q-NJxgwnjupr;Z*ZkXn(fL{cB8n zePiavN8FE&WgnJXDnAXtYH?eYx~+2O&E9G}JB%SV{Kgx&Ww*B-PO!#oeubT4uPnB} z69pii)!~jkK|mwzlmC-vmqORe!(kSWoAve$ai~5ImXxCCu?#Ovc-c3b2Dok9D5E+_ z5q^39GUWm@S;Y7E6mZGmPR(da>Cg|TTL4{vo2>0He72`^z|M8Lk)3ZSdzdJ1}I^gXO#LvaqaaEyrMfAU=q<&ZJp*;Trj>_555zuxkZCdH=h3I1xCXhV#6=y@} z4^$SZn8C;x+i+Qg!>pQOeZXy37OPN8hYJ%fP@mV)uy>Wk0b%HM__bv8goQK&fJT&s zK+I8%cbgK4^|VkoklsCOgN+Y#9?&x{--AXI$sp9Hyn+iyO4aLjG#b~;}u`@f%G;HaG^7Bpdm z0CCVFJEA8DVP*>WTnT_*8I~n1roKjec=$M9Za~Z`HzBR8kutA6KFbCa?5Nln`Lub! z2)D~gTr#52WA$Rf&LXqci*hy>bE-dt(ZGpFXwb#C6Z4ByI~)?f1I4CA7ldjgWh)MS z@A%Z#PGjbAk#zAC6JhK?Cqb1fdzT*U)5hAoFj8b*_oyxLK}FulTNuJ_W8L;}q3q6G z|3jNVi_h(emX;RAHGNNmX64E1VP>x=j2kh#m=e2AL6MqvxH*vSu9#w`{i&=@MJ+J<= zkNM@8ALaHJ9n_h)tGSGAa)FC2(0pGo;gEtC#gO(BGJ~=EdiqC#1?Gcy+Uzmg+zvhu zn%DS%wJdOY)qKxqZmMi4TU&nrH4xBD|NK3AEGg^*7g0|w{m~$*xETi>5h3?!gMMWM zRje3c{=~7mIkxurM^&yK|EKKkK_QTieXn7lNIUQ3vu__}YvRJEYp0D;uv6BA0d(!ay9zb``` zZnBN{IyR=4mVsQJ8Bou!8SnC7>n;e@15M7)-=3Y7cK_<@69IyVs-@}Xasz4bPU>ty zD$;AWf)a)vhhb{|XBk1lhKeE~k)WG>G`r`Lx`(mn0US_gPu5NRZKADoYL0;uPxSWF zY}s~zLB?w~+X~o^|Iq2SmegqVfhZ0gQe!-wFjt|}`BmSkVdddkPAS?wGjmP@(CFZF zzR2Xk00$bGfCScG(bFC%mP_!`ixBI1dC05?Vh# zUVU3an&`DFiY$@(L1K`fnQ_Da2*|f95P_=fo_N*|X8Jv*acZxxSgC?cc(?Z+fG!Th zq@cjyfLqAG$h5D7@PTDR(G%|jqc08OC~@rC@Yg?U8(nf;VdS0O7k7O2HQu`8ju%gP zsk!xeCBX9+vt1n~Dt`bVp@elO{n!*lKXEiDqfNXd--n5E1)!L=vK{fq;S`E>i!0CR zoG9J!mZ7b!RdGoIjwWsc=%ZN}E>se|V8u6<`Z{bE3~gMH=EEGL`a3r%7Q3n6{mpyo z#j@HJ9~m0m8m3jY3aLyj(p%kKI9S#pT{AmJ{yO^l`c~_A!GQz>1n`kc{s}L&*o$Tr zbMu5mln@}b8A@p;l^eBXY-qG)Sa75AvdK=-$WtM6)h#VSlPN1Q0lBQM*DO##n~B{} zfhYp(mzNjaq3(G*EqW}FE8(Y2_=s|xAlT|Wy>0N=qNG|6R8jRoiQ%%`i|wh6RqD%E z{s8)&sS|z{E{=E|xdZi+p%7W%OpMsWT~_)32N!^^>mW@FSGyz0j0!R^{%b{cnpuMf z5y{Mws~4XLLrgt0SqC6IdT_8jD=!2ADj1a+(RjdV4%OGBF$aZQFFdyYjEv?#70(?b~*=u`xAg=S87bu9~W> zY${D5H*L3y6`Ec=tB8w>VO^zNq+C7&SEO8}TsBJ^CR{dG+K5{fZd?jIz$P@i$YvHP z{CKs*U|+JvANtM5Z9abVimn7ZR_l5D0^@$uqAFaDK;Y5OM3Sd|M-kS}aDD=wm`TIl zYS&iR*LgM@jFieOngwS=9A^G5uoFE1-0ie68f&r*LG-gQg8FRQxk?_?bn{{Q8pD?p;5iUFsEDut5G9bB25F*PC@nA z9_~Ve3oIXsjf?Ga+f}zc8e5_|~);cUezmxq&4+|(|OOk6AUCxuOeyzCSsjD3sP zRa!f8RSj{u<{RClT=o8dUD5Bcf=_!+T4G1^T;OCWs~*G@Aj=;A4Jb8tAV!NE^`Vc| zfC%K_F#d^MB8`(<)Z+i@>nSMs8dT7|m`HE2_fv#T*kijhpXuQs#C0M^5Ih8K@%{}6 zxpH3ccvUAD85xbya0Ai3IyYgQRa26>%(*5;gjj>5bcIrbR8*gFBQ8fq;Ua!Qeo)z^ zw114vp)vfoM#DcF6$*@I#G&0TYj``A|w8g*rL(RZE>}?x=VK~E`Q11QlVlrhR73lxGP$|;U9Z<&>)kHMhF5;c;AU) z->j;@3-5gOcR9RwzRUwdEy0f+sM}xR6Gij+iPY(M0DsxtMwjz|szS5GKuLGD>g0x43NDy_USnGM?tLbgNcA@@UeH(3+|Z_MerWJwnpO5> zC#YA-#w6fiz#-StY{s<3Dwb+)FlSzVAB&*hW9Of4*I)Pj}(kMP#LC&M)LX`+o&x)f`S34e4vo; zuC2zfMFgB+KE-}@IUZ^L=;dy%OlbAjjt*7*5lfN=PTBbqQy9+!Ud6wF>${>J&> z84hn-#$f|)b?p*v`bNZWD`^pIJi1MbO*CpNC2!|=jL;POIV@;X$Yeq6Ds5Pht-Z*r zAqb>iUx@8VB?{9e3IG;)b1Inb?|r*-vIsup<|I!4o|#d>!Ks;z?2N19z4l!V)6P`# zw7JI)&~_brFv~eSl~@xvo2@2U4!CITIGLW9&zhjHgZ6JXUmYbBC#OE8fAx$Io3Avu z!M&3aMfae`lo)?HKj^ynt)km==z6uJv#gH9Os)!BK~>HCYIO;PbNRj8 zXS~9*TKg)LcJ|>lgxfpryi>dQhLFh{&{#v3nthhtR}Q&Id*(<);Q08cA{In8roZt= z#ICed)ago^Q;kCovX7aa89{_iEe;m?BS>*b4Y5id#ty%Iwp)S)Br{po;NoI_Lg`7m zTur*XglX-9g#UD3+_|$4FWj@7i!9-~@mMh^u zov*BXg@920lM!?#>0I03V(R-8*4W6kvWkd+K$tp>CI0H8X!)x&`t(E#&Vm&{eY9-bMk&3xs*0!cnXjF)9N^bYkh-GcI)^NSjoayqW4Z$EGL zk5(Ps1A7G4@E zAN{U`$^wZ$h3oyCczWo@KE?fC@CpOGDF}4%=rfKh$1m_JVI-)xp(G7qF){b!0{prY zOCtQCwO!lvIX@6&Cz~TWO?M=o<7!dE@j#^UHJYWMRx%rOz9fR6Z$@E7%|G7 zs%df%F`};Cv!0F}fFf&P|HIx1Oq^aUECd*=kXe~fgT1S<*wq)RAqKDn?2Xku$@pKuHyVPe*M!?t^+;Qp}J z=-1~m6uQkm6D~VFDC>@_57GF4Xev9q}Uhz>SgkNRa|T(qjo8^ zeoN|(n-*ApxIZ_i_3LA03t8Hm&0A-P03f_f%Z`Omsonnheb@2P@W@D9NfH0I z-}02?N!wel9307#nsl^+21#D#BI`2x-Y+Ue zlsRZ|dAFyU{TF@ZQ~$g9m449_t^D_(u@FH4+!j80Bv8zTQHrc#wZc0~nFb8=*og)S z02Nb|d81Msl1NR>OgiR& zp-s9p+YU@@+8vD5l20gpYoW6J9r-Sb{Vm0mTf011Ba{BnWAT<^YUHZ2b>vZ->eN7a z^!CMp{BirTwan-f;uo@pw13D&aphN=0|0uIy1Aa6BT!IK*sVf^ zTK~TgJ3n9l_MS-At8Pls2ZN`_1^g?C8hP1ZK!z{-Gsp?(F1^EjW5SYi^f!eF-@o#G zJauo;v=x1)sQuGdO5`f8IUyl66e#NWJ2UEj`L87F>kk)HEM0+1oQ!( z8x?%D1oWZ@{inbI~Z<({yap8{Q;w? z{_M;#)2+Vx->8?k?QCKynEP6-k6P?XUof=Y_Kn0+^>i-pSIqWF9v|^~l5|2HGpWX! zzjyPx-B|#ovMJd`7I7sWDr%HRd!B*yeQ=TpjIsL(4D0e(WY zb^(ydDOlgfT#sV|`{crSiHQe$na{ZHCqY~q0L5{}fBo7TxHbNru7W=i2(Vuy*#^g( zUr==17Nvrf8W2LH>i;e$6Nf5x6pB1|1kndqF`-2wg!1JT?q02?SyKzLhUzpa7EX{! zD(x!S*#G_*_#e4}VI7xa*q^(pjV0438v1FJBwyA7FeX8uTzSncX_OSU8vc%W)h@TG zDVZEzT%<2Uld71;(hBm;2{Es`sH^RRAd;Cij>~GRDlZofkRrB2flNTjaKQXljj}4% zG<&xPKt876N%H8;RWKEAQ`IJ^8NKToiO0#wp*8|t`PfM@BuW>t79ISJ_c-x={QVNa zu{OBq``kDrk{0J;y?snTMduYvB_WsAYN8>QLwpm@z-2l+SEgeTE&~<_6qQ~a!we;H(;k;7)_5TBP=VE1ypSR1U zenW}`NQhPX&6~1&PIo`>@B5WDOJ=pU`b^APrSq7no`bnji#L%4_azm7jXPiJr6s95 zT&e=+>Ff8i{<0}5uJP(G=|m69iQfBDdx%6msFS>ReQHW#7?WWU=AkFUUgL9Qp-b>t zqdG_O{01I*gr-RvJw6z{n2!jK*h5j<92bo9e~JjA?hrjO(#x3cs z@}@d{8uh1U?&5a`SmZL`w^2yYRWCDx!O`faNGMU*JBr`8c$sU@*ehDL# z)EsyV$;imkMNFv?qBqh5dOYB*#x;s~;(_+7Pwn=q5#^ilE z=yby|$nKIQ9+UgR^DPgew6klozOOK-I1V}e>E`sqf(hm0eT=`x>6XL%a89*yKvtpF znZhBYyj;$U+n)8t;j3KpAc-SP9?#NWPw3|E8gX?{QhKSZMj2+?c9t3~W*@U$jTVu( zx5=@mpikt&@a0&)5(PWizf+JJb2}S+r!t3TO*%gK2FTpIa>$+haR8syH#s5RIm+aB z=KaO^Z+RZM=GbB0P8ORz7z;+w{Eb`}`pu*+~wXN|DeApBNl(VH}9 zqazNfwd+LnPxLW|FtEBPugzkJ6Y&GFL|%bLbbDwHH~&QR2au}Rz;Ol_pd04J#c}yL zFTG--f?rBt-fF93bnSWnpuClLU1}w{-(G;)Bh@sUH`Qn-U*P3%yM(GAw>)un|2QY$ z;!}I~S-}?^`<1yRyU@uBaz4H%3M?NQO>ey!YahdpRh4`tqq^h%Unh2Z*8d;6zA~zc zux)o!g3=(;At_SQ%|^PrQyQeZLsGgyx?8%tq`SMjyXy?T@xA9g-&uS4$F-Q*v*($4 zuKT{?mW256-!7jNjN1PaC_O`lGTr@|Ckt#pc)lGEe$g*p1cM(>^DPB5?u%Do7hhOK z=VY$_Lqt38v~JK}LilNSdL{}`2FF4Bdt6NkKm7^6D9K&b`jJta1^6oq zU)D9TTIpJhh7EcGgC+W!fo8y8hDdW|*WI%lL}Ha8EvP|_eP2U4CX;dx0{xb1*0wEg zpzl>3#o-J=;iH2UUxklImn*7>JyGH02o@j)WR1sY|1tr&d_Uv_Oh9YhBib^X_g|eS zTbg;39M`%Cq!c9dpm{1kNFbCMKqx2`D-t~QNQOIeLP@au?%i50Kb=(yC4PT(4gCvE zW#cCXoYmhSN8P&;V1-8M!m|PGkUmtas&|NpZPivA2ILS39vS$67LiP|zfn>LTNU?{ z4Iw3VeymVE(n$#M@}mXnz}`hy?j}A6lzZ4;nim2FS5gf|!-Mu+;Ttc5*_1D*8@Cas z3c_DEiRMDUSu6wM_0b|MR~g*7NYVf%pLkCjr(IVt#P~x-5lOBr+P`!J9wiaC;-0I@ zrWuGMDv-*`Mx>2Ef<_YHHSpO$*`orbF3ExIE&d4ZfO&~t_ZuDxMgQSxU zxTbA_)?=oEG|{Of5_tj)>t(YJtoR;xD2@2BjV@(kDa>ir>UIuPzFiD{uegyN+xo?A zyW=Q-TCJvApP8Z({48<38)5WRtz3nUMC9$IBM$eiG)z6uN^o=09H>H0CmBO zbC_al%OREU@c_hR0)=${llP%BXnvg9FF`MwGYsW>fL*@ljmF&8d@usPRGcr@gJS8* z^dn%|*sZ160m^Y|>3p2uTj%y%7xsz;ZQ#l*&^O(+g{klBE^eMLXE3ISQ$Qr9S&u5K z?ej$musav&9^4tfqk8X47SL&WEb^wAN8HnsRX@2hNt{)^H%y#9wP_>m`He;b;OL21 zeyVja$m`~MH^*cbV%zn+`k(5hh`06!>&|ZX7pLj;Jnk;+RF(NsN|FwAmBl?4DhuIJ7V7@(tKc`ARq0nIwC2J6{W$myB0&Q=2rs5+1N|^f^ z*o2TYpAuX5$ct=KnHF^Kk0;mC>Zcp|p=bW|^=$T8_WPy?bMHJ`_)O%IT#9x4#S3v-v+&}crIrz)cSO7Az|ND2ZZkfLv->cSF->DggkER!B z%mYmupkp(s(bCcaU~oWNrDNOunW+*B&;tQiEhEkd(5MY4-wDOU!~iih3IV}#GSR}I zE&v!oJ&Z&EYJ|%OLj*+UuLyF#_pgxp^}i&H&P(>66Iqir`KL7l5p0q?B$ting5*ie zycwGl2sf>!Yt-P|2pIkjufh^R{AF9_uX@O%?Dr{>f<~ zpEyg!i!m}IOpj(p`KNVrS2ZYL8G-!7Fyf=+3S>r^JS>Iansx2SPL6Nw+HLo-DR_T{!U`(?OE>MINBPM%T3Kx81o}Lxn-NJ zhaFOBc3L;;5r6L(C!L61?_zf2bz(`mH-2Ece6j^-j^|xF8g?0QHtAPFQ$012Kt$oH zUtE@%RomMW2%oEtC)!gTY6$pScz!8b5-8@GCEjgJxvG;(&qd zQZ#HhXFk!9y~dYL$Aj%O9oI-(>sZ<7Vs1>bdqK><7^_Up?$}KHS|Q<6RA<>a+7>5+tHFP~{zSQt{XD2|ypI`}Ro zG8;&1O)XqiK2=DmF?^5Qzd#qI#PqP5Wu?6QY1|);V`d6l+`N}*z@$)JD2;V0xHUbyNnnthE^qg@o>Bd|++U)}{jb<(aLx?KW8 zdyf}c)z#H|$}NQy$CG|sbwKb;Us+2pyy7~p3>7L00tu}$nzsJ|3G+*Kq*e;a8zGl( z+R(fiNj0=Fl=jX{4J{+>-1eO9^3vM`Z>YEa!B-YbdXC6%sy>p)8!^15U&D4xJ9C`0 zEKNdTz6ln4Hnof_l7GcC6b%2+VzY2n)t)uCVJE7;8|W<-wge+n08tr$2ySC$j!y&< z)J|V>aegw97oHZ7^QuQaAHoA1-ux$B7*kHeea z4P7cO9~A9x_U4)^V}BssperjWX*JmG?E>*Netxeq3u)knY@7+y)Vzy&&&Ej+B+vtN znfDL2U_jp-_wO3uuZXfV)Gw%`Kg3K);l(bg@~l;Aw-8SC40T^s-!(eAp|9_l>43i- z?-4ibBP2n3MJ}swE{|vt*POfAS}VL&yimUHt7j8TVKnLAPEG;Fh-mF#7in>Vfu3hY z_li@&--c%x(C{R47kM+B0-m24b077+!6WE4Eb=i7tj6;0BCaFOxYrR< zDo-@k)yo-y3@gtniWO0hNKG!9Wc&sJ>KW`3?4S~tu8UXdj;(7Dy@eiqrP4o_BxFTr zW%-sm$3#-;1)E}SBye#2vJsm=N0TF@C$9-hv1`t2$H71j~#Tmy+Rnkzu!7*A^gk@oRjiHc(Q%MB2C4LZgS z1eqT=prxSjZGp)tG9ZQaE~q&qC_EVGg5pogjG&+xTgQH55J>WP&J*o31lC@$yD~d> z!!M>sR_AjEYAXWcXpc`dF%U9~$vaC=Jp%SaPeEufPSz$j)@ehjPhFE9LA%g$;hI_g z+b1v}V65nZVOlo>6qii@t^2hFIvRUKIm zLqkeLUbrCyI3a$xkPtuLI0$ZeR9H}cP;v|x*~}k_bV?C-nG%EGK~qdUSv8#5T$UO% zcl+b!q%&tWG+ZqIzQMsqK+>Bte*G|RzO6Z-b-Vq2$`VNIOG0Cm$Whajnjiy(W99nh z8ZclNRmiESscj_rC~&6VmG-zZso z)_Hix6&N27DOBRlQN#OR=?UhRnj6Y}8?|CrPxr&w%=Qk)*l{MOLDe1){GLDKm{r6s zzVIloXv#G&0ME!522`<$W3U_)Xr?1HK4J1tq}zJ^3pC)7lTaQ}LjR{^zx=d+h}m86 z5Bzk8ebU**&vg1{C|3?S4BwWY13HRDK?dB{JJ_57R|vCH7{`n62iX{wyVK2_YB@8W z0*-Ab;zrSIkw75zG%os&s}0T1K1F^%)amRnCu_-~j6U@)px5Z1(mjE^Hn_HStlLlS zZ%zzVQKDxU*E6XwEtFSErJq?XcBrwYI>F-sX)f~Ix-)j@EKha*Lptp#-;?Jp+~`30 z0gWqX&-VRKyCy6VwFl;`xTE(XtCZN6^2uVZ6FlVA}M-%Yq>@4^6cn@g4t;SUDE{H2PGu~ z+(R8#J`{nS;S@lT+rM!(-#}A}KxkxSqN^+P{qx45M#mQnxdINJ<$|)Z%1}g6r3VMv z(|i5S>$7d?6|VOt(!&hppf=lWNM%^5Vsv3@BzO=o!!c@z7?i%PPH%F*UGjh_Ww!GE zZYg3U2uLZsb}iOkVFg~xllH^e^pa^QMdcq~{U?#n8C1!x%n?o|p?sa|m2ZLi!BV@g zYWE12X)7uHCSdr4<@5To?^FvlOZX%U&luUO?@nx)Y_KP-5ljI3b~u+^4g~6--`D(} zbb&vBJsL)U^kSVDR-ze&pG|ftN%fZ4;vZTangNsl%X=^%Sh{j^LtzS$vEu%ZrZS1O z1mMjf0*MNIRrqIv8FDvt<$tmP{=7-AAW?~`^=xDk2GhhJt#0Y}7lU2V4M~u@8<%K4 zsJosFyCH(U^=)nL1qQ-sz*;KPhv!?idPnu-E{n$}xi~EZtAI}H-ek?EK}a6Ll>N@@ zR^pMWu9>jx&r{~EN^r=sQS*VtYerF%C5P416KCX(9c&x_1=IG4YLX~j|4!8}Br>%5 zSJvn_sBOH~p&Tt=Jjf(kind0umcd|Lt;;y0I4LESn1cHw=ft1tq@q9wLf*WKlGxbT z*4EaH3>|gWO$?IF3s*Kah1}d_3DL2juEa)1eE4?_sw#kJ8_GwB7Gq=X=3@PMjpw-X zrdYM&7nwY9U*O1{bC#G2qkY!UI5XADWF~Nqul2b)^9O2aUh9MbBYVb-#A+ zCj!Gh=+yhom$}A^y8Oagh}Ds1M~83z5dQcvn!E}EA7F0$#Ka~TD!Fa@SPp2!2cT&t z#LVv$4eJeqW8(t1Tk^ag{Jn@iRrDcsnmM|@UGXgA9}g@h`DjtS>@Ju z3Aa@aY6gp?!JWC`E5>)K=|~+176w~v(5|f((p+Bc-XY$ zQJpb$$=HjQ6%0jbvMo;!wl9~2+LcS6(lx5R%?GMM zKD#w$7eEm(?tvsT3Mu=lMS>) zKT9r*5`O(MHZq#&fQ=+hkZ3bz_UFu*geHsY7x*@6d=)zNaoSP{(0Bx*sAv|a*i!*% zgLpkKfLr5Y$hA{UTzq4FeV((h8&$yP#S8ueb$*{Uu8C5f0U$pD2I%tB1R&wNoUXRd z(`;;P9K0-4P^)Wbd9Y9o2!5)nu2w8ikSnM<17e5SmWo|l48GGhFm34W(^$PjIFo&M zu=E>4+uq~Ia(td3rR6ch`564d(-#Ri^f>6QY-sD=T-(&;BP|B~v5WG%M!Xyguc?lbIVZ~&MfwR3WEwvm9|1j_#O7r?B7Uyi4(zcGMz z8$SJ3hAXR(!9CO(-Zway#8>7*hZ~CT=;xO6$0A0@_xY;6zrzl=Fge=UuKV1| z_j`LqY+(W8dH#`oyjy%J*Nx+SS#^?DvFF~!{qL++_6i4I$p_&nH;ujLTSt#!mMWf_ zMD9tWA3F;Mz4{Ck!m%kV0h`#iJ>ED1=OJ>@8~cZ(ks^cm;^u^}B zdzfA44<2Fbv#~L_^!eA7TIn4VABSP zi30qKiB1#>l+MoXIlhR#-_fe=d{$Yijf=6^$!I*t>ACVHHr#t{zfhTn$HV#Rg3er` z^>%4tVA!O-0$p1o@Y4PU4u|>SW>Ag7dZ(!>u_{d)AN6nxgW++G?BN}=AD+As$hYl4mda1srM)|z z(5CnU&DHGEDFH?WVc9(Ydgg69_Vdkqk_g={!!XICjoqi3aB#n{pBnH?lbYkan67|< z9XMd#6mw|0KirP69MMmH9Dg|p_vBF(V2s^sY*BL~yFN&Jrc?3UO`&M1N%g!{+s;7V z(gxG-Sdq!6y4;Jg+ddHGmBvi?d!rwGFgsKAAl~S>*AdqCc+SH^@AM*W-CJn_e{tGx zAWU;8lH#;4$aY#$moC0gi%WLa(OayaqciVkY%p!HHcvOv}aI zwYv>$?3}GF>Vaij$GtRu5k6y@)=nO~V-xe{6ec;vDc^mPkw#cLi2`l{nbPfjDcq+n zWH-16%9r;`JYm<9hhAkP6AYeMTh#FbTMR%bWFTNjP!^F56}?7IxPgiMMtb@PsrAV=AkOxxDJx6)+5CW zJD2x?UmbMEEvD0i*Ht*zmoIebo=(DbUobz1nLR_XDH5I)c$OGvCb=1Yj`HL{XS&%j zcKxUW-fHgQ@xb>g3%oxKD2Eb88k4$Xy!)yMbZI7$h>3}raE1cWMHQwby@^C1MKS{| zgpL9>oWo#Kcg(lWe{>2vW-`2~{6gNirCVOy_%Ia`QV|l8!NYT1Q}bSM$()q%@u0dh ze2%$>u*A;uk<&yQ@g&b|XD12Ggte7xr~cPexr3ut)u#~?SNL2AtZ^P}l%#3~gQ@g| zwB@^~l(*{lO=g&UiBkj0h9hqKQbNPqPuG`qCs0byO={;o!O;Y@?Bzx-hj#G+ zn1q234s}iL(hwgfYZ~-g#0LoG>ufKR(n;>C3o4AWGTZkR_rrCAh2a{LNJj&y>bg z2y*Ig--3v2PS0RggHPB?r)XncqVhivj^u%eZe#DBwTO+s>&T`~9f(rlqC$Xp`(nC# z-)3l#?Ns3e`gU9%J_C7ss3iVLoIy1}lIevnr1r(v?A!UoJwk=1E6qjEQa86po30DGtECzT@9P_OI=bal!E3z7 zN!YYzZn?+`yKbTQ7S@*8!&buwy~gF)U5<3SrTyjt|CZ9y2NNlQp|87(_-7N_k9K3L z^p}wYI2K-edcm$kY9AWzwwz%{+*mtm&L;FOp6{-^eO7LR>ekQ2#@RpdI#+JUdiEq~l!TG~>u2jNPOIE4&c<7waW~F9W^*!OfUz5mU1xY zis6@HZg-y(2nm=~J3>(Q`-e4})u_=_fm;F*0~Lb?eS4cs+V?U(&XlNz@zfA=vf{io zweWm$Fu@VdF(p&ucu=7be?n6?FDJ5641GXjRu=c$V@MH7Wb0JA1^@gc1m=bLtgtJd z;zlI`PQO4S5{ok z27EgzL$C!~3p@zqOKdpC+W9aP1a{aSz;ZT)r4@vzRFc3t6SyrCv8)LyiwpLx?%}1p z>7z1y;$mUAk8iPB3$asLe~+AWewKlmi|A}+3zF|=FY*O}AWdy%7{^4pi%SVxnca>u zp5^3)iM!sJEa$=m3%k=}fqYx&ik%p)%TGUMl=x9x5Gxa0W47PT?_BLWN#lf;ys%ivZoD7^P1?5^s~NR-9W{L^TB%-p%!FjSMjRr#*S~ zv#ZapzV7PMksc`WFJYsO=Dlduu8MJ1=At1KN=`*Vc=aU~`O93Fr+F|FnavB!BY~HD z`^I1}ZHu8&Buf`MCpeUdFn}bwY?;scRZOr%Hg8dk@*uvov+|{gI`%ofor-}fkOzUt zPo$F&Kp+R?-;Fx=R2~PYtp?#BuK>myTvipqmtWRx0{krvUIbTq;|LXeqn&GivOjVr zlTOo{9&^2eJ9xghHf)OUAj4erhSbm}^_q$q!j|1BRhWmpKK_UaDr2Xj+_47ZrRb$3 zP@V1{>CrKyNTMO3zq9}@us>tmo3)2yAW-m&xaVCz5U8X4vo-727ke#|aS9`imfUjW zcYBn1pw(*vvx88=6?Sb<0KInVaAHbShCRammM-;{h!i~wPSv4}Se7(BfL-@@4%D5u z#FT{IoE)h4Uns`L`UhjWTP*Ewt$mHE){^us=}dE~k$SR#nvzK_(Q1!9wIIXd<*(My zH7&v6Zvz8G^d^^B+I*`J1%v`g-?u8q z3}?@#qqWl!mTQ|zJSP!KkletRp!+`f~gb~46(QI>#?szwZJt0g? ztoSM@ejayo`RRb#L^?t)PYWtJ@u%?iG(|`8wBe6M64LRkNLv!JuxFjHYKwuz`%6Z% z``b{V5oP0psoEcjY?IAXl;=hDfnA=ONfN|?r{{&ky==BupEq_zv>P@xRj~Hg*AEZf z9Ia-uIj3Zz>KVaYudA-s$mxven7yucH?NIC+jBleE=}Wc{LT5CVKc_mqz3mFp@Nac zdd7uQ-K-eCcXI`m;@DR7^na-AaPdfPKhi5QvPncVyVe@E z1*PhO%;yk`J0rG%FqjIwCGHgrXYR<;DfFAMs5IvD*^*9W`)hGzI^0!qlRd|$?4`1L zcz$j_n(#dPdDj=X3|fCu(HuVd$sXb8gajq6V@0{~o~fy&rKPbk+0Vs_9*WNTQAH9q zMt`Qhj|QdbD$Nx52i42RUu`u0qw4jhs!-J?oGPvV?%n2o&YJf7fVsJ>y;A<)B`wq0 zKFtx>aV|m@T{t)3?XdPctqO|nH(oS6wpys1jqGd0zSP3Xsro4q5njq+^rh4GKmtOo z+pMtRjK{>ju^49@e2`bbU&K2<8g@p_Dssbs%8B8}4sWMJ%E$f>e)w^$JB(=>`QReP0 zizvmXa~_*WZkLz|X!=J{`L+Y@X!-J0%RFk6vObBCA9%;BkRIt_b}eAwIvIa!u%h{A z&1fBq9?JjK2LngfGHs@LCn> z7U1%wASiiPv;RKi>pg6+X~88RXfZnB$q*5imn;zroB#E5>YcH@{-*J3jrX0Nld~}w zH^Y#MhKAV@4zYvJYEsf}%jHdi#P()>#l&_5m)@~T17?&_Od_7L5k(+0*n|l1?udzq zd~t+>v-d4YK7r&_P>4|=RZuj7XK$|8+oSbOmdY8968!@NN&w}}-x67Th7qeP3_B$` z839H$q*sU}0{0VT$?Pb>r&QE(HA>Bo*;5v@!)t46+hKoeYPfXH%|;LkDr&}3|0~{zE!QcRB~?5!9uLj{)+=wW&)y&i?0gIfW`BMH$AEl2DV%*3 ztyU|bT+oyg*7^`E_XZJ+1?QxaRg~{yv0G0rp zJx`r~47N%2Jdnp=x!lCg$cSF4<;&rBEcYr3-Q|(3fzbKEvahbF8`~Vi6l0i^y<^J8 zmw$yuS!ZLX26 z;!xU5&MV(t62n>&T3Q;t)r^TP`oK1APF@2P+L&>g;-Y<@){nTFQ#jfOXV%x(Q#c(7 zIP64W+JD&pZGyowu~nWZTzY$L|~uxF^SerPDxVs|m%!5wmQr4%F9$B$?X zP9*H%sr&ET9UdM6c_;v%naCOU;h>k#%6Aa%0&s@!yA3+2QtP#Umt6Y%6=X#{00kPU z-~xS(0C9~#fGj_aj5MnL-a6(Qzm-{qfQu@e8I*}~xIV95d?&TNyN8Q6-80dTkT+uk zB$>Q<6GjNuqK!`i&d~X~(!aI53R|Yw%`Ska72DemWdP7j0N?U!MDca3zd$8yRzFz`z(Lo z=1W~RZjqsMNEy`_uP9+T(uGY7(a|NT>*}eVZiYc+l$#|%!I>%NZ zs}`KsUFgVBk~(h3?{`Ux!!qJqReYm%7^pZ6xRNnri>1$>V;l4;7O<^-(kIW4Jj-vZyorVz=LZb{gyV{e z4&c~RH0{GAh-)JHI1mHF!!29YP9Fp!p~WXt33AfB89KT8 zF|{fbJ+Jk+nq` zs4N!4LqZ6DRr3*K!CF$A!n&DzJm)}`^~>(v9y;f=I_0-eSwHZ)YSl^Nkx1)#^cG0Q zZT&H4w09YaJRWm(svr-&*5IJ|KJ>I7Y!!0ENj86X*q)g3sR#KW&({U^yX@nY=+^a& zMK*RJ)xH^~Dy0VBo2k&z&FJq)xA%T1e7(NFQsU^V8>lmqdz8k@Ve zwXE`x3~t~pZ3CIOc8M@@7V-mQ&qPa_(ZzoTmo1TH_fj{Go;xp;hNI5)NeGn?ciD5M z6}RR)Yw3CsA_(vCs%L2AnhVS;`*M=hr~1+bpQexcSr~SyxYcvf*|^U;oTAslLUPfn zt@9}G$Ff|_=4tAcDU92p*J^PfE|Qg}`v@*{wpQ~=JectIc;u`(dr(q-p}E4biAP7B zgQ{G)oDA2&;?}f394z$V=-#nY>A20ZAz8hI(KRyu1_O%sxn)Ciqtk`+=YCrF@%r=( z`SIYJ42h~2WYc`wYR~KIf~DoY-;w-~@D9_QG6Lf3rJgF@Xm!_t!#pin!wxe%W26T4 zsySOiz~b#~(r1|aiP{OkKp(4=2#cQv?0W6A0}x*qOXdtz(pA5)0UwobST*d8XjiIi z;Afq^`WqfMN7MVhq(P8hnFYF(J3W0c%#2ZM@{`@SyXhR)${;GlQ4xEHllyJ&($vZ! zmFqJq@S&!w)n*i|A9)QS9O2zUnBDZ<9F1)nvhFao%I$S->on<&I~Ir+7I|3X$vZqa zT5{1fFMLo8+1Yb@5(T}F)++YJy@eH5Lu;1f276?2DxCJ|?$|*0_)s8+hpYPe$9;&& z*GKXmr&w-C%bb+4m=BX?O|&QG+)+j&qK$NVG(#W=_98ZU0|!WV<#Uuy?Z?NryY$mk@gj`g9~N1uqv zT9B`7p||G~(0uxiC@M#^4KjAT)*y7ftKNB#j=rhd+o4hcfp%soTwNRIMUs+b^5{6C z`*}yoKDH2d^y^*T&I3kuhmIHx<5OOmpjISrBCZahNeKZGxDFb-e7`|)Q@DTZO#66j zaT%)Vz$ea-%^v~^9$^3*D@=#of6FA!C@KDgkMW%GYxs`#%SPJ*VG}oXck%kBWzw>f z&dLxcC#y$o8%~|tkx6wJ6iBL+83dQZH@@kRph65i`rVEi1S$gdcxbdBfI=7aHp~t6 z^@1EdFhK%2@hcwTgaMJ?Fyn8KCo^vkDs8M3>mIN@Yp+ggZLG|-CJlZ50YT&3RH=RE zk@+i`VAY`XMi1;=e3bGScdy>~XaUDI=H_Q)8mJs9GBSQ+K0Sl{w1Qrmf{7gL?%tQJ z^9}?QzmW{El)TF&;E}ZDYRiq*>K$_oi|6%L_;`61I%!(ig&!n%zT~yx*MuArNPaI* zr}Fbxm+-jpDKkD75ylHH*w|k|yJFQLeVO=yxsp*pRKh>B0P+gIpwGig?r4~=PFqeq zX_&`^4zcvsc^#&KTrSdD5B|4N#3KFGClR@+q8FX_f8iUotCTxuq3t>|YvU36_|g}I zfiJ&*={g5NL1N7l5vis-`Ln=@grP)H!*VSs-IYwbKUR7K@GLTdEdbA2KEb1tlXT=y z51X=)ZG0o;-(2Esmx%8>mlWieFSg@Y38tV>mPf!35v|_f(8tHO=`=RWnRH~D`@(${Z<9s&N6gqR&oC)?w->}A3$CPj z#~+67VWUuKezv0On)>PSdT!7Sx$+FX4StI3YG?lxhx`DSAb!-u^6_JaVKVN;(scRe z%hsPie>4ufIB+SOodi7dz88PZ%fb#ba*o>e(oylJP)Ys9P9(CnC9eALtcr^9kRM>F zKA5gc6rRIP{m3uR5m}rx0dSt=Nm0t$-$EJ*h$~F(I}(qh-dhzJ z%$I5jHnDJ&*C<@FrVe1HooKt@g5?^qZ8~%Gh9v-1;kZ*>4=z5ME)W_qEpp?&Js6Fo zld?L;UA3~*<1;=%Gqe283Rxg-sU5#8_pMDnk6l%8x9;`WalyT$_WBku`^f`q{*6Y~3xx42Zs?a}Cb%1i1?IK0_1gOB76VbO+WbPVoec9v z&;fP>a`Y8~OL1;Rl}O$c)3ZvT0(q@`p!%=#%xkYJguhUihA6II!GarLWB1(}_TV5Y z^p_k~3(?zo7=q`o8pY#j-EWp0Lt7n-?w96IPs&Ug*;|4+@Zqsnx~>5HoNp^LE%%UH zzrmM+HT@&q0uqcz66~YL0xY!N!!meKns3dwzLAe%Ck}`o7+Qj_{-Wv%0gHVm+W@Zy zxWB;lqrNpTkZNf!PMrUWjFeQR%-#qkd2?c=j$=Fwd8ID=#|3=`sCAKSTHRcO( zfYRXj6TtST?l`SF%Y5FHm5oFbjc?{b2cUb$YuSktlboEaEYthbj7jPEjaWb0ZK4Te z7bCIKfH9TB@$$yM5cn%-Q+X|vbC2emDN4)gCg+O;)^jmtzf~-t#Gx3&u;j?y$Q`8C z`w2GQ95Oo!hcP-*=TEe~o=<}9U(kCjdv>wh4u^@7vf>;t{q^##h&PcfcB7CSTZ@|92>JkkS?Ix&?%9@+pfaE_G4rd|W1d^eK-gn&(z=4>lH zm*N*%6gHT2%};LL)E-Ob$+(fR`=gwWmQ!vJ($Vzbw@K@wnfcHB`o9Ho1Hgh6s|)7H zIU@DgssAK_HeeWe^akiYqhl4B362?W?Z00crm*SHfD+OSqdf946&+GoL3 zU(W$B9zzdDd)6!JLn`(6G&f*!?v{Iw9W2fzgQInlSbQtAC7hNRKw_XtA)U(f)6953 zwLrNDoSo#Bo>KYAP@`s13Xqh{({P9z5p`9Ka!^v5yw)9IFw7a~n$jwHWBQt0borm` z;|J~vZRip{kzDZ==5i3RNbRixdAY!3dn##9ZrkqjCsyN!-UOQ zP}tTp3X>c5Wq0__xAlA_Fnhk}tG+}=^ZnP+n5yA!QAT{iw*Xtw12ur{c}@+BfQ$f- z(1VM`7Lhww_t;S4_~O#2-yi07_7E41b27)Mh5zZGjd^I+yLH<0vPfrlJ)Ypk{1Kxy zcWbc6{p2E@iHwX|Z;fR#7OSvGHEM&3YBow;LbY72csdfYp-`+?i8b*AP050LF6%}8 z9PDtOl}5Ns+Q4S%QT_stAymWQS@4s8dvKvu-WU4Bw>zt+U~W^-aiN5z2&`H9>$Tm)sxcgr=n zA6|dlYSDj&ho4ZBA$qv;(_O=Qh3UuP z3%3*+xCGIq8>+A!RRTMvJk~Nu2r|^SRW4E^r_)Tz#XATt4M4&L;+yD)e(v%eHEb{9 zwuz;80PeKdPc>I&2olYU;3?N@j)knTI(fz~h&mPK3cl8q|C3*GG)Uh0CLC8^v%>P_ zdO&|PTPN~*OL!W8paKc!<(K%`v))5A{1l|0vYr0nw^-l0owfe6&U?zce(^lCTJ4#J zn|JlNXv!YPo5RhjwQW3?YrDH3Y+#mH!gtj)1q;t3p~Q@a%wdPf*bXv%nUqjO3aH)+ zQc_a8I5%bD=sSPm+=8iuo#;a>_rz2hAV-RTqit@k-q0#u;r0rc#Ku{0`D4t1)XE^Q zh7`S>ZlRJ=Tk2_gBbpBUay4~R%k%!yey*okkQFk`?a9PZ%-!Zp9Jkj91_b1~L4mdy z)&``fZ<5N(=Ph-hBn3W$Iqe-Np=indo%}3(^q|3{x!>OeysdCNnMP0nMVMg9=?0sAYcJRa%AkA3K2%hhQx1MSo`Y1x4kyDrv!fz4j3zx((#-`=Av8u0m zn3W-WtV5;`4%-!=oE~XBr!P(64kw3i7sgcsn}cZP%4|Ee_mZ374cqwNY7gO{>`nQ3*YcuQU4CB1+FfMTmWDcK*X!4DJ9y_mt8D`O*<`~N5e&J z$w(vJ2+`@Fqg!D1EdTs((WH)#bz!Cu+;Y9jFf3;x)<*1gTBBKSejv$2C-e>-4BZ^M z9#q?hH1^V;DNExN(A97)A|K&9E&#Ej_ZcSjGk=`_|EGSQ5GO%QZQR{YFp5es9Ck8# z-}-WgzxVs1Qhl3;@K3U8+3As_XR+w~sTbvRG@3CNXCM3?yU; zAktp;(NaEXc+49t<5JJ0Ht|cRa?Z3we1yj4-8~OLi?Q z_4x(*c5!W1>2a(+QsWXAAzJrLTS3&_t_ix`N%I`Ueam-cjnHY+cHU;wX2UgfF?b|V zyN$W}i#$GbCSIeipKk=B6Y3AN3C5#HI->E!h;kVSG+ZuyeeF;k8MNt6Xyp_1TfUD{ z8PQ^cqM!=1G@08iI=4yaQqhl_yA0FzJ_~l)&8p)V67+1Ld7Kh}`%YRLW*38bR-RYI zH4Lv98Z^48JqT0h?6eGZK@Pqj5Jhae8uWfAPlGFf;wwD+GjS16X4Q^n+>X7sCk@Js zK|*Q{K)5(Rx3RHlV4Z6YjbOX?M+({B(UWip^>t`@f<@dv$$F1t8I30VQkL(*y^xWv zwfnHBPV4y`Hzwnb^0DG#Z)RoWs{~+2mh)IfeLN-&N1{DEaR9g&)pubVJh!VpIpXo% zU0uyD&kxApN2{Ut(7x0VJq%3nz-Qo`>k9&w$~TqaHY9$@uNwr{7q=P91a^BFV&ZRO3y?>t=hld55! zrD&*Vu5#-fztv5 z-74&LpBJC{ZO0+nraM>q+p85v$oPtdWn&}^#}3q^uginikAe`nekMLL_3rtR@Z1q; zgc0H{U~i-M%9$-U&Gq+xKb$QGeKRnqHW>I|E9^VUjg^?gK8l0oqg6VATg8#`wUia> zQmuHH_W(OFLp9YrJvHM)x!&Lx_V>>>ozf;V6Le-84qC{={F&wlO<3^U{KR+}L5+{Vz|#ZD^_(vMCq(e_bGpB>BJ082@toU0200>K<5f?9@) z9~YWb7)~?}ss%fqCHB(8_J34;3EIh%%|Jh?eq@J2v)kCb6vwZ(np{3=WbtfU5vsy}$T)txc zL*24@*8!ES9DM($9O?A_#P7civao!RK+weK${B@gxl+Nw!KTy4YDEJt_zs|8cchnx zlU86(8?8!N+v1P(hghJS{`To7a91Ap5sq=zQ0OX{x^m|6du&uT7!ZMe1JHKp!S$qY zT7}>(8e_FI%9hz%lHXe@;X{l3*DzaZh!^fxa%F~+JR z2To>EOb{t_qP6*UjgmhT4#VD@fbfcx57(Fxn7UQ z?OFg38jj6jXWRqpFN3dJAh9*wxhP9CY5y%Zm_q{oTr*TCIwnRzNmURHS`-v$8d3`r zw!==~L=f}NSwSINKzF#1&DQ=5783;!i#zJr@j|^}^1Ug*4hMj>VyiR%P9{7wJips~BWMs=yAFM;P z0Dp@!_K(|JTU(o(993aKRFhi2nKLzwBZ00>KLaDsd83c4Gk?Je;l#F}VWb#Cwvx#{ z%AOW*L`^*{?e|I))STuzv3P zzOu3cgif%bDmNNS3gPF;TJje2{EHCSL%0$5UjGFMKB<}lokB@5F{fGk@{o_L>R@sd zU?8$RlNA6o{XGzZ$mf2(^X&zS;KTU@kK-2q#iTOM)U|Q(D~odKQzD?Nv9G3-uc|QQ z&x;_5H1H)=>_ePT;suwcy4$`p7`A3`npeE; z>&flLzT$i$GNnk+Zvl?RWz;JlZ}ldu2cLdIgMxx8@|v1guh#US2STSMPKT4Wo-JB} zT7XU(U^HK;?M>R|-XOvFGRQt2HDBv9m%)U49q%5ryHIKCd$qbRft45S$qS0vPvqkM zy8s6-75;>}!-IV8XnN21F{W!KH=`x}I*sgZQq}VC=<`mKNE{gt)~KDofC1x<_?c_5 zDqvawNhi~;0;{~>UG~JH5PW!Cs??g(^FnpyK^eF})Y@urUDD|uGgGjK#a&)n>BE_C z2qkqZ6Kj45Jkhh2X;uQ>oj~>&_dbf1=Lh>w|fJ?(*XGnOL z7oX0OpgmaV<3{8EzdAfPV9rg{S@Q*h%IxfSJF8p@cB;d&^ip|9w^h6TzR>N|JEs$% z0AGO8lFR|q)gMI8%iAE|QaE8&!JLY(64Nla>8V;DFBcQ(PNAwyf=QI?RF`t9MxuRM zd>Na9LT2|07KD`2%Xs^+(2Amg?%PuKzi7Qz-B>}YP*1OS@#n8E=i~KTu_$;S+RuCh znr!T|P}ym=g<6dd66an(M}R~fiTTgLN+wK5^^!9Ji=HXYI|8J%*=O375?|Cuh8)oS;G058MZ`+0JL!sJ) ziJNpkR5V_m4LY%RE-+->WNQfw-!=4WCcbZ$9A1UejL1OmhEG`}z?-)1`+s6^<8-+M z9>nL~3goMv#g|X>a}(n~K7q5psjF9P z8he@t)3odHdNFouz2fDa?&(1QIfUy+pn6QH!-8~FxAH5ws`%KFgfWy>1Lteu-v7JIUmnxG0pEu?4O;P*G&1qIEvgmdrG6)1e(wW;Vu=Dw*^ez-U z9uSX$O92SzKvEuo^Z2@bY`&7C=`938smcn(#uGUTMzvGP0}&0-bK=oT{v9)UTZ0#{ zGH#{$Y9g+E$mh!-~2}rj30vt5~dgBR%M8 z9Y&ALq~kNG@2X!7jHWEsbM52J1_=_AkdVKH0oDCa-T$6MG@7xIFJiMJm=!4@GQA;@ zil?RG8&iFE3~u3+1qt2n!U7<5;{CGmIjYYcT4?`r?cQ1I8LFx|piPhyJZnMxN7S$& zbQNoAoeL#ir^o8Zt2LPqor}pxQhZX3Ih%E3xh#Jra$R(V=-GUrF&6?@0=>TaePAT? zU?DtP6`n)v-IU(9`z@q?`s+vzVz8T5FR;Z1RKYkh0YBrmKw``=YX?=IOk&Wck81*^ z`9p{YE0TRpxCJ^L`Hc+3-4kafex|NoAcWL@$nc5IO;>wFwQAJuPz0?0AMIhz8kCJjcq+R?>=Z9g$&^E&)|MVvdyM022{D2_ z#oD(f-bih-*&{Ng#|bOBFK$&MA#F80+E@DY@f6CICin|OB*AW30v)8K1evUFfYU@u zruB;;xVb=UR?&>KXKF@#XQe4^9LxK0RL%8bj{sBSY;}TTsL^3*$x2iuLTW?kq1PwJ zZ>y`&D`$BLj;^xu4Nr4o9BmdQ2t-wlJE6*Zbo9Zsr|lFVIEzuRJ)$5L;fgfRx6P0d z7JT?GUv)7z`z2LK{;pp$7YhQvx3$QmfQ~ARs?-UU z?e%qpHk$VI{dm+%R575PtKKjW+`Skm!3~y%@*i2T25#+$LB2A6wa-9R)mt-W2-;7` zN4$!S$Ema`eu7;WONfY_p+{yNx4|(kL7qL|a7;S=_>@-l0(b|&-u0;eU$FP3y=RTd z&})li+RELD8EV1DxpW=f;$T)aHnkG(!H00^@Y0qMo*sFyX5+P#abtI=VxiO**5zZV z;sFjemez^_`j4Y=9v2QMBr698R>X@hK%2)r(FKBOH{Jcn0DfP{e>S`z^kwNx{P(2k zudCr=Nn}k>&GE55kDh2V6@0&n>T6ff;S?PH01pVn3x2AJu>iH4E^e)Nd>3>H&6SNA z^k)$|(ctqi3fq+tiX0e~Ptmowl}}$-dEH*#rGn;5;c;a%?E6Zf8zcu1kNs*%ypOEj z7WadB0u6Z#+zfaOcv83Awp<8R&QR>+;JQ=5N}PrY7zf?Sk2_zv$dbz%LB89db8*v8 z1Um$rUFu>QZa+z(laqS9um0(;4|%@%+g~@9_uo5a=EO;w&FgRdg<^Mh+=RIG(fd3; zn$UMY;RS9$ZYk{f55faF@*)ArFtwUXVm>7>d1WJ;QcjmHGh#}QEHzfHo)jcOL(P+G zlS4j6(*r7&(0|E@_`9*}yq=*d4<&WKPSltXzpJ24`Lk-;4-wO8xvtl2m&^q3I zJiio6s1Nst?|#(pSShpwZ&pUNN&neyT~hwJF4LD0L7A^Oe}h3%as^Fcicq_|xZEK- z0;sAxJG8M&sm>q!5Lp(^gr3kkAc}iw&<=;ybdp}r)Yq4h9oiYOKok(60HUz{txTuaeSlNkk+4Y+ zwlQV}JdXI>JRf{lkFKLrIoQ7PABnN~A(a!7X4{02p6A-xKAk#4?Bmx|WC~N)MBMMb zKQ*Jx?<;GMgluhlPtE#3f)gKF%e-a(3OEDx(}ouR8zmOefUC zmM#fiI;#;?5H=lc8G}IAJ8N@^#T}^=m%BL-YbxYl9u$pRflnLK=S_a%(bSNUmTn3% z-3GgDy|07U&{6dIxTxWJ#5>;aFF~BPqn!KOEf-y@kQ!j6(+xB+N3TNY`8~6Lj!-Ob zI<}97(5zO-Hs&5^VispgbX`FNLV^`mFEBpeR6m1>vH1W~q3sb>LI92777vgnf7(f0 z{-lfny?dM)S4)lGblB&LA*cJ6rE`b=9?Xaa%o;qN#97+2*Na3zF2VYIr@Cf^$6OBC zBi&}UO=o78oi#MPH8-1dw$j4m;vvO6cL)8;OVp_;D{%sCtsy*oR^x69=h!fu7P)CTKbae~VoXZDU9@!p6S!NRd7 z6r--$p@laj?opD(SXr&_yIA7oP8(RQHPTyxe`e`)yl zH`)@S%d?no_|m)|9*Ca>cFddGB<)c7(;b<&-X_zD3h1bvtf&k*8mVi(pl5Oj9&Ktm z8f?^Cf676aQO*d|VEzYw3MT8v4)LQiGIvEcr@^mR>*%y14~H-^B}=r%UY*-=gzhGt z>EW&1Fs#dZSW?ZUXO;Z_5<0fRvOkb4b^FH#hr$Ncbz?`TLGEulw_!kw9ZLxWRt%UD z!zug+9k+fj8Nz(Cg2Itlq1$qN;=n@H3G8q*(SKxosc_LfimD>w1RLh%mRW->{*{(b zykrMLC$2aA?0E3ls#4T88`+s~qDIiCc~gEp<1QrKSb>HFshERE&%n#64jkLC|D#bW z1V1@WvcZ3r4?}MD{2=tr*MS*z3eFbOW4Zhuw~mTAY}&7rQ^9j(u4)Cs^KJB_DRIh* zS@oi3B0=Eth+_6X(5$TOoovZB{{zJUiGQxB_s7D0lQAO21CU--&t+K=O>f{F*yLlR zl?&NMY3AO$uZt0Yv9XOw6K=#0YHB-izYL2dU|1eX?6D2qZNHa zGUI2-h*-i73@h-o^9Cb0Ke65L+X1AJ15WK6 z2hMEp*=1ryj9^mtm0B}t>g;7Bm9^g+qDE@*;=N`%VgHKn1;}6HXioh|a~mXLdDSv)?NkxBOwxB?K|PT5zqdO| zg_O1~f;%CtCm0u4ivzF15*nQj5Uh(Kibv7vL$_{LU(Q*)RyzoGi@NAYIX8h>k_7kb z(@|;FjOOn?X0UweoZRi(G^P#wl?Ka!8Ac+z*Hx}0AC2cBKSZ)fq};t%S9$;l}n z^8v}3pqOxZ)8UcNu8Q``eSH|f~F zfCSUepECfHf-45bqSZ|-K1^>zfH4{eM>9a~6rYl!I}sXT%)nIrJ=+-S;lTHoHZFpP z`oCBz9J-?`Ir96y+z|wZ_<)tOD3I6+fHgf~B$lU305Fu81^`+miG5&&G{I(r_HSV? zUrfbCL}23M&%hvdFx!No@1y%SL;-Lei$>WB1iYNWW;&v*ARw+(w*@EC$6uBT*fA(J zeh&_w0}!);SOy>iZW(w5q|}z7=>`!4K9bmf34-&lOq%nG6$gHQ^+UL*v8}*~t~x;{ zW2c=(Z&flw{Euj4G>I=FuKlknqmj*|86f*SEI&OyCzbjeWO-NePNzy%LOsL%d#i`2rdG2$y+#QI@{Pm06_o^Nti} zt~EXhEev}`Sy$KVeCH1dpUYx{^Wb7RyR7A@u4q4Ri4~f5dEkhCPBHT;cgb+4$X3Af zar@kppmrANw+eD|y8_`L0G7Gx_Bf*tblXnX*4J+Wr~1(?msRgDLu;)b1&7BgKBiq9 zef>=Em1J z50Z~gQ~iz4<9g@n!6&ZB#2T?$Mj=^ZI}k?5R^ zjL&eda09M9R25&r$|zwTkPEQPM&zXXokxwmgvS~urwBM7HW@sKfrbbCsxZf_pxeH> zzP2)fE_t{>%RIJ#C|N5Von-s@qXN}JAo_N))yKyXF@P5#=XxZi>D%HW7TmLDX9Hd4 z2Sa~DihfLdtm*&^CM%!pH!jRimd~1hv&4(;R~qsM4lADID7-G zXv86cQjOg}-^gKHQ{h0V%!7l&bmI;fdgh*V#~^s=DLM)KT%Cqg``YAa9@pzYfACpE z(&}L~_TPZxP$Dk*bj4qMae?f7%eWf;M3XPt#>I>SQ>T}hFedjeLes+DZyY_I=f7hy zMKpcd(d2VL1zG(m7hmse67b7deso{mxVntJ*A7@cN>z|>xv;x}UfnhjCEj|gc73&F zA#-=S?Ufun)=>;`uVbEuT<^9HvRKxSS2ECu-!#-CVkF#1x>vvbEP8pFu%2Vnls%PY z<;KO^wZP|eyg?O&!d~m`y&b|fGtxf3LPTSLU0`EE|TC$aFp#Bry!v#YDm0Klw-gv5{Ta3h0C zDh|Ho> z-Wy`R-Ey*g(;ep+5Qun#>TxwQqrZ&ZviGTH^TN;ONIw6H5jLN=>ALr;NA56Rc85;h z3t%)&I}s-5mJ!b zh^}6@542h&y|p0ZZbT1t(|2*#W5+|FL$r-Xy48uzg&>6OuJcYMtD;mdULvc+ZTi)vOtQ#zle`27wSyThP(bv8316 z?_=ZfgyyI$2CgKbbK;j^d>Fe#2MG&0Y5=%(2)JHu7kxJDsL%cmT+}Zz=6)1W=$Wm0 zr1yi9EG*`B=cSwcV)%e3YA#c5_B0k2NchW4_l%MV$PQty@8g{bPPg~PbTQ%lyYpTRClZnQ$mm>J=(Sl?{{ zObcV;Vx^fr1vj*!zXvq9GKR>{`aK0r<*EQzuBW4@0*Nc>z z^uH=nSZJugLU3~{O1YU|i9m)G=>>D%|0?M#W7^mI4 z%PFu36lTD!gDV8WHME#)ZE;KE4nMs6`EB_q_7OqjNc<^iKEzSP!`t2u0h9ZJjL&X% zamr}4S!g@U`Kja$^G?9k<5`-fE&3QWb+p5JsoUGux#z*OGaysb6vIUKIRyFF_?zkm z5lQ3$diumN>M)1?CV6mqObA~QKYreqNY40ec$!Yd+k?@ zO-IKkl9aT^!y^Ly;$N6&I$hXGf>tjK(=lJ>BZ@Yytq?HuhzsFQ*VdLEq3`d^?~G-% z9o~@P*`j(@u23wYu;$jBvR_@fg#;F*;IC^uB`yJ1;pctBRb39f!l2Xzd>|8( znrfc%xm7**x4N(*_T$${K`2ZcK8$HZ=wg%YP7#;jy~hFXHsZDbEeOfYw4}8bX`HYy z&gu2T#cAF5eEr+Yo}aZToo+)&0yY@2P%VG+L27eGI?McOVS@$)rMBckO;w*)KVTSG zbY)u{f{u~UoD7aAv#~2OVkj)Jb0Rlk6&fqC5s&O+Fi&BK982IpZDqF1Uq)`N>gY|O)p17zxbpGIzAuC^0n+npH>wJZT>m`I#_Ot=v8cnTCToy znc+E|d(9BvSg|C4Fwn>jN|Z`?*ew~|zso*aOYWn?^GQx{d1{gz+bb8)Pjx(P8!O2e z{=m|76-AUSSZgLj%u~O+Ex;M@aY1Ld_P6*F4bZe!#E6E%`u2GT#_GJqK^D)TmTQFv zkyFZ3D9QKq>KPjTZm!#kwm%cqCtX+fHW0n(8g8~=wxOxIJX+dc{Vd9qaie?hE4)fd zxVa<@1MH{CjuZjMK+9UGnV=t7>1rRMvSP=3e8yIi<_C%hu zqtc!;QlJ@Sf{_6I(Y+?AWBGC3akW4C?Il@jcBQVnryGm<%O!y(YqxV|iM+Szv;*_g z%;Y9cfqs?6m4(&S)y>V#D)hzGDrBiw!Wb~|CZap`9(|s*RG6%z-s}1{R`2QkF)fH_ ze*oPsXOe69&UbGjr5N$J8kjFy0o1iX-^z*+C}afGOrA9Yc`50Dr$KZAAF@AhNK_OS zARgfTk9c5mtBhFRYeGjM<_8v)Kz}70fc+lrn>Qk6d2JL+m$F?JFA$so zi-3AKn6?{0zo4QHEWw_hP4$^(%Ya;Um;}~h`3q}8rk{X*j4tifmK~n0hv>6$+~L{= zZ6v#U+Tye?AE04KFfcMQI>MaiYN{X=Xjp3YfC|}-wXhb7ZP}39OJBQ)_U&5HR~lp4 zfBBMCZ&Z)*Pfb~^^u9BYnwna~oBD0VBoYogRW~ltJmU{ju_84KT71IEk`ej8Mg=?i zbra8q{#)#}OZ<*OV>#jy5*F-e2>5l=$ynpp9JLOQ+P=mS7zK(#HIrcIE;BN3QgRI2jL%ge(6sA37GsqY`9G2utg zKxNMR)5e2wfL`MlM?=^F#f<6L-bK6(1Zb7LxBS^J`vH~oM~DDL&3<3A;1-pk9XRZ=NwIwmSv!0Fw(5Kt(10z zBTj>8Um7?YLN@2TOG=6J^K^r6X9t%=U1B&&0i-N;q95h{WyfYpCE@z==1HfRf160U zA}wPVd_I!rWzwJpDd4E|c$fKMB+g~&F;ynms}kgYqf0>2KfMfYm($SWa; zVUjk_n0cXIs&iQkBIs8B{+s&e+j9cJ!`w4tVvt>Su#gP&?e=Q0u{`h3_T^R%nb zh5{wZ+-p2n7imG-2Y2Zyw^TrgjoedHrlFr&>wx6hfpP1d`?*=YPK(#wy>W_N1^yzOUP}XL26@^>_#G%gS#`Li=#S`1T;jOp*jSFWyV^mCT76S>d8Xb;| zbC#Hg9*bv6)R!Up^T!WDi8?&T-FKgB!AYD_(#OV6L$6Plizw1;ueZc6_e}-4^GzIm zJj@R)ZmLxGAJ=P~*DCjI9MRv~^4e-RiA=poQy4U0DE-8W@`;jMLksk6;68;uw&1pX zXHxVAc(F5{p*WiVgp;RI(9u<&j^PDiq#CzLdR%RfvOsK%3F@aN-PNTyH={MSOKb6J zoaf|4{8@D#ytBa76=)Hfr=<)eC|Fp~FLT<&PPVUj2FhnoW3r$h)r4(rK3^p{Mm1Mg zGwDxz`yQg{vq`CXDomTWH-c9U3%hCI@yEc%a;`yC==8!4MfB)MAr1M=!Nu+}Jhl35 zuVV3tW19Ru_Xi_SZtz-iCE*GOo%T{F`pG3a3BOza>Ng9&v>XO5!Q5uj+E}mELGkEp zkD1g$wp6+A#2pAPq^G(GOHS_xI2!1dhksVRfe|)%>gpBhMaMInn`g-{>5qj=x|<0H zOUNDL!sxqucH4xH;I$D9p789&Z)F8K#U~DIq<#8axPh;qJ9AUX$hJ;C#x^YHMv)lpgz>tAYx0*+flfO2YlV&eV2wPtyFIpFkJovJ0;#9g<*n+R^6 z@7AszU}7p3;HL)-fMk$h8=QE+n?HLD7(1$Pjwa^jkkT>!^lRiE3bZ?*Te#_S!Yy}X(0fwwK{F@!2-ajt~;(>aHg+G~9 z#>K{P$Whsg`5<}6Rp>pJYW)epaO?#i{OI8UF2ZP^(R4Bs8UG|Q2(@0)2hye){p`Zi zVlB?%eew-|altDALw`DZuj9kONibI~94Pvw&fHw=)726BD-dX`b~Mh>FG;=Lmqm6~ zY0Ri31SFj1K_P13{BUL;`Sc~YQ9#JHJLsFo3`&uR7voD15Drwv)RZXe*EmDITvZ*v zx=VpE<;N5r1W=lKVC$@%*}d6kM-*)mY=Mh$ID@}o&2#gf9b zlps?S(@z7o)mdF56Qc|LpO6roy-h6CBE#Mq+vtiy#IxztSv9U~+R=56F0!or#tTi} ziyR&7L(LhezD~t%0Eky@M!VmMnwq920K@esaoI|UiE#t*yny-2e0II#_DHILM~DAQ z5GD~(BS5Bel$<=_a1?|aRUdca>5T>9oO-_O&7ZO;sQ9F+RiLJxvV~t4p0MR(I23}=2FA3x%`lIr+*n}u%#SFOMaP3I{~;lao<(kK2Dd!goNv;s;k_%wqy z+&Y?=M+|rkY|k7`!t)rD@I;w!OG3512DpX37WXwdJoEnWOS6q3Es`O?f^CPYfJ7U; zOGKfFaQ>sAR6U~kyP3^q;C)sj-UsAh8jcwW63U=Pn}&>CUge&#MHrxt4|@>gkFRU*0y#q%fD%m975jEdt-t z2H>Ta-e0{mhAYjrGHSW6nYqkQqbaZ-L#lRz7k~98;OEF_ld@ch)uJvFEhfF!y)LfY z2Z=r1l*K3j;Y&$Ni>_mQd*kk&(AO{2CM^QW?H?SP2|4g=xW49=Mh!>+Pr(MwcX!nY zF4c65|J>N95?Si$>@Bx0-ZfHDQK_WO{Nq~_KQc&UDPbSVRG^k;^Be~5&vBwP`WU?6Er8fG8O~R? z=i2|B21E%{%5-ytOM?ZPT@X4yI-AHIMY!18v()gRPa=J7Run(1CuG%DQ;P+@gpIgG z^;q3B?0MOa8}yV4&Z>{S;xI#t5d+FlV?)!!_p5&Wt6>ghK6fOg)s3IT9+>D${XqZUwZLt5TTt8}xm&)Ti{#T`1*M4F+Zg@U3xc(-`dv}3| zO>YwYT{rk>BlgUqzOp!43KX;WLE4^5v-V7u0LIiBKacW@OB}quv{Z4d@H_rKYupYU zlHq40uo}&|98UDZ>7j#!fh~Xp#tzz%mpNaN|ByY>Yt>L$YO?&GjW&9wsi}E$bMsc> zry?!R7CK_Oi^)hb58yftND+h{pY@x_z}<Ky^HFC?Zg6nU?Eo^W zLxWJls&{c%=hB}2=0t)JQBNxlBg|N?HALt9_EKOUuZ52p^PEFrSQV{t&j0_ACow(e=JnkT;U@7>h7&d=;r_jgdq{;~AOkf!^h}(3RqO|ncoX?u zEJFej{L#6boqYacy5!85U)cCG)jvZ*zW>t*c8lggi{aMR0cW+w3y~ax?NYUmMpQ_k zFaW+>(TqbRN5b;m<^iqjRx~XpzhSysiS3Dp#So5AN%`z#Kajmo03PH?@C&k)a^=FP zCs@+M#)N-`gTrpK$Svpc${h)q794`F*-U=Az1^iug3zJU@@AV1=%& z*)TKP*w_Gw2Vn87^`V3LwzwtRC`u24MG2hj)pv@(6YSnBCv3d9y`B4WTd-V$XKNm# znNE12GGf>)UR#Wv{G|vvNw4zA7wmtz0Nxz?KJO!6*&9Yxg`E5A=PXPP)mW&ax5bm{ zJ5L^MzVN>4{VMK_O~X^9;hcB;kO-aXQI2Y0NWY#_dWbvt>9xKqbrq{CiW8u2i`08x zx6Q!<^Ci)Jbhy~l3~JVnC~fTd9nSEN>0?xhD8j|!gtFUR#?h|+Rgeb579$z=#x9L= z=L_|c07jnKWh`H&2G?$S6O&5bHGAy;f&)Y57fhb z$ZLLs^cesxS|2X8Gvj-I<9%TFOt7}HvVz(cT3kNE`T{GJ3-8r> z2^}fN=XW>#Lg*J9Dv^~%b^feUl8yECQ~ba36^faulf~)qtG#vm9KPm4=7Qm_JRO|R z3_|e2Tb--4z)tk4TzD3KKnDmos{-5fnD)pI)3|iOT-;B6t0^D7YD0l;BcB)sg%T2#5abNE+z6AlcSXp|bv^ml!qc#v7@op}boXS)I5#sCn zef0|I+6cn#fi9c&_M^BSD4cvsbh4-96t%p&PKzgCi^(uOJq;9f%*@Q8aV&In1Oo2o zMrip=P?6zr!zCmOl}^ji)d8pQ(auGMUm3J~miu4Z+fh5ei@hn=SoF`t7Fol=+Wg{q zfQzSADH5PCnwQuu9qi|Gz21BtUtx!Su(^18f$XJ!6MlBSJl@m1%>qmAF9R+6zBh{f zCW+ZlRVb@rgULfgPz$=WHNA&3H(C~D=`WHlMQPE*E(~Fw*3&ubue(WS2?yL;%F;d8 zw)Cves~AN-w#QMY@mPCmT!1DMwiT9zSX3fYVl>F3U%oC5Y!PzH-H`cQqCyR;l{r<( z4=USRh?igH6ijeAa!Nc=GAaJ-rh!kKfala;?~M6v477zy2pVVs&AuK``UD@0B!zuAW7QJuB={EI1keK zO2@Ii^St+K6?)idwV8~&q}B_GTTPcqv4{w_OQNUKLYALD+r9uY|2@BnY+fcgA8oX) z#+3Pd<=kf^56Ri(*SV9Ht7WsA-;GTWhtK0ZLW?LExe3@0r+81 zt*_!Bj!C=a{!|tqw5S%UT|k#x3n&1Nf3@o~7d=*zpj#M;LwtRoZBol0t2!(yDk`p^ zphd10*~LCns|)}n^5SM^H2^Hcy@=F7fOS&uV6L+Fnga;q8du7cNO;~eqVe|uSO7ZQ z8KkE0NSD7rzi)0agm=I3&!1(VC&*$O=H9VJ7)#Z;@yq)_PpN0@CF>cX!EC@NRkv)s zw%8g5_Ml5m4dFzJ&9lMSG#Qzhq9P)k*7IHOM_ zubA}p#wK^H)#s@*2%}W1+F~N-Bcu@=9ui_S$LSdV$$y?y+mfL^eGz~qj2uK0LaSqe zZaq@^-Mi&Ov3L98dBK36w|^iq@tPMmU>^1^FEb-!hy+Q*nq*0g)kd647H*} zO1epHh+QM1>ii{!^Z24%o!&Q|skepRyGL*JdMtlX<{(4dr#-Qvtf~AzYBlYH#pwmt5eB?l6d}N{8qp>xrbw>pRWK{F6s= zShcBNNlHj00{GxYNJMOw)9=HJkNPEgAbU*gyDsu@$EyRM3 z)-GpA4$}Y^7I!NhqO9Av8ah_iPnmy`1(JkSH!z=_{H8T&%c9p^Q`f(4yIBTgjIN|} zhptkrP>#r>*cQ)NklGi0qib=bDNR-%OtDV+?=JKJ61QceFcn2*CFQk@h($5{x780e zg_v7=W;JiN)k>`MLP%aCN7=@+!Ft@ycjxH@!hJ1nL{!poxVr7maPS&zGCC_SD?>s@ zmzR+#n8zX}wlXv{?1@PH-RWX~cMx`Ym>Diu;NAqR3tHOqjQ;|1zr#@3QvY*o&Rz)X zupz30bwGAhFJLTr3^+_aXGfh8gN~gv^=#xL<2;UK)@8pUl1mlHzi;5)9KZP4xS2fE z-pNJTloWLTO4J%fL{v2UQ~pW03OyPc8iy_a$3I^i>jYdRd)v;MP6FxbVA+9nNNucQ zKhl1l6%8qd*~OuJaY_8yYZ95z7kF0JN9V3}V^Q?lfrK^dZt7)rqduqf{Tc22!J4O9 z{llI$9OQ!^yytSd8In)M*vH7%!NZ!a_!=;m^(4l_qs9St*9vFOdCWFXkUx3^h+vqN z($+^-TS{P^n?SSyI&4 z@3hb!R%FP(Vafi}yIT;pbPoB~l(M)2ot%ayXxj}8zCsv?iHZ3M;Jvut+L>*XA%6(0 zAPvrQ6~-@LzJ&iECL!T{+>SWPfA+K9Pvj8m1N$lVw4T#1u5XC}_nntB8pRIK!3I8J|6v@NxaOTl^<{za` z+udK=M@q3fZ1goKgc5SdP-3NU+h<&4K708P@P}RWDWaGy>_ zq3eD;R+cV0M1o@xe=J+)_#-s5+p~6R7`RyeZGPJB13-qsyB~^khD8 z7@j6!9wEMX@!ina4vKZM*h8-goz+42r#Yh#bQsWB)+~9Il$6wKcC(0oJ_+~t-*+2t z64rYZhdvrkQ@-@K2iAsa2%ukkdLkY@OrR1=|FL?!ttqh_#X}9O zZ#IX-djw?0H?wwxTGU(2-IF8L)+3%=<30t05VrfsfuWfV0 zXjqPEkmA8376pm#HED8rZ(72qeyb*2DXgAmBkDOjC!T{TmZI~i4Do9};sC;}L!V-J zz)}6zzGguo!6sKF1G0>>wC1Kd>%gh(xpabY{KSA6Trc74Xxy3)O^-Iy7lQ;m7LJIY z{Bo*RjyBh+pC$m`K>DG7y)~Sa{Z^M`bnnH~?Xt^i0GuX!dr7Fb=X z<Jw zq8F}MJO^Rwc#qLc-hV%X2x+hx4E=U1!abB$BqXwL#kH|)&^dnNDe@4v~K3tulTPQ@%LR9?)95zP-M_w$(JFT_AnGUK}5`^;MCYzbOGOuSL81B_O}roo3k| zO4VA>ZMb+#my7{eaUP1YwU#D$1UG^QirgBm=bupsQ!w^rTJi>#uOFv_tb$?xe@m(im8gJO% z2HOb({K9sYQR^2n!}B5HX0cX>YPZRCDEizJ!OxV^NpOdpC}dYY*F5`yKdEd3+GaQ7}{As~_US zZ)wsv;=JD$Soh?J7Yfm_v~O08*qm{8WYZoaD%stWpjj@l6K zprjn7U)|j?qPk1)r6&SYgaou?n#IAvw#>08yI7g2B7-SXXj2}f-a4tOlkmbyd@DA@ z0p*)11Axd%B5HoOOatROyWJ8zUK|pznc~$U;acwm=ecevDOR$-OTX{Wk z24?Ei-;2IkFCPth@@CUU1jP}9%wL_VL>P~U)=87~Xf}$Wk{yr3`!LuKGp$3ij}53{ zb)8PA;q+GFyu|&-ymq*InHUNc9Lv%6({YrVDod$+CDc=syzxO{KK%TV8wn0*ZHMRg z4nZijAXpEKV@%AL-e}QSZLUQ5(cvHDNLR-|^R4xi-}Zi@x|h6(8I9un`|+FZ>B%8a z_4x}38Ry4}(i?G>m#)_R;8N~LP}|aDIyfzj*^865!Q@rsmA|RMcuiJbva(<_{%jO5 zq+-jc_4en4>=#P6edBNK5!Fdp2;NV}<(0|JE7Oq>yo~7a2@=qe96<{*6@0e`jgh@ zitM{DM{o_W8%tAluDA3d7>rL|!}?TBlg@Uw@4pah-AN7%zeV&}(w+O3CNJxVMkE%2 z^WhUS1o3Y%DKb1UKBJ9f#ZQ&ri1j1QD`Og41bTukPBwft&(qe!z0(<=rtu%pQ$aK2 zo?6~PqkM0y`6Y{#MN3t|h?0wIddm&)I3oBjl}nE>%CpMl{n)=Mm&6e;L^4b|%Br02p*Dg?@Jd`cx(MmXYS#z+ z^)RpUQ^r0|I4Zl!M+3UXKOw2|$xYUC$^ctQr&Gw4p-YSWKw$fZro@JquyGAuiofwY zdGiaQv^c7oG6$p`b5DaK4Pf1z0!r3G(lmaz3ZJLPo?gHn^u4lJ$_vBfNVPNo{YX}@ z#w)(;H^S!%m=nqd==qI!Fx8x&-0htddS#f3CjduXQ@8Opn9ed(g);tj2jPg0{TWt_ zr|JywkSlmuYewDvyg3Ps{Nl2uMQzPOlAx%7_HDJ)3)XlLm#OUdM%rnBmh2eaZwE}IF>7dhjr}jQJZhPl*J`}#I>&fH znBOVUV(e9OpX-O}H+u(28Ifsam$1;%X*WjQhN``j=d&AM3>UoZlY8ZiSC@(|a;T~l z>@vD*HC-nMH+n{V44BjR^ulBCgUmQ?-Ss{KOb{=UJ*ylX7w_f+!X*c?_N}M-qlb^( zE)RDXl>>Y=jVd@d_n?_T>J_Zd0g~VV$>LBEl-eUaw3Z3Zj8!@O^q|0gn{6e5(i8y? z&Yb#U1c0S7&k#5$=)Cb|6|#VaUkSd5kunZ{3$i~xP?!!#<4x93>~TpbGt(JT_C~Ms zB?byfPQ<^T3JCR*67$iWNkzktmNG^ry6eS;r8*-JoHV^&&j*wYyoq~f;KHZiGcXw! zP<&XFc6~}{F3f#~dj#l{TNm7DV+Rl(#liuTGoV0;;OXw^`P{M~4Q$38%@^WDpXonH z0C(VDPk$1E5mVtjFFV!TUwrVudwIG*^^)$g0yH6AwIf_8I63j4TwgIQknh!OKmHSh znfLI=j2)=dvI~%-o|ECey#tF4PZX+`USGR9O8!CpC&`1h7^m4oQkwE`7mxoxpG%Pj zjBAEj48-CH>xn)}Yr>u-Obh>?^+-le#{=AR-oRo1BOGG2yJ&Dg?N=Ni-eDww3SR4L zwweJQ&i-%m#`BeQL(!mLe0kLE7X+9Vy*m|ff#a@WY`=g21xyPn(~;DvaXAX=MiXOl zssIPu&{TOjId(AP$MK(@T;?{u>y;jZ*LOa9%%hi%UYkQcEAvUW3lj$}~p%Jqt`jLL)=R)n=y&VQu$2DOpXHRW5cFT(Y7+*U zdP(-K(9KUr_FW&AHD=lN_Wv*1-a0I*aBmwO8fgTiL+S1=1p(<$x}>{1Mw6jPxss^)2QMQ14HXw z&{6SvcM|OZY551v-*dvnD531ESYV9L4WzC6XQR{fe0LA!9fhYq^#;Vx#ph$C#2+ek zYE{Gh#nb0;Rzh2jHh&hfpDcQY4*ozCQLWWUfR$*Jlo@;+!J!Y5$ce^9eDjWl04>OG z>u2uw#l3c1!wX$%83DRT9ttw+RIwL|vi!ua!&*H8g@{_4bf$pbl7HYnO}W3g4=n6Y zgLb?+YePyg@Mc6Z&NyKF)ytP%%b$75iEqn)T{q3GC_VbN2BLuc24~5kF%@5RiJU#a z&=1K;H32o?i&ApI8R-F@U9pTCr!Rdsj*uQf3*Jr}wfa_4@{J>MAWRQ{k^j7_|k+3i_!AB_Fey zGT5`cmp-szr}zA;#XeioYtY*a_E9~<>;3o%#du|phVtGDt_;AYi4&;kI}&2aeDYO5RKx` z#brf%<%V_Iqnzo#5fkAY|64IpJ$4#|-#6E2Lo5D_gZTBit~6>6m>?;^22rDq5BgOb z<*F77O%pkYIl!AKz+21{ryOIy2;+~@5vBPNfu+~|vaGsnj`mMTCsU9Ht(lv&>SE^3 z@EU=)V)8~puGh@BXSZCv)l?JoJdHxQR!Z}bQ>TxbFr#QO6KCqH zS?tVhJ!pK`$_s2o9{X?*s|h|CZMnW*^B~+G9{ZzU0A;HOcgi1Zte)Y)fil`IN7m2X zDtg^ur0%`|0-0X2&YE5_ zwCU`H8s?|Iz9@m!z`IaU#n8rFSO36N}c$y=xY;Xp6I2d_|hi zNpuQ0R})r9Nw#ijLlI$FOm3Adq+oZVg%8-wFr;vh<#cQ|dm6|#G@5O7)?3$YcI$>R zzy>>aQ)&grNdjehTeXKS1?+@Eew=GX*Qj2=?WUQJw|!_Tfi|cZtyv%JM8d#0L80-* zX7+;cGc4Ka0ey~)kqq~4*S$L#bH$7b^YLslrFv;j=}_c!uIcCCJADM=QnA0{?23e} z`Fa8YnEl2%cx4=j^tMz&jJF$3TDwBag5KtCj?MZ8?`mb@NArq1tGzzc>xSW)9j!4l z4gS0l%Bd-<6kK=x_@504Nz(4ILa^7pL74p_=6+K$j5P6p*d$~uNy03H)y*TgMh2Y? zd2C^FOyk|WQtJIM;W2N8Dwaa6TGm<5CE8s;XZmSz*S7#Re*@9{iG?_E>t? zD}+Wiyji4RS(TcIDdru?{iM;*`%y!5cCouGLVj-txn-Q#%c+}*_x{IVOZ-o#s1`pZ zPQUxHH{}gS>0}#v3Yf(oF8@s4&4=pEUv<-WYREPNvfAp6MHnRcMc*Tp;Y~E6#mC~<51G7`Do1fN-SV~rX<)yt{Kn6fBqa2~84^idV~YNL`P!6{ z7M;YV)cpO(hd?RHKRN=ANres~upcR4LjqreCp&~-pb|!l+(ct|&}%BqZ_n^__YX1r zDeg3Lm(|{aKq_2$wd>(i(~+7D3En5A28Io+5`~qTjye%V?Ob31yc z5}-1Kyvv?(@eXd_0C=CsSyCo|_t9TVpiCFrz1c&cKWBO?_t@2zb(n{bs{qNAgmDuD z7#wd>Z-*(9-@{%ln5Y@T0>M`nOi&DUn*KEg4R^1z6biA#(kj!Dh`t+7C}bo6y!MXy z6}e!U2O7SAL?7DE|HeeNjOQ_g2C=*P;* zvMph<6|bp4FF*p(*KUrEmS<)50(OF|b&1g+(5n`@sK6iG>ayNPNb zj(4j-qM6|5&wqPrFh)WzkPRF@USL!d2nKwf-RosBfzoFUtk#~@^`->21!O>0N9$mb ztSQ&Mh}xs&H;vSg^0CX)nCjksZ2Lv3-zMHchoMozKaaFVrr^~o#O{W&y1`JF#=)LG zr_XBRYxC!;fgSI)L&`*T$r{-0`*Z7TexO|+M=bS{-zp~gjcu2`K7+2m0{s&9r8Cm= z)NEO#{?weS|8FU>4jozt?;^0bkH?4xax-c&UB5V6<<%Arh>GE}e1pHX#ntp8WU`D> zwrtU(G>SLxA7<~H}I zKT~ItK29H|{X^yB&x5~P!pweyeBpIAAof{5Q&imO zzWOt7MFno7=wtVWSbDj%x8gho>~wsNtpvr>b&uC__^S_>!`j5)QBQ(UeV33rJzev! z;(6?a@nP&-N$OH4`<>BASHo^rxv&w*yQ+V9W+HzD&n%+2%jcITJ6fsr;3@a^_`0r0 zynHT)uvT6t_2+|+g(F1Bq_;6_Ea;N_)HEvug-l;@$NrMPd9(fvzDi316xq7Bt8Qsh z@8RsSaXcX_iiFSM@|)z;PA1Q3e8EM5;sMGgaRR8AMOMC1if4s@qZCQFzM4|`( z%_D{PxZ->WoNSHVfMqA3)0MXTD}mCml|I2OwbgHL7EeA=)FMx`&BX8fsqr)Na8H|+ z32l>CD1JOkgdhoVuY-tjp@J5{nIf~V)t$F>`a_#|VnhXnyt*Gys8@Ba$4EmjVf$8j zA*XYi3VXyfHBQCt|KRtgKd#v6)SSEi+$kd8FE$jF;QY9qXEUG)!J)|cj5VB*>pEEj z%N+(L<8AkK`gj^PM1yx}y6aS^mowvNoZ!YtE+|7;&~u0{vNkHr(P_lV$eR(_T5YFaK-@4uIFL;_Ketp~Q9 zcZYS11{P3LZ~UaOBHVk{q0oTq*hd}l(q^|;AoU>$-SJV&)#P^?Qq8{fUu>P zLkK_t)CqE?1;nCR9s84s0_9iTKoJ7d6jy5d$*)ecY`EPhtvsvxi{lH1b4HTnZ)x^~ zxrU8Ky28U~!$Tr^FYuOUbXhzvNyw<^-Z}kBrW#rs%Qs-OfCHr)8~oYddo#+@Q{H8hmHGM@g0iI0Vh&Wgrha{H`eB<07z0b|5BjbCL7D4;W* z#5`|s%0-du!VV_)&;M*r2KF%0>_2dR($vz5*k$++Aygw^aU4(S09S3ib&4=~Av+#F z@9}dhu&1lxYrt`L4|Sz0dWbW(7?$VG(I@NAVy-~!!@cMX>m&gPWDP-B46_j<<`=)% z=JekNF1qsa@{ok%f#(RT^CuGFZa)c|V@MlCxhiPf+NQR8mCt|Ghh7o#v2M}20P%&< zVzn%Z{Gg6CD^Bu50}EQR3-7_2c**3wRDoK-TB9n}8P=r-4qkqwW9B=*_P&W<_tcmN z#jQF9ZiU)wRfQoS;6n5|v6y`9>iqKA%!`5u+42QZBGA_JRQ!a=GBz7`j3#2}=WsB7 z5!W(Cx#9!aa_GovvAkIu{oJ=3&b(UbdYOeApwkZ*oo*nH@Gei{=d^UJY2JUtIa`9d z?!zSC#bh&DQNPRvU3ep)fJF3-K!sw{uR{?LqrX6Pt&Hz55vdwns+`Z6hm?>&HXh)n z40*io_ASGL*pgfKdWQ{TDV(6`wN)47BBSnL7Acc!QFb~vAKi)U5CRhfv5oHp@rhCn9n;LQ1f%v z?jh^l&gcEB>#-eIS$dDUznE9GZNRKWFS-$oQQns^F#b*@19wxF+RSI4wd{d@Px^iA zrd?{k5;`WTvkM%qS-VMs@iykUEgt>uF|N`I_lTB3b+i7zaj1QYSHf2dT}?l*fi%GT zZ|nd~`Zkyl*>$0QcQRBx3AF|C#+adqsp4dC;o-qk6tsT6zm+7}Ta?cS|9Vc_p*8tz zGqFOW6-oeN_`cJ6nw~{fXTJ0bTO9kHY)Z?=9N6T1J8y-~%Pm|=B{Ek|$UGtQ*q32` z6?pywWOmL4G|&wn??bS-R#i}f4fMEH0zkgC(mUJZ*$r6iS`e^7e2te?1xy=c;i%a#I8;1}-whENaN&^`n#2njmX37>TZTbpAH%$V%=%}9{hJJoq zCDwMZDCnu8dl4qFod%sL}pUIn9)KA}65inLh`9qIs3)2WFZsq%UjTu&Q{~b*S7R z`v=SM5kP+PX5NJrm9xYSWTiV}V4=#TS4wdtFnwxQPu#P3xE$Q!ui@42o)r2ji}JVi zzO~U*84ve^_p`T(dVAGnz<3=X`?w0c&UaFT1Fs_1tCh(GwLP!hyL%7yMQJC$cEk{y zt#usZ=$ehpa4Fi`vQ0A4!^?^T=@T2Xysx$mTG2`dY9@SfBq!9tn;qPh3$NC1LE zsl@804oKZGGGX@*j3-Rl0yIL$%BJsmxY~!DeRJ~I<$rTHmVEc@YyUp6q+N&cq%brG z6oxp~Y*io3kN2%03@T}}zep|tA>Tr)-=~n2HlL zK!A2ei`fGOP?!LU3!vy3)y*+tcrkv!G!pB|SJgddIjx&9Gm7%5)U!S;?#J6nIZYK~ zLjLT_7Jyz=>DHTJd~!F5N9^(gQlM4v?`%IajgaA$*Bcq>6x1&ZYKn#vWqV>`V#*fS z+^b}F_MiUj%O*e`!uEM`wx_SJ9(G9_ZXArMsGtydryy-wgv($l7n*e5Ko$DmjF@GV z2<}b^_2cAzqm5reeo=faeMv`CfT7(<$_pbI(&De zDlCW9P3HZ0FR9>+7?q`xOGHg3QzDaWbqw(>Kz6IpPlt1g{FQ^)8`@S!ns2dzT`jk9*!6P&rY#jpKhBfKSIRW4$M-7K*9r6n zn4Uwsoq197NL?%bp^I)V=R`iwLAARU!HO=h1MCtH=)K{0|0pRDm z!S%n=o|r=@|i7?l>9N>u(r^iO#qgBYS^ZMqbNa|db84(bMO>r1k2t?6WX zl9@W5-vc-Qt0fN}$WQce+heBe^Sk#Q^Uu;RZ&Ay5ie!+CQhL0xBwh_dcsY#>oYAJq zOWRv-$yH}NGp7Tx+aFg*PvZz$xz&jLwokjnZeB_$ZPFYcUd7-3PyAZ0fdhN zj>olL1s{?Dz0~kQ*H*cRTqmAXiF#9qb=@Kft>@)7!DefobCkU(LWXUCp_ZQ{I;e9C z0<#TF`mnP_`rC;0`rGZ{xM+Qlak0p0vF@)grp=3V(7vCUs)hxm`|%U>ev3hs85f%C z_cZ!G{^qZ5s^c&Io$nO%x}eP4Yqo0=Wv!inUM#bD=S`cQ)y89kN0yPQo*iZ9@j^Q~ zOB>-H`CHt#RnJW{rx~Ov<&DR+Ok{aAHb`|#s{-du#(RheBC6g-RP_X_5#-9gPn|Yf z5aT}nDkZ6ni07)^3Z`szHy=xH8gt~l%Mj&m+3e2A5k*#9@+=ESEWX$}-1|ydvS_I# zgn7;;(HuH}jrfI8^B215kE28VAPFO8h$i)gE&0H~(wu#i-&V`|e$2_fVN_yM%LhYY z_Y2?hCh==X5f32i9>LbCw3REY$=MlbSD1Qp&~fDM3w>O{HQA8*)A19<_MVVc38H2$ zrL4;wC08-190^Vad#El9VqlWzV~e z>uG$Qlocifc{DOKfiEl$q(0bfz1hUG zFik@|E0YgL7(4obt^{Gd@bDl8pe@tLXyT96bHpwxhP-_*vOHPZXBc0Mgf>pPnGHP& zv-E_MIiDRa^s}^O~u#bNNF$q-Vn3i9062WhFlIc+il6GnX!~0 zRQK{Z$i>kV_PM5rYgj4z{`z?X&&JRaP`f3&T>Y)Lp@+80CEu^cZWWZJgtdww9x+k@ zR@c1U8jj6r$GU>#OkL&6rly&nK9B49_4B>(`PXnKz(_`0@|?4@RW$sdTwcWteAIl< zV0-#b{P+!}%;o4Zxc%FfmsB8~U5m;UJC-dC%KA#KHFM%klF| z`^?B6wpTViP$>=HbGaSB!j3&M>(&cOJUoOAMD!L$k|ezpZ!9Oi2XTALALnvi=)i7} z^DmRjY!0v4zcF@>4dO`XOabR3bL}vpn=4;v-kp?}c^|em8Pta2F~Q!RGJL>4oqz)R zpm>#0qFGL>p)HU$!PNW^$VPdapxo~;W$1pXdX=#K({59|g-K0av^7a14<1BSGIgA% z&PdSPfK@60sovAjIK#d#AM-x$-`#zCHlRF}pYbeQ$uqvO;<$TlDs2@@wO}T$=TGqx zyqEb@KTG;RqXg`Mz@^+vdZfga7U9{8Y*U?E5Ds8asO4x1+vGAc zRI|D&GLtOLyb}`dS#&#NSKe|a&?gL0mr8QC(`v9EnY8N8rx<=O(e_M#aGDxSGNQ#Z z9^73;QE8b!kVjpV%EdfoJ)bmnhdD3(rY6Z%KCJ1~*52+igL1Qta15K*3|)lIpR;}n zz5PXDLR?RLFQf8BTd4raT5bYYY_A3IZpGlsDvM=e1Vz@N@Dp}?zBSA22*a_~)PllI z7Kc1X$j4p#)@u!I8RnsEJe}PftC^}ELPaN3ws4_<0|mG7^@yjYoK@npwB6~2FM!UK z%)-aKPu>~tAMG-j!h`-?E+*YubdO$mQ+nQkK%fV9+VxwV`ppaZ>b=L>4Nn%OQM>cc zuco`o^qQU*@Z1ryyg+|vY45CgJHy(-+~+I8M03|2eS)1-F2GZJzHfq_BpjBgKdc_; z=^_B(zDiGpi`8|xx0tp&vy~vEC1myr_C*GP;v9O^XMgRF$Y!gf0r9;bPcV@7-*d>X zMY9qzPsb$CZ94~N!hHqeM9{)-*;`XPD~Z~zHoLO-4)i5cm0RQ0icQb7QjZnPNf!Ui zi4|(VIlfWXXPB+wG=;-qVUwiwn#f$k@bn{mN#4hqWnuZ4Sy#vV^~dca3~}sdoqSX@ zh|q0b-6XBi{Rq$jggLGBA|sFe6y>1FW;=WpXi!sF$Y7T=j?2T}6h_-tcQlmIP% z5UZ|%m(Ry%lqEH7o7_ZOt|PiFkKeHwx3Ul_t$(Hu6%-fqDhouUk}uC>aNIu_rY0&* zwMN3E8fi^0g?zZ(8e3DdfwNTIs7gEh_}CmZfSTg~uGyk)V6p;fUp2WjMYK5N62$cg z?AX(7ETfGa*Vy@78?Ol5tH5Xfy5Gc>ZQzh$`&7(jXuWf{A=>fm1j;5M6h8D6WM`-O z8AoEVXA)f{kx#g3=%kF*r+bZf`w(*IAiqspz>RDxbRAiTYq1%0MAj`Ivhv`D^i)YAy?4eY_yv zrT@C>K0Vf=y$F6xDEDoI15_o)UTHEx5!io-H<*U#S?zT1g1!HKlE_ZdRVVcjg1z1m z9ZbbDE%r-v^BVZi^^(FcJa@~&Uu#VIWz`XS6N947PGy4wUewm^Mn+kVAnfBxED{h9 z1SzvjKD&wY&OrShaQEzh1HlV36-H3v)tb}#rDzMgp{8c@{8o{)&>D@Ka%X0L$)|CN zSmco*AadzAnsXjjl2%^+n z7=6C^Q2eF0wu4W|}FUzHx}$4Ug%v|2rckUJ%2k~ZMht7vwvthSneC2ntB<}=j2$wW#!XZp*vTupE8U* z^(I-t9C(;!(_sa;z0&w@p->7D&uE&p+($C1Tv4?qk(b;x8ccZ%a!YmDSE#`Lk^ED! zhRfW8~~MmW3FTfL^PfEbaQ0VGgG+3lpCYONxsc%E1>p*Spt6HeQtS7dGJ=kzeSG z-BxD=ykhn~`-z^w^QrpeZvXmg5b`A%biKY~=$&PCFq5dD4|Ki1m`~{S5hC#37J^UJ zc+~ai1MJ$NG*+N&*;r&MRAi9id(dm<;!rfoB-tVh-C}+ul20Ew(sWf_L0ecYG&bE)!mMGmX~S1)Z;cT;+T`YO#C zm*AvHjaZAyDHGehvnZVsXGiscxBl;hf=6cwzMB9lgv*xd+T?Ct(Qd7R>s37QB?)m; z2Mr)sn*`MKoi0$)+jL8%5QSkzJG=+formShxd#2kJqR;p1O_>y;mSwLqI*oo#uevQ?T z{bXiqlHWhW-N0!MKVD6O1Y%?x9Akezp+}LXrV8quZg8pD2B!OFWb*tdlKE;Z*3;q7 z1w0#*Cn{0)Y~}I@mp=E-hpYZ?d*DJg`jHi!yE{`x>1;F)4?e$oRJXX7xbH z!bP{3r_CDjob*)8kKZN0O|3nWSQxtJ0Aj5SF@cktn6QJNd^_Lb$_J;G5BfM+{qyiu zPJzoDjs-|ZCpL!q$NnUn_5+7-+J|R;rORZ4acI?H7zg}u)6Tsg?T!g75kRj)C<#B< z__!{v@9bQitV>>o5jN1Ees5|B#v(8F$0Z<`39fs2(HA;4T*}A6;p|ulR|sE7h3e=O zA}`1FP8qy3HrAO9VPfV8hgB?i@o#k#fY+9uYc(jPwwetHKH53;H{4_Wap;fy$=7S1)%JVLXey_xMi6(J8!JmDq+jR#_K5~;qh-i zZ<|LQYL*YS?dOb%Jn%4Nq!jFyCnnoi1w98VY$O>8R7{p#Y;Sk#33-ME0x_J~ ztG~ZDEO&XK?*@A-n`Fkv0;){8gS8ESK&H2AYsbuocU1-FxzaWwH=S;W2dQ=RxU0k` zOknQ{f+sxsTsy`4JQ%utPDnM{}y2GWZ!Ga+#*>#9uvK;Y`B&9zVB-;2cSWFS;5m~2n*{s5#4U{$ZX&im#5y@(X9bIvb2_I1^geaUVwLF`lN^7{Zj3A`f zxauaW6}^*(y!K(q{`1tM!^Vu$KJQ)U!Q4%C0gT9whqsi0QKBoY!t} zrSFaHvgKsfzADZ6e4yJfGO?+{(C=toHGBBJ$wN6$_7zLk$cDv2)ZMt>3}i_8tLLM) zo}e?>;i>Hm6Gqz zUcBEN2>M9u<9e{FE&4-_sqt2RV#vLmMak>?v01N||9Fp;ID6KmSLu3x0r(*c^ZgXb z6P)2E2LU&w!nPmy&#S&apryvkTW_)2&(#v=#fK(L^06sj&o3<0Rh!-Cl9H2`DK!HU zSi;}?Omgx=1B2i7^_eZ3rD@c1a~tCmy%(3bi6YExxmbc77CkvEq26;iB%o|QqmgeX z2;mLX(rpCGLl3?y3$X*{Kt?`dm&&d(Fd2vq*~0jqpIZnNKZv;`HVHfJyN%QmLvzL^ zS1cT^M9R^`e{1qD7`pmgTUiwtctqagqwuUnG~@Wy8NU+;W%k2t8Wih{fo{qM>fC;h zXG1Xeb2oJetmXAY|N9ko?MjD_$0ppzJ?;pqLcR2qBy{%6Vt{8ngoC3xHw6Z99(%x$Xlma)6Po<@{W4?|T|!3)9A=B$UPZd0>yBr6mH+ zm-Az3@$e|!do%D36gd?QH+Y0gO|L-2={z7R<#gCUf0pKlNZ8GFmlkwl<~ZJ&BYH1G z)<8;bJjo%BYi`8tqmhr=r*u&q){>z4@c{Jl5gPna?(vR0xjy+Pl= z(sultKa2AW0TEqSnJ@jmER&&R?bn+UbeclnCT^bf9ePy7Z(>l|L*xMt5O|GwpnkH z>}_B`RzVPxIBnYc?82L4BvRGt$x@Ukn*3G&ND1@vkq2}&z4%)W&C<1jsS)W8%-qntjBL8tsFRxU*#fj9Pgt6`*na6T&P_ z#+Fp_feGtt_qMYxuKOCxhuekQ8m`@5->p%rrYaV*quk_iKN}x(T@QS>SH~NGvlH0$ z@A1*aG2)=s>Px|TImm0R^JG)LAS;CtC&t!v&$3`zo6XNWp^m@sfG;@4!{f(n9nS>@ zpKtF>tN&ln0YJgO7{s6qT0?lkCpI1Y_s@f@w9GwN$h-u71E9byfGeg~f1m&Qi!)SY zf&p0YbaXer?vF;6}H?64;3yKUT2Ua5Jsd0mO!_)kOSH!#>|1*_irbn`^~| zK{|)=NtvWkIt{gvqcApDQ&5iR@tYrXD}1`o09igi+|wNcgHV1AuUG>Rz9yL5!8EffE_lry*w@%0PcqwFstc=ChPr9^O;|-r)F38ks&99nM zRW4CqTU4mCC{A>ZXv?^UDzwpS7^?fp@o%xQGz7;x&<-4U4 zyHN*mFnj*PMz0#$ulmj+(;SA?t2Mb?~WsU3k3hZ7{_2RdCyB@YBDFi<3mm9;A z5zG(8v2|AkGrn6`xl$E_|eM#seDHYA6T8gAb z!CKoRPlXe$?bnlc1%?G+H(saf;fS7G+4e8>cTH4*weOgc=uORMZ5bEuRL6*x(TB@LNEhAP z?wlB668Gs>mwpwM#gDF;ZF@TFewnbI{S^ z^C83^tbGCo zf0Y-3`-GjpNuNCkTBvZvq`#St#|IXr1P0$77N5Bg$v9_0$+1t80LOsTcc+nV%wSdb z#i7Rp1achK(_{4l&T0|QVPUTUuckz0(2j(h+_|U1_iccKKgtq1)NmG%W@Gar8{IA~ zJpb1yAO$#ixpEB;_`1_O-u<7-8HgTp_KRcFz#r&>sO$+o?RF!{Xz!gn1Vq6 z83@k(e+Pm*mlahm*+XMe8cQ>adC>^MvxR2~F1B^H=gD)Ru=H9Y% z?nI(I4WPozW|&*YI?dL;_Kz6)R!K;1Bv_C~Fs8ZR4X>)Z$asB8M~p78_XD5akJj+r zxn4r-`@>qW{Pe-6Ujv)p7k5$*^PO+4Fp*^QT@D{{Ra>nAyYHi+oK`yDTFZ%_#BF&V zYQ)|w5`UJ~SZ;rn$&tKH^n>WRc*MhjM1)7q^admZ3kp z#pGb32(kM_$ImJvF6H=dFyKDIr;DzxA6tQ!B?TvAkdTszO2Ypu2GrULgH_B4Mh6wR z?_d*entz0BY##w%E3(ek6`t$m#N}k5-~XKQ%odw0zOj11z<-&`joNKc;(mdh+;%2? zH*4@SSd&#yNfMbsMkZY0+Csc6o-wqh(9l?(T3>V617BvG?1r^? zqaN^GB)54f%->}DuVf66NErSy;%}oEti*UUUylm-5`cw#QoTW7f;Glv1nTx#-BC5# zMDPx796Xbi*Hw%Q{oKX7MM$At<=vUM3hH!^SQarFpU)Q4x0TyKYmqT@+kLHepEGiA z|1$@Pqx>OK>w`YFj|N7yskI2(GF`&E=W}4kafhJ@qx%#(Nr_iU`Wa^g6co7}3VpwgE&` z!f$z9w*TNjTD-)J{s^@aH|xo!33|30nQ(u6@GMdV>hp@cv1|KQFve=Zp^pWmvJFyq zvl;4lQ7#m3P}^E5&W9rKEOi8rYP-%H;nC_Rie}B{|N8cDv+;=>EI6mgxG8kyHnJFp zdW>N?^rPba(?-Mu^*khrQV9{{c|CUF0SFdNPd?wVMA!wjHP9?8D-P0=-HEV%xMf_x zNE&?%1M_fi8!>8m-!>7|7@2lUeX7iI>Hd>ii;i%${1B}=vTy3n{C$jAKGVO#ZvkA~ zGK0Pn=SB?bv|TLVQC6A-Y35gm%oz7euem_qQevx=;A3#2H`7zfD1!^R|8w>~HDcVvhZk8I0 zz*aLnYXn-CUZ$3-GoXe(=Oi-Y(SM_AT$QY?%MA2J8K0t`v57nS{TwN7h0PgPWQ6+Y zkg}9CH9UrP|11*?UkE&%R)*fMH6xE`Ur~#@8rH$ire3agP-sRjuW5ro8!bM@v<6`= z?1uRx%lN__k1fufDkcE_DjkpwotvV!tmb2{_^_TgMw_i6g~u|2SA?2aCDQ=OxCICW zg5|qB)a;Z8;#6VH7X=~{*VV;odr}D*y6t1urvz(YHUY;{(Z;5|zx=k4QjE*i!N}Yo z>995MF_Rv7cCu%mXW)u~3JkaK=E_C&v-pkq^$lDJ|uuE?b_T}a@q_%V1yVIr;$YPx{sjVn2KGS!#nM|OD3Qb zO9ZaG{CjO}dMV4|Ox>4!o42o|KNe#>nm!n<%G&s})1pfFy+Q-y89yyh!g>0z zX!pL12=sdA2zBpN|L0lzim!eG?14??;`Bp-TJ^`Z&eeYN)sQ3PP7ewJqqEm;0u@ac zOQA#KdXM)!9;@#e)w#S_+$Of-<`qvxzq_Z+kRm*ROot0+-CU)<@Sf>L$IVQSc9E6eUj>zZodQ?35u#USiDLtd^~ zOIKH0yY2S|BTv#Ro7GnK%b3{6urHE694I)z zy-{CTu78;`bw`CKah`!Vaf2I5jDEx*U_ z_lF5PKzBHw;RQe=hHVjOnY{WJr?JnY*AFPBS0h;760nHj8&zRQ&y21l#0H?6`qmyJFf(L@!u#!vVT85FXT3W9{e=z~k>xVy|;kQMa$_<6dJA^Is@`PpCZx6||X_ zfhcAwv&oCP_U)egT~*K={so!7si&K2W^}zcC8>tl!Fhv6}4SjfzFjp&pA>t9 z_~8l;5s@eK!U`CjlAvN>OuIj0{pXk}!wBfHJ9@5y@E3UImM!N*mSd>k>rG2C-PC+c z@(JEt+*G~2zf209u2_Ed(|nY5;C#jMFz&g2*0Jq8zmQCk{aek4)6BA&p0!NA+ZT$! zaSQ!A0n~uS^~Z~&K!>I{W_suFTwK)W;TDbL_5bAk8{Z(ConF*nXAYMQ71hadf~TEt z$CTjy>ozgpDvK7HquSEH%cASp`VH~ZVvEgg#!<6Xw~C8M%9BE|;6xc;qB-1b!LO1u zCF#B5tgfsh3p~{Ow0Aoe*(+%iUrf%8MuXhbCeFCrBXxPG5rR+kU&(S{$7_%ksQNfF zA-VCw&nZVD5m)pYWl4RhZ=rQ!?S0783+sl>;7q)UAN zX}qVYkaSz3Kn$?x|NG+Y;t`zw0hAMYe^ZB0uEXIlyl)c|B!hCd9g0R)l;`<^o z({R0K%OM#Gw-{7r1X0}FS<|k64tjvfl(`B+@Xd*lib`})P*DBQZN-T{Z=lete{pw; z6wmwINyxhdJ(kR@9q`<&^GYhD{<%n(BK~WUmQS-?w0&xs#KQUidwwgat-S!uw9ECH zUcCs@wO@%`zXaZjW81|Yq!nCb4>G+c;1`$P$LvgSpiiqP6yiYBkXum*2=x2P3hlnA zS^Y5!tsJg1e)7ay)TuQ|yi}vZwp4obV)IEBv%r1!P-YcGP;u zP=Od?bKySG$&!4cA_DO!!hy;PnV3qW<^RW&w6e0Ahr7;^qsqC`D?L8hup|h#-5v3b zr?aUC~AOVI;gi5V0sAd?Km_*2cclNYCH{_>IK^%F68i7J^kO zebw(g_^V6U&$iUIQu>?{)h2I&M`eZvqGM$qW_#`}W$Nzx_X&wYhq$zBqXGT`FYEh==#t9AS9bZ zOBLF@B=65|(Q$T{+hTtBYrl_nE_h4zu_*Vs)XF(NQ!E7k42ewTU9K}Ai&9HZa->$i zH`iE>V7V+wmma}KVsz^Dg^R+>J-v|Fyn(@CssD6HL&FJI=GSgIP^N_X7Lx~zbOPnn=|*egyW z{hhznUCV?|sHmpJQb>r^Vy%hg4VQwlg}euu@Tev2G$*)P+ry$GLsA+Jy{{&)3#2V( z{lxRDf8Tsjox(P?Zr#dZx%1om29mKsx4EtiqNiZ~{9*;>Zp<&kjINu7TT-qbjY-G| z(q8n)vQ>zWHPh6H!gAc-BH;6xDrTh2D2b-4NP{j!ExlSpbF96@GW3vni@+)?&ci?4 zKQK`77+;qp_hDaDOt8kZup*$%=TB{$)pNN`qSOzx@&^33KUjzQFE1)Rz_6h#lXjxj zk(tG+<6XibhGy9yMcIu+X-o?l2v_3KKt%;dS34ldbLkltqZwoGN{h#w`^%?oPIP2SEo*U7e=eYWYIXu7 z6OOKZzZ0sjf6z6Z^6(_mIcPhNf_3#)H{4UNQ=ZSOlD%mo(irXfCC%`9Bya z-1mm%;YZKxc^oGtq^AgPC$Ej(ls1+@sthSZeTqMXOpYDTYj87P8SM9_I1T>&>+f3M;)-> zZADZbX+eLZpU-{Qn*7JdMP@Th^meUc#*W1OfrHu#c7cnY8XUEf1SIuP=ZvwlzWL}d$4+?H zM{Zpn*);Tvovk=t{_-S*LBnu0+zJ90eP9&=Ea_9-$z{jX2+{>WBj(;C^*dDPKW7VLhp6-d^g~Q8{t&@URSzw!))DG zTb;gn*%SkuSPsygEcVsTAZ;jrYq5NLq&MGmXM+qbij<73^hies=`Yo%h8FzbXm6lB zZP8)XdiYEwt#XGo2@el^cQQ!0+Do5=<$9Xot0mI-V60%UvQpcB zv&SErZ%H=vwm5zT=jaok9M0gn>LE6`Zr@X`VhMv4|KOK@n_ZDTxF zZ*=_snehYQ(J4NShVhRzmwARV00&fz85ua55;n|WVGv;O<*JKIEY6tPv_v|;)L`!( z&y@^3jh*+p{TpYsJHv-pA00ia&=S|_$b5g>Mgvp9=QB>t`w(jCv*+6HT?Uz=@uA5| zwK+=Xwr(|&|M86>H~dNzsDB6@*>nJ|PTuVoSF`c_yGFI%%d>4>XZYOQ^0++wSAoj+ zeG#rqCxW-1yOX&;?RDGyXL^>FSE?T`T(2+k%Rc_jJcW5C3=9qr1k+|$e#*Ogg3)c# z731xvPYC*cKL;A*{CP3^ko)8}Gal`@I+0y6IHIKyx&oA)-(*_)eCz3}XIv{eyexEY zgAH(1_r-6#T5nSBED=2za5Y3bgCX+S(aG3JA z!^S;7i=B_D`+M!$y+hZ&02}hr2M-<;>U?r-?d>zOj9!zaO8E#@fC*Bz-*j+cU$&$IZV=%hj?uY8Bh> zehE?o&%fI9aq(G695~$--eYNVx`mMkZQZEpT)x?1rAnXnK5IL^k=JCu(Vom!UXDF( zrJnQdrJpe54`oeFJ(suX>WY)=x-Rp2`{tfK_|#0YPo?lLJNw^lcO(?6TBYJ*|W7Y$9{lvhu0d!D$oXX>$_=*3ST{&{Tw zX9=)=-Kzzf++v1k&vkp-s&Drd!HWXg!SkS7CN9eGyw?jJuCc2?^Hc1g=q1lywOD63iSv=|KFAvR=>CXRa zm297~Y04wutZw#h;L%)8y`Tii&cVXu&0Q><$=Dp0xm#=rkdpM~%2}gRX)|wIHt^Kn zD-xhCoU?}q$Na-)>(8bJ7v)}Wl1d0RDlY76IL^z*cdbkp=vR{i&nGG_@dlpC92^m| z=#hj+gcIxJe*c^cN@{B#O$gezY1Uei+fmWL=33@%P&0NGI5&8xOp4x~7k>Q;aIuI& zgQLK$buWBfE}FP9`(Ac)O6-?YwCDwn4BRqCENTT!Lj$#gm$%|>Lm|}TX?$(^&+f3a We}!?l-!tI2C4;A{pUXO@geCxT(r4BH diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/sql/install.php b/code/ryzom/tools/server/ryzom_ams/www/html/sql/install.php index 0bf6c640c..8f8a7868d 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/sql/install.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/sql/install.php @@ -379,6 +379,39 @@ ON DELETE CASCADE ON UPDATE NO ACTION) ENGINE = InnoDB; + + + -- ----------------------------------------------------- + -- Table `" . $cfg['db']['lib']['name'] ."`.`ticket_info` + -- ----------------------------------------------------- + DROP TABLE IF EXISTS `" . $cfg['db']['lib']['name'] ."`.`ticket_info` ; + + CREATE TABLE IF NOT EXISTS `" . $cfg['db']['lib']['name'] ."`.`ticket_info` ( + `TInfoId` INT UNSIGNED NOT NULL AUTO_INCREMENT , + `Ticket` INT UNSIGNED NOT NULL , + `ShardId` INT NULL , + `UserPosition` VARCHAR(65) NULL , + `ViewPosition` VARCHAR(65) NULL , + `ClientVersion` VARCHAR(65) NULL , + `PatchVersion` VARCHAR(65) NULL , + `ServerTick` VARCHAR(40) NULL , + `ConnectState` VARCHAR(40) NULL , + `LocalAddress` VARCHAR(60) NULL , + `Memory` VARCHAR(60) NULL , + `OS` VARCHAR(70) NULL , + `Processor` VARCHAR(70) NULL , + `CPUID` VARCHAR(50) NULL , + `CpuMask` VARCHAR(50) NULL , + `HT` VARCHAR(65) NULL , + `NeL3D` VARCHAR(70) NULL , + PRIMARY KEY (`TInfoId`) , + INDEX `fk_ticket_info_ticket1` (`Ticket` ASC) , + CONSTRAINT `fk_ticket_info_ticket1` + FOREIGN KEY (`Ticket` ) + REFERENCES `" . $cfg['db']['lib']['name'] ."`.`ticket` (`TId` ) + ON DELETE NO ACTION + ON UPDATE NO ACTION) + ENGINE = InnoDB; diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/sql/ticketsql.sql b/code/ryzom/tools/server/ryzom_ams/www/html/sql/ticketsql.sql index eb95f792b..36ec38cae 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/sql/ticketsql.sql +++ b/code/ryzom/tools/server/ryzom_ams/www/html/sql/ticketsql.sql @@ -335,6 +335,39 @@ CREATE TABLE IF NOT EXISTS `mydb`.`email` ( ENGINE = InnoDB; +-- ----------------------------------------------------- +-- Table `mydb`.`ticket_info` +-- ----------------------------------------------------- +DROP TABLE IF EXISTS `mydb`.`ticket_info` ; + +CREATE TABLE IF NOT EXISTS `mydb`.`ticket_info` ( + `TInfoId` INT UNSIGNED NOT NULL AUTO_INCREMENT , + `Ticket` INT UNSIGNED NOT NULL , + `ShardId` INT NULL , + `UserPosition` VARCHAR(65) NULL , + `ViewPosition` VARCHAR(65) NULL , + `ClientVersion` VARCHAR(65) NULL , + `PatchVersion` VARCHAR(65) NULL , + `ServerTick` VARCHAR(40) NULL , + `ConnectState` VARCHAR(40) NULL , + `LocalAddress` VARCHAR(60) NULL , + `Memory` VARCHAR(60) NULL , + `OS` VARCHAR(70) NULL , + `Processor` VARCHAR(70) NULL , + `CPUID` VARCHAR(50) NULL , + `CpuMask` VARCHAR(50) NULL , + `HT` VARCHAR(65) NULL , + `NeL3D` VARCHAR(70) NULL , + PRIMARY KEY (`TInfoId`) , + INDEX `fk_ticket_info_ticket1` (`Ticket` ASC) , + CONSTRAINT `fk_ticket_info_ticket1` + FOREIGN KEY (`Ticket` ) + REFERENCES `mydb`.`ticket` (`TId` ) + ON DELETE NO ACTION + ON UPDATE NO ACTION) +ENGINE = InnoDB; + + SET SQL_MODE=@OLD_SQL_MODE; SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS; diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/sql/ticketsystemmodel.mwb b/code/ryzom/tools/server/ryzom_ams/www/html/sql/ticketsystemmodel.mwb index 4e9f85fa4ef274ed3dd0c290afe464d27e8a1168..42d148d172b72b3e1f4284c785fb913dd8620a91 100644 GIT binary patch literal 19027 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 DUUp*< diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/templates/show_ticket.tpl b/code/ryzom/tools/server/ryzom_ams/www/html/templates/show_ticket.tpl index aae1bf9f9..a7cbc7777 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/templates/show_ticket.tpl +++ b/code/ryzom/tools/server/ryzom_ams/www/html/templates/show_ticket.tpl @@ -224,6 +224,7 @@
  • {if isset($isMod) and $isMod eq "TRUE"}
  • Show Ticket Log
  • {/if}
  • Send Other Ticket
  • +
  • Show ticket Info
  • diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/templates/show_ticket_info.tpl b/code/ryzom/tools/server/ryzom_ams/www/html/templates/show_ticket_info.tpl new file mode 100644 index 000000000..cf256946b --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/www/html/templates/show_ticket_info.tpl @@ -0,0 +1,97 @@ +{block name=content} +
    +
    + +
    +
    + Additional Info + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Shard ID: {$shard_id}
    User Position: {$user_position}
    View Position: {$view_position}
    client_version: {$client_version}
    patch_version: {$patch_version}
    memory: {$memory}
    server_tick: {$server_tick}
    connect_state: {$connect_state}
    local_address: {$local_address}
    os: {$os}
    processor: {$processor}
    cpu_id: {$cpu_id}
    cpu_mask: {$cpu_mask}
    ht: {$ht}
    nel3d: {$nel3d}
    + +
    +
    +
    + +
    +
    +

    Actions

    +
    + + +
    +
    +
    +
    + + Actions +
    + + +
    +
    +
    +
    +
    +{/block} + From 52f90e92619826d79877003ee6e5fee3831a6b61 Mon Sep 17 00:00:00 2001 From: Quitta Date: Tue, 27 Aug 2013 22:54:45 +0200 Subject: [PATCH 122/313] ticket_info class done, however I think I better add a field to the Ticket DB, instead the way I do it now.. --- .../ryzom_ams/ams_lib/autoload/ticket.php | 6 ++-- .../ams_lib/autoload/ticket_info.php | 34 +++++++++---------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket.php index 8ecd00b15..386ec5ddf 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket.php @@ -85,12 +85,14 @@ class Ticket{ */ public static function create_Ticket( $title, $content, $category, $author, $real_author, $for_support_group = 0) { + //create the new ticket! $ticket = new Ticket(); $values = array("Title" => $title, "Status"=> 1, "Queue"=> 0, "Ticket_Category" => $category, "Author" => $author, "Priority" => 0); $ticket->set($values); $ticket->create(); $ticket_id = $ticket->getTId(); + //write a log entry if ( $author == $real_author){ Ticket_Log::createLogEntry( $ticket_id, $author, 1); }else{ @@ -98,11 +100,11 @@ class Ticket{ } Ticket_Reply::createReply($content, $author, $ticket_id, 0, $author); - //send email that new ticket has been created + //forwards the ticket directly after creation to the supposed support group if($for_support_group){ Ticket::forwardTicket(0, $ticket_id, $for_support_group); } - + //send email that new ticket has been created Mail_Handler::send_ticketing_mail($ticket->getAuthor(), $ticket, $content, "NEW", $ticket->getForwardedGroupId()); return $ticket_id; diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_info.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_info.php index b0be3e5ca..25f07440e 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_info.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_info.php @@ -27,19 +27,10 @@ class Ticket_Log{ //Creates a log entry - public static function createTicketInfo($info_array) { - $dbl = new DBLayer("lib"); - $query = "INSERT INTO ticket_log (Timestamp, Query, Ticket, Author) VALUES (now(), :query, :ticket, :author )"; - $values = Array('ticket' => $ticket_id, 'author' => $author_id, 'query' => json_encode(array($action,$arg))); - $dbl->execute($query, $values); - } - - - //return constructed element based on TLogId - public static function constr_TInfoId( $id) { - $instance = new self(); - $instance->setTInfoId($id); - return $instance; + public static function create_Ticket_Info($info_array) { + $ticket_info = new self(); + $ticket_info->set($info_array); + $ticket_info->create(); } @@ -54,14 +45,12 @@ class Ticket_Log{ $this->setTicket($values['Ticket']); $this->setShardId($values['ShardId']); $this->setUser_Position($values['UserPosition']); - $this->setView_Position($values['ViewPosition']); - + $this->setView_Position($values['ViewPosition']); $this->setClient_Version($values['ClientVersion']); $this->setPatch_Version($values['PatchVersion']); $this->setServer_Tick($values['ServerTick']); $this->setConnect_State($values['ConnectState']); - $this->setLocal_Address($values['LocalAddress']); - + $this->setLocal_Address($values['LocalAddress']); $this->setMemory($values['Memory']); $this->setOS($values['OS']); $this->setProcessor($values['Processor']); @@ -80,13 +69,22 @@ class Ticket_Log{ } //Load with ticket Id - public function load_With_TId( $id) { + public function load_With_Ticket( $id) { $dbl = new DBLayer("lib"); $statement = $dbl->execute("SELECT * FROM ticket_info WHERE Ticket=:id", array('id' => $id)); $row = $statement->fetch(); $this->set($row); } + //create ticket info + public function create() { + $query = "INSERT INTO ticket_Info (TInfoId, Ticket, ShardId, UserPosition,ViewPosition, ClientVersion, PatchVersion,ServerTick, ConnectState, LocalAddress, Memory, OS, +Processor, CPUID, CpuMask, HT, NeL3D) VALUES (:id, :ticket, :shardid, :userposition, :viewposition, :clientversion, :patchversion, :servertick, :connectstate, :localaddress, :memory, :os, :processor, :cpuid, :cpu_mask, :ht, :nel3d )"; + $values = Array('id' => $this->getTInfoId(), 'ticket' => $this->getTicket(), 'shardid' => $this->getShardId, 'userposition' => $this->getUser_Position(), 'viewposition' => $this->getView_Position(), 'clientversion' => $this->getClient_Version(), +'patchversion' => $this->getPatch_Version(), 'servertick' => $this->getServer_Tick(), 'connectstate' => $this->getConnect_State(), 'localaddress' => $this->getLocal_Address(), 'memory' => $this->getMemory(), 'os'=> $this->getOS(), 'processor' => $this->getProcessor(), 'cpuid' => $this->getCPUId(), +'cpu_mask' => $this->getCpu_Mask(), 'ht' => $this->getHT(), 'nel3d' => $this->getNel3D()); + $dbl->execute($query, $values); + } ////////////////////////////////////////////Getters//////////////////////////////////////////////////// From fa86db3cc7338ed5c529d4c038ef01b6e140f6f7 Mon Sep 17 00:00:00 2001 From: Quitta Date: Wed, 28 Aug 2013 02:24:08 +0200 Subject: [PATCH 123/313] updated, stroing to the db seems to work! --- .../ryzom_ams/ams_lib/autoload/ticket.php | 9 +++++- .../ams_lib/autoload/ticket_info.php | 28 +++++++++++++----- .../ams_lib/ingame_templates/createticket.tpl | 21 +++++++++++++ .../ryzom_ams/www/html/func/create_ticket.php | 2 +- .../ryzom_ams/www/html/inc/createticket.php | 6 ++++ .../ryzom_ams/www/html/sql/DBScheme.png | Bin 198382 -> 205565 bytes .../server/ryzom_ams/www/html/sql/install.php | 13 ++++---- .../ryzom_ams/www/html/sql/ticketsql.sql | 9 ++++-- .../www/html/sql/ticketsystemmodel.mwb | Bin 19027 -> 19332 bytes 9 files changed, 70 insertions(+), 18 deletions(-) diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket.php index 386ec5ddf..51f88cbed 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket.php @@ -83,7 +83,7 @@ class Ticket{ * creates a ticket + first initial reply and fills in the content of it! * for_support_group defines to which support group the ticket has to be forwarded */ - public static function create_Ticket( $title, $content, $category, $author, $real_author, $for_support_group = 0) { + public static function create_Ticket( $title, $content, $category, $author, $real_author, $for_support_group = 0, $extra_info = 0) { //create the new ticket! $ticket = new Ticket(); @@ -92,6 +92,12 @@ class Ticket{ $ticket->create(); $ticket_id = $ticket->getTId(); + //if ingame then add an extra info + if(Helpers::check_if_game_client() && $extra_info != 0){ + $extra_info['Ticket'] = $ticket_id; + Ticket_Info::create_Ticket_Info($extra_info); + } + //write a log entry if ( $author == $real_author){ Ticket_Log::createLogEntry( $ticket_id, $author, 1); @@ -104,6 +110,7 @@ class Ticket{ if($for_support_group){ Ticket::forwardTicket(0, $ticket_id, $for_support_group); } + //send email that new ticket has been created Mail_Handler::send_ticketing_mail($ticket->getAuthor(), $ticket, $content, "NEW", $ticket->getForwardedGroupId()); return $ticket_id; diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_info.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_info.php index 25f07440e..67c33bc41 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_info.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_info.php @@ -1,10 +1,9 @@ setTInfoId($values['TInfoId']); $this->setTicket($values['Ticket']); $this->setShardId($values['ShardId']); $this->setUser_Position($values['UserPosition']); @@ -58,6 +57,8 @@ class Ticket_Log{ $this->setCPU_Mask($values['CpuMask']); $this->setHT($values['HT']); $this->setNel3D($values['NeL3D']); + $this->setUser_Id($values['UserId']); + } //Load with tInfoId @@ -78,11 +79,12 @@ class Ticket_Log{ //create ticket info public function create() { - $query = "INSERT INTO ticket_Info (TInfoId, Ticket, ShardId, UserPosition,ViewPosition, ClientVersion, PatchVersion,ServerTick, ConnectState, LocalAddress, Memory, OS, -Processor, CPUID, CpuMask, HT, NeL3D) VALUES (:id, :ticket, :shardid, :userposition, :viewposition, :clientversion, :patchversion, :servertick, :connectstate, :localaddress, :memory, :os, :processor, :cpuid, :cpu_mask, :ht, :nel3d )"; - $values = Array('id' => $this->getTInfoId(), 'ticket' => $this->getTicket(), 'shardid' => $this->getShardId, 'userposition' => $this->getUser_Position(), 'viewposition' => $this->getView_Position(), 'clientversion' => $this->getClient_Version(), + $dbl = new DBLayer("lib"); + $query = "INSERT INTO ticket_info ( Ticket, ShardId, UserPosition,ViewPosition, ClientVersion, PatchVersion,ServerTick, ConnectState, LocalAddress, Memory, OS, +Processor, CPUID, CpuMask, HT, NeL3D, UserId) VALUES ( :ticket, :shardid, :userposition, :viewposition, :clientversion, :patchversion, :servertick, :connectstate, :localaddress, :memory, :os, :processor, :cpuid, :cpu_mask, :ht, :nel3d, :user_id )"; + $values = Array('ticket' => $this->getTicket(), 'shardid' => $this->getShardId(), 'userposition' => $this->getUser_Position(), 'viewposition' => $this->getView_Position(), 'clientversion' => $this->getClient_Version(), 'patchversion' => $this->getPatch_Version(), 'servertick' => $this->getServer_Tick(), 'connectstate' => $this->getConnect_State(), 'localaddress' => $this->getLocal_Address(), 'memory' => $this->getMemory(), 'os'=> $this->getOS(), 'processor' => $this->getProcessor(), 'cpuid' => $this->getCPUId(), -'cpu_mask' => $this->getCpu_Mask(), 'ht' => $this->getHT(), 'nel3d' => $this->getNel3D()); +'cpu_mask' => $this->getCpu_Mask(), 'ht' => $this->getHT(), 'nel3d' => $this->getNel3D(), 'user_id' => $this->getUser_Id()); $dbl->execute($query, $values); } @@ -158,6 +160,11 @@ Processor, CPUID, CpuMask, HT, NeL3D) VALUES (:id, :ticket, :shardid, :userposit return $this->nel3d; } + public function getUser_Id(){ + return $this->user_id; + } + + ////////////////////////////////////////////Setters//////////////////////////////////////////////////// public function setTInfoId($id){ @@ -229,4 +236,9 @@ Processor, CPUID, CpuMask, HT, NeL3D) VALUES (:id, :ticket, :shardid, :userposit $this->nel3d = $n; } + public function setUser_Id($u){ + $this->user_id = $u; + } + + } \ No newline at end of file diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/createticket.tpl b/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/createticket.tpl index dd19fd291..4445fca39 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/createticket.tpl +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/createticket.tpl @@ -58,6 +58,27 @@ + + + {if $ingame} + + + + + + + + + + + + + + + + + + {/if} diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/func/create_ticket.php b/code/ryzom/tools/server/ryzom_ams/www/html/func/create_ticket.php index af51c6969..5fce2d9de 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/func/create_ticket.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/func/create_ticket.php @@ -18,7 +18,7 @@ function create_ticket(){ }else{ $author= Ticket_User::constr_ExternId($_POST['target_id'])->getTUserId(); } - $ticket_id = Ticket::create_Ticket($title, $content, $category, $author, $_SESSION['ticket_user']->getTUserId()); + $ticket_id = Ticket::create_Ticket($title, $content, $category, $author, $_SESSION['ticket_user']->getTUserId(),0, $_POST); header("Location: index.php?page=show_ticket&id=".$ticket_id); exit; diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/inc/createticket.php b/code/ryzom/tools/server/ryzom_ams/www/html/inc/createticket.php index 4c3dce7b8..40b6610ec 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/inc/createticket.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/inc/createticket.php @@ -25,7 +25,13 @@ function createticket(){ } + if(Helpers::check_if_game_client()){ + //get all additional info, which is needed for adding the extra info page + $result[] = $_GET; + $result['ingame'] = true; + } + //create array of category id & names $catArray = Ticket_Category::getAllCategories(); $result['category'] = Gui_Elements::make_table_with_key_is_id($catArray, Array("getName"), "getTCategoryId" ); diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/sql/DBScheme.png b/code/ryzom/tools/server/ryzom_ams/www/html/sql/DBScheme.png index ce876402b9e9173bcd9473a61bda2b4dbd331655..63cb4c92bc242b230225fec89307e0bbb87a538b 100644 GIT binary patch delta 188689 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

    +p;ez`f;hZCau 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

    )ed%33z9 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

    ti!?pWWRN{iTYJZRQS&CsXvy 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 delta 181471 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�VTn-s7N_!=gKb$DZk7#RXeW&v#!M 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/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 Date: Wed, 28 Aug 2013 05:31:06 +0200 Subject: [PATCH 124/313] ticket showing works outgame, but needs fancyfying :D --- .../ryzom_ams/ams_lib/autoload/ticket.php | 5 +++ .../ams_lib/autoload/ticket_info.php | 10 ++++- .../ryzom_ams/www/html/inc/show_ticket.php | 2 + .../www/html/inc/show_ticket_info.php | 39 +++++++++---------- .../www/html/templates/show_ticket.tpl | 2 +- .../www/html/templates/show_ticket_info.tpl | 7 +++- 6 files changed, 41 insertions(+), 24 deletions(-) diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket.php index 51f88cbed..321409b24 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket.php @@ -283,6 +283,11 @@ class Ticket{ $statement = $dbl->execute($query, $values); } + //hasInfo + public function hasInfo(){ + return Ticket_Info::TicketHasInfo($this->getTId()); + } + /*FUNCTION: postreply * returns all possible statusses * diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_info.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_info.php index 67c33bc41..513967c99 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_info.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_info.php @@ -33,7 +33,15 @@ class Ticket_Info{ $ticket_info->create(); } - + public static function TicketHasInfo($ticket_id) { + $dbl = new DBLayer("lib"); + //check if ticket is already assigned + if( $dbl->execute(" SELECT * FROM `ticket_info` WHERE `Ticket` = :ticket_id", array('ticket_id' => $ticket_id) )->rowCount() ){ + return true; + }else{ + return false; + } + } ////////////////////////////////////////////Methods//////////////////////////////////////////////////// public function __construct() { diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/inc/show_ticket.php b/code/ryzom/tools/server/ryzom_ams/www/html/inc/show_ticket.php index 3179a0e25..d69fbc319 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/inc/show_ticket.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/inc/show_ticket.php @@ -36,6 +36,7 @@ function show_ticket(){ if(Ticket_User::isMod($_SESSION['ticket_user'])){ $show_as_admin = true; } + $entire_ticket = Ticket::getEntireTicket( $result['ticket_id'],$show_as_admin); Ticket_Log::createLogEntry($result['ticket_id'],$_SESSION['ticket_user']->getTUserId(), 3); $result['ticket_tId'] = $entire_ticket['ticket_obj']->getTId(); @@ -66,6 +67,7 @@ function show_ticket(){ $result['statusList'] = Ticket::getStatusArray(); $result['sGroups'] = Gui_Elements::make_table_with_key_is_id(Support_Group::getAllSupportGroups(), Array("getName"), "getSGroupId" ); } + $result['hasInfo'] = $target_ticket->hasInfo(); return $result; }else{ diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/inc/show_ticket_info.php b/code/ryzom/tools/server/ryzom_ams/www/html/inc/show_ticket_info.php index e2ee00150..14f9c026c 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/inc/show_ticket_info.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/inc/show_ticket_info.php @@ -9,29 +9,28 @@ function show_ticket_info(){ $target_ticket = new Ticket(); $target_ticket->load_With_TId($result['ticket_id']); - if(($target_ticket->getAuthor() == $_SESSION['ticket_user']->getTUserId()) || Ticket_User::isMod($_SESSION['ticket_user'] )){ + if( $target_ticket->hasInfo() && (($target_ticket->getAuthor() == $_SESSION['ticket_user']->getTUserId()) || Ticket_User::isMod($_SESSION['ticket_user'] ))){ $result['ticket_title'] = $target_ticket->getTitle(); $result['ticket_author'] = $target_ticket->getAuthor(); - - $result['shard_id'] = $_GET['ShardId']; - $result['user_position'] = $_GET['UserPosition']; - $result['view_position'] = $_GET['ViewPosition']; - $result['client_version'] = $_GET['ClientVersion']; - $result['patch_version'] = $_GET['PatchVersion']; - - - $result['server_tick'] = $_GET['ServerTick']; - $result['connect_state'] = $_GET['ConnectState']; - $result['local_address'] = $_GET['LocalAddress']; - $result['memory'] = $_GET['Memory']; - $result['os'] = $_GET['OS']; - $result['processor'] = $_GET['Processor']; - $result['cpu_id'] = $_GET['CPUID']; - $result['cpu_mask'] = $_GET['CpuMask']; - $result['ht'] = $_GET['HT']; - - $result['nel3d'] = $_GET['NeL3D']; + $ticket_info = new Ticket_Info(); + $ticket_info->load_With_Ticket($result['ticket_id']); + $result['shard_id'] = $ticket_info->getShardId(); + $result['user_position'] = $ticket_info->getUser_Position(); + $result['view_position'] = $ticket_info->getView_Position(); + $result['client_version'] = $ticket_info->getClient_Version(); + $result['patch_version'] = $ticket_info->getPatch_Version(); + $result['server_tick'] = $ticket_info->getServer_Tick(); + $result['connect_state'] = $ticket_info->getConnect_State(); + $result['local_address'] = $ticket_info->getLocal_Address(); + $result['memory'] = $ticket_info->getMemory(); + $result['os'] = $ticket_info->getOS(); + $result['processor'] = $ticket_info->getProcessor(); + $result['cpu_id'] = $ticket_info->getCPUId(); + $result['cpu_mask'] = $ticket_info->getCPU_Mask(); + $result['ht'] = $ticket_info->getHT(); + $result['nel3d'] = $ticket_info->getNel3D(); + $result['user_id'] = $ticket_info->getUser_Id(); if(Ticket_User::isMod($_SESSION['ticket_user'])){ $result['isMod'] = "TRUE"; diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/templates/show_ticket.tpl b/code/ryzom/tools/server/ryzom_ams/www/html/templates/show_ticket.tpl index a7cbc7777..1ac5b2f46 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/templates/show_ticket.tpl +++ b/code/ryzom/tools/server/ryzom_ams/www/html/templates/show_ticket.tpl @@ -224,7 +224,7 @@
  • {if isset($isMod) and $isMod eq "TRUE"}
    {/if}
  • Send Other Ticket
  • -
  • Show ticket Info
  • + {if $hasInfo}
  • Show ticket Info
  • {/if}
  • Show Ticket Log
  • diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/templates/show_ticket_info.tpl b/code/ryzom/tools/server/ryzom_ams/www/html/templates/show_ticket_info.tpl index cf256946b..54f8e13d2 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/templates/show_ticket_info.tpl +++ b/code/ryzom/tools/server/ryzom_ams/www/html/templates/show_ticket_info.tpl @@ -16,7 +16,7 @@ - + @@ -60,7 +60,10 @@ - + + + +
    Shard ID: {$shard_id} Shard ID: {$shard_id}
    User Position: {$user_position}
    nel3d: {$nel3d}
    user_id: {$user_id}
    From a45299bfe513facca5e1a3abad092d69d345ceeb Mon Sep 17 00:00:00 2001 From: Thibaut Girka Date: Wed, 28 Aug 2013 08:08:22 +0200 Subject: [PATCH 125/313] Add support for LibVR --- code/CMakeModules/FindLibVR.cmake | 32 ++ code/CMakeModules/nel.cmake | 1 + code/nel/CMakeLists.txt | 4 + code/nel/include/nel/3d/stereo_libvr.h | 160 ++++++ code/nel/src/3d/CMakeLists.txt | 7 +- code/nel/src/3d/stereo_display.cpp | 4 + code/nel/src/3d/stereo_libvr.cpp | 642 +++++++++++++++++++++++++ 7 files changed, 848 insertions(+), 2 deletions(-) create mode 100644 code/CMakeModules/FindLibVR.cmake create mode 100644 code/nel/include/nel/3d/stereo_libvr.h create mode 100644 code/nel/src/3d/stereo_libvr.cpp diff --git a/code/CMakeModules/FindLibVR.cmake b/code/CMakeModules/FindLibVR.cmake new file mode 100644 index 000000000..eba9c347e --- /dev/null +++ b/code/CMakeModules/FindLibVR.cmake @@ -0,0 +1,32 @@ +# - Locate LibVR library +# This module defines +# LIBVR_LIBRARIES, the libraries to link against +# LIBVR_FOUND, if false, do not try to link to LIBVR +# LIBVR_INCLUDE_DIR, where to find headers. + +IF(LIBVR_LIBRARIES AND LIBVR_INCLUDE_DIR) + # in cache already + SET(LIBVR_FIND_QUIETLY TRUE) +ENDIF(LIBVR_LIBRARIES AND LIBVR_INCLUDE_DIR) + +FIND_PATH(LIBVR_INCLUDE_DIR hmd.h + PATH_SUFFIXES include/LibVR +) + +FIND_LIBRARY(LIBVR_LIBRARY + NAMES vr + PATH_SUFFIXES lib + PATHS +) + +IF(LIBVR_LIBRARY AND LIBVR_INCLUDE_DIR) + IF(NOT LIBVR_FIND_QUIETLY) + MESSAGE(STATUS "Found LibVR: ${LIBVR_LIBRARY}") + ENDIF(NOT LIBVR_FIND_QUIETLY) + SET(LIBVR_FOUND "YES") + SET(LIBVR_DEFINITIONS "-DHAVE_LIBVR") +ELSE(LIBVR_LIBRARY AND LIBVR_INCLUDE_DIR) + IF(NOT LIBVR_FIND_QUIETLY) + MESSAGE(STATUS "Warning: Unable to find LibVR!") + ENDIF(NOT LIBVR_FIND_QUIETLY) +ENDIF(LIBVR_LIBRARY AND LIBVR_INCLUDE_DIR) diff --git a/code/CMakeModules/nel.cmake b/code/CMakeModules/nel.cmake index 1547e2260..2fa51eb3a 100644 --- a/code/CMakeModules/nel.cmake +++ b/code/CMakeModules/nel.cmake @@ -322,6 +322,7 @@ MACRO(NL_SETUP_NEL_DEFAULT_OPTIONS) OPTION(WITH_NEL_TESTS "Build NeL Unit Tests" ON ) OPTION(WITH_LIBOVR "With LibOVR support" OFF) + OPTION(WITH_LIBVR "With LibVR support" OFF) ENDMACRO(NL_SETUP_NEL_DEFAULT_OPTIONS) MACRO(NL_SETUP_NELNS_DEFAULT_OPTIONS) diff --git a/code/nel/CMakeLists.txt b/code/nel/CMakeLists.txt index 2a392ddf9..53bf071e3 100644 --- a/code/nel/CMakeLists.txt +++ b/code/nel/CMakeLists.txt @@ -45,6 +45,10 @@ IF(WITH_LIBOVR) FIND_PACKAGE(LibOVR) ENDIF(WITH_LIBOVR) +IF(WITH_LIBVR) + FIND_PACKAGE(LibVR) +ENDIF(WITH_LIBVR) + IF(WITH_INSTALL_LIBRARIES) IF(UNIX) SET(prefix ${CMAKE_INSTALL_PREFIX}) diff --git a/code/nel/include/nel/3d/stereo_libvr.h b/code/nel/include/nel/3d/stereo_libvr.h new file mode 100644 index 000000000..76d1966fe --- /dev/null +++ b/code/nel/include/nel/3d/stereo_libvr.h @@ -0,0 +1,160 @@ +/** + * \file stereo_libvr.h + * \brief CStereoLibVR + * \date 2013-08-19 19:17MT + * \author Thibaut Girka (ThibG) + * CStereoLibVR + */ + +/* + * Copyright (C) 2013 by authors + * + * This file is part of NL3D. + * NL3D is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * NL3D 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 Affero General + * Public License for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with NL3D. If not, see + * . + */ + +#ifndef NL3D_STEREO_LIBVR_H +#define NL3D_STEREO_LIBVR_H + +#ifdef HAVE_LIBVR + +#include + +// STL includes + +// NeL includes +#include +#include + +// Project includes +#include +#include +#include +#include + +namespace NL3D { + +class ITexture; +class CTextureUser; +class CStereoLibVRDevicePtr; +class CStereoLibVRDeviceHandle; +class CPixelProgram; + +#define NL_STEREO_MAX_USER_CAMERAS 8 + +/** + * \brief CStereoOVR + * \date 2013-06-25 22:22GMT + * \author Jan Boon (Kaetemi) + * CStereoOVR + */ +class CStereoLibVR : public IStereoHMD +{ +public: + CStereoLibVR(const CStereoLibVRDeviceHandle *handle); + virtual ~CStereoLibVR(); + + /// Sets driver and generates necessary render targets + virtual void setDriver(NL3D::UDriver *driver); + + /// Gets the required screen resolution for this device + virtual bool getScreenResolution(uint &width, uint &height); + /// Set latest camera position etcetera + virtual void updateCamera(uint cid, const NL3D::UCamera *camera); + /// Get the frustum to use for clipping + virtual void getClippingFrustum(uint cid, NL3D::UCamera *camera) const; + + /// Is there a next pass + virtual bool nextPass(); + /// Gets the current viewport + virtual const NL3D::CViewport &getCurrentViewport() const; + /// Gets the current camera frustum + virtual const NL3D::CFrustum &getCurrentFrustum(uint cid) const; + /// Gets the current camera frustum + virtual void getCurrentFrustum(uint cid, NL3D::UCamera *camera) const; + /// Gets the current camera matrix + virtual void getCurrentMatrix(uint cid, NL3D::UCamera *camera) const; + + /// At the start of a new render target + virtual bool wantClear(); + /// The 3D scene + virtual bool wantScene(); + /// Interface within the 3D scene + virtual bool wantInterface3D(); + /// 2D Interface + virtual bool wantInterface2D(); + + /// Returns true if a new render target was set, always fase if not using render targets + virtual bool beginRenderTarget(); + /// Returns true if a render target was fully drawn, always false if not using render targets + virtual bool endRenderTarget(); + + + /// Get the HMD orientation + virtual NLMISC::CQuat getOrientation() const; + + /// Get GUI center (1 = width, 1 = height, 0 = center) + virtual void getInterface2DShift(uint cid, float &x, float &y, float distance) const; + + /// Set the head model, eye position relative to orientation point + virtual void setEyePosition(const NLMISC::CVector &v); + /// Get the head model, eye position relative to orientation point + virtual const NLMISC::CVector &getEyePosition() const; + + /// Set the scale of the game in units per meter + virtual void setScale(float s); + + + static void listDevices(std::vector &devicesOut); + static bool isLibraryInUse(); + static void releaseLibrary(); + + + /// Calculates internal camera information based on the reference camera + void initCamera(uint cid, const NL3D::UCamera *camera); + /// Checks if the device used by this class was actually created + bool isDeviceCreated(); + +private: + CStereoLibVRDevicePtr *m_DevicePtr; + int m_Stage; + int m_SubStage; + CViewport m_LeftViewport; + CViewport m_RightViewport; + CFrustum m_ClippingFrustum[NL_STEREO_MAX_USER_CAMERAS]; + CFrustum m_LeftFrustum[NL_STEREO_MAX_USER_CAMERAS]; + CFrustum m_RightFrustum[NL_STEREO_MAX_USER_CAMERAS]; + CMatrix m_CameraMatrix[NL_STEREO_MAX_USER_CAMERAS]; + mutable bool m_OrientationCached; + mutable NLMISC::CQuat m_OrientationCache; + UDriver *m_Driver; + NLMISC::CSmartPtr m_BarrelTex; + NL3D::CTextureUser *m_BarrelTexU; + NL3D::UMaterial m_BarrelMat; + NLMISC::CQuadUV m_BarrelQuadLeft; + NLMISC::CQuadUV m_BarrelQuadRight; + CPixelProgram *m_PixelProgram; + NLMISC::CVector m_EyePosition; + float m_Scale; + +}; /* class CStereoLibVR */ + +} /* namespace NL3D */ + +#endif /* HAVE_LIBVR */ + +#endif /* #ifndef NL3D_STEREO_LIBVR_H */ + +/* end of file */ diff --git a/code/nel/src/3d/CMakeLists.txt b/code/nel/src/3d/CMakeLists.txt index b92f58dba..2769d8e2d 100644 --- a/code/nel/src/3d/CMakeLists.txt +++ b/code/nel/src/3d/CMakeLists.txt @@ -696,14 +696,16 @@ SOURCE_GROUP(Stereo FILES stereo_ovr.cpp stereo_ovr_fp.cpp ../../include/nel/3d/stereo_ovr.h + stereo_libvr.cpp + ../../include/nel/3d/stereo_libvr.h stereo_debugger.cpp ../../include/nel/3d/stereo_debugger.h) NL_TARGET_LIB(nel3d ${HEADERS} ${SRC}) -INCLUDE_DIRECTORIES(${LIBXML2_INCLUDE_DIR} ${FREETYPE_INCLUDE_DIRS} ${LIBOVR_INCLUDE_DIR}) +INCLUDE_DIRECTORIES(${LIBXML2_INCLUDE_DIR} ${FREETYPE_INCLUDE_DIRS} ${LIBOVR_INCLUDE_DIR} ${LIBVR_INCLUDE_DIR}) -TARGET_LINK_LIBRARIES(nel3d nelmisc ${FREETYPE_LIBRARY} ${LIBOVR_LIBRARIES}) +TARGET_LINK_LIBRARIES(nel3d nelmisc ${FREETYPE_LIBRARY} ${LIBOVR_LIBRARIES} ${LIBVR_LIBRARY}) SET_TARGET_PROPERTIES(nel3d PROPERTIES LINK_INTERFACE_LIBRARIES "") NL_DEFAULT_PROPS(nel3d "NeL, Library: NeL 3D") NL_ADD_RUNTIME_FLAGS(nel3d) @@ -714,6 +716,7 @@ NL_ADD_LIB_SUFFIX(nel3d) ADD_DEFINITIONS(${LIBXML2_DEFINITIONS}) ADD_DEFINITIONS(${LIBOVR_DEFINITIONS}) +ADD_DEFINITIONS(${LIBVR_DEFINITIONS}) IF(WITH_PCH) ADD_NATIVE_PRECOMPILED_HEADER(nel3d ${CMAKE_CURRENT_SOURCE_DIR}/std3d.h ${CMAKE_CURRENT_SOURCE_DIR}/std3d.cpp) diff --git a/code/nel/src/3d/stereo_display.cpp b/code/nel/src/3d/stereo_display.cpp index 0f6d0cbfb..eace867fc 100644 --- a/code/nel/src/3d/stereo_display.cpp +++ b/code/nel/src/3d/stereo_display.cpp @@ -35,6 +35,7 @@ // Project includes #include +#include #include using namespace std; @@ -78,6 +79,9 @@ void IStereoDisplay::listDevices(std::vector &devicesOut) #ifdef HAVE_LIBOVR CStereoOVR::listDevices(devicesOut); #endif +#ifdef HAVE_LIBVR + CStereoLibVR::listDevices(devicesOut); +#endif #if !FINAL_VERSION CStereoDebugger::listDevices(devicesOut); #endif diff --git a/code/nel/src/3d/stereo_libvr.cpp b/code/nel/src/3d/stereo_libvr.cpp new file mode 100644 index 000000000..8ce64e07c --- /dev/null +++ b/code/nel/src/3d/stereo_libvr.cpp @@ -0,0 +1,642 @@ +/** + * \file stereo_libvr.cpp + * \brief CStereoLibVR + * \date 2013-08-19 19:17MT + * \author Thibaut Girka (ThibG) + * CStereoLibVR + */ + +/* + * Copyright (C) 2013 by authors + * + * This file is part of NL3D. + * NL3D is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * NL3D 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 Affero General + * Public License for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with NL3D. If not, see + * . + */ + +#ifdef HAVE_LIBVR + +#include +#include +#include + +// STL includes +#include + +// External includes +extern "C" { +#include +} + +// NeL includes +// #include +#include +#include +#include +#include +#include +#include +#include + +// Project includes + +using namespace std; +// using namespace NLMISC; + +namespace NL3D { + +extern const char *g_StereoOVR_fp40; //TODO: what? +extern const char *g_StereoOVR_arbfp1; //TODO: what? +extern const char *g_StereoOVR_ps_2_0; //TODO: what? + +namespace { +sint s_DeviceCounter = 0; +}; + +class CStereoLibVRDeviceHandle : public IStereoDeviceFactory +{ +public: + // fixme: virtual destructor??? + IStereoDisplay *createDevice() const + { + CStereoLibVR *stereo = new CStereoLibVR(this); + if (stereo->isDeviceCreated()) + return stereo; + delete stereo; + return NULL; + } +}; + +class CStereoLibVRDevicePtr +{ +public: + struct hmd *HMDDevice; + struct display_info HMDInfo; + float InterpupillaryDistance; +}; + +CStereoLibVR::CStereoLibVR(const CStereoLibVRDeviceHandle *handle) : m_Stage(0), m_SubStage(0), m_OrientationCached(false), m_Driver(NULL), m_BarrelTexU(NULL), m_PixelProgram(NULL), m_EyePosition(0.0f, 0.09f, 0.15f), m_Scale(1.0f) +{ + struct stereo_config st_conf; + + ++s_DeviceCounter; + // For now, LibVR doesn't support multiple devices... + m_DevicePtr = new CStereoLibVRDevicePtr(); + m_DevicePtr->HMDDevice = hmd_open_first(0); + m_DevicePtr->InterpupillaryDistance = 0.0647; //TODO + + if (m_DevicePtr->HMDDevice) + { + hmd_get_display_info(m_DevicePtr->HMDDevice, &m_DevicePtr->HMDInfo); + hmd_get_stereo_config(m_DevicePtr->HMDDevice, &st_conf); + nldebug("LibVR: HScreenSize: %f, VScreenSize: %f", m_DevicePtr->HMDInfo.h_screen_size, m_DevicePtr->HMDInfo.v_screen_size); + nldebug("LibVR: VScreenCenter: %f", m_DevicePtr->HMDInfo.v_center); + nldebug("LibVR: EyeToScreenDistance: %f", m_DevicePtr->HMDInfo.eye_to_screen[0]); + nldebug("LibVR: LensSeparationDistance: %f", m_DevicePtr->HMDInfo.lens_separation); + nldebug("LibVR: HResolution: %i, VResolution: %i", m_DevicePtr->HMDInfo.h_resolution, m_DevicePtr->HMDInfo.v_resolution); + nldebug("LibVR: DistortionK[0]: %f, DistortionK[1]: %f", m_DevicePtr->HMDInfo.distortion_k[0], m_DevicePtr->HMDInfo.distortion_k[1]); + nldebug("LibVR: DistortionK[2]: %f, DistortionK[3]: %f", m_DevicePtr->HMDInfo.distortion_k[2], m_DevicePtr->HMDInfo.distortion_k[3]); + nldebug("LibVR: Scale: %f", st_conf.distort.scale); + m_LeftViewport.init(0.f, 0.f, 0.5f, 1.0f); + m_RightViewport.init(0.5f, 0.f, 0.5f, 1.0f); + } +} + +CStereoLibVR::~CStereoLibVR() +{ + if (!m_BarrelMat.empty()) + { + m_BarrelMat.getObjectPtr()->setTexture(0, NULL); + m_Driver->deleteMaterial(m_BarrelMat); + } + delete m_BarrelTexU; + m_BarrelTexU = NULL; + m_BarrelTex = NULL; // CSmartPtr + + delete m_PixelProgram; + m_PixelProgram = NULL; + + m_Driver = NULL; + + if (m_DevicePtr->HMDDevice) + hmd_close(m_DevicePtr->HMDDevice); + + delete m_DevicePtr; + m_DevicePtr = NULL; + + --s_DeviceCounter; +} + +void CStereoLibVR::setDriver(NL3D::UDriver *driver) +{ + nlassert(!m_PixelProgram); + + NL3D::IDriver *drvInternal = (static_cast(driver))->getDriver(); + if (drvInternal->supportPixelProgram(CPixelProgram::fp40) && drvInternal->supportBloomEffect() && drvInternal->supportNonPowerOfTwoTextures()) + { + nldebug("VR: fp40"); + m_PixelProgram = new CPixelProgram(g_StereoOVR_fp40); + } + else if (drvInternal->supportPixelProgram(CPixelProgram::arbfp1) && drvInternal->supportBloomEffect() && drvInternal->supportNonPowerOfTwoTextures()) + { + nldebug("VR: arbfp1"); + m_PixelProgram = new CPixelProgram(g_StereoOVR_arbfp1); + } + else if (drvInternal->supportPixelProgram(CPixelProgram::ps_2_0)) + { + nldebug("VR: ps_2_0"); + m_PixelProgram = new CPixelProgram(g_StereoOVR_ps_2_0); + } + + if (m_PixelProgram) + { + m_Driver = driver; + + m_BarrelTex = new CTextureBloom(); // lol bloom + m_BarrelTex->setRenderTarget(true); + m_BarrelTex->setReleasable(false); + m_BarrelTex->resize(m_DevicePtr->HMDInfo.h_resolution, m_DevicePtr->HMDInfo.v_resolution); + m_BarrelTex->setFilterMode(ITexture::Linear, ITexture::LinearMipMapOff); + m_BarrelTex->setWrapS(ITexture::Clamp); + m_BarrelTex->setWrapT(ITexture::Clamp); + drvInternal->setupTexture(*m_BarrelTex); + m_BarrelTexU = new CTextureUser(m_BarrelTex); + + m_BarrelMat = m_Driver->createMaterial(); + m_BarrelMat.initUnlit(); + m_BarrelMat.setColor(CRGBA::White); + m_BarrelMat.setBlend (false); + m_BarrelMat.setAlphaTest (false); + NL3D::CMaterial *barrelMat = m_BarrelMat.getObjectPtr(); + barrelMat->setShader(NL3D::CMaterial::PostProcessing); + barrelMat->setBlendFunc(CMaterial::one, CMaterial::zero); + barrelMat->setZWrite(false); + barrelMat->setZFunc(CMaterial::always); + barrelMat->setDoubleSided(true); + barrelMat->setTexture(0, m_BarrelTex); + + m_BarrelQuadLeft.V0 = CVector(0.f, 0.f, 0.5f); + m_BarrelQuadLeft.V1 = CVector(0.5f, 0.f, 0.5f); + m_BarrelQuadLeft.V2 = CVector(0.5f, 1.f, 0.5f); + m_BarrelQuadLeft.V3 = CVector(0.f, 1.f, 0.5f); + + m_BarrelQuadRight.V0 = CVector(0.5f, 0.f, 0.5f); + m_BarrelQuadRight.V1 = CVector(1.f, 0.f, 0.5f); + m_BarrelQuadRight.V2 = CVector(1.f, 1.f, 0.5f); + m_BarrelQuadRight.V3 = CVector(0.5f, 1.f, 0.5f); + + nlassert(!drvInternal->isTextureRectangle(m_BarrelTex)); // not allowed + + m_BarrelQuadLeft.Uv0 = CUV(0.f, 0.f); + m_BarrelQuadLeft.Uv1 = CUV(0.5f, 0.f); + m_BarrelQuadLeft.Uv2 = CUV(0.5f, 1.f); + m_BarrelQuadLeft.Uv3 = CUV(0.f, 1.f); + + m_BarrelQuadRight.Uv0 = CUV(0.5f, 0.f); + m_BarrelQuadRight.Uv1 = CUV(1.f, 0.f); + m_BarrelQuadRight.Uv2 = CUV(1.f, 1.f); + m_BarrelQuadRight.Uv3 = CUV(0.5f, 1.f); + } + else + { + nlwarning("VR: No pixel program support"); + } +} + +bool CStereoLibVR::getScreenResolution(uint &width, uint &height) +{ + width = m_DevicePtr->HMDInfo.h_resolution; + height = m_DevicePtr->HMDInfo.v_resolution; + return true; +} + +void CStereoLibVR::initCamera(uint cid, const NL3D::UCamera *camera) +{ + struct stereo_config st_conf; + hmd_get_stereo_config(m_DevicePtr->HMDDevice, &st_conf); + + float ar = st_conf.proj.aspect_ratio; + float fov = st_conf.proj.yfov; + m_LeftFrustum[cid].initPerspective(fov, ar, camera->getFrustum().Near, camera->getFrustum().Far); + m_RightFrustum[cid] = m_LeftFrustum[cid]; + + float projectionCenterOffset = st_conf.proj.projection_offset * 0.5 * (m_LeftFrustum[cid].Right - m_LeftFrustum[cid].Left); + nldebug("LibVR: projectionCenterOffset = %f", projectionCenterOffset); + + m_LeftFrustum[cid].Left -= projectionCenterOffset; + m_LeftFrustum[cid].Right -= projectionCenterOffset; + m_RightFrustum[cid].Left += projectionCenterOffset; + m_RightFrustum[cid].Right += projectionCenterOffset; + + // TODO: Clipping frustum should also take into account the IPD + m_ClippingFrustum[cid] = m_LeftFrustum[cid]; + m_ClippingFrustum[cid].Left = min(m_LeftFrustum[cid].Left, m_RightFrustum[cid].Left); + m_ClippingFrustum[cid].Right = max(m_LeftFrustum[cid].Right, m_RightFrustum[cid].Right); +} + +/// Get the frustum to use for clipping +void CStereoLibVR::getClippingFrustum(uint cid, NL3D::UCamera *camera) const +{ + camera->setFrustum(m_ClippingFrustum[cid]); +} + +void CStereoLibVR::updateCamera(uint cid, const NL3D::UCamera *camera) +{ + if (camera->getFrustum().Near != m_LeftFrustum[cid].Near + || camera->getFrustum().Far != m_LeftFrustum[cid].Far) + CStereoLibVR::initCamera(cid, camera); + m_CameraMatrix[cid] = camera->getMatrix(); +} + +bool CStereoLibVR::nextPass() +{ + // Do not allow weird stuff. + uint32 width, height; + m_Driver->getWindowSize(width, height); + nlassert(width == m_DevicePtr->HMDInfo.h_resolution); + nlassert(height == m_DevicePtr->HMDInfo.v_resolution); + + if (m_Driver->getPolygonMode() == UDriver::Filled) + { + switch (m_Stage) + { + case 0: + ++m_Stage; + m_SubStage = 0; + // stage 1: + // (initBloom) + // clear buffer + // draw scene left + return true; + case 1: + ++m_Stage; + m_SubStage = 0; + // stage 2: + // draw scene right + return true; + case 2: + ++m_Stage; + m_SubStage = 0; + // stage 3: + // (endBloom) + // draw interface 3d left + return true; + case 3: + ++m_Stage; + m_SubStage = 0; + // stage 4: + // draw interface 3d right + return true; + case 4: + ++m_Stage; + m_SubStage = 0; + // stage 5: + // (endInterfacesDisplayBloom) + // draw interface 2d left + return true; + case 5: + ++m_Stage; + m_SubStage = 0; + // stage 6: + // draw interface 2d right + return true; + case 6: + m_Stage = 0; + m_SubStage = 0; + // present + m_OrientationCached = false; + return false; + } + } + else + { + switch (m_Stage) + { + case 0: + ++m_Stage; + m_SubStage = 0; + return true; + case 1: + m_Stage = 0; + m_SubStage = 0; + return false; + } + } + nlerror("Invalid stage"); + m_Stage = 0; + m_SubStage = 0; + m_OrientationCached = false; + return false; +} + +const NL3D::CViewport &CStereoLibVR::getCurrentViewport() const +{ + if (m_Stage % 2) return m_LeftViewport; + else return m_RightViewport; +} + +const NL3D::CFrustum &CStereoLibVR::getCurrentFrustum(uint cid) const +{ + if (m_Stage % 2) return m_LeftFrustum[cid]; + else return m_RightFrustum[cid]; +} + +void CStereoLibVR::getCurrentFrustum(uint cid, NL3D::UCamera *camera) const +{ + if (m_Stage % 2) camera->setFrustum(m_LeftFrustum[cid]); + else camera->setFrustum(m_RightFrustum[cid]); +} + +void CStereoLibVR::getCurrentMatrix(uint cid, NL3D::UCamera *camera) const +{ + CMatrix translate; + if (m_Stage % 2) translate.translate(CVector((m_DevicePtr->InterpupillaryDistance * m_Scale) * -0.5f, 0.f, 0.f)); + else translate.translate(CVector((m_DevicePtr->InterpupillaryDistance * m_Scale) * 0.5f, 0.f, 0.f)); + CMatrix mat = m_CameraMatrix[cid] * translate; + if (camera->getTransformMode() == NL3D::UTransformable::RotQuat) + { + camera->setPos(mat.getPos()); + camera->setRotQuat(mat.getRot()); + } + else + { + // camera->setTransformMode(NL3D::UTransformable::DirectMatrix); + camera->setMatrix(mat); + } +} + +bool CStereoLibVR::wantClear() +{ + switch (m_Stage) + { + case 1: + m_SubStage = 1; + return true; + } + return m_Driver->getPolygonMode() != UDriver::Filled; +} + +bool CStereoLibVR::wantScene() +{ + switch (m_Stage) + { + case 1: + case 2: + m_SubStage = 2; + return true; + } + return m_Driver->getPolygonMode() != UDriver::Filled; +} + +bool CStereoLibVR::wantInterface3D() +{ + switch (m_Stage) + { + case 3: + case 4: + m_SubStage = 3; + return true; + } + return m_Driver->getPolygonMode() != UDriver::Filled; +} + +bool CStereoLibVR::wantInterface2D() +{ + switch (m_Stage) + { + case 5: + case 6: + m_SubStage = 4; + return true; + } + return m_Driver->getPolygonMode() != UDriver::Filled; +} + + +/// Returns non-NULL if a new render target was set +bool CStereoLibVR::beginRenderTarget() +{ + // render target always set before driver clear + // nlassert(m_SubStage <= 1); + if (m_Driver && m_Stage == 1 && (m_Driver->getPolygonMode() == UDriver::Filled)) + { + static_cast(m_Driver)->setRenderTarget(*m_BarrelTexU, 0, 0, 0, 0); + return true; + } + return false; +} + +/// Returns true if a render target was fully drawn +bool CStereoLibVR::endRenderTarget() +{ + // after rendering of course + // nlassert(m_SubStage > 1); + if (m_Driver && m_Stage == 6 && (m_Driver->getPolygonMode() == UDriver::Filled)) // set to 4 to turn off distortion of 2d gui + { + struct stereo_config st_conf; + hmd_get_stereo_config(m_DevicePtr->HMDDevice, &st_conf); + CTextureUser cu; + (static_cast(m_Driver))->setRenderTarget(cu); + bool fogEnabled = m_Driver->fogEnabled(); + m_Driver->enableFog(false); + + m_Driver->setMatrixMode2D11(); + CViewport vp = CViewport(); + m_Driver->setViewport(vp); + uint32 width, height; + m_Driver->getWindowSize(width, height); + NL3D::IDriver *drvInternal = (static_cast(m_Driver))->getDriver(); + NL3D::CMaterial *barrelMat = m_BarrelMat.getObjectPtr(); + barrelMat->setTexture(0, m_BarrelTex); + drvInternal->activePixelProgram(m_PixelProgram); + + float w = float(m_BarrelQuadLeft.V1.x),// / float(width), + h = float(m_BarrelQuadLeft.V2.y),// / float(height), + x = float(m_BarrelQuadLeft.V0.x),/// / float(width), + y = float(m_BarrelQuadLeft.V0.y);// / float(height); + + //TODO: stereo_config stuff + float lensViewportShift = st_conf.proj.projection_offset; + + float lensCenterX = x + (w + lensViewportShift * 0.5f) * 0.5f; + float lensCenterY = y + h * 0.5f; + float screenCenterX = x + w * 0.5f; + float screenCenterY = y + h * 0.5f; + float scaleX = (w / 2 / st_conf.distort.scale); + float scaleY = (h / 2 / st_conf.distort.scale); + float scaleInX = (2 / w); + float scaleInY = (2 / h); + drvInternal->setPixelProgramConstant(0, lensCenterX, lensCenterY, 0.f, 0.f); + drvInternal->setPixelProgramConstant(1, screenCenterX, screenCenterY, 0.f, 0.f); + drvInternal->setPixelProgramConstant(2, scaleX, scaleY, 0.f, 0.f); + drvInternal->setPixelProgramConstant(3, scaleInX, scaleInY, 0.f, 0.f); + drvInternal->setPixelProgramConstant(4, 1, st_conf.distort.distortion_k); + + + m_Driver->drawQuad(m_BarrelQuadLeft, m_BarrelMat); + + x = w; + lensCenterX = x + (w - lensViewportShift * 0.5f) * 0.5f; + screenCenterX = x + w * 0.5f; + drvInternal->setPixelProgramConstant(0, lensCenterX, lensCenterY, 0.f, 0.f); + drvInternal->setPixelProgramConstant(1, screenCenterX, screenCenterY, 0.f, 0.f); + + m_Driver->drawQuad(m_BarrelQuadRight, m_BarrelMat); + + drvInternal->activePixelProgram(NULL); + m_Driver->enableFog(fogEnabled); + + return true; + } + return false; +} + +NLMISC::CQuat CStereoLibVR::getOrientation() const +{ + if (m_OrientationCached) + return m_OrientationCache; + + unsigned int t = NLMISC::CTime::getLocalTime(); + hmd_update(m_DevicePtr->HMDDevice, &t); + + float quat[4]; + hmd_get_rotation(m_DevicePtr->HMDDevice, quat); + NLMISC::CMatrix coordsys; + float csys[] = { + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, -1.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f, + }; + coordsys.set(csys); + NLMISC::CMatrix matovr; + matovr.setRot(NLMISC::CQuat(quat[1], quat[2], quat[3], quat[0])); + NLMISC::CMatrix matr; + matr.rotateX(NLMISC::Pi * 0.5f); // fix this properly... :) (note: removing this allows you to use rift while lying down) + NLMISC::CMatrix matnel = matr * matovr * coordsys; + NLMISC::CQuat finalquat = matnel.getRot(); + m_OrientationCache = finalquat; + m_OrientationCached = true; + return finalquat; +} + +/// Get GUI shift +void CStereoLibVR::getInterface2DShift(uint cid, float &x, float &y, float distance) const +{ +#if 0 + + // todo: take into account m_EyePosition + + NLMISC::CVector vector = CVector(0.f, -distance, 0.f); + NLMISC::CQuat rot = getOrientation(); + rot.invert(); + NLMISC::CMatrix mat; + mat.rotate(rot); + //if (m_Stage % 2) mat.translate(CVector(m_DevicePtr->HMDInfo.InterpupillaryDistance * -0.5f, 0.f, 0.f)); + //else mat.translate(CVector(m_DevicePtr->HMDInfo.InterpupillaryDistance * 0.5f, 0.f, 0.f)); + mat.translate(vector); + CVector proj = CStereoOVR::getCurrentFrustum(cid).project(mat.getPos()); + + NLMISC::CVector ipd; + if (m_Stage % 2) ipd = CVector(m_DevicePtr->HMDInfo.InterpupillaryDistance * -0.5f, 0.f, 0.f); + else ipd = CVector(m_DevicePtr->HMDInfo.InterpupillaryDistance * 0.5f, 0.f, 0.f); + CVector projipd = CStereoOVR::getCurrentFrustum(cid).project(vector + ipd); + CVector projvec = CStereoOVR::getCurrentFrustum(cid).project(vector); + + x = (proj.x + projipd.x - projvec.x - 0.5f); + y = (proj.y + projipd.y - projvec.y - 0.5f); + +#elif 1 + + // Alternative method + // todo: take into account m_EyePosition + + NLMISC::CVector vec = CVector(0.f, -distance, 0.f); + NLMISC::CVector ipd; + if (m_Stage % 2) ipd = CVector((m_DevicePtr->InterpupillaryDistance * m_Scale) * -0.5f, 0.f, 0.f); + else ipd = CVector((m_DevicePtr->InterpupillaryDistance * m_Scale) * 0.5f, 0.f, 0.f); + + + NLMISC::CQuat rot = getOrientation(); + NLMISC::CQuat modrot = NLMISC::CQuat(CVector(0.f, 1.f, 0.f), NLMISC::Pi); + rot = rot * modrot; + float p = NLMISC::Pi + atan2f(2.0f * ((rot.x * rot.y) + (rot.z * rot.w)), 1.0f - 2.0f * ((rot.y * rot.y) + (rot.w * rot.w))); + if (p > NLMISC::Pi) p -= NLMISC::Pi * 2.0f; + float t = -atan2f(2.0f * ((rot.x * rot.w) + (rot.y * rot.z)), 1.0f - 2.0f * ((rot.z * rot.z) + (rot.w * rot.w)));// // asinf(2.0f * ((rot.x * rot.z) - (rot.w * rot.y))); + + CVector rotshift = CVector(p, 0.f, t) * -distance; + + CVector proj = CStereoLibVR::getCurrentFrustum(cid).project(vec + ipd + rotshift); + + x = (proj.x - 0.5f); + y = (proj.y - 0.5f); + +#endif +} + +void CStereoLibVR::setEyePosition(const NLMISC::CVector &v) +{ + m_EyePosition = v; +} + +const NLMISC::CVector &CStereoLibVR::getEyePosition() const +{ + return m_EyePosition; +} + +void CStereoLibVR::setScale(float s) +{ + m_EyePosition = m_EyePosition * (s / m_Scale); + m_Scale = s; +} + +void CStereoLibVR::listDevices(std::vector &devicesOut) +{ + // For now, LibVR doesn't support multiple devices + struct hmd *hmd = hmd_open_first(0); + if (hmd) + { + CStereoDeviceInfo deviceInfoOut; + CStereoLibVRDeviceHandle *handle = new CStereoLibVRDeviceHandle(); + deviceInfoOut.Factory = static_cast(handle); + deviceInfoOut.Class = CStereoDeviceInfo::StereoHMD; + deviceInfoOut.Library = CStereoDeviceInfo::LibVR; + //TODO: manufacturer, produc name + //TODO: serial + devicesOut.push_back(deviceInfoOut); + hmd_close(hmd); + } +} + +bool CStereoLibVR::isLibraryInUse() +{ + nlassert(s_DeviceCounter >= 0); + return s_DeviceCounter > 0; +} + +void CStereoLibVR::releaseLibrary() +{ + nlassert(s_DeviceCounter == 0); +} + +bool CStereoLibVR::isDeviceCreated() +{ + return m_DevicePtr->HMDDevice != NULL; +} + +} /* namespace NL3D */ + +#endif /* HAVE_LIBVR */ + +/* end of file */ From 2f07d292e5fbb874025cfc86211bb0a36ecf33f5 Mon Sep 17 00:00:00 2001 From: Quitta Date: Wed, 28 Aug 2013 22:24:47 +0200 Subject: [PATCH 126/313] Fancified outgame show info page and made ingame info page --- .../ams_lib/ingame_templates/layout_user.tpl | 9 - .../ams_lib/ingame_templates/show_ticket.tpl | 2 +- .../ingame_templates/show_ticket_info.tpl | 237 +++++++++++------- code/ryzom/tools/server/ryzom_ams/credits.txt | 5 + .../ryzom_ams/www/html/img/info/client.png | Bin 0 -> 3889 bytes .../ryzom_ams/www/html/img/info/connect.png | Bin 0 -> 1893 bytes .../ryzom_ams/www/html/img/info/cpuid.png | Bin 0 -> 2897 bytes .../server/ryzom_ams/www/html/img/info/ht.png | Bin 0 -> 3553 bytes .../ryzom_ams/www/html/img/info/local.png | Bin 0 -> 2086 bytes .../ryzom_ams/www/html/img/info/mask.png | Bin 0 -> 3300 bytes .../ryzom_ams/www/html/img/info/memory.png | Bin 0 -> 3101 bytes .../ryzom_ams/www/html/img/info/nel.png | Bin 0 -> 2727 bytes .../server/ryzom_ams/www/html/img/info/os.png | Bin 0 -> 3428 bytes .../ryzom_ams/www/html/img/info/patch.png | Bin 0 -> 4119 bytes .../ryzom_ams/www/html/img/info/position.png | Bin 0 -> 2289 bytes .../ryzom_ams/www/html/img/info/processor.png | Bin 0 -> 2933 bytes .../ryzom_ams/www/html/img/info/server.png | Bin 0 -> 2230 bytes .../ryzom_ams/www/html/img/info/shard.png | Bin 0 -> 8804 bytes .../ryzom_ams/www/html/img/info/user.png | Bin 0 -> 2468 bytes .../ryzom_ams/www/html/img/info/view.png | Bin 0 -> 2526 bytes .../www/html/inc/show_ticket_info.php | 2 + .../www/html/templates/show_ticket_info.tpl | 120 +++++---- 22 files changed, 218 insertions(+), 157 deletions(-) create mode 100644 code/ryzom/tools/server/ryzom_ams/credits.txt create mode 100644 code/ryzom/tools/server/ryzom_ams/www/html/img/info/client.png create mode 100644 code/ryzom/tools/server/ryzom_ams/www/html/img/info/connect.png create mode 100644 code/ryzom/tools/server/ryzom_ams/www/html/img/info/cpuid.png create mode 100644 code/ryzom/tools/server/ryzom_ams/www/html/img/info/ht.png create mode 100644 code/ryzom/tools/server/ryzom_ams/www/html/img/info/local.png create mode 100644 code/ryzom/tools/server/ryzom_ams/www/html/img/info/mask.png create mode 100644 code/ryzom/tools/server/ryzom_ams/www/html/img/info/memory.png create mode 100644 code/ryzom/tools/server/ryzom_ams/www/html/img/info/nel.png create mode 100644 code/ryzom/tools/server/ryzom_ams/www/html/img/info/os.png create mode 100644 code/ryzom/tools/server/ryzom_ams/www/html/img/info/patch.png create mode 100644 code/ryzom/tools/server/ryzom_ams/www/html/img/info/position.png create mode 100644 code/ryzom/tools/server/ryzom_ams/www/html/img/info/processor.png create mode 100644 code/ryzom/tools/server/ryzom_ams/www/html/img/info/server.png create mode 100644 code/ryzom/tools/server/ryzom_ams/www/html/img/info/shard.png create mode 100644 code/ryzom/tools/server/ryzom_ams/www/html/img/info/user.png create mode 100644 code/ryzom/tools/server/ryzom_ams/www/html/img/info/view.png diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/layout_user.tpl b/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/layout_user.tpl index b876fa1c5..83712192c 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/layout_user.tpl +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/layout_user.tpl @@ -1,14 +1,5 @@ {extends file="layout.tpl"} {block name=menu} - -
  • Dashboard
  • -
  • Profile
  • -
  • Settings
  • - -
  • Create New Ticket
  • -
  • Logout
  • - -
    Dashboard
    Profile
    diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/show_ticket.tpl b/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/show_ticket.tpl index 377f57de4..fe27b2ce7 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/show_ticket.tpl +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/show_ticket.tpl @@ -10,7 +10,7 @@ {if isset($isMod) and $isMod eq "TRUE"}Show Ticket Log{/if} Send Other Ticket - Show Additional Info + {if $hasInfo}Show Additional Info{/if} diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/show_ticket_info.tpl b/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/show_ticket_info.tpl index 2516620c2..9edb32cda 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/show_ticket_info.tpl +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/show_ticket_info.tpl @@ -1,96 +1,149 @@ {block name=content} -
    -
    - -
    -
    - Additional Info - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + {/block} diff --git a/code/ryzom/tools/server/ryzom_ams/credits.txt b/code/ryzom/tools/server/ryzom_ams/credits.txt new file mode 100644 index 000000000..21c8eeb2a --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/credits.txt @@ -0,0 +1,5 @@ +################################################################### +CREDITS REGARDING THE AMS +################################################################## +- Muhammad Usman (Charisma Layout http://usman.it/themes/charisma/) +- prdatur (Icon Set http://opengameart.org/users/prdatur) \ No newline at end of file diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/img/info/client.png b/code/ryzom/tools/server/ryzom_ams/www/html/img/info/client.png new file mode 100644 index 0000000000000000000000000000000000000000..83443dd76800d9000862d96ae749167c5a62b6e8 GIT binary patch literal 3889 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 literal 0 HcmV?d00001 diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/img/info/cpuid.png b/code/ryzom/tools/server/ryzom_ams/www/html/img/info/cpuid.png new file mode 100644 index 0000000000000000000000000000000000000000..c3e531ff3d013e36758031d66bb1db854b94d023 GIT binary patch literal 2897 zcmV-X3$FBuP)krowa5jkP0^y?*F##ET_5wJF2@uGC^S23$sA$U01ifrG8y#paSPgOLGrZvP! zRl~G~I<^!6utrczv$J~nS8}&_BUpdjpogCTYG!LEp9W~YKfm?O=hN|zU%M94%Rjh4 zvM?o-fumcIZ+{DRyUH_5jx}vaw8j{i6!54AJLAZ>2B$&<@(5AdL|yi!1+cIlsX{P{lv3ZN9Q_7mKk{A77!`^ts->`OnRTz`3;YTDuW&MpuC z6W)Fcj&jSFu64-A1!qh{XF(Vik=_)h0dfTK;4OFqF^6#lse!1FrQw;MdS?GC4|Yef zD3CJ=@vd(EVF+Ov7>P&}k!VjRB5Apo_%hE!|sT@LKYLKjpn`D zSdnT&Rcm9+G(z*V2hFu&=l8$&eE$3o{f6?~mwpMe`l}4y`89667C6}wE^cP15$b$| zF*PP_!+J7^+&zLj{aL0fOe+WxytaVrnV=S)ySj|al*8LO7Fb?^Z|nKD=+!O-+bv8L^qyY2R%psgWu+zukLg5fo0{4$4G?nl#Z`)3ot6+ zU;sA{;r5^nMH$UMqYSkSr1e`eV3t1os9o zE+fMtl4zuEA`&BBXJJ*yGUeI}8yxQU7z`?mMiv*~>s#5slw~<>n&vRZ*cXvPL;@ng zp31%D3swK;R#u_x!~Tt9cHe;RKD_V~Yt$zbOj={oNEDb(gkB%+b>ZPC(#s=74Vi6a zB}C{LSoB~G_#|@i+6u-CJ39l4GO~6?80Jzvn#etC*;myA=iJa3GZ7Jh_!!W{daVMQ zKHUHN$nGw5hwTErcaKRHO44&s2bgrBJAm8Aa5!mmt!dh#unI*5nFDJDYap?(d2xZI z^@P3cF2i9%CxfLW_=_9at0Hosst=rV2j01nK zaBHLIfPI_tRsnoPS$Q;)4J8VD1s3<*AcF+3D2BOXl&%h+Xp;4 zYOoI0R)m{dQhhLRH$-IDTD#@FKk(j9j4_q!VBr|foH$vL|Qbq#-aGkYT-do#GLBuNeu?`mUA zC4$Bed(Rk=N>52mvt!o3Rv9h&d88*TgwVin8qo-g2`oEU zNZObjP8vRVP;hrYGOb}{8H^SFd*2*%E4?owTh`h;Ns>HFl4RnXD~w7q%R4_SUPHS> zw!IfnB06|AT=;`tO-NT07A~)Va|AV*)KdkR)UAXZO#mZU*M=b)m_|4*V7nW+e*)76 z)|S90!ksK_j} z|1itDd*1u4G)<3_BpDiG$ExCHQ1MhCo>NQ0n;ig%mEc>W&#;vjA6SZkwdQWtgB=HPJH36ln zB2A1`jZjp9VkXin9k_g6*zXd)zSDWFMt4>9mUHgCEX(evY1&VdWNNLES!m~G+k%zH zhct6>^MadIa{`hBqN)v0^=otT?|pwK8xOz7_9j_~QIds}%sY})F$Vghif9aZ8OWP< zuXaWl9`KS$)Ds@}2I zzMCY;y);dav&3Vp8|f)U=7|>va!O_h=HljNc1jq7Ac9CPB0*JiRh_8nbP~%wNAH&Yziv+bn~2QA`u^N=S(@g~xu~k~Nspnk0s*9+ zdsIN}sR8v2Tm^9=)MCt7ybG#2F~$sxF$ZN?9=tKJ_nW3!3n6r3j4s9~j0iw2A`?|P zGREv!YwspWvNNmLk+sH(Nd1)8PJ3^51dY=6;jKtFGh#oh=*#yFRVTttSddN6AM4%0O4CEn-G8ZWB#tPl-PTA0B}d*+*m zIU4)qptj8UaJvx^3C5U#s!mmPYOU=%=l1Km-ma=DYnsNz7?}aqB9a?phR(U6cdl^G z=H8kPY9{mC;v@DLE}pJn02|xNji;V!e$t=kNCbk26vmiBRh=7=U?^ipbO9JPmsK`iZsXW9A_aoNB#JP5RguJaZ6Zo?iQ7ZSH{iy+3W- vN9^ew{7%{b``(}bSHRzGfX}keJ>dTUP(UbR?6?6W00000NkvXXu0mjf=T3w> literal 0 HcmV?d00001 diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/img/info/ht.png b/code/ryzom/tools/server/ryzom_ams/www/html/img/info/ht.png new file mode 100644 index 0000000000000000000000000000000000000000..fcf468e71e7df2919bdf1d111a0a5577a42bc88a GIT binary patch literal 3553 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 bna2MDEBL_rDs&iV00000NkvXXu0mjfY=iqd literal 0 HcmV?d00001 diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/img/info/local.png b/code/ryzom/tools/server/ryzom_ams/www/html/img/info/local.png new file mode 100644 index 0000000000000000000000000000000000000000..4cb748c8df7f93429032b4dbb73d86a595e7d58f GIT binary patch literal 2086 zcmV+>2-)|EP)4$R~5(q=id9h=bIUOGI5;Paa_AioCF#*s6YXA z0h>OAx_|{`gH$9I>_F^TRIn)<7Az1F5<&t*K`f9ekXl3+Eouu@oi=SkUUlL}{2F`S z^Ue3V_pq25*G{9hq7^HSbabWfoA3UA=bU@a9pL}=37f4%<$o?p1bKAK`TD~P;}@QK z?CkAdKK+gES00%9wIwl5fcU^~KaQ{{nsd@PGRv!5;>OiW zX1kgH_06^9d)=6?f1&`k5|y)4{xkDam2aLoeUCgkdDyPkcg@wSH>umKWv-I8Z5d=a z^OcSGPj4(|Pvx3B9~;24<X@b<9SbgPCO7?({5<+rqL8lGGqK2JHyfr>cIT)$LCvsexfa zZgM_U3gaYB?M5$=OaN=}R$Kr5!b*n+BQpjY zwHA4fi<2h~Q+>AqZ3Lu*$Z`{ez5`2;>Zh8B0Aq}ZGjJvJsM}8nAPB%N`{I1N&lfu} zU)uvXIaT=Lqn|tW;gxrp?_VecM~H55W)5=M+!iafF%K`1e7I# z!7XXjNF0DFc+OUhZSu58oA64V@QR{ zIw&foge3@45{^_$1gy$Y1_S^F!0SY@X2W{`Ta5_Iw>RO~7AhqlhbtjG+k%t=T5AA6 zDfEe?!t!nmDc~r1cTpt>BnN?vl|usH0*J*Bzy=W5N=+1t#bLdHN51?J`n?GCR)p29 z2Hw1~hQ%vukWwAU$auMgz!nHxg}}EEcs2spLcx)6T?xw)d*b`d2Vjjd-~cFS<{~rp zGT_)Yh&9)m8I6kOxq}Ssx0&fl?ABZFi}XV*01zt-rlAGEH^u~co*(D{TM4o)f)D_K zU`c^qKRNI}%e5`IuEa;RZl|XKc%uxAf|FVsHbz5h4Jn5kl7R0BIJO+PSVDjV z0nG2g4NVI1p#W?Q`rSCbywd)|N)yjA&;+0*1|?8Kx8Jz3`SL4^OYbb-S_{rC%pT1d zl*Tcb%|L(%lhrb^EZf`M@d{YIy?J*4!{~LB&MOx;p1ru*`+2LcF96uZ03cGs=o0{4 z0Ci@FZo=7HtNmNwe)_lN>1y!M*}3Xtf#()}ETE z89)S}4WK?0?*T{^00ST$KA<5`44{{5?%v$&K6i7o`+}|LP{mj0=V!`i!mv@Cof@A6 zv0`mVDzz8A$u*KBZ!KP{{dudcU*dsqGz_o}pfy}002$%ENhmRBM-PJ@Eeuy5gQ4L7 zu!vyW5|xs#rpJnI^~~(#xl?o1PhDGD6Ww-Hf9q!bkJ}ydiZ*Bs1MA};L^KRA0igF^ z$D^U7hGKWRjX}GP&Wj*|l7t{|RVmB0PI9hkgARZkgF~GTg+_wO@R=SuAGi(o`Tm1> zHy^?7|DK2C)I)&Lnhn>Ho1x=QfV+Y0FVB8avG4tUfDy#qeDrSgTL8d+0KB-n!&rx< Q^Z)<=07*qoM6N<$f^HewJ^%m! literal 0 HcmV?d00001 diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/img/info/mask.png b/code/ryzom/tools/server/ryzom_ams/www/html/img/info/mask.png new file mode 100644 index 0000000000000000000000000000000000000000..2f0685bded1706469ba74ff7754f5b16b46a8513 GIT binary patch literal 3300 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 rvj4bzsem_|*H2U1E%>PbpC8>lP^Fld z5JVFSY9QkEV^ftz?WfWTwWSSh14R%~sZxsskw8_Of+E@=-UU^?KiDc3$q>b51{;GrMbNUkF6v3y*Z?Ud_xs=YRg^IsfMceq+C}%S`_|Cl?X6 z_V=@6`&I0{W-k{e#$4P<};?5hFzN`RYzHv$KM>y=XP-o9f8J9qA8XlOg* z=PweeJV|jAANly-vu$uIN(lq~+qmm5K0;5q!o;m=y zuLo`@6pA+u^bgXvbt_5S#T|EkfVbcFcC52xEyLY^^^ZJ%_Id1loqzw_7x>G+{UE2u zp5rHvKS3&sOif=RfGyxz&B7&Dg4hUP4DcRp~{O`k01E+wqz@-3Ur5U`hu5^Kw2hFYg^J;)Q?3T5T z$TG+7YxeNfFMo?`_wNHG%uLU->zWasd+tfnw9dEw=YGEUh0oAzG|m9$!}gA^7^`Ew zKqUZa1I=)60eQSKv{+V$mjPbQB|38S?xCUWU*6g`NVDkx#k<~fJ5#e4I6XSb*w`s* z)hZ$o>4;LPgpMLaAZr`ac8XStD2j;V7#9XM)?%%tzPN~W4y`rLIkGIHR;yC4*KY^L zxZEAuY~T_Kh@vPWNeYP5j9j&YKlc)kyM-LGtF-LxOjD`6rL@6r0WxjO(1DGsBMEK4(AEIp&GO z3}^iL$QKL5aSS-Db+lS3h#(>qyNYNfn5<2jX0$Vd2#7$wkY{r816`Fab>C z<*g%oc0H9q5R?*}Q(Sx9>*(&OP+O=`tjNh&R;xrwjE)inL6k<+@&<}1@Y{`u_(x|0^9n2&W9aMaV|0u%PzvlO0prqH zTMMvTn$GzNj~)I$LYUGl$X|QL?;OD+hHB!6D(ZqjheH zA0dD&;@z@y@RoLcHNbA=gL9CjZ4Tag3&U6K;-&NF$t5|!(Qdbyo1Lb&x1TtdP{(ZHdU0vyIz0>D?S5`N7Bq6Hl*Mo&J?_)F&yiBMXj z1mY-WW@?fP<7X*X`Y^^&*i@vrWv2(_?Bxu2nsE*s7Hb{OI;_cZMUIunU zz<$3R&KOLVQJY^NNfN{v8m%-$HX)bSj6U-uN=HO-j1m=qx}`BX>Fx6N`R|0lx)YRM zGmG4iVZU)fbK0iOo_)W~@W>vzx)N;G;@FX+?A$d%ynh>&F|Gc%J*k#s%V zKqb5ni&l)DJi_R+M@d^Pa`^%xLbFjvaO4X`q9}(}5ju`&HB+=!SZipfDWd%X3dK!e zq19?pD0b0m)i5T*Illbmlw9}MaKF{Mne30p>8MIDt!A*WO zh5>OtLgjKr+LxlNQSgJ3Q53scMf{>uVhVi|DGa`fiuui=5CT)#OU$EUUIEh z2OLC6gdp^8EmNJJVScX0%=9=qxQHl)&^fF^wz>~*Skt}CloFba8d;Xn(N1xKwFV`E zjv`vk8Y0?9$B;`Hqj1*Hte0pkE?`|7P-Gb>rBF&?tsl>a=4?YO>j8FYn)-a!I*hX@ z5kz#z2YeEvM4?3^E~GT!0D1NVO9&QDd?u;9i#cnt&ga%Hs} z%|=?$Rm{w>NFcwL|jO4#8(c%_YqjE&ElNjZWpA|IWKe8TE8%7 z$>kz|IeevYSx>EFYgHTxzD#**xx4(Ofq@~?Rz{YENwGei?L0e&5*280%Y1~BZ^Q2^ zXX|7aC?Sp$;wa+G*)f{U#(lsEd_(X>U=rV0X#m+;Wqv0oEd#Fu{t(!P?|BvQ>0c+Y z-uP&*Zygt?0Z-vOLNDMOFBgC*d^e~8n6&^$0oWFx2j5p40tWDnkP^O>iCN#Rei1YH zdTkNUuz+Vb;#YSGizlK3x)#880JmA87;BYC9l-7IQit(6%eB_4>BF5yV2a=R h2Fq>MPLB5WC9-62AgIuU>mPsWNa>X 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 literal 0 HcmV?d00001 diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/img/info/position.png b/code/ryzom/tools/server/ryzom_ams/www/html/img/info/position.png new file mode 100644 index 0000000000000000000000000000000000000000..ffa627e5296dbce66f33bf46e7340684e96351a1 GIT binary patch literal 2289 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 LNkvXXu0mjf(Xm~C literal 0 HcmV?d00001 diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/img/info/processor.png b/code/ryzom/tools/server/ryzom_ams/www/html/img/info/processor.png new file mode 100644 index 0000000000000000000000000000000000000000..411be15c2c2ea43af4638ae4cdc463eca35b9038 GIT binary patch literal 2933 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 literal 0 HcmV?d00001 diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/img/info/server.png b/code/ryzom/tools/server/ryzom_ams/www/html/img/info/server.png new file mode 100644 index 0000000000000000000000000000000000000000..7edc55e2f5e4d54356ef0f4ff0fe02ac4da83d93 GIT binary patch literal 2230 zcmV;n2ub&eP)UFq6<>l*3GtdZP(lBIlY|r zoSyr6?~i{x=g3IdWNXL%IiKXsIeGJ5p6~npKELMzb?VfqQ>RXyI(6#QxgcmC;k$gQ zLQ|kY-R`*fgdq1(ywtOXd;LA#!F_jqy7|^z!6@g8R#6BcR0UO271g6ETBtz@L;`A{ ztHm4J*5PZ7h187$M@ntqeR28GoFRS*D1fLwPn_jl=gnFK8k2M7KgN^czL)mzdRU2i z6cAvVU>o3A;006_9Fy$f49`5j!tW2~rwgVy1~el@nScRUTmX^h6H#yL2G7m9;pT*3 zb#$im*Ej#PAnv=X`zEEmRUr_AhDNYL;Dn&$BYAk0M}9a>a?#@GlKFDUa*qKmKnUrg z1=w6{fqt)Y*+ZXe`O%v%?|7nB^Ih@QU}3_t#nN2LIQHu63C~yhqn}c{8dcOV1S1fR zLR978vlGnE7unqxA`sF7vnzH?H(d<~Al)khI^bS3ft%ZWUwq*^o1S>^!H&IDg0u>C zTQ+O-3{F)J0op>w8hQP13z7T#!aHdVfY%GCFrMP+r$^YfIm8v)Tlw0)&ektn*Kp-( z!S0z!*_L66aUcy;E{Z@!VZ)wlB9Hy!7d=1jy}D%+)&|Com6%VK*tNZJ%c0@Cl_}VB zfM;RVI(cYdx&6MY!#xBv@Ho8jt09I)R@i=NlpA*U68Y@4GGG)I_a>^G5vo6gZJLr^z+|+zxyFiFyyf^Ua|`|j_G3BF1fr(Q&8p>>J;q|ja1(jAv}bpRH`sfnLU<<|HZ?FmIO)5*UHjybDcvla zVhrivx{5rjz#Dc1AA9bR&TsfUK?>yns^cLNYCzX5jt!;R|K`%_yW{2ZQo*jcf;^A| z=7-~z#m<0p{piVT;I)6I%Wo&m@uRb)$=|#&TmI6Wmqx=IcWVUOm_6|>g_WGobwt<5 zym=g1Xl&N8qMlJ;G^p);{^8E2gg^^~qlEkocrJ-yX3ISO+{E$^UYb8Kn9wJaIXmSF z9OT!}18K))Y;dyNI6YTxADB1Zsn{X~Xhq%0MI&0|rrWNMK#s!)-XfLA;#HNJX= zf)aAr(AP`k8OOn^wlv%=Tzo49jV;YlXpccE&(B^MPyh75!g13U7ILI>PJ31Zvg{Y< zGS-ucRqK!tbO74{%d08AJMQT6LxR%G03%1@1bl!id>)k@j>}S2U>VsbD;GqdqR_f) zYr`!|X$wbHA>xOYAghCM_a`qWCrn$oRdEJTP!!TaFb}L&&lx~c2rQrkXbQAG@YUX1 z`fj|k8IJPo<9|js9268z#Lc4Vpa9)K7Feu4OJ|KPsvmSx2#V7SMmn8!I5m}L`rk_s za2Q)EmNF%$jEuJ7)s)J8pNZ|byT5Sfri%<%Q@My|PM zbNJe)PIdijBtH}MtI9V&-_rNsp60Et>)L};l~?1-)-cjNp(w;|yL!W;`)+Uf_&xhJ zUfbN>*fjgf5YN9j!_-s(zfYxN2nJ^Li2*kEd3TH48jrk;&<9-UqukHfqh4 zio=oP8Crcld99g(nx&!ejCP2TQ<#|kGLmm)G&Zku1d|K=Ux zJrjeuzzfgLa^STjxtxjDt6-Rd(FJq$=z=xs2w@_tXBJ2RGr%%31#61v83nq6x#_f> z44_;W3u#fybQnCk%+f?r!E}O0-dzo(GB!<(9$K0-F6;ES8kXoC7^@^iwQr^h_FQpE zzia$dsi9nU&^)q7lq-Ux31jZ~qBUv@VN_pP1d_mHRbU2LaMr|G$3aIhH<_{LmhyI6 z)Tg$G1FBZcRX8!6$FKy8D<*@ZWhxbywq_req{WLr-?g>bT7?1xc6ZMit6Dyf=tm8p3b1QQ&G?q1w5*XrYd+em14zaXuM2f z$>PrbaHoa(vH#5JV?vOKXRLQlW$f%+!5Ot&VIh}EE^_9R1SWt{WEHOUy>*#*e;9!- zvH%!cn?3h#@X&(*A*6e5HDw9{Ra41iZAK<_c6R%_{C;iQ%(9hm1@o3GriFY?&AA*f zhm7P&U>ZnO#jQD-H9l+^_GpUIe_OZzpe;}ehRBy~*EU^L!x5@mt#gW^AcPRAimNKv z(U8Y3SnlZ5vbkRfW|8AuClEtMZ~=%T`&^dyRut>Txr{S0zoy=3xnfla5fRXfoPu3~ zoTAktb-L?0WZAD(#sV@mKICLz71`g4NWtsAqCb~CnT+AM2aqmo0a}q?-HU9rjT9zM zJ8Bc=K=sHVV_4gV(YZkDz#8YX=~UY&$|B>HMmEr_{yL{!Cy>I6$T_KA{a-|iuYHgU z#<@;~6r>X|q=RAJ+cdJ^f6FF#f5RQ#FHUEj``XzWe6G}%MGeSk)?7MW+y_xxt=F!- zpw87eAAz-dYAcZsa;#3BI(6#QsZ*y;ojP^u)VZkq7fKLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=00004XF*Lt006O$eEU(80000WV@Og>004R=004l4008;_004mL004C` z008P>0026e000+nl3&F}000+%Nkl2*>{;7@_g5AoU-f;et66Gm{Z>X8NdYG!kGkuKn-+k-^;I_ zUc6ca-!Zi<;r>!+#5MBdo-5IO$=?zr_%&T2jjUILi`QXonTIw(O-fm{OO z7!XB3^!?A14vcjrdEt)V^3mHuU_1vGer@p&w z=f=cQ1F&e(qCaqb6;msXg!Ix4)6J!Gw(R^3z0OAd_2wV)xsP80&lKF<2OKOIuLE;I z&I4*eg#O?m8DIeD0w#dzzzkpt++DDC^;&+u^$9wM+ZmYXLy<`qwSJS%AP;221bFM zAm0Sp4U~ZRIEa&h;{T!{(|`-0c4G~}E33_qw$EQTjE-@Q#_NR+k z({UHg$Is&WOaGDJMn7x={9X*R0K`WM0*E*C5eGR2Yy`0#+$6{dD4zmx3Q+VXG{%9V z$}ozl;VP z7V^JQ5WR)i-UPA>qz39Nkmqj$?J-chUn}`$d0_5&`OzeA_1?>Q=l?y|UwIvj1IK!zCE7rCfY=Rk z2;>A{csd|Y1rCk-*@8g21%vg1YaeQ59%Lu555#Z*k^rR|WI2c+L~=n?bX=Umv|INb zH2@zy^}@fa2)2C7pRZ!o{%e^(_XMuK>PnQHXQ)U3YrHXB*dUXh;mRVAt-x_0!iC-( zDhQ+wLMs>_D6}vQ>;<+0F98dTn*XQ(IAi(w zvx?0bPZaA->ZW}!P!X%?@PztB7;N}s}3!JV(4zLN_ z?gF6l!gX259#fu1h$yH_fI1M(KsC^jhxIy6)&?CEZ}f;9Q2>6e_=a+S(*-hNvT67M zR($eezV^kd$vp+ucF;}*y#n+)uh*pmc9++1)dxa&PZdR8VuTI$9*MI{;=Cg5-VP#K<@WQcw4j?XZb1)$xz{LD~qR zqWtkR;7KkuKoe_(`&J}gK<76#o$bVI|*t9w3SjEwIyG{^PHN&^%C^A@2uk2r`>IZ?pTL z;#_c&(D$>AnAMAk)*Dprsla9&SLQ)D=3xW)*y$GpLVC?JrTW~GUHhIR&fDC4)h(QS z?lN*iUaTuYutvQ2)XF4Cu*U^T87NW6PD$eJkZu>`M64@`vry<@8^{xf~JG0glt?OLz3FX(6AiF$88?{-U~#O20|^B3})dwkAI!9y+brDiLfvE z_MO}Iyt3tg3}8;<{E8yq^zTKr8MN*EHQ`E|8@_%W{;tQXWYKp+CCG1%QEnd}zi5)wD)g@TolB+`QF zI?3#Xg38$*l!vcd%ZBx7&i!H&>P*3XKmK=icW&dvIoC1eboXYRiJK2=V?+_EFx4Uz zH%L!<9ao-wI}Ho!u`*z*f>^11bpFQS>=ydVey&R}6!3*gNJe zaOTpmzZ=F}7r$SDV34FA;(16~kj+U#rl7J48mb^*2s$Sv-FqbyqXOZAKL}=6;5u+1 z4Vetgu7+hNKvM(gh0LbA2ma;9s~8z6=kyyEkgbB}fAoLI4^A-SxYHQ3w?4aW`!mlT z_STp{WI><{fv}S77}x`pF&Kv}dtqQqpaB&j=(Avd4h&V|_$79+#6pmCpdtdZOQ9$R zD3Y!O^zMbBxWqIRLZzT+UhI!#VKfbfAC@kZEN{_>hQQ8CR863$aOXX{*wr?~-=6(R z%1$q0*Q=Y@_{Os|ReT;>A5F%SeODcky}_OB8?3)s^aV5(3^$8(4idLu)g~C4gtB6& z4nxv`9dWR1$T|v6Pz9f)Dg<+9Kp4<`g8gwgI1G~s&@?D269@q*ytFlyfe8!htKlOp zl7-VDla~xlfvF2B40!074jz2`QK~tX87t~Y8L;N5+b|NPRJPPHV7+s+5Cn25%Ll=Z9 zyxs83p~T05cvfN>a9pimaUIk|VR%Ylg{DJkIYgr1_$0fBpshzxtb-Dg%qWLa zLy&S{XiPGa5uiy_9ZWxHDrDrL^+d?#AZH88OJIH#%#M2AFfiq9oR=ArViOWR!5t6p zVb87+W>ud<`R5zR_YbrE$=j(jPC+l0&R}}qWsXV8U0(Fw;w=wsaD;c-9-}A=Sr{6l_B{Hsb zvO*B!<)E@N>?|FB>%Ezv^~f5gEJI5=;IY>!K4wt-yG7U7-;h71A2kzQNYJ8Zg`lZbOYz4V3n`nD^ zB{NGe$1&mqQ`x>7k4k}rBXgEi1jS}GlgWPWf6_*`6Wnl7jPIPJ@?0gX+V9df;SkQc z)PzlRO_0w?w6Kk1sl0n>Ub>(fl86stroiwiOgSz-1U^+DrROEpVM%#R@RqCclkab3 zuxlHUX?4{8O$+76!`3VBAk&kgY)K2l`K{Ni+qN=(bO5;dY}QF(hRboZy;u`jHaxzW zq3zQ-=fWAB)@t&T26&?n{-eWX=Y)-!7Bp)Dy%_L0xK<$sbct@dn2IEr8|9PS02?GA&JbnbJYanyIB9^ zlT?N-Kncqo^RUW8`iYDI)=f`lqlB&O1A zdHEn)wm-q6=eF|2<#YK$Oywj;r`__?md?^{S)_3B>k`{`y?j8)lT=`wJdsG4U402U zH>{&5oI>Oj{4MjTIJ<^DH{C*_ZH&famdJ5?$K{qikw5AHY}fjB+8%B!jWoi!-OiL%&e_sYIK0jkG{r@uWaOnPcG*(b8A^qqBGx6 z*`Cwra&3Htg+CXFI)W-RCJl$xV%6=o{6%4Bi+ARP5rblV;k|G zP9}TT<=pt;+W)6jh%A_MN}$}|`u%Xa{PK?OTSR2(QYtU{Iuoa##o+5BSe?B@g9cIu zS*+tzBRJi|^zC_-QR0*|&F8F-FXYnJT9)FVpcD#2yvfv*bch27h0CokN}hjkB^A*g zRKT6NgcHBIl#wrAMEtQ{nwDNR+-tqrG+`Z>u`PB?-bLd+)g*-S+AY>y$8k$ytOeYy{e}ReqgNWiHd?zgApFg&oYbpzK zdh)EAfT5il2h9l<9iO3nIeRZSm*OYxV#()Askoq#T&$Y- zm_;VJ4@yj;OO{i+u!*wVIAgnq=qr>f{a{lMmt6S}_uu>^FRk53dv}JROa#;@DkvmAvjU%PYkvRUx zMG_%x>)+DDrS6i7M4>)dQ{?zR#H@1 zLJ&ykDnm|&qFJ*kT6PjfC_;KlB{e=pQF9Dy$|kmS7SUZh7`*>UYHKbf=S$o=ZXM{% zy9pdOkCgJm@ql+9FK)iKyF32!Y1!hT8=8;1CR?dr$o~5tX4CQKkh$$Hnw$MBIVVQ- z(rSG3&%if(F)GlJ8)kbji&0;PZkjmxEV?PteFj2>;IVV5p54Icst1q*A)?h)yN5Gd zZnx|S@=p4FCG#H|0DyR|&+SjXeS5#XZgI4sZpVUI|AN+6$(CEMq5ZN83G{5Dxz)#s zE5bC)%M-?Bhc(WDupm}hft?s7F_^`y7ldbIaGAnfF^kNa=NWkVF>2n(F_Fe8m0SKdctkA7^CXO+XM7CKfhAsu-9^-G>$^!ym0iEtO0<2VNb^>^m*vjLNJt ziImOVZ1I2gv_d(Zy0d{WpL9 zeV^;}qpI??l|~X+2Q&eV9te|9Q&N>rn#O0h>Ox7ylxB@+K=SeDiu>O>8pMu7c=&05 z@r_;fN8a8#Dm3`0P*L4yaDkTp0CKT%S+lv?!rsX&;72Pj*Ok+cy8ljJ$>~SUoh>s| zXt)&U@z(pOP|^TIxZoeMdU*Z601!Z3tikx65Xi(-`6Z>8_U20UVUfA)W@#NhS=hJh zpngSTl&KS^D^?YPwuXA+4k3g+nJE1Q$Ww`kVvUrm$}U!}dnoB$71m;UIHVCHTQKn6$yxhjZ@y;M-) z%I#YedU3;s=5;MCI^nRSrzg;s&$`P`e_Yt*eIPF2D?+dwhyjOysU>}No7b(2UE0#7 zQ{Nz&KR?)&oU)gUj+Nf`tmgo>C(gwbR1=A8Znk>kO*+C$C9#-JYpXd2Xa<&s}JW4^lNz&YG&4Y9+kOWFKG?Z!qRKv^wOw$m) znQ5C?gMk93lMYni^u?pv4fowDyH;QA5NmMgnJqO#*OxuD#W5V0P;$z_*D7(9AU$Pc z=t2itJqzS&M3}eY+mCFXj-wT7#PU22Q1C2K(l1hO0mY`c zd0%U*wX}1NPnf2LrU^PbP1;(GRwYD7I#VhXN+RGYFl5EM%8o+nBH;IF&EbfSZQ?430lYf~?SbpM&(o<}B=tOgTB7H zaG^+DW->1??-e2%cCq}BNWifnooycoIroyfhW0iKzh4pwOBx$ZaphIvNY5N!`_ZF$ zGFj&Ym9TUHyP9wcQi^LrA^EM1KWu+scBdr@1%(jcDut^QAQ&DguzPok@d;bR!Wz-A zA`(;_9g_U@b;01U%Xrcu9ybX1BtmLP9f~DIEM_pL+uuDnkX2<{xvtB3KuACa{PLzd zZ*SYWYDIKq@8SR>rwZ)rPt)1uLpKC|zs}inMK(Wno-bco;<0;S^>T;qZWl|!tWL#M z3k6@jRqhbK6pQcX=q%s$WM2tZ+`E>(5mAna^uCKJ0bz+>Z_yA zU$;8iIBS-VM8f8&r^gPJORjbKm36*s)?wRgNnUw%nl(#o9@^;AJ>S7H6i%VyKyC@@ z>J_bRE?3Ui*ne2@=I$bP*`>MB?}PFXo(S z$5Bkr*gW~<=z+nb*}s{VwDWnF=l`C-otoy()g>Of%cdpnvZr5gehf6jOCA=SJS8}I zNN~vlhetL!Z2G!GZX(V0cT)&ZhX%6e{`}0?^yIjWZ7WJervL;c&~Wa2@olFh*6!Su z8ruJ1`f({m$D-cQ6@fb48aXn>?|!UUzqU-c5l);GJhx5K+oOoZLAW4H7(A}AWrt+x zJVjTJ;!Bq)=Cw;+c)g4?4JIZ_nX~7L!!y&ab@p^Azy00h6H1ZQ0hA)W?Ty5ZRC0QJ zac^+VwadfdsZ7Px@p=V*NRDc3{0(X%>8iWGEwtK-D;g5~@gLT77OM0;lbS5ox7&D182NYENeA+*`;#POl7i8Hc~I~TY`@hf*pG`lIINWdPMU2 zZj_WLty=NPO0Q{PyNW0N;qt&{Nq*Yo)t!QZt5Fx!$mf+^O}}0u$^n^b!J&D5IULrn z`{MHG^<7<-PuB%IcjZbizF924y;thH|E1FyflW6HaoGY_SO$zuLMQ}w0ere-?@`!$ z3|@OrsTX&+ELt29O^pUp*YsqvoXY3j5nu|K1QHsR+o_5w<+rr%zD2>P-!H*c1T2+S zLZ5D)7vq|%!uT!3s%3~!R53FnIDE{Yl!um9#kETWniRCOnCv{_W(`y3XSJDxLOMO& zKEr2f0WS%aJZl%L034tgj~N@*uWz~~5Y!MF3=b9ETtQ7+rbru7IcLu1=xA2hjzSo4 zgB?f>}Vd7Wg%$$ z&aJJ#Ub!l)wYHkrWyQ&%f;~Kv-*bMvG}zytN{yW@@za|X@p=~&44suc`mAG5rz@B0 z+}RHI+ydj{rD7^=PmGP1h7%Jdg@BG(CM#Bi#Z5Oht&y4tcs}Rq9smT0e8%PAM_F7K z%4OKMKlK(c4Oquc7Rt9>uc2ws*rG@$AYe+uK_xUK`5Bv8Z3@37SapT4w)d-g;MDOG z`2!0U7M9K=C4rzuv7`{<6RV5b5-jesv=y!*CIt1GCW86AGwRK)xa|h9a`VrWAteOr z;mASB9S>4EaKg>puw0A$;TK9awY%g}kWLFW+>i6d9<>uV;`eI}N>LJmqOOZvCgU7Z zic#QvZ9$SR6y5DqD&H8Dx-aZ;m0di?FzO;Upm1|AIw2VxE*Fa>H8gNa^^GN@Y>F!a zAqbcnvzmkiI}J?a^X^G6?D9a?bHXf8tWw~5iYzZy)1H^6y#J#EL-4E32DySuG^Fv& zKg-#4P7MHOj-GbSpZr$qU9rrNnY^O0!Dci8UZ{O*}lbp1gIgQHW zqJdApe(+3cbKx20)F?`MQZ2)ze!ZSmDva46-hKCh7iN##|LV?( z+M$uLvBF4YjB(FL+Dj1x0g^DZxsBN^77Ct^TH}~7ii;I8g=lL=Kb*rF!+qP{t z1y`$$-}B9Hy!eYp@8A0)YR=!)oPXbK%)Q4}FMUd0(I=-gO2&d1Kxw1FEa!z>oN3j#H^cabWf@x^JR(d};1P<>0YL;Mb-h++jH7A*{b~ zDFQA<8q69PM<7o+Xe`d7G**RIE+cEVK}0Bzk0TBO@IrxhOifislzM?ndDv*}TWk64 z0J5QSY448O*punXIpVz-VVdB|l_k9O)*t`l!G*5ExDSkYK$`W@ShxTI3sxwgFg6Bf zco^0g^rHy9MgzXU~$$7{F0Yo*+2t@QxVwE#D5)I`K5YvV`T!9q3bw9x$EuXz2V zE|!;;aq{E`|9%cYm;r(SA`2lr^!k19f`<|@s9q0NDWpR~C|0ZJo;!y)j;ZSVqru9` zkt|6bHJk_JIEsjJY_e%K1>~HU%8s}v4g;aIM3SuH%|E??&->P}0QS}dthOlm9BF7# zDi1+xjil9t)Eb&|c%BDUEJ9W)2=koKZi^!4TjMBtJS!AFGMq<@F-1gIS>vWDCZa;w zFHgo{7sMGP+^hbY3t&ryVQ+;&k_97yI88A=H4Udwf>)_PXBo0i2QUV#SOk?yV2%T$ z6gST(SXm^-ZJVF4vsk}7Z}D$ zK+Fr^uEE6QPPFT*NTV*|)m4B9(ZT|v)m8M91pPR+0Ej6?B`LR>G<~+`dFKsdG2{HI zC~g`-ZH!K$q{BqXcKa4duJPi50$c#D3lu%zp&cF$O*l}>Vz}hu#Q81`9ee;=ruTz0 zz$uj=%4O(0M{W#6u?VXa7-R4}&st+RlT!Fe@<<^`o@G+*gtf)bGi_RfT5Huwl8gJt zCl$zZFSZQ!Y;p0*5r*?Y56|rOAvmGY1NPMz&Uam8#^T5mUxU&XQX9nWHiQsxhKIrP z9Fdghdmge@3;i%OJ%Af)-F~lEWY!)RS}Qd=`a4rBe!>{bfVFP6c?7l2>Ot_&%C38x zm8l(QL@eBQnaEqA|v^)-vGQR!2%Hbi=OLci_JJ4&Hxg z=#vjl(rV@)$OxAM!n<{jj^cRr$3MqCk3WxG=7{@!s4QFCy5M#tm(lA7}yljh| zj%jOc&IEyMCW%h81|#bC9p{Q(`%!CLcagj90_VKD4tT8-H5p?(rPR2!_JQ5g)5o8E z`swGkZ=V`s#K;w#v|X!@zEn_Yf-Fg3je+X*5H}h~I~_16dZ7ibGc*yAVT|>J5UWzkPZt*#O|#Jm zc5dJP;O?oZU6Zw1r6h#sdLE-B0VN4+5Lil66Xv;TcDwr2nKQQAZd+|_9y`uzyI4GJ zg{U*fSrLv?7eX`#>5Lg;3173kWaByUMne->YaOliu#|El&+{2$%-(|I>=}~s-Yp{| zHQ)1m5AdYcWbzzo6j7SzGVXS5CS@;ioO&X}k`bcLg=h&O>dqQmAcSZ$#==3p$-&A8 zOJ6sFpG$Q}M4B;{Nh#aCJYS5JnqOI7o^h<55L#CmV}guf04P^VNzOaHLSYe%MV#}< zU4v^lj#KBHHwU{lA);(UO6_1)`ONxnW-hiwBpG9o<2aHr77HN)rPQ)=-E&f@s#L0G zjVS>T)>^Wzn}PGhV=Q5eMM8)+=e#}mTu*6#!(4LB+2m#icS}Tyh!P^|59+1uIL?aJ zddL`4HpUb;IDjFdoH3RT>MG)#CycS)AbkqJb&vYD3b+wea?TZFEVI@|LI`ir`CV)6 z`jKfp>U|}np?EnA( literal 0 HcmV?d00001 diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/inc/show_ticket_info.php b/code/ryzom/tools/server/ryzom_ams/www/html/inc/show_ticket_info.php index 14f9c026c..84bea6d97 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/inc/show_ticket_info.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/inc/show_ticket_info.php @@ -31,6 +31,8 @@ function show_ticket_info(){ $result['ht'] = $ticket_info->getHT(); $result['nel3d'] = $ticket_info->getNel3D(); $result['user_id'] = $ticket_info->getUser_Id(); + global $SITEBASE; + $result['SITEBASE'] = $SITEBASE; if(Ticket_User::isMod($_SESSION['ticket_user'])){ $result['isMod'] = "TRUE"; diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/templates/show_ticket_info.tpl b/code/ryzom/tools/server/ryzom_ams/www/html/templates/show_ticket_info.tpl index 54f8e13d2..d66d43162 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/templates/show_ticket_info.tpl +++ b/code/ryzom/tools/server/ryzom_ams/www/html/templates/show_ticket_info.tpl @@ -11,61 +11,71 @@
    Additional Info - - - -
    Shard ID: {$shard_id}
    User Position: {$user_position}
    View Position: {$view_position}
    client_version: {$client_version}
    patch_version: {$patch_version}
    memory: {$memory}
    server_tick: {$server_tick}
    connect_state: {$connect_state}
    local_address: {$local_address}
    os: {$os}
    processor: {$processor}
    cpu_id: {$cpu_id}
    cpu_mask: {$cpu_mask}
    ht: {$ht}
    nel3d: {$nel3d}
    + + + + + + + +
    + + + + +
    + + + {if isset($isMod) and $isMod eq "TRUE"}{/if} + + +
    Show Ticket LogSend Other TicketShow Ticket
    - - - - - -
    -
    -

    Actions

    -
    - - -
    -
    -
    -
    - - Actions -
    - - -
    -
    -
    -
    - +
    +
    +
    + + + + + + + + + +

    Additional Info For Ticket [#{$ticket_id}]

    +
    + + + + + + + + + + +
    + + +
    +
    + + +
    + + +
    + + +
    + + + + +
    + + +
    +

    Ingame related

    + + + + + + + + + + + + + + + + + + + + + + +
    Shard ID: {$shard_id}
    User_Id: {$user_id}
    User Position: {$user_position}
    View Position: {$view_position}
    Client_Version: {$client_version}
    Patch_Version: {$patch_version}
    Server_Tick: {$server_tick}
    +
    +
    + + +
    +

    Hardware & Software related

    + + + + + + + + + + + + + + + + + + + + + + +
    Memory: {$memory}
    Processor: {$processor}
    Cpu_Id: {$cpu_id}
    Cpu_Mask: {$cpu_mask}
    HT: {$ht}
    OS: {$os}
    NeL3D: {$nel3d}
    +
    +
    + + +
    +

    Network related

    + + + + + + + +
    Connect_State: {$connect_state}
    Local_Address: {$local_address}
    +
    +
    +
    +
    +
    +
    + +
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Shard ID: {$shard_id}
    User Position: {$user_position}
    View Position: {$view_position}
    client_version: {$client_version}
    patch_version: {$patch_version}
    memory: {$memory}
    server_tick: {$server_tick}
    connect_state: {$connect_state}
    local_address: {$local_address}
    os: {$os}
    processor: {$processor}
    cpu_id: {$cpu_id}
    cpu_mask: {$cpu_mask}
    ht: {$ht}
    nel3d: {$nel3d}
    user_id: {$user_id}
    - +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Ingame related
    Shard ID: {$shard_id}
    User_Id: {$user_id}
    User Position: {$user_position}
    View Position: {$view_position}
    Client_Version: {$client_version}
    Patch_Version: {$patch_version}
    Server_Tick: {$server_tick}
    Hardware & Software related
    Memory: {$memory}
    Processor: {$processor}
    Cpu_Id: {$cpu_id}
    Cpu_Mask: {$cpu_mask}
    HT: {$ht}
    OS: {$os}
    NeL3D: {$nel3d}
    Network related
    Connect_State: {$connect_state}
    Local_Address: {$local_address}
    +
    From 600b016e1d296c6322e8d16aa88c891eb04c6c9d Mon Sep 17 00:00:00 2001 From: Quitta Date: Thu, 29 Aug 2013 02:08:05 +0200 Subject: [PATCH 127/313] added images in the ingame part, added config properties & fixed time bug in queues page --- .../ryzom_ams/ams_lib/autoload/ticket.php | 1 + .../ryzom_ams/ams_lib/img/info/client.png | Bin 0 -> 4674 bytes .../html => ams_lib}/img/info/connect.png | Bin .../ryzom_ams/ams_lib/img/info/cpuid.png | Bin 0 -> 4715 bytes .../server/ryzom_ams/ams_lib/img/info/ht.png | Bin 0 -> 5052 bytes .../ryzom_ams/ams_lib/img/info/local.png | Bin 0 -> 4843 bytes .../ryzom_ams/ams_lib/img/info/mask.png | Bin 0 -> 4943 bytes .../ryzom_ams/ams_lib/img/info/memory.png | Bin 0 -> 4796 bytes .../server/ryzom_ams/ams_lib/img/info/nel.png | Bin 0 -> 4615 bytes .../server/ryzom_ams/ams_lib/img/info/os.png | Bin 0 -> 5027 bytes .../ryzom_ams/ams_lib/img/info/patch.png | Bin 0 -> 4826 bytes .../ryzom_ams/ams_lib/img/info/position.png | Bin 0 -> 4737 bytes .../ryzom_ams/ams_lib/img/info/processor.png | Bin 0 -> 4696 bytes .../ryzom_ams/ams_lib/img/info/server.png | Bin 0 -> 4450 bytes .../ryzom_ams/ams_lib/img/info/shard.png | Bin 0 -> 4621 bytes .../ryzom_ams/ams_lib/img/info/user.png | Bin 0 -> 5017 bytes .../ryzom_ams/ams_lib/img/info/view.png | Bin 0 -> 4323 bytes .../ingame_templates/show_ticket_info.tpl | 19 +++++++++++ .../tools/server/ryzom_ams/www/config.php | 1 + .../ryzom_ams/www/html/img/info/client.png | Bin 3889 -> 0 bytes .../ryzom_ams/www/html/img/info/cpuid.png | Bin 2897 -> 0 bytes .../server/ryzom_ams/www/html/img/info/ht.png | Bin 3553 -> 0 bytes .../ryzom_ams/www/html/img/info/local.png | Bin 2086 -> 0 bytes .../ryzom_ams/www/html/img/info/mask.png | Bin 3300 -> 0 bytes .../ryzom_ams/www/html/img/info/memory.png | Bin 3101 -> 0 bytes .../ryzom_ams/www/html/img/info/nel.png | Bin 2727 -> 0 bytes .../server/ryzom_ams/www/html/img/info/os.png | Bin 3428 -> 0 bytes .../ryzom_ams/www/html/img/info/patch.png | Bin 4119 -> 0 bytes .../ryzom_ams/www/html/img/info/position.png | Bin 2289 -> 0 bytes .../ryzom_ams/www/html/img/info/processor.png | Bin 2933 -> 0 bytes .../ryzom_ams/www/html/img/info/server.png | Bin 2230 -> 0 bytes .../ryzom_ams/www/html/img/info/shard.png | Bin 8804 -> 0 bytes .../ryzom_ams/www/html/img/info/user.png | Bin 2468 -> 0 bytes .../ryzom_ams/www/html/img/info/view.png | Bin 2526 -> 0 bytes .../ryzom_ams/www/html/inc/show_queue.php | 1 - .../www/html/inc/show_ticket_info.php | 4 +-- .../www/html/templates/show_ticket_info.tpl | 32 +++++++++--------- 37 files changed, 39 insertions(+), 19 deletions(-) create mode 100755 code/ryzom/tools/server/ryzom_ams/ams_lib/img/info/client.png rename code/ryzom/tools/server/ryzom_ams/{www/html => ams_lib}/img/info/connect.png (100%) create mode 100755 code/ryzom/tools/server/ryzom_ams/ams_lib/img/info/cpuid.png create mode 100755 code/ryzom/tools/server/ryzom_ams/ams_lib/img/info/ht.png create mode 100755 code/ryzom/tools/server/ryzom_ams/ams_lib/img/info/local.png create mode 100755 code/ryzom/tools/server/ryzom_ams/ams_lib/img/info/mask.png create mode 100755 code/ryzom/tools/server/ryzom_ams/ams_lib/img/info/memory.png create mode 100755 code/ryzom/tools/server/ryzom_ams/ams_lib/img/info/nel.png create mode 100755 code/ryzom/tools/server/ryzom_ams/ams_lib/img/info/os.png create mode 100755 code/ryzom/tools/server/ryzom_ams/ams_lib/img/info/patch.png create mode 100755 code/ryzom/tools/server/ryzom_ams/ams_lib/img/info/position.png create mode 100755 code/ryzom/tools/server/ryzom_ams/ams_lib/img/info/processor.png create mode 100755 code/ryzom/tools/server/ryzom_ams/ams_lib/img/info/server.png create mode 100755 code/ryzom/tools/server/ryzom_ams/ams_lib/img/info/shard.png create mode 100755 code/ryzom/tools/server/ryzom_ams/ams_lib/img/info/user.png create mode 100755 code/ryzom/tools/server/ryzom_ams/ams_lib/img/info/view.png delete mode 100644 code/ryzom/tools/server/ryzom_ams/www/html/img/info/client.png delete mode 100644 code/ryzom/tools/server/ryzom_ams/www/html/img/info/cpuid.png delete mode 100644 code/ryzom/tools/server/ryzom_ams/www/html/img/info/ht.png delete mode 100644 code/ryzom/tools/server/ryzom_ams/www/html/img/info/local.png delete mode 100644 code/ryzom/tools/server/ryzom_ams/www/html/img/info/mask.png delete mode 100644 code/ryzom/tools/server/ryzom_ams/www/html/img/info/memory.png delete mode 100644 code/ryzom/tools/server/ryzom_ams/www/html/img/info/nel.png delete mode 100644 code/ryzom/tools/server/ryzom_ams/www/html/img/info/os.png delete mode 100644 code/ryzom/tools/server/ryzom_ams/www/html/img/info/patch.png delete mode 100644 code/ryzom/tools/server/ryzom_ams/www/html/img/info/position.png delete mode 100644 code/ryzom/tools/server/ryzom_ams/www/html/img/info/processor.png delete mode 100644 code/ryzom/tools/server/ryzom_ams/www/html/img/info/server.png delete mode 100644 code/ryzom/tools/server/ryzom_ams/www/html/img/info/shard.png delete mode 100644 code/ryzom/tools/server/ryzom_ams/www/html/img/info/user.png delete mode 100644 code/ryzom/tools/server/ryzom_ams/www/html/img/info/view.png diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket.php index 321409b24..e263ff700 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket.php @@ -246,6 +246,7 @@ class Ticket{ } $this->title = $values['Title']; $this->status = $values['Status']; + $this->timestamp = $values['Timestamp']; $this->queue = $values['Queue']; $this->ticket_category = $values['Ticket_Category']; $this->author = $values['Author']; diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/img/info/client.png b/code/ryzom/tools/server/ryzom_ams/ams_lib/img/info/client.png new file mode 100755 index 0000000000000000000000000000000000000000..d9c327ad5bc42a37f4cbf85f713c2d44cc101baf GIT binary patch literal 4674 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 Ef(o7LCIA2c literal 0 HcmV?d00001 diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/img/info/connect.png b/code/ryzom/tools/server/ryzom_ams/ams_lib/img/info/connect.png similarity index 100% rename from code/ryzom/tools/server/ryzom_ams/www/html/img/info/connect.png rename to code/ryzom/tools/server/ryzom_ams/ams_lib/img/info/connect.png diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/img/info/cpuid.png b/code/ryzom/tools/server/ryzom_ams/ams_lib/img/info/cpuid.png new file mode 100755 index 0000000000000000000000000000000000000000..019d08befb79220e01d8b7ed887a7a603e483561 GIT binary patch literal 4715 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 R$SeQ=002ovPDHLkV1f~-1Q!4R literal 0 HcmV?d00001 diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/img/info/mask.png b/code/ryzom/tools/server/ryzom_ams/ams_lib/img/info/mask.png new file mode 100755 index 0000000000000000000000000000000000000000..2dd19175d33e01003b7d4a190697bf78729bbe6e GIT binary patch literal 4943 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 N002ovPDHLkV1nn%Ri^*| literal 0 HcmV?d00001 diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/img/info/memory.png b/code/ryzom/tools/server/ryzom_ams/ams_lib/img/info/memory.png new file mode 100755 index 0000000000000000000000000000000000000000..c1b4a26c333fe14388ece9c2b6583c38be4908cb GIT binary patch literal 4796 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 WJl)$0=%nNT0000^FS? literal 0 HcmV?d00001 diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/img/info/nel.png b/code/ryzom/tools/server/ryzom_ams/ams_lib/img/info/nel.png new file mode 100755 index 0000000000000000000000000000000000000000..344c05ff9d72ebf6e77eab7e21e5db1a404a1d40 GIT binary patch literal 4615 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νsj$1CbWnbJPsuS;t6!8 z9%-XG%8181k9bhZV6;vxRu~iDoFfcF(lsUl{QpyAexbhbOr9ByK)sliO6eF6psVjfKBQ0 zz4lXd{ws~hIk%8D(B-tJZKTTh)j-^`W_LPOsh;jDRZ?W@sTj$4eRc?*JFgJl*-=PI~|V002ovPDHLkV1gC3vOE9) literal 0 HcmV?d00001 diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/img/info/os.png b/code/ryzom/tools/server/ryzom_ams/ams_lib/img/info/os.png new file mode 100755 index 0000000000000000000000000000000000000000..d8a50a5e23852c42a591da0ba121ec469be7a6a6 GIT binary patch literal 5027 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 literal 0 HcmV?d00001 diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/img/info/patch.png b/code/ryzom/tools/server/ryzom_ams/ams_lib/img/info/patch.png new file mode 100755 index 0000000000000000000000000000000000000000..cf468d12516eb231d5edc6153cb4b16b131de2ac GIT binary patch literal 4826 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 A;{X5v literal 0 HcmV?d00001 diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/img/info/position.png b/code/ryzom/tools/server/ryzom_ams/ams_lib/img/info/position.png new file mode 100755 index 0000000000000000000000000000000000000000..476e3693ddec948b3011d4a9be10ba83cb8e6134 GIT binary patch literal 4737 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 literal 0 HcmV?d00001 diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/img/info/shard.png b/code/ryzom/tools/server/ryzom_ams/ams_lib/img/info/shard.png new file mode 100755 index 0000000000000000000000000000000000000000..752ff61ff5d08f30933a80d426ed3832ad295d74 GIT binary patch literal 4621 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 literal 0 HcmV?d00001 diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/img/info/view.png b/code/ryzom/tools/server/ryzom_ams/ams_lib/img/info/view.png new file mode 100755 index 0000000000000000000000000000000000000000..00363775c01df48e65e52870f1e1ba3e7a5411bb GIT binary patch literal 4323 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 Re(wMP002ovPDHLkV1n1yDcS%4 literal 0 HcmV?d00001 diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/show_ticket_info.tpl b/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/show_ticket_info.tpl index 9edb32cda..9233f0c0f 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/show_ticket_info.tpl +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/show_ticket_info.tpl @@ -62,25 +62,33 @@

    Ingame related

    + + + + + + + +
    Shard ID: {$shard_id}
    User_Id: {$user_id}
    User Position: {$user_position}
    View Position: {$view_position}
    Client_Version: {$client_version}
    Patch_Version: {$patch_version}
    Server_Tick: {$server_tick}
    @@ -92,25 +100,33 @@

    Hardware & Software related

    + + + + + + + +
    Memory: {$memory}
    Processor: {$processor}
    Cpu_Id: {$cpu_id}
    Cpu_Mask: {$cpu_mask}
    HT: {$ht}
    OS: {$os}
    NeL3D: {$nel3d}
    @@ -122,10 +138,13 @@

    Network related

    + + +
    Connect_State: {$connect_state}
    Local_Address: {$local_address}
    diff --git a/code/ryzom/tools/server/ryzom_ams/www/config.php b/code/ryzom/tools/server/ryzom_ams/www/config.php index 7d8f202d7..6e2769079 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/config.php +++ b/code/ryzom/tools/server/ryzom_ams/www/config.php @@ -73,6 +73,7 @@ $AMS_LIB = dirname( dirname( __FILE__ ) ) . '/ams_lib'; $AMS_TRANS = $AMS_LIB . '/translations'; $AMS_CACHEDIR = $AMS_LIB . '/cache'; $SITEBASE = dirname( __FILE__ ) . '/html/' ; +$WEBPATH ='http://localhost:40917' ; //defines the default language $DEFAULT_LANGUAGE = 'en'; diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/img/info/client.png b/code/ryzom/tools/server/ryzom_ams/www/html/img/info/client.png deleted file mode 100644 index 83443dd76800d9000862d96ae749167c5a62b6e8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3889 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 diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/img/info/ht.png b/code/ryzom/tools/server/ryzom_ams/www/html/img/info/ht.png deleted file mode 100644 index fcf468e71e7df2919bdf1d111a0a5577a42bc88a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3553 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 bna2MDEBL_rDs&iV00000NkvXXu0mjfY=iqd diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/img/info/local.png b/code/ryzom/tools/server/ryzom_ams/www/html/img/info/local.png deleted file mode 100644 index 4cb748c8df7f93429032b4dbb73d86a595e7d58f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2086 zcmV+>2-)|EP)4$R~5(q=id9h=bIUOGI5;Paa_AioCF#*s6YXA z0h>OAx_|{`gH$9I>_F^TRIn)<7Az1F5<&t*K`f9ekXl3+Eouu@oi=SkUUlL}{2F`S z^Ue3V_pq25*G{9hq7^HSbabWfoA3UA=bU@a9pL}=37f4%<$o?p1bKAK`TD~P;}@QK z?CkAdKK+gES00%9wIwl5fcU^~KaQ{{nsd@PGRv!5;>OiW zX1kgH_06^9d)=6?f1&`k5|y)4{xkDam2aLoeUCgkdDyPkcg@wSH>umKWv-I8Z5d=a z^OcSGPj4(|Pvx3B9~;24<X@b<9SbgPCO7?({5<+rqL8lGGqK2JHyfr>cIT)$LCvsexfa zZgM_U3gaYB?M5$=OaN=}R$Kr5!b*n+BQpjY zwHA4fi<2h~Q+>AqZ3Lu*$Z`{ez5`2;>Zh8B0Aq}ZGjJvJsM}8nAPB%N`{I1N&lfu} zU)uvXIaT=Lqn|tW;gxrp?_VecM~H55W)5=M+!iafF%K`1e7I# z!7XXjNF0DFc+OUhZSu58oA64V@QR{ zIw&foge3@45{^_$1gy$Y1_S^F!0SY@X2W{`Ta5_Iw>RO~7AhqlhbtjG+k%t=T5AA6 zDfEe?!t!nmDc~r1cTpt>BnN?vl|usH0*J*Bzy=W5N=+1t#bLdHN51?J`n?GCR)p29 z2Hw1~hQ%vukWwAU$auMgz!nHxg}}EEcs2spLcx)6T?xw)d*b`d2Vjjd-~cFS<{~rp zGT_)Yh&9)m8I6kOxq}Ssx0&fl?ABZFi}XV*01zt-rlAGEH^u~co*(D{TM4o)f)D_K zU`c^qKRNI}%e5`IuEa;RZl|XKc%uxAf|FVsHbz5h4Jn5kl7R0BIJO+PSVDjV z0nG2g4NVI1p#W?Q`rSCbywd)|N)yjA&;+0*1|?8Kx8Jz3`SL4^OYbb-S_{rC%pT1d zl*Tcb%|L(%lhrb^EZf`M@d{YIy?J*4!{~LB&MOx;p1ru*`+2LcF96uZ03cGs=o0{4 z0Ci@FZo=7HtNmNwe)_lN>1y!M*}3Xtf#()}ETE z89)S}4WK?0?*T{^00ST$KA<5`44{{5?%v$&K6i7o`+}|LP{mj0=V!`i!mv@Cof@A6 zv0`mVDzz8A$u*KBZ!KP{{dudcU*dsqGz_o}pfy}002$%ENhmRBM-PJ@Eeuy5gQ4L7 zu!vyW5|xs#rpJnI^~~(#xl?o1PhDGD6Ww-Hf9q!bkJ}ydiZ*Bs1MA};L^KRA0igF^ z$D^U7hGKWRjX}GP&Wj*|l7t{|RVmB0PI9hkgARZkgF~GTg+_wO@R=SuAGi(o`Tm1> zHy^?7|DK2C)I)&Lnhn>Ho1x=QfV+Y0FVB8avG4tUfDy#qeDrSgTL8d+0KB-n!&rx< Q^Z)<=07*qoM6N<$f^HewJ^%m! diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/img/info/mask.png b/code/ryzom/tools/server/ryzom_ams/www/html/img/info/mask.png deleted file mode 100644 index 2f0685bded1706469ba74ff7754f5b16b46a8513..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3300 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 rvj4bzsem_|*H2U1E%>PbpC8>lP^Fld z5JVFSY9QkEV^ftz?WfWTwWSSh14R%~sZxsskw8_Of+E@=-UU^?KiDc3$q>b51{;GrMbNUkF6v3y*Z?Ud_xs=YRg^IsfMceq+C}%S`_|Cl?X6 z_V=@6`&I0{W-k{e#$4P<};?5hFzN`RYzHv$KM>y=XP-o9f8J9qA8XlOg* z=PweeJV|jAANly-vu$uIN(lq~+qmm5K0;5q!o;m=y zuLo`@6pA+u^bgXvbt_5S#T|EkfVbcFcC52xEyLY^^^ZJ%_Id1loqzw_7x>G+{UE2u zp5rHvKS3&sOif=RfGyxz&B7&Dg4hUP4DcRp~{O`k01E+wqz@-3Ur5U`hu5^Kw2hFYg^J;)Q?3T5T z$TG+7YxeNfFMo?`_wNHG%uLU->zWasd+tfnw9dEw=YGEUh0oAzG|m9$!}gA^7^`Ew zKqUZa1I=)60eQSKv{+V$mjPbQB|38S?xCUWU*6g`NVDkx#k<~fJ5#e4I6XSb*w`s* z)hZ$o>4;LPgpMLaAZr`ac8XStD2j;V7#9XM)?%%tzPN~W4y`rLIkGIHR;yC4*KY^L zxZEAuY~T_Kh@vPWNeYP5j9j&YKlc)kyM-LGtF-LxOjD`6rL@6r0WxjO(1DGsBMEK4(AEIp&GO z3}^iL$QKL5aSS-Db+lS3h#(>qyNYNfn5<2jX0$Vd2#7$wkY{r816`Fab>C z<*g%oc0H9q5R?*}Q(Sx9>*(&OP+O=`tjNh&R;xrwjE)inL6k<+@&<}1@Y{`u_(x|0^9n2&W9aMaV|0u%PzvlO0prqH zTMMvTn$GzNj~)I$LYUGl$X|QL?;OD+hHB!6D(ZqjheH zA0dD&;@z@y@RoLcHNbA=gL9CjZ4Tag3&U6K;-&NF$t5|!(Qdbyo1Lb&x1TtdP{(ZHdU0vyIz0>D?S5`N7Bq6Hl*Mo&J?_)F&yiBMXj z1mY-WW@?fP<7X*X`Y^^&*i@vrWv2(_?Bxu2nsE*s7Hb{OI;_cZMUIunU zz<$3R&KOLVQJY^NNfN{v8m%-$HX)bSj6U-uN=HO-j1m=qx}`BX>Fx6N`R|0lx)YRM zGmG4iVZU)fbK0iOo_)W~@W>vzx)N;G;@FX+?A$d%ynh>&F|Gc%J*k#s%V zKqb5ni&l)DJi_R+M@d^Pa`^%xLbFjvaO4X`q9}(}5ju`&HB+=!SZipfDWd%X3dK!e zq19?pD0b0m)i5T*Illbmlw9}MaKF{Mne30p>8MIDt!A*WO zh5>OtLgjKr+LxlNQSgJ3Q53scMf{>uVhVi|DGa`fiuui=5CT)#OU$EUUIEh z2OLC6gdp^8EmNJJVScX0%=9=qxQHl)&^fF^wz>~*Skt}CloFba8d;Xn(N1xKwFV`E zjv`vk8Y0?9$B;`Hqj1*Hte0pkE?`|7P-Gb>rBF&?tsl>a=4?YO>j8FYn)-a!I*hX@ z5kz#z2YeEvM4?3^E~GT!0D1NVO9&QDd?u;9i#cnt&ga%Hs} z%|=?$Rm{w>NFcwL|jO4#8(c%_YqjE&ElNjZWpA|IWKe8TE8%7 z$>kz|IeevYSx>EFYgHTxzD#**xx4(Ofq@~?Rz{YENwGei?L0e&5*280%Y1~BZ^Q2^ zXX|7aC?Sp$;wa+G*)f{U#(lsEd_(X>U=rV0X#m+;Wqv0oEd#Fu{t(!P?|BvQ>0c+Y z-uP&*Zygt?0Z-vOLNDMOFBgC*d^e~8n6&^$0oWFx2j5p40tWDnkP^O>iCN#Rei1YH zdTkNUuz+Vb;#YSGizlK3x)#880JmA87;BYC9l-7IQit(6%eB_4>BF5yV2a=R h2Fq>MPLB5WC9-62AgIuU>mPsWNa>X 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 diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/img/info/position.png b/code/ryzom/tools/server/ryzom_ams/www/html/img/info/position.png deleted file mode 100644 index ffa627e5296dbce66f33bf46e7340684e96351a1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2289 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 LNkvXXu0mjf(Xm~C diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/img/info/processor.png b/code/ryzom/tools/server/ryzom_ams/www/html/img/info/processor.png deleted file mode 100644 index 411be15c2c2ea43af4638ae4cdc463eca35b9038..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2933 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 diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/img/info/server.png b/code/ryzom/tools/server/ryzom_ams/www/html/img/info/server.png deleted file mode 100644 index 7edc55e2f5e4d54356ef0f4ff0fe02ac4da83d93..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2230 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 diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/inc/show_queue.php b/code/ryzom/tools/server/ryzom_ams/www/html/inc/show_queue.php index 513c5f52f..3d2a7aac8 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/inc/show_queue.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/inc/show_queue.php @@ -83,7 +83,6 @@ function show_queue(){ //if queue_view is a valid parameter value if ($queueArray != "ERROR"){ - $result['tickets'] = Gui_Elements::make_table($queueArray, Array("getTId","getTitle","getTimestamp","getAuthor()->getExternId","getTicket_Category()->getName","getStatus","getStatusText","getAssigned","getForwardedGroupName","getForwardedGroupId"), Array("tId","title","timestamp","authorExtern","category","status","statusText","assigned","forwardedGroupName","forwardedGroupId")); $i = 0; foreach( $result['tickets'] as $ticket){ diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/inc/show_ticket_info.php b/code/ryzom/tools/server/ryzom_ams/www/html/inc/show_ticket_info.php index 84bea6d97..4f1e98d61 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/inc/show_ticket_info.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/inc/show_ticket_info.php @@ -31,8 +31,8 @@ function show_ticket_info(){ $result['ht'] = $ticket_info->getHT(); $result['nel3d'] = $ticket_info->getNel3D(); $result['user_id'] = $ticket_info->getUser_Id(); - global $SITEBASE; - $result['SITEBASE'] = $SITEBASE; + global $WEBPATH; + $result['WEBPATH'] = $WEBPATH; if(Ticket_User::isMod($_SESSION['ticket_user'])){ $result['isMod'] = "TRUE"; diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/templates/show_ticket_info.tpl b/code/ryzom/tools/server/ryzom_ams/www/html/templates/show_ticket_info.tpl index d66d43162..23eaebdab 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/templates/show_ticket_info.tpl +++ b/code/ryzom/tools/server/ryzom_ams/www/html/templates/show_ticket_info.tpl @@ -17,61 +17,61 @@
    Ingame related
    - Shard ID: {$shard_id} + Shard ID: {$shard_id} - User_Id: {$user_id} + User_Id: {$user_id} - User Position: {$user_position} + User Position: {$user_position} - View Position: {$view_position} + View Position: {$view_position} - Client_Version: {$client_version} + Client_Version: {$client_version} - Patch_Version: {$patch_version} + Patch_Version: {$patch_version} - Server_Tick: {$server_tick} + Server_Tick: {$server_tick}
    Hardware & Software related
    - Memory: {$memory} + Memory: {$memory} - Processor: {$processor} + Processor: {$processor} - Cpu_Id: {$cpu_id} + Cpu_Id: {$cpu_id} - Cpu_Mask: {$cpu_mask} + Cpu_Mask: {$cpu_mask} - HT: {$ht} + HT: {$ht} - OS: {$os} + OS: {$os} - NeL3D: {$nel3d} + NeL3D: {$nel3d}
    Network related
    - Connect_State: {$connect_state} + Connect_State: {$connect_state} - Local_Address: {$local_address} + Local_Address: {$local_address} From a86f192c844fe76baf4b9c5073949a48dc7aba48 Mon Sep 17 00:00:00 2001 From: Quitta Date: Thu, 29 Aug 2013 05:01:47 +0200 Subject: [PATCH 128/313] dashboard added that shows the newest ticket, the amount of tickets in your todo list and tickets waiting on YOUR response --- .../ams_lib/autoload/ticket_queue.php | 6 +++ .../ams_lib/autoload/ticket_queue_handler.php | 37 +++++++++++++ .../{home.tpl => dashboard.tpl} | 0 .../ams_lib/ingame_templates/layout_user.tpl | 2 - .../ryzom_ams/ams_lib/translations/en.ini | 2 +- .../ryzom_ams/ams_lib/translations/fr.ini | 2 +- .../ryzom_ams/www/html/inc/dashboard.php | 35 ++++++++++++ .../tools/server/ryzom_ams/www/html/index.php | 6 ++- .../www/html/templates/dashboard.tpl | 52 ++++++++++++++++++ .../ryzom_ams/www/html/templates/home.tpl | 53 ------------------- .../www/html/templates/layout_user.tpl | 3 +- 11 files changed, 138 insertions(+), 60 deletions(-) rename code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/{home.tpl => dashboard.tpl} (100%) create mode 100644 code/ryzom/tools/server/ryzom_ams/www/html/inc/dashboard.php create mode 100644 code/ryzom/tools/server/ryzom_ams/www/html/templates/dashboard.tpl delete mode 100644 code/ryzom/tools/server/ryzom_ams/www/html/templates/home.tpl diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_queue.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_queue.php index 8751efbaa..ef0b15bd2 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_queue.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_queue.php @@ -39,6 +39,12 @@ class Ticket_Queue{ $this->params = array('user_id' => $user_id); } + public function loadAssignedandWaiting($user_id){ + $this->query = "SELECT * FROM `ticket` t LEFT JOIN `assigned` a ON t.TId = a.Ticket LEFT JOIN `ticket_user` tu ON tu.TUserId = a.User + WHERE (tu.ExternId = :user_id AND t.Status = 1)"; + $this->params = array('user_id' => $user_id); + } + public function createQueue($userid, $groupid, $what, $how, $who){ if($who == "user"){ diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_queue_handler.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_queue_handler.php index 116473c07..142dc8912 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_queue_handler.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_queue_handler.php @@ -59,4 +59,41 @@ class Ticket_Queue_Handler{ $this->queue->createQueue($userid, $groupid, $what, $how, $who); } + //================================================================================== + //Info retrievers about ticket statistics + + public static function getNrOfTicketsToDo($user_id){ + $queueHandler = new Ticket_Queue_Handler(); + $queueHandler->queue->loadToDoTickets($user_id); + $query = $queueHandler->queue->getQuery(); + $params = $queueHandler->queue->getParams(); + $dbl = new DBLayer("lib"); + return $dbl->execute($query,$params)->rowCount(); + } + + public static function getNrOfTicketsAssignedWaiting($user_id){ + $queueHandler = new Ticket_Queue_Handler(); + $queueHandler->queue->loadAssignedandWaiting($user_id); + $query = $queueHandler->queue->getQuery(); + $params = $queueHandler->queue->getParams(); + $dbl = new DBLayer("lib"); + return $dbl->execute($query,$params)->rowCount(); + } + + public static function getNrOfTickets(){ + $queueHandler = new Ticket_Queue_Handler(); + $queueHandler->queue->loadAllTickets(); + $query = $queueHandler->queue->getQuery(); + $params = $queueHandler->queue->getParams(); + $dbl = new DBLayer("lib"); + return $dbl->execute($query,$params)->rowCount(); + } + + public static function getNewestTicket(){ + $dbl = new DBLayer("lib"); + $statement = $dbl->executeWithoutParams("SELECT * FROM `ticket` ORDER BY `TId` DESC LIMIT 1 "); + $ticket = new Ticket(); + $ticket->set($statement->fetch()); + return $ticket; + } } \ No newline at end of file diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/home.tpl b/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/dashboard.tpl similarity index 100% rename from code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/home.tpl rename to code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/dashboard.tpl diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/layout_user.tpl b/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/layout_user.tpl index 83712192c..af2f23f07 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/layout_user.tpl +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/layout_user.tpl @@ -1,7 +1,5 @@ {extends file="layout.tpl"} {block name=menu} -
    Dashboard
    -
    Profile
    Settings
    diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/translations/en.ini b/code/ryzom/tools/server/ryzom_ams/ams_lib/translations/en.ini index 1af2c3e70..82d8c023a 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/translations/en.ini +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/translations/en.ini @@ -1,7 +1,7 @@ ; This is a sample configuration file ; Comments start with ';', as in php.ini -[home] +[dashboard] home_title = "Introduction" home_info = "Welcome to the Ryzom Core - Account Management System" diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/translations/fr.ini b/code/ryzom/tools/server/ryzom_ams/ams_lib/translations/fr.ini index 81f58af42..6e9a66ab0 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/translations/fr.ini +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/translations/fr.ini @@ -1,7 +1,7 @@ ; This is a sample configuration file ; Comments start with ';', as in php.ini -[home] +[dashboard] home_title = "Presentation" home_info = "Bienvenue sur le Ryzom Core - Account Management System" diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/inc/dashboard.php b/code/ryzom/tools/server/ryzom_ams/www/html/inc/dashboard.php new file mode 100644 index 000000000..752156c6c --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/www/html/inc/dashboard.php @@ -0,0 +1,35 @@ +getTUserId(); + $result['nrToDo'] = Ticket_Queue_Handler::getNrOfTicketsToDo($_SESSION['ticket_user']->getTUserId()); + $result['nrAssignedWaiting'] = Ticket_Queue_Handler::getNrOfTicketsAssignedWaiting($_SESSION['ticket_user']->getTUserId()); + $result['nrTotalTickets'] = Ticket_Queue_Handler::getNrOfTickets(); + $ticket = Ticket_Queue_Handler::getNewestTicket(); + $result['newestTicketId'] = $ticket->getTId(); + $result['newestTicketTitle'] = $ticket->getTitle(); + $result['newestTicketAuthor'] = Ticket_User::get_username_from_id($ticket->getAuthor()); + return $result; + + }else{ + //ERROR: No access! + $_SESSION['error_code'] = "403"; + header("Location: index.php?page=error"); + exit; + + } + + }else{ + //ERROR: not logged in! + header("Location: index.php"); + exit; + } + + +} \ No newline at end of file diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/index.php b/code/ryzom/tools/server/ryzom_ams/www/html/index.php index 6ce06739e..b52ccce3b 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/index.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/index.php @@ -8,7 +8,11 @@ session_start(); //Decide what page to load if ( ! isset( $_GET["page"]) ){ if(isset($_SESSION['user'])){ - $page = 'home'; + if(Ticket_User::isMod($_SESSION['ticket_user'])){ + $page = 'dashboard'; + }else{ + $page = 'show_user'; + } }else{ //default page $page = 'login'; diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/templates/dashboard.tpl b/code/ryzom/tools/server/ryzom_ams/www/html/templates/dashboard.tpl new file mode 100644 index 000000000..5d593eb26 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/www/html/templates/dashboard.tpl @@ -0,0 +1,52 @@ +{block name=content} + + + + +
    +
    +
    +

    {$home_title}

    +
    + + + + +
    +
    +
    +

    {$home_info}

    + +
    +
    +
    +
    +{/block} + diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/templates/home.tpl b/code/ryzom/tools/server/ryzom_ams/www/html/templates/home.tpl deleted file mode 100644 index 6198b53a1..000000000 --- a/code/ryzom/tools/server/ryzom_ams/www/html/templates/home.tpl +++ /dev/null @@ -1,53 +0,0 @@ -{block name=content} - - - - -
    -
    -
    -

    {$home_title}

    -
    - - - - -
    -
    -
    -

    {$home_info}

    - -
    -
    -
    -
    -{/block} - diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/templates/layout_user.tpl b/code/ryzom/tools/server/ryzom_ams/www/html/templates/layout_user.tpl index 8da6ca313..301af12b6 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/templates/layout_user.tpl +++ b/code/ryzom/tools/server/ryzom_ams/www/html/templates/layout_user.tpl @@ -1,8 +1,7 @@ {extends file="layout.tpl"} {block name=menu} -
  • Dashboard
  • -
  • Profile
  • +
  • Profile
  • Settings
  • Create New Ticket
  • From 01f6d6fabd854498ce43128d476aed44ec6ed13e Mon Sep 17 00:00:00 2001 From: Quitta Date: Thu, 29 Aug 2013 22:19:37 +0200 Subject: [PATCH 129/313] ingame dashboard also fixed! --- .../ams_lib/configs/ingame_layout.ini | 3 +++ .../ams_lib/ingame_templates/dashboard.tpl | 26 +++++++++++++++++++ .../ryzom_ams/ams_lib/translations/en.ini | 1 + .../www/html/templates/dashboard.tpl | 20 +++++++++++++- 4 files changed, 49 insertions(+), 1 deletion(-) diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/configs/ingame_layout.ini b/code/ryzom/tools/server/ryzom_ams/ams_lib/configs/ingame_layout.ini index e76cf2acf..f19d43265 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/configs/ingame_layout.ini +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/configs/ingame_layout.ini @@ -14,6 +14,9 @@ title_bg_color = "#303030" ;default info text color info_color = "#00CED1" +;notification color +notification_color = "red" + ;Account (user/admin/mod) name color team_color = "red" user_color = "green" diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/dashboard.tpl b/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/dashboard.tpl index 81ba9d934..af87d13aa 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/dashboard.tpl +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/dashboard.tpl @@ -27,12 +27,38 @@ + + - + diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/show_ticket_log.tpl b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/show_ticket_log.tpl new file mode 100644 index 000000000..dfd44b11b --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/show_ticket_log.tpl @@ -0,0 +1,26 @@ +{block name=content} + +

    Log of Ticket #{$ticket_id}

    + Title: {$ticket_title} +
    + + +
    + + +
    + + + + + + + + + + + + + +
    Tickets Waiting for Direct ActionTickets TodoNewest TicketTotal amount of Tickets
    {$nrAssignedWaiting}{$nrToDo}{$newestTicketTitle}{$nrTotalTickets}
    +
    +
    +
    diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/translations/en.ini b/code/ryzom/tools/server/ryzom_ams/ams_lib/translations/en.ini index 82d8c023a..bebed5301 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/translations/en.ini +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/translations/en.ini @@ -5,6 +5,7 @@ home_title = "Introduction" home_info = "Welcome to the Ryzom Core - Account Management System" + [settings] [syncing] diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/templates/dashboard.tpl b/code/ryzom/tools/server/ryzom_ams/www/html/templates/dashboard.tpl index 5d593eb26..b1c73fe04 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/templates/dashboard.tpl +++ b/code/ryzom/tools/server/ryzom_ams/www/html/templates/dashboard.tpl @@ -43,7 +43,25 @@

    {$home_info}

    - +

    This is the GSOC project of Daan Janssens mentored by Matthew Lagoe.

    +

    The features as admin covered in this project are:

    +
      +
    • Manage user accounts
    • +
    • Make users moderator or admin
    • +
    • browse user's tickets
    • +
    • Create a new ticket for a specific user as admin
    • +
    • Create a new support group (and link an email to it)
    • +
    • Add mods to support groups
    • +
    • Assign ticket to you
    • +
    • Forward ticket to a support group
    • +
    • Add hidden messages to a ticket only viewable by other mods
    • +
    • Browse ticket queues or create one dynamically
    • +
    • Sync changes after the game server is back up after being down for a while
    • +
    • Browse the log of a ticket
    • +
    • Browse additional info sent along when a ticket is created ingame
    • +
    • All the above can be done while ingame too
    • +
    +
    From 362e395779fde1f42581024088c8840340fc0126 Mon Sep 17 00:00:00 2001 From: Quitta Date: Sun, 1 Sep 2013 18:15:41 +0200 Subject: [PATCH 130/313] small bug fix --- code/ryzom/tools/server/ryzom_ams/www/html/func/add_user.php | 2 +- .../tools/server/ryzom_ams/www/html/templates/dashboard.tpl | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/func/add_user.php b/code/ryzom/tools/server/ryzom_ams/www/html/func/add_user.php index 75f04cc82..a7f17b0aa 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/func/add_user.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/func/add_user.php @@ -48,7 +48,7 @@ function write_user($newUser){ try{ //make new webuser - createWebuser($params['name'], $params['pass'], $params['mail']); + $user_id = WebUsers::createWebuser($params['name'], $params['pass'], $params['mail']); //Create the user on the shard + in case shard is offline put copy of query in query db //returns: ok, shardoffline or liboffline diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/templates/dashboard.tpl b/code/ryzom/tools/server/ryzom_ams/www/html/templates/dashboard.tpl index b1c73fe04..778ced05f 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/templates/dashboard.tpl +++ b/code/ryzom/tools/server/ryzom_ams/www/html/templates/dashboard.tpl @@ -66,5 +66,7 @@ + + {/block} From 1867c9d19682b786b3b1cfcc98aa023fffdf1b8a Mon Sep 17 00:00:00 2001 From: Quitta Date: Mon, 2 Sep 2013 04:15:18 +0200 Subject: [PATCH 131/313] started working on the module + small addition --- .hgignore | 7 +- .../{ => oldmodule}/ryzommanage.info | 0 .../{ => oldmodule}/ryzommanage.install | 0 .../{ => oldmodule}/ryzommanage.module | 0 .../drupal_module/ryzommanage/config.php | 90 ++ .../ryzommanage/ryzommanage.info | 10 + .../ryzommanage/ryzommanage.install | 31 + .../ryzommanage/ryzommanage.module | 949 ++++++++++++++++++ .../templates/ingame_register.phtml | 116 +++ code/ryzom/tools/server/ryzom_ams/howto.txt | 7 + .../tools/server/ryzom_ams/www/config.php | 2 +- .../ryzom_ams/www/html/inc/register.php | 7 + .../ryzom_ams/www/html/templates/register.tpl | 2 +- 13 files changed, 1216 insertions(+), 5 deletions(-) rename code/ryzom/tools/server/ryzom_ams/drupal_module/{ => oldmodule}/ryzommanage.info (100%) rename code/ryzom/tools/server/ryzom_ams/drupal_module/{ => oldmodule}/ryzommanage.install (100%) rename code/ryzom/tools/server/ryzom_ams/drupal_module/{ => oldmodule}/ryzommanage.module (100%) create mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/config.php create mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.info create mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.install create mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module create mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/ingame_register.phtml create mode 100644 code/ryzom/tools/server/ryzom_ams/howto.txt create mode 100644 code/ryzom/tools/server/ryzom_ams/www/html/inc/register.php diff --git a/.hgignore b/.hgignore index 9fb922bbb..5b6bebb35 100644 --- a/.hgignore +++ b/.hgignore @@ -199,7 +199,8 @@ code/nel/tools/pacs/build_rbank/build_rbank code/ryzom/common/data_leveldesign/leveldesign/game_element/xp_table/skills.skill_tree code/ryzom/common/data_leveldesign/leveldesign/game_element/xp_table/xptable.xp_table code/ryzom/tools/server/sql/ryzom_admin_default_data.sql - +code/ryzom/tools/server/ryzom_ams/drupal +code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib # Linux server compile code/ryzom/server/src/entities_game_service/entities_game_service code/ryzom/server/src/frontend_service/frontend_service @@ -210,6 +211,6 @@ code/ryzom/server/src/ryzom_admin_service/ryzom_admin_service code/ryzom/server/src/ryzom_naming_service/ryzom_naming_service code/ryzom/server/src/ryzom_welcome_service/ryzom_welcome_service code/ryzom/server/src/tick_service/tick_service -# WebTT temp dir +# WebTT temp dir code/ryzom/tools/server/www/webtt/app/tmp -code\ryzom\tools\server\ryzom_ams\old \ No newline at end of file +code\ryzom\tools\server\ryzom_ams\old diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage.info b/code/ryzom/tools/server/ryzom_ams/drupal_module/oldmodule/ryzommanage.info similarity index 100% rename from code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage.info rename to code/ryzom/tools/server/ryzom_ams/drupal_module/oldmodule/ryzommanage.info diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage.install b/code/ryzom/tools/server/ryzom_ams/drupal_module/oldmodule/ryzommanage.install similarity index 100% rename from code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage.install rename to code/ryzom/tools/server/ryzom_ams/drupal_module/oldmodule/ryzommanage.install diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage.module b/code/ryzom/tools/server/ryzom_ams/drupal_module/oldmodule/ryzommanage.module similarity index 100% rename from code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage.module rename to code/ryzom/tools/server/ryzom_ams/drupal_module/oldmodule/ryzommanage.module diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/config.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/config.php new file mode 100644 index 000000000..322d50357 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/config.php @@ -0,0 +1,90 @@ + 'Ryzom Manager', + 'machine_name' => 'ryzommanage', + 'description' => 'Ryzom Login Service integration - inserts and updates users and manages accounts.', + 'module' => 'ryzommanage', + ); + $vocab = (object) $vocab; + db_query("CREATE TABLE ryzommanage_querycache ( + `SID` INT NOT NULL AUTO_INCREMENT PRIMARY KEY , + `type` VARCHAR( 64 ) NOT NULL , + `query` VARCHAR( 512 ) NOT NULL + );"); + +} + +function ryzommanage_uninstall() { + db_query("DROP TABLE ryzommanage_querycache;"); +} + +function _ryzommanage_vocab_fields() { + +} \ No newline at end of file diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module new file mode 100644 index 000000000..2acbdbe2b --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module @@ -0,0 +1,949 @@ + ring users ---- nel user & nel permission ---- hook_user_cancel ---- remove character data on server +menu items that do stuff + +*/ +error_reporting(E_ALL); +ini_set('display_errors', 'on'); + +$path = drupal_get_path('module', 'ryzommanage'); +require_once ($path . '/config.php'); +require_once( $path . '/ams_lib/libinclude.php' ); + + +/* +Drupal 7 ryzom core module +Copyright (C) 2013 Matthew Lagoe (Botanic) & Paige Offerdahl (Tobi) + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 of the +License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +//output template +function loadTemplate($template,$vars) +{ + extract($vars); + include($template); +} +/** + * + * Function ryzommanage_admin + * + * @takes Nothing + * @return array $form + * + * Info: Creates the box's etc that go in the ryzom admin menu + * + */ +function ryzommanage_admin() +{ + $form = array(); + //admin menu items + + + $path = drupal_get_path('module', 'ryzommanage'); + require($path . '/config.php'); + $form['ryzommanage_serverurl'] = array( + '#type' => 'textfield', + '#title' => t('Server url'), + '#default_value' => $cfg['db']['lib']['name'],//variable_get('ryzommanage_serverurl', 'localhost'), + '#description' => t("The url of the ryzom server to integrate with."), + '#required' => TRUE + ); + $form['ryzommanage_mysqlport'] = array( + '#type' => 'textfield', + '#title' => t('Port for MySQL'), + '#size' => 5, + '#maxlength' => 5, + '#default_value' => variable_get('ryzommanage_mysqlport', '3306'), + '#description' => t("The MySQL port of the ryzom server to integrate with."), + '#required' => TRUE, + '#element_validate' => array( + '_check_port_value' + ) + ); + $form['ryzommanage_dbname'] = array( + '#type' => 'textfield', + '#title' => t('Database Name'), + '#default_value' => variable_get('ryzommanage_dbname', 'nel'), + '#description' => t("The MySQL database name to connect to."), + '#required' => TRUE + ); + $form['ryzommanage_username'] = array( + '#type' => 'textfield', + '#title' => t('MySQL Username'), + '#default_value' => variable_get('ryzommanage_username', 'root'), + '#description' => t("The MySQL username to connect with."), + '#required' => TRUE + ); + $form['ryzommanage_password'] = array( + '#type' => 'password_confirm', + '#title' => t('MySQL Password'), + '#description' => t("Confirm the MySQL password.") + ); + return system_settings_form($form); +} +//validate registration webpage +function ryzommanage_form_alter(&$form, &$form_state, $form_id) +{ + if($form_id == "user_register_form") + { + $form['#validate'][] = '_webpage_registration'; + } elseif($form_id == "user_profile_form") { + $form['#validate'][] = '_webpage_profile'; + } +} + +function _webpage_registration(&$form_state) +{ + + $user = checkUser($form_state['account']['name']['#value']); + $email = validEmail($form_state['account']['mail']['#value']); + + if ($user != "success") { + form_set_error('name', t($user)); + } + if ($email != "success") { + form_set_error('mail', t('Not a valid email address, please check it and try again.')); + } + +} + +function _webpage_profile(&$form_state) +{ + $email = validEmail($form_state['account']['mail']['#value']); + + if ($email != "success") { + form_set_error('mail', t('Not a valid email address, please check it and try again.')); + } + if ((checkPassword($form_state['account']['pass']['#value']['pass1']) == "success" ) and ( $form_state['account']['pass']['#value']['pass1'] == + $form_state['account']['pass']['#value']['pass2'] )) { + } +} + +/** + * + * Function ryzommanage_menu + * + * @takes Nothing + * @return array $items + * + * Info: Creates the menu item in the admin interface + * + */ +function ryzommanage_menu() +{ + $items = array(); + //page for client registration + $items['register'] = array( + 'title' => 'register', + 'page callback' => '_collect_register', + 'page arguments' => array(1, 2), + 'access callback' => 'user_access', + 'access arguments' => array('access content'), + 'type' => MENU_CALLBACK, + ); + //main menu item + $items['admin/config/ryzommanage'] = array( + 'title' => 'Ryzom Server Integration', + 'description' => 'Ryzom integration information.', + 'page callback' => 'system_admin_menu_block_page', + 'access arguments' => array( + 'administer site configuration' + ), + 'file' => 'system.admin.inc', + 'file path' => drupal_get_path('module', 'system') + ); + // First submenu item + $items['admin/config/ryzommanage/serversettings'] = array( + 'title' => 'Ryzom Server Settings', + 'description' => 'This is the first child item in the section', + 'page callback' => 'drupal_get_form', + 'page arguments' => array( + 'ryzommanage_admin' + ), + 'access arguments' => array( + 'administer site configuration' + ) + ); + // Second submenu item + $items['admin/config/ryzommanage/nameregister'] = array( + 'title' => 'Name Registration Settings', + 'description' => 'Configure default behavior of name registration module.', + 'page callback' => 'drupal_get_form', + 'page arguments' => array( + 'name_registration_admin_settings' + ), + 'access arguments' => array( + 'administer site configuration' + ) + ); + return $items; +} +function name_registration_admin_settings() { + $form = array(); + $form['ryzommanage_game-name'] = array( + '#type' => 'textfield', + '#title' => t('Game Name'), + '#default_value' => variable_get('ryzommanage_game-name', ''), + '#description' => t("Name of game used on registration pages."), + '#required' => TRUE + ); + //this is not the TOS url used in the create account page, you change that in the config of the client with the ConditionsTermsURL value + $form['ryzommanage_TOS'] = array( + '#type' => 'textfield', + '#title' => t('Terms of Service URL'), + '#default_value' => variable_get('ryzommanage_TOS', ''), + '#description' => t("The url of the TOS for your server."), + '#required' => TRUE + ); + $form['ryzommanage_register-welcome'] = array( + '#type' => 'textarea', + '#title' => t('Registration Welcome Message'), + '#default_value' => variable_get('ryzommanage_register-welcome', ''), + '#description' => t("Registration welcome message on first page of create account."), + '#required' => TRUE + ); + + return system_settings_form($form); +} +/** + * + * Function ryzommanage_menu + * + * @takes Int $element, &$form_state + * @return Nothing + * + * Info: Used by ryzommanage_mysqlport to validate ryzommanage_admin will run form_error if port is not between 1 and 65535. + * + */ +function _check_port_value($element, &$form_state) +{ + if ((!is_numeric(parse_size($element['#value']))) or ((parse_size($element['#value']) > 65535) or (parse_size($element['#value']) < 1))) { + form_error($element, t($element['#value'] . ' is not a valid value for the MySQL port, it must be a valid value. You must choose a number between 1 and 65535.')); + } +} +/** + * + * Function ryzommanage_block_info + * + * @takes Nothing + * @return array $blocks + * + * Info: Info for block that shows the user menu + * + */ +function ryzommanage_block_info() +{ + $blocks['ryzommanage_usersblock'] = array( + // info: The name of the block. + 'info' => t('Ryzom Manage User Block'), + 'status' => TRUE, + 'region' => '-1', // Not usually provided. + 'visibility' => BLOCK_VISIBILITY_LISTED // Not usually provided. + ); + return $blocks; +} +/** + * + * Function ryzommanage_block_view + * + * @takes Nothing + * @return array $block + * + * Info: View for block + * + */ +function ryzommanage_block_view($delta = '') +{ + $block = array(); + //The $delta parameter tells us which block is being requested. + switch ($delta) { + case 'ryzommanage_usersblock': + $block['subject'] = t("uppercase this please"); + $block['content'] = top_bar(); + break; + } + return $block; +} +/** + * + * Function _collect_register + * + * @takes + * @return Nothing + * + * Info: Determins what to send back to client, if the client is ryzom core then send the http data if its a browser send to / + * + */ +function _collect_register($nids, $collection) +{ + syncdata(); + //if not using ryzom core client show registration page + if (check_if_game_client()) { + return_client_httpdata(); + } else { + //redirect to registration page + header("Location: /user/register"); + } +} +/** + * + * Function check_if_game_client + * + * @takes Nothing + * @return Boolean + * + * Info: Returns True if connecting client is ryzom core + * + */ +function check_if_game_client() +{ + //if HTTP_USER_AGENT is not set then its ryzom core + if (!isset($_SERVER['HTTP_USER_AGENT'])) { + return true; + } else { + return false; + } +} +/** + * + * Function return_client_httpdata + * + * @takes + * @return + * + * Info: Returns ryzom core formatted html for use in registration via client + * + */ +function return_client_httpdata() +{ + //check if values exist + if (isset($_POST["Username"]) and isset($_POST["Password"]) and isset($_POST["Email"]) ) + { + //check values + $user = checkUser($_POST["Username"]); + $pass = checkPassword($_POST["Password"]); + $cpass = confirmPassword(); + $email = checkEmail($_POST["Email"]); + } else { + $user = ""; + $pass = ""; + $cpass = ""; + $email = ""; + } + //if all are good then create user + if (($user == "success") and ($pass == "success") and ($cpass == "success") and ($email == "success") and (isset($_POST["TaC"]))) { + $edit = array( + 'name' => $_POST["Username"], + 'pass' => $_POST["Password"], + 'mail' => $_POST["Email"], + 'init' => $_POST["Email"], + 'unhashpass' => $_POST["Password"], + 'status' => 1, + 'access' => REQUEST_TIME + ); + user_save(NULL, $edit); + header('Location: email_sent.php'); + exit; + } else { + $pageElements = array( + 'GAME_NAME' => variable_get('ryzommanage_game-name', ''), + 'WELCOME_MESSAGE' => variable_get('ryzommanage_register-welcome', ''), + 'USERNAME' => $user, + 'PASSWORD' => $pass, + 'CPASSWORD' => $cpass, + 'EMAIL' => $email + ); + if ($user != "success") { + $pageElements['USERNAME_ERROR'] = 'TRUE'; + } else { + $pageElements['USERNAME_ERROR'] = 'FALSE'; + } + + if ($pass != "success") { + $pageElements['PASSWORD_ERROR'] = 'TRUE'; + } else { + $pageElements['PASSWORD_ERROR'] = 'FALSE'; + } + if ($cpass != "success") { + $pageElements['CPASSWORD_ERROR'] = 'TRUE'; + } else { + $pageElements['CPASSWORD_ERROR'] = 'FALSE'; + } + if ($email != "success") { + $pageElements['EMAIL_ERROR'] = 'TRUE'; + } else { + $pageElements['EMAIL_ERROR'] = 'FALSE'; + } + if (isset($_POST["TaC"])) { + $pageElements['TAC_ERROR'] = 'FALSE'; + } else { + $pageElements['TAC_ERROR'] = 'TRUE'; + } + loadTemplate('templates/ingame_register.phtml',$pageElements); + } +} +/** + * + * Function checkUser + * + * @takes $username + * @return string + * + * Info: Returns a string based on if the username is valid, if valid then "success" is returned + * + */ +function checkUser($username) +{ + if (isset($username)) { + if (strlen($username) > 12) { + return "Username must be no more than 12 characters."; + } elseif (strlen($username) < 5) { + return "Username must be 5 or more characters."; + } elseif (!preg_match('/^[a-z0-9\.]*$/', $username)) { + return "Username can only contain numbers and letters."; + } elseif (db_query("SELECT COUNT(*) FROM {users} WHERE name = :name", array( + ':name' => $username + ))->fetchField()) { + return "Username " . $username . " is in use."; + } else { + return "success"; + } + } else { + return "success"; + } + return "fail"; +} +/** + * + * Function checkPassword + * + * @takes $pass + * @return string + * + * Info: Returns a string based on if the password is valid, if valid then "success" is returned + * + */ +function checkPassword($pass) +{ + if (isset($pass)) { + if (strlen($pass) > 20) { + return "Password must be no more than 20 characters."; + } elseif (strlen($pass) < 5) { + return "Password must be more than 5 characters."; + } else { + return "success"; + } + } + return "fail"; +} +/** + * + * Function confirmPassword + * + * @takes $pass + * @return string + * + * Info: Verify's $_POST["Password"] is the same as $_POST["ConfirmPass"] + * + */ +function confirmPassword() +{ + if (($_POST["Password"]) != ($_POST["ConfirmPass"])) { + return "Passwords do not match."; + } else { + return "success"; + } + return "fail"; +} +/** + * + * Function checkEmail + * + * @takes $email + * @return + * + * + * + */ +function checkEmail($email) +{ + if (isset($email)) { + if (!validEmail($email)) { + return "Email address is not valid."; + } elseif (db_query("SELECT COUNT(*) FROM {users} WHERE mail = :mail", array( + ':mail' => $email + ))->fetchField()) { + return "Email is in use."; + } else { + return "success"; + } + } else { + return "success"; + } + return "fail"; +} +function validEmail($email) +{ + $isValid = true; + $atIndex = strrpos($email, "@"); + if (is_bool($atIndex) && !$atIndex) { + $isValid = false; + } else { + $domain = substr($email, $atIndex + 1); + $local = substr($email, 0, $atIndex); + $localLen = strlen($local); + $domainLen = strlen($domain); + if ($localLen < 1 || $localLen > 64) { + // local part length exceeded + $isValid = false; + } else if ($domainLen < 1 || $domainLen > 255) { + // domain part length exceeded + $isValid = false; + } else if ($local[0] == '.' || $local[$localLen - 1] == '.') { + // local part starts or ends with '.' + $isValid = false; + } else if (preg_match('/\\.\\./', $local)) { + // local part has two consecutive dots + $isValid = false; + } else if (!preg_match('/^[A-Za-z0-9\\-\\.]+$/', $domain)) { + // character not valid in domain part + $isValid = false; + } else if (preg_match('/\\.\\./', $domain)) { + // domain part has two consecutive dots + $isValid = false; + } else if (!preg_match('/^(\\\\.|[A-Za-z0-9!#%&`_=\\/$\'*+?^{}|~.-])+$/', str_replace("\\\\", "", $local))) { + // character not valid in local part unless + // local part is quoted + if (!preg_match('/^"(\\\\"|[^"])+"$/', str_replace("\\\\", "", $local))) { + $isValid = false; + } + } + if ($isValid && !(checkdnsrr($domain, "MX") || checkdnsrr($domain, "A"))) { + // domain not found in DNS + $isValid = false; + } + } + return $isValid; +} +function generateSALT($length = 2) +{ + // start with a blank salt + $salt = ""; + // define possible characters - any character in this string can be + // picked for use in the salt, so if you want to put vowels back in + // or add special characters such as exclamation marks, this is where + // you should do it + $possible = "2346789bcdfghjkmnpqrtvwxyzBCDFGHJKLMNPQRTVWXYZ"; + // we refer to the length of $possible a few times, so let's grab it now + $maxlength = strlen($possible); + // check for length overflow and truncate if necessary + if ($length > $maxlength) { + $length = $maxlength; + } + // set up a counter for how many characters are in the salt so far + $i = 0; + // add random characters to $salt until $length is reached + while ($i < $length) { + // pick a random character from the possible ones + $char = substr($possible, mt_rand(0, $maxlength - 1), 1); + // have we already used this character in $salt? + if (!strstr($salt, $char)) { + // no, so it's OK to add it onto the end of whatever we've already got... + $salt .= $char; + // ... and increase the counter by one + $i++; + } + } + // done! + return $salt; +} +function createUser($values) +{ + + $login = $values[0]; + $pass = $values[1]; + $email = $values[2]; + + $salt = generateSALT(); + $hashpass = crypt($pass, $salt); + + $params = array( + $login, + $hashpass, + $email + ); + + try { + $hostname = variable_get('ryzommanage_serverurl', 'localhost'); + $port = variable_get('ryzommanage_mysqlport', '3306'); + $dbname = variable_get('ryzommanage_dbname', 'nel'); + $username = variable_get('ryzommanage_username', 'root'); + $password = variable_get('ryzommanage_password', ''); + $dbh = new PDO("mysql:host=$hostname;port=$port;dbname=$dbname", $username, $password); + $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + } + catch (PDOException $e) { + watchdog('ryzommanage', $e->getMessage(), NULL, WATCHDOG_ERROR); + $nid = db_insert('ryzommanage_querycache')->fields(array( + "SID" => NULL, + "type" => "createUser", + "query" => json_encode(array( + $login, + $pass, + $email + )) + ))->execute(); + return true; + } + + try { + $statement = $dbh->prepare("INSERT INTO user (Login, Password, Email) VALUES (?, ?, ?)"); + } + catch (PDOException $e) { + watchdog('ryzommanage', $e->getMessage(), NULL, WATCHDOG_ERROR); + $nid = db_insert('ryzommanage_querycache')->fields(array( + "SID" => NULL, + "type" => "createUser", + "query" => json_encode(array( + $login, + $pass, + $email + )) + ))->execute(); + return true; + } + + try { + $statement->execute($params); + } + catch (PDOException $e) { + watchdog('ryzommanage', $e->getMessage(), NULL, WATCHDOG_ERROR); + $nid = db_insert('ryzommanage_querycache')->fields(array( + "SID" => NULL, + "type" => "createUser", + "query" => json_encode(array( + $login, + $pass, + $email + )) + ))->execute(); + return true; + } + + createPermissions(array($login)); +} + +function createPermissions($values) { + + try { + $hostname = variable_get('ryzommanage_serverurl', 'localhost'); + $port = variable_get('ryzommanage_mysqlport', '3306'); + $dbname = variable_get('ryzommanage_dbname', 'nel'); + $username = variable_get('ryzommanage_username', 'root'); + $password = variable_get('ryzommanage_password', ''); + $dbh = new PDO("mysql:host=$hostname;port=$port;dbname=$dbname", $username, $password); + $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + } + catch (PDOException $e) { + watchdog('ryzommanage', $e->getMessage(), NULL, WATCHDOG_ERROR); + $nid = db_insert('ryzommanage_querycache')->fields(array( + "SID" => NULL, + "type" => "createPermissions", + "query" => json_encode(array( + $values[0] + )) + ))->execute(); + return true; + } + + try { + $sth = $dbh->prepare("SELECT UId FROM user WHERE Login='" . $values[0] . "';"); + $sth->execute(); + $result = $sth->fetchAll(); + foreach ($result as $UId) { + $statement = $dbh->prepare("INSERT INTO permission (UId, ClientApplication, AccessPrivilege) VALUES ('" . $UId['UId'] . "', 'r2', 'OPEN');"); + $statement->execute($values); + $statement = $dbh->prepare("INSERT INTO permission (UId, ClientApplication, AccessPrivilege) VALUES ('" . $UId['UId'] . "', 'ryzom_open', 'OPEN');"); + $statement->execute($values); + } + } + catch (PDOException $e) { + watchdog('ryzommanage', $e->getMessage(), NULL, WATCHDOG_ERROR); + $nid = db_insert('ryzommanage_querycache')->fields(array( + "SID" => NULL, + "type" => "createPermissions", + "query" => json_encode(array( + $values[0] + )) + ))->execute(); + return true; + } + + return true; +} + +function login_form($login_form) +{ + $login_form['#action'] = url(current_path(), array( + 'query' => drupal_get_destination(), + 'external' => FALSE + )); + $login_form['#id'] = 'user-login-form'; + $login_form['#validate'] = user_login_default_validators(); + $login_form['#submit'][] = 'user_login_submit'; + $login_form['name'] = array( + '#type' => 'textfield', + '#title' => t('Username'), + '#maxlength' => 12, + '#size' => 15, + '#required' => TRUE + ); + $login_form['pass'] = array( + '#type' => 'password', + '#title' => t('Password'), + '#maxlength' => 20, + '#size' => 15, + '#required' => TRUE + ); + $items = array(); + if (variable_get('user_register', USER_REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL)) { + $items[] = l(t('Create new account'), 'user/register', array( + 'attributes' => array( + 'title' => t('Create a new user account.') + ) + )); + } + $items[] = l(t('Request new password'), 'user/password', array( + 'attributes' => array( + 'title' => t('Request new password via e-mail.') + ) + )); + $login_form['links'] = array( + '#markup' => theme('item_list', array( + 'items' => $items + )) + ); + $login_form['remember_me'] = array( + '#type' => 'checkbox', + '#title' => t('Remember Me'), + '#default_value' => 0 + ); + $login_form['actions'] = array( + '#type' => 'actions' + ); + $login_form['actions']['submit'] = array( + '#type' => 'submit', + '#value' => t('Log in') + ); + return $login_form; +} + +function top_bar() +{ + global $user; + $userId = $user->uid; + if (user_is_logged_in()) { + // Logged in user + return "
    Notepad | Mail | Wiki | Profile | Craft Recipe-Book | Occupations | News/Events | Account | Logout
    "; + } else { + return drupal_get_form('login_form'); + // Not logged in + } +} + +function ryzommanage_user_presave(&$edit, $account, $category) +{ + if (isset($edit['unhashpass'])) { + $pass = $edit['unhashpass']; + } elseif (isset($_POST['pass']['pass1'])) { + $pass = $_POST['pass']['pass1']; + } + + if (!isset($edit['name'])) { + $name = $user->name; + } else { + $name = $edit['name']; + } + + if ($account->is_new == 1 ) { + createUser(array($edit['name'], $pass, $edit['mail'])); + } else { + user_edit( array($edit['name'], $pass)); + } +} + +function ryzommanage_form_user_register_form_alter(&$form, &$form_state, $form_id) { + // Modification for the form with the given form ID goes here. For example, if + // FORM_ID is "user_register_form" this code would run only on the user + // registration form. + + // Change the data for the username and email fields + $form['account']['name']['#maxlength'] = '12'; + $form['account']['name']['#description'] = '5-12 lower-case characters and numbers. The login (username) you create here will be your login name.
    The name of your game characters will be chosen later on.
    '; + $form['account']['mail']['#description'] = 'Please verify that the e-mail address you enter here is valid and will remain valid in the future.
    It will be used to manage your Tempest in the Aether account.
    '; + // Add a checkbox to registration form about agreeing to terms of use. + $form['terms_of_use'] = array( + '#type' => 'checkbox', + '#title' => t("I agree with the terms and conditions."), + '#required' => TRUE, + ); +} + +function ryzommanage_form_user_profile_form_alter(&$form, &$form_state, $form_id) { + // Modification for the form with the given form ID goes here. For example, if + // FORM_ID is "user_register_form" this code would run only on the user + // registration form. + + // Change the data for the password field + $form['account']['pass']['#description'] = 'Password must be 5-20 characters.
    '; +} + +function user_edit($values) { + + $username = $values[0]; + $newpassword = $values[1]; + + $salt = generateSALT(); + $pass = crypt($newpassword, $salt); + + try { + $hostname = variable_get('ryzommanage_serverurl', 'localhost'); + $port = variable_get('ryzommanage_mysqlport', '3306'); + $dbname = variable_get('ryzommanage_dbname', 'nel'); + $ryusername = variable_get('ryzommanage_username', 'root'); + $password = variable_get('ryzommanage_password', ''); + $dbh = new PDO("mysql:host=$hostname;port=$port;dbname=$dbname", $ryusername, $password); + $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + } + catch (PDOException $e) { + watchdog('ryzommanage', $e->getMessage(), NULL, WATCHDOG_ERROR); + $nid = db_insert('ryzommanage_querycache')->fields(array( + "SID" => NULL, + "type" => "user_edit", + "query" => json_encode(array( + $username, + $newpassword + )) + ))->execute(); + return true; + } + + $sql = "UPDATE `nel`.`user` SET `Password` = ? WHERE `user`.`Login` = ?"; + + try { + $q = $dbh->prepare($sql); + } + catch (PDOException $e) { + watchdog('ryzommanage', $e->getMessage(), NULL, WATCHDOG_ERROR); + $nid = db_insert('ryzommanage_querycache')->fields(array( + "SID" => NULL, + "type" => "user_edit", + "query" => json_encode(array( + $username, + $newpassword + )) + ))->execute(); + return true; + } + + try { + $q->execute(array( $pass, $username)); + } + catch (PDOException $e) { + watchdog('ryzommanage', $e->getMessage(), NULL, WATCHDOG_ERROR); + $nid = db_insert('ryzommanage_querycache')->fields(array( + "SID" => NULL, + "type" => "user_edit", + "query" => json_encode(array( + $username, + $newpassword + )) + ))->execute(); + return true; + } + + return true; +} + +/** + * + * Function syncdata + * + * @takes Nothing + * @return array $values + * + * Info: Runs functions to finish syncing data when shard is offline + * + */ +function syncdata () { + + try { + $hostname = variable_get('ryzommanage_serverurl', 'localhost'); + $port = variable_get('ryzommanage_mysqlport', '3306'); + $dbname = variable_get('ryzommanage_dbname', 'nel'); + $ryusername = variable_get('ryzommanage_username', 'root'); + $password = variable_get('ryzommanage_password', ''); + $dbh = new PDO("mysql:host=$hostname;port=$port;dbname=$dbname", $ryusername, $password); + $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + } + catch (PDOException $e) { + watchdog('ryzommanage', $e->getMessage(), NULL, WATCHDOG_ERROR); + return true; + } + + $query = db_select('ryzommanage_querycache', 'q') + ->fields('q', array('SID', 'type', 'query')); + + $result = $query->execute(); + + foreach ($result as $record) { + watchdog('ryzommanage_usersync', $record->query, NULL, WATCHDOG_ERROR); + + switch($record->type) { + case 'createPermissions': + case 'user_edit': + case 'createUser': + watchdog('ryzommanage_usersync', $record->type, NULL, WATCHDOG_INFO); + $SID = $record->SID; + $num_deleted = db_delete('ryzommanage_querycache') + ->condition('SID', $SID) + ->execute(); + $func = $record->type; + $func(json_decode($record->query)); + } + + } + +} +/** + * + * Function ryzommanage_cron + * + * @takes + * @return + * + * Info: Runs the syncdata function with the drupal cron + * + */ +function ryzommanage_cron() { + + syncdata(); + +} diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/ingame_register.phtml b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/ingame_register.phtml new file mode 100644 index 000000000..1f0fc0859 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/ingame_register.phtml @@ -0,0 +1,116 @@ +
    + RYZOM CORE INGAME REGISTRATION +
    + +
    + +
    + + +

    {$home_info}

    +

    This is the GSOC project of Daan Janssens mentored by Matthew Lagoe.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + You must accept the Terms of Service';} + else { + echo ' +
    id="caption-Username">Desired Username: + + width="42%">
    id="caption-Password">Desired Password: + + width="42%">
    id="caption-ConfirmPass">Confirm Password: + width="42%">
    id="caption-Email">Email Address (to which a confirmation email will be sent): + account.', this);" /> + width="42%">
    + colspan="2">YES, I agree to the terms of + service';}; ?> +
    + +
    + +
    + + +
    + +
    + 5-12 lower-case characters and numbers. The login (username) you create here will be + your login name. The name of your game characters will be chosen later on. +
    + +
    + 5-20 characters. +
    + +
    + Retype your Password +
    + +
    + Please verify that the e-mail address you enter here is valid and will remain valid + in the future. It will be used to manage your account. +
    + +
    \ No newline at end of file diff --git a/code/ryzom/tools/server/ryzom_ams/howto.txt b/code/ryzom/tools/server/ryzom_ams/howto.txt new file mode 100644 index 000000000..f930ce26f --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/howto.txt @@ -0,0 +1,7 @@ +-Edit config .php file +-Atm also edit the autoload function's paths +-call the install.php function in your terminal located in the sql folder + +for mailing: +-set imap settings in config file (also make sure the folder exists that you will use for mail logging and storing emails) +-make sure imap is enabled. \ No newline at end of file diff --git a/code/ryzom/tools/server/ryzom_ams/www/config.php b/code/ryzom/tools/server/ryzom_ams/www/config.php index 6e2769079..25679ac64 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/config.php +++ b/code/ryzom/tools/server/ryzom_ams/www/config.php @@ -54,7 +54,7 @@ $TICKET_MAILING_SUPPORT = true; //You have to create this dir at first! $MAIL_DIR = "/tmp/mail"; - +$TOS_URL ="http://createyourtos.com"; $MAIL_LOG_PATH = "/tmp/mail/cron_mail.log"; $cfg['crypt']['key'] = 'Sup3rS3cr3tStuff'; diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/inc/register.php b/code/ryzom/tools/server/ryzom_ams/www/html/inc/register.php new file mode 100644 index 000000000..966f75c08 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/www/html/inc/register.php @@ -0,0 +1,7 @@ +
    - {$tac_tag1}{$tac_tag2} + {$tac_tag1}{$tac_tag2}
    From ef6121203863ff1045aa2a3224bbd62bdbd8a716 Mon Sep 17 00:00:00 2001 From: Quitta Date: Mon, 2 Sep 2013 04:42:56 +0200 Subject: [PATCH 132/313] including the module problem --- .../ryzommanage/ryzommanage.module | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module index 2acbdbe2b..95f309e57 100644 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module @@ -9,13 +9,12 @@ delete user hook --- ring_open -> ring users ---- nel user & nel permission -- menu items that do stuff */ -error_reporting(E_ALL); -ini_set('display_errors', 'on'); - -$path = drupal_get_path('module', 'ryzommanage'); -require_once ($path . '/config.php'); -require_once( $path . '/ams_lib/libinclude.php' ); +module_load_include('php', 'ryzommanage', 'config'); +module_load_include('php', 'ryzommanage', 'ams_lib/libinclude'); +global $MAIL_DIR; +echo $MAIL_DIR; +echo "booo"; /* Drupal 7 ryzom core module @@ -56,8 +55,8 @@ function ryzommanage_admin() //admin menu items - $path = drupal_get_path('module', 'ryzommanage'); - require($path . '/config.php'); + //$path = drupal_get_path('module', 'ryzommanage'); + //require($path . '/config.php'); $form['ryzommanage_serverurl'] = array( '#type' => 'textfield', '#title' => t('Server url'), @@ -299,7 +298,7 @@ function _collect_register($nids, $collection) return_client_httpdata(); } else { //redirect to registration page - header("Location: /user/register"); + header("Location: user/register"); } } /** From ebc2c1c1445890849a0a9f72799db9e7b9dcb050 Mon Sep 17 00:00:00 2001 From: Quitta Date: Mon, 2 Sep 2013 16:23:07 +0200 Subject: [PATCH 133/313] reworked config file to work with drupal and added extra config fields to the configuration form in the module --- .../drupal_module/ryzommanage/config.php | 44 +++--- .../ryzommanage/ryzommanage.module | 129 ++++++++++++++---- 2 files changed, 127 insertions(+), 46 deletions(-) diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/config.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/config.php index 322d50357..a2bf18c20 100644 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/config.php +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/config.php @@ -1,5 +1,4 @@ 'textfield', - '#title' => t('Server url'), - '#default_value' => $cfg['db']['lib']['name'],//variable_get('ryzommanage_serverurl', 'localhost'), + '#title' => t('Shard server url'), + '#default_value' => $cfg['db']['shard']['host'], '#description' => t("The url of the ryzom server to integrate with."), '#required' => TRUE ); - $form['ryzommanage_mysqlport'] = array( + $form['ryzommanage_shardmysqlport'] = array( '#type' => 'textfield', - '#title' => t('Port for MySQL'), + '#title' => t('Port for MySQL of the Shard'), '#size' => 5, '#maxlength' => 5, - '#default_value' => variable_get('ryzommanage_mysqlport', '3306'), + '#default_value' => $cfg['db']['shard']['port'], '#description' => t("The MySQL port of the ryzom server to integrate with."), '#required' => TRUE, '#element_validate' => array( '_check_port_value' ) ); - $form['ryzommanage_dbname'] = array( + $form['ryzommanage_sharddbname'] = array( '#type' => 'textfield', - '#title' => t('Database Name'), - '#default_value' => variable_get('ryzommanage_dbname', 'nel'), + '#title' => t('Shard Database Name'), + '#default_value' => $cfg['db']['shard']['name'], '#description' => t("The MySQL database name to connect to."), '#required' => TRUE ); - $form['ryzommanage_username'] = array( + $form['ryzommanage_shardusername'] = array( '#type' => 'textfield', - '#title' => t('MySQL Username'), - '#default_value' => variable_get('ryzommanage_username', 'root'), + '#title' => t('Shard MySQL Username'), + '#default_value' => $cfg['db']['shard']['user'], '#description' => t("The MySQL username to connect with."), '#required' => TRUE ); - $form['ryzommanage_password'] = array( + $form['ryzommanage_shardpassword'] = array( '#type' => 'password_confirm', - '#title' => t('MySQL Password'), + '#title' => t('Shard MySQL Password'), + '#description' => t("Confirm the MySQL password."), + '#suffix' => '
    ' + ); + + $form['ryzommanage_libserverurl'] = array( + '#type' => 'textfield', + '#title' => t('Lib server url'), + '#default_value' => $cfg['db']['lib']['host'], + '#description' => t("The url of the ryzom's lib db to integrate with."), + '#required' => TRUE + ); + $form['ryzommanage_libmysqlport'] = array( + '#type' => 'textfield', + '#title' => t('Port for MySQL of the Lib'), + '#size' => 5, + '#maxlength' => 5, + '#default_value' => $cfg['db']['lib']['port'], + '#description' => t("The MySQL port of the ryzom's lib db to integrate with."), + '#required' => TRUE, + '#element_validate' => array( + '_check_port_value' + ) + ); + $form['ryzommanage_libdbname'] = array( + '#type' => 'textfield', + '#title' => t('Lib Database Name'), + '#default_value' => $cfg['db']['lib']['name'], + '#description' => t("The MySQL database name to connect to."), + '#required' => TRUE + ); + $form['ryzommanage_libusername'] = array( + '#type' => 'textfield', + '#title' => t('Lib MySQL Username'), + '#default_value' => $cfg['db']['lib']['user'], + '#description' => t("The MySQL username to connect with."), + '#required' => TRUE + ); + $form['ryzommanage_libpassword'] = array( + '#type' => 'password_confirm', + '#title' => t('Lib MySQL Password'), + '#description' => t("Confirm the MySQL password."), + '#suffix' => '
    ' + ); + + + $form['ryzommanage_ringserverurl'] = array( + '#type' => 'textfield', + '#title' => t('Ring server url'), + '#default_value' => $cfg['db']['ring']['host'], + '#description' => t("The url of the ryzom's ring db to integrate with."), + '#required' => TRUE + ); + $form['ryzommanage_ringmysqlport'] = array( + '#type' => 'textfield', + '#title' => t('Port for MySQL of the Lib'), + '#size' => 5, + '#maxlength' => 5, + '#default_value' => $cfg['db']['ring']['port'], + '#description' => t("The MySQL port of the ryzom ring db to integrate with."), + '#required' => TRUE, + '#element_validate' => array( + '_check_port_value' + ) + ); + $form['ryzommanage_ringdbname'] = array( + '#type' => 'textfield', + '#title' => t('Ring Database Name'), + '#default_value' => $cfg['db']['ring']['name'], + '#description' => t("The MySQL database name to connect to."), + '#required' => TRUE + ); + $form['ryzommanage_ringusername'] = array( + '#type' => 'textfield', + '#title' => t('Ring MySQL Username'), + '#default_value' => $cfg['db']['ring']['user'], + '#description' => t("The MySQL username to connect with."), + '#required' => TRUE + ); + $form['ryzommanage_ringpassword'] = array( + '#type' => 'password_confirm', + '#title' => t('Ring MySQL Password'), '#description' => t("Confirm the MySQL password.") ); return system_settings_form($form); @@ -195,7 +275,8 @@ function ryzommanage_menu() return $items; } function name_registration_admin_settings() { - $form = array(); + global $TOS_URL; + $form = array(); $form['ryzommanage_game-name'] = array( '#type' => 'textfield', '#title' => t('Game Name'), @@ -207,7 +288,7 @@ function name_registration_admin_settings() { $form['ryzommanage_TOS'] = array( '#type' => 'textfield', '#title' => t('Terms of Service URL'), - '#default_value' => variable_get('ryzommanage_TOS', ''), + '#default_value' => $TOS_URL, '#description' => t("The url of the TOS for your server."), '#required' => TRUE ); From e992a3aef0fed1575501d06259de5da808ad80ee Mon Sep 17 00:00:00 2001 From: Quitta Date: Mon, 2 Sep 2013 18:32:10 +0200 Subject: [PATCH 134/313] added hook_user_insert, now the user gets also added to the shard + a ticket_user entry gets created that couples the drupal id to it --- .../ryzommanage/ryzommanage.info | 3 +- .../ryzommanage/ryzommanage.module | 475 ++++++++---------- 2 files changed, 212 insertions(+), 266 deletions(-) diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.info b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.info index 0f33e5cf6..f5e91be64 100644 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.info +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.info @@ -7,4 +7,5 @@ configure = admin/settings/ryzommanage ; Information added by drupal.org packaging script on 2012-04-24 version = "7.x-.01" -core = "7.x" \ No newline at end of file +core = "7.x" + diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module index 669663d88..6684ec0c8 100644 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module @@ -9,13 +9,15 @@ delete user hook --- ring_open -> ring users ---- nel user & nel permission -- menu items that do stuff */ - +error_reporting(E_ALL); +ini_set('display_errors', 'on'); global $TOS_URL; global $cfg; -include 'ams_lib/libinclude.php'; -include 'config.php'; +include 'ams_lib/libinclude.php'; +spl_autoload_register('__autoload'); +include 'config.php'; /* Drupal 7 ryzom core module @@ -40,143 +42,7 @@ function loadTemplate($template,$vars) extract($vars); include($template); } -/** - * - * Function ryzommanage_admin - * - * @takes Nothing - * @return array $form - * - * Info: Creates the box's etc that go in the ryzom admin menu - * - */ -function ryzommanage_admin() -{ - $form = array(); - //admin menu items - global $cfg; - - $form['ryzommanage_shardserverurl'] = array( - '#type' => 'textfield', - '#title' => t('Shard server url'), - '#default_value' => $cfg['db']['shard']['host'], - '#description' => t("The url of the ryzom server to integrate with."), - '#required' => TRUE - ); - $form['ryzommanage_shardmysqlport'] = array( - '#type' => 'textfield', - '#title' => t('Port for MySQL of the Shard'), - '#size' => 5, - '#maxlength' => 5, - '#default_value' => $cfg['db']['shard']['port'], - '#description' => t("The MySQL port of the ryzom server to integrate with."), - '#required' => TRUE, - '#element_validate' => array( - '_check_port_value' - ) - ); - $form['ryzommanage_sharddbname'] = array( - '#type' => 'textfield', - '#title' => t('Shard Database Name'), - '#default_value' => $cfg['db']['shard']['name'], - '#description' => t("The MySQL database name to connect to."), - '#required' => TRUE - ); - $form['ryzommanage_shardusername'] = array( - '#type' => 'textfield', - '#title' => t('Shard MySQL Username'), - '#default_value' => $cfg['db']['shard']['user'], - '#description' => t("The MySQL username to connect with."), - '#required' => TRUE - ); - $form['ryzommanage_shardpassword'] = array( - '#type' => 'password_confirm', - '#title' => t('Shard MySQL Password'), - '#description' => t("Confirm the MySQL password."), - '#suffix' => '
    ' - ); - - $form['ryzommanage_libserverurl'] = array( - '#type' => 'textfield', - '#title' => t('Lib server url'), - '#default_value' => $cfg['db']['lib']['host'], - '#description' => t("The url of the ryzom's lib db to integrate with."), - '#required' => TRUE - ); - $form['ryzommanage_libmysqlport'] = array( - '#type' => 'textfield', - '#title' => t('Port for MySQL of the Lib'), - '#size' => 5, - '#maxlength' => 5, - '#default_value' => $cfg['db']['lib']['port'], - '#description' => t("The MySQL port of the ryzom's lib db to integrate with."), - '#required' => TRUE, - '#element_validate' => array( - '_check_port_value' - ) - ); - $form['ryzommanage_libdbname'] = array( - '#type' => 'textfield', - '#title' => t('Lib Database Name'), - '#default_value' => $cfg['db']['lib']['name'], - '#description' => t("The MySQL database name to connect to."), - '#required' => TRUE - ); - $form['ryzommanage_libusername'] = array( - '#type' => 'textfield', - '#title' => t('Lib MySQL Username'), - '#default_value' => $cfg['db']['lib']['user'], - '#description' => t("The MySQL username to connect with."), - '#required' => TRUE - ); - $form['ryzommanage_libpassword'] = array( - '#type' => 'password_confirm', - '#title' => t('Lib MySQL Password'), - '#description' => t("Confirm the MySQL password."), - '#suffix' => '
    ' - ); - - - $form['ryzommanage_ringserverurl'] = array( - '#type' => 'textfield', - '#title' => t('Ring server url'), - '#default_value' => $cfg['db']['ring']['host'], - '#description' => t("The url of the ryzom's ring db to integrate with."), - '#required' => TRUE - ); - $form['ryzommanage_ringmysqlport'] = array( - '#type' => 'textfield', - '#title' => t('Port for MySQL of the Lib'), - '#size' => 5, - '#maxlength' => 5, - '#default_value' => $cfg['db']['ring']['port'], - '#description' => t("The MySQL port of the ryzom ring db to integrate with."), - '#required' => TRUE, - '#element_validate' => array( - '_check_port_value' - ) - ); - $form['ryzommanage_ringdbname'] = array( - '#type' => 'textfield', - '#title' => t('Ring Database Name'), - '#default_value' => $cfg['db']['ring']['name'], - '#description' => t("The MySQL database name to connect to."), - '#required' => TRUE - ); - $form['ryzommanage_ringusername'] = array( - '#type' => 'textfield', - '#title' => t('Ring MySQL Username'), - '#default_value' => $cfg['db']['ring']['user'], - '#description' => t("The MySQL username to connect with."), - '#required' => TRUE - ); - $form['ryzommanage_ringpassword'] = array( - '#type' => 'password_confirm', - '#title' => t('Ring MySQL Password'), - '#description' => t("Confirm the MySQL password.") - ); - return system_settings_form($form); -} + //validate registration webpage function ryzommanage_form_alter(&$form, &$form_state, $form_id) { @@ -274,34 +140,8 @@ function ryzommanage_menu() ); return $items; } -function name_registration_admin_settings() { - global $TOS_URL; - $form = array(); - $form['ryzommanage_game-name'] = array( - '#type' => 'textfield', - '#title' => t('Game Name'), - '#default_value' => variable_get('ryzommanage_game-name', ''), - '#description' => t("Name of game used on registration pages."), - '#required' => TRUE - ); - //this is not the TOS url used in the create account page, you change that in the config of the client with the ConditionsTermsURL value - $form['ryzommanage_TOS'] = array( - '#type' => 'textfield', - '#title' => t('Terms of Service URL'), - '#default_value' => $TOS_URL, - '#description' => t("The url of the TOS for your server."), - '#required' => TRUE - ); - $form['ryzommanage_register-welcome'] = array( - '#type' => 'textarea', - '#title' => t('Registration Welcome Message'), - '#default_value' => variable_get('ryzommanage_register-welcome', ''), - '#description' => t("Registration welcome message on first page of create account."), - '#required' => TRUE - ); - return system_settings_form($form); -} + /** * * Function ryzommanage_menu @@ -622,115 +462,52 @@ function validEmail($email) } return $isValid; } -function generateSALT($length = 2) -{ - // start with a blank salt - $salt = ""; - // define possible characters - any character in this string can be - // picked for use in the salt, so if you want to put vowels back in - // or add special characters such as exclamation marks, this is where - // you should do it - $possible = "2346789bcdfghjkmnpqrtvwxyzBCDFGHJKLMNPQRTVWXYZ"; - // we refer to the length of $possible a few times, so let's grab it now - $maxlength = strlen($possible); - // check for length overflow and truncate if necessary - if ($length > $maxlength) { - $length = $maxlength; + +/** + * + * Function ryzommanage_user_insert + * + * @takes $pass + * @return string + * + * Info: Hook that's being called after creating a drupal user, we need to do it like this to access the drupals newly created user's id. + * + */ +function ryzommanage_user_insert(&$edit, $account, $category){ + if (isset($edit['unhashpass'])) { + $pass = $edit['unhashpass']; + } elseif (isset($_POST['pass']['pass1'])) { + $pass = $_POST['pass']['pass1']; } - // set up a counter for how many characters are in the salt so far - $i = 0; - // add random characters to $salt until $length is reached - while ($i < $length) { - // pick a random character from the possible ones - $char = substr($possible, mt_rand(0, $maxlength - 1), 1); - // have we already used this character in $salt? - if (!strstr($salt, $char)) { - // no, so it's OK to add it onto the end of whatever we've already got... - $salt .= $char; - // ... and increase the counter by one - $i++; - } - } - // done! - return $salt; + createUser(array($edit['name'], $pass, $edit['mail']), $account->uid); } -function createUser($values) + +function createUser($values, $user_id) { $login = $values[0]; $pass = $values[1]; $email = $values[2]; - $salt = generateSALT(); - $hashpass = crypt($pass, $salt); + /*$salt = generateSALT(); + $hashpass = crypt($pass, $salt);*/ + + $hashpass = crypt($pass, WebUsers::generateSALT()); $params = array( - $login, - $hashpass, - $email + 'name' => $login, + 'pass' => $hashpass, + 'mail' => $email ); - try { - $hostname = variable_get('ryzommanage_serverurl', 'localhost'); - $port = variable_get('ryzommanage_mysqlport', '3306'); - $dbname = variable_get('ryzommanage_dbname', 'nel'); - $username = variable_get('ryzommanage_username', 'root'); - $password = variable_get('ryzommanage_password', ''); - $dbh = new PDO("mysql:host=$hostname;port=$port;dbname=$dbname", $username, $password); - $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); - } - catch (PDOException $e) { - watchdog('ryzommanage', $e->getMessage(), NULL, WATCHDOG_ERROR); - $nid = db_insert('ryzommanage_querycache')->fields(array( - "SID" => NULL, - "type" => "createUser", - "query" => json_encode(array( - $login, - $pass, - $email - )) - ))->execute(); - return true; - } - - try { - $statement = $dbh->prepare("INSERT INTO user (Login, Password, Email) VALUES (?, ?, ?)"); - } - catch (PDOException $e) { - watchdog('ryzommanage', $e->getMessage(), NULL, WATCHDOG_ERROR); - $nid = db_insert('ryzommanage_querycache')->fields(array( - "SID" => NULL, - "type" => "createUser", - "query" => json_encode(array( - $login, - $pass, - $email - )) - ))->execute(); - return true; - } - - try { - $statement->execute($params); - } - catch (PDOException $e) { - watchdog('ryzommanage', $e->getMessage(), NULL, WATCHDOG_ERROR); - $nid = db_insert('ryzommanage_querycache')->fields(array( - "SID" => NULL, - "type" => "createUser", - "query" => json_encode(array( - $login, - $pass, - $email - )) - ))->execute(); - return true; - } - - createPermissions(array($login)); + //Create the user on the shard + in case shard is offline put copy of query in query db + //returns: ok, shardoffline or liboffline + $result = WebUsers::createUser($params, $user_id); + echo $result; + //createPermissions(array($login)); } -function createPermissions($values) { +/*function createPermissions($values) { try { $hostname = variable_get('ryzommanage_serverurl', 'localhost'); @@ -777,7 +554,7 @@ function createPermissions($values) { } return true; -} +}*/ function login_form($login_form) { @@ -841,7 +618,7 @@ function top_bar() $userId = $user->uid; if (user_is_logged_in()) { // Logged in user - return "
    Notepad | Mail | Wiki | Profile | Craft Recipe-Book | Occupations | News/Events | Account | Logout
    "; + return "
    Notepad | Mail | Wiki | Profile | Craft Recipe-Book | Occupations | News/Events | Account | Logout
    "; } else { return drupal_get_form('login_form'); // Not logged in @@ -863,7 +640,7 @@ function ryzommanage_user_presave(&$edit, $account, $category) } if ($account->is_new == 1 ) { - createUser(array($edit['name'], $pass, $edit['mail'])); + //createUser(array($edit['name'], $pass, $edit['mail'])); } else { user_edit( array($edit['name'], $pass)); } @@ -1027,3 +804,171 @@ function ryzommanage_cron() { syncdata(); } + +function name_registration_admin_settings() { + global $TOS_URL; + $form = array(); + $form['ryzommanage_game-name'] = array( + '#type' => 'textfield', + '#title' => t('Game Name'), + '#default_value' => variable_get('ryzommanage_game-name', ''), + '#description' => t("Name of game used on registration pages."), + '#required' => TRUE + ); + //this is not the TOS url used in the create account page, you change that in the config of the client with the ConditionsTermsURL value + $form['ryzommanage_TOS'] = array( + '#type' => 'textfield', + '#title' => t('Terms of Service URL'), + '#default_value' => $TOS_URL, + '#description' => t("The url of the TOS for your server."), + '#required' => TRUE + ); + $form['ryzommanage_register-welcome'] = array( + '#type' => 'textarea', + '#title' => t('Registration Welcome Message'), + '#default_value' => variable_get('ryzommanage_register-welcome', ''), + '#description' => t("Registration welcome message on first page of create account."), + '#required' => TRUE + ); + + return system_settings_form($form); +} + + +/** + * + * Function ryzommanage_admin + * + * @takes Nothing + * @return array $form + * + * Info: Creates the box's etc that go in the ryzom admin menu + * + */ +function ryzommanage_admin() +{ + $form = array(); + //admin menu items + global $cfg; + + $form['ryzommanage_shardserverurl'] = array( + '#type' => 'textfield', + '#title' => t('Shard server url'), + '#default_value' => $cfg['db']['shard']['host'], + '#description' => t("The url of the ryzom server to integrate with."), + '#required' => TRUE + ); + $form['ryzommanage_shardmysqlport'] = array( + '#type' => 'textfield', + '#title' => t('Port for MySQL of the Shard'), + '#size' => 5, + '#maxlength' => 5, + '#default_value' => $cfg['db']['shard']['port'], + '#description' => t("The MySQL port of the ryzom server to integrate with."), + '#required' => TRUE, + '#element_validate' => array( + '_check_port_value' + ) + ); + $form['ryzommanage_sharddbname'] = array( + '#type' => 'textfield', + '#title' => t('Shard Database Name'), + '#default_value' => $cfg['db']['shard']['name'], + '#description' => t("The MySQL database name to connect to."), + '#required' => TRUE + ); + $form['ryzommanage_shardusername'] = array( + '#type' => 'textfield', + '#title' => t('Shard MySQL Username'), + '#default_value' => $cfg['db']['shard']['user'], + '#description' => t("The MySQL username to connect with."), + '#required' => TRUE + ); + $form['ryzommanage_shardpassword'] = array( + '#type' => 'password_confirm', + '#title' => t('Shard MySQL Password'), + '#description' => t("Confirm the MySQL password."), + '#suffix' => '
    ' + ); + + $form['ryzommanage_libserverurl'] = array( + '#type' => 'textfield', + '#title' => t('Lib server url'), + '#default_value' => $cfg['db']['lib']['host'], + '#description' => t("The url of the ryzom's lib db to integrate with."), + '#required' => TRUE + ); + $form['ryzommanage_libmysqlport'] = array( + '#type' => 'textfield', + '#title' => t('Port for MySQL of the Lib'), + '#size' => 5, + '#maxlength' => 5, + '#default_value' => $cfg['db']['lib']['port'], + '#description' => t("The MySQL port of the ryzom's lib db to integrate with."), + '#required' => TRUE, + '#element_validate' => array( + '_check_port_value' + ) + ); + $form['ryzommanage_libdbname'] = array( + '#type' => 'textfield', + '#title' => t('Lib Database Name'), + '#default_value' => $cfg['db']['lib']['name'], + '#description' => t("The MySQL database name to connect to."), + '#required' => TRUE + ); + $form['ryzommanage_libusername'] = array( + '#type' => 'textfield', + '#title' => t('Lib MySQL Username'), + '#default_value' => $cfg['db']['lib']['user'], + '#description' => t("The MySQL username to connect with."), + '#required' => TRUE + ); + $form['ryzommanage_libpassword'] = array( + '#type' => 'password_confirm', + '#title' => t('Lib MySQL Password'), + '#description' => t("Confirm the MySQL password."), + '#suffix' => '
    ' + ); + + + $form['ryzommanage_ringserverurl'] = array( + '#type' => 'textfield', + '#title' => t('Ring server url'), + '#default_value' => $cfg['db']['ring']['host'], + '#description' => t("The url of the ryzom's ring db to integrate with."), + '#required' => TRUE + ); + $form['ryzommanage_ringmysqlport'] = array( + '#type' => 'textfield', + '#title' => t('Port for MySQL of the Lib'), + '#size' => 5, + '#maxlength' => 5, + '#default_value' => $cfg['db']['ring']['port'], + '#description' => t("The MySQL port of the ryzom ring db to integrate with."), + '#required' => TRUE, + '#element_validate' => array( + '_check_port_value' + ) + ); + $form['ryzommanage_ringdbname'] = array( + '#type' => 'textfield', + '#title' => t('Ring Database Name'), + '#default_value' => $cfg['db']['ring']['name'], + '#description' => t("The MySQL database name to connect to."), + '#required' => TRUE + ); + $form['ryzommanage_ringusername'] = array( + '#type' => 'textfield', + '#title' => t('Ring MySQL Username'), + '#default_value' => $cfg['db']['ring']['user'], + '#description' => t("The MySQL username to connect with."), + '#required' => TRUE + ); + $form['ryzommanage_ringpassword'] = array( + '#type' => 'password_confirm', + '#title' => t('Ring MySQL Password'), + '#description' => t("Confirm the MySQL password.") + ); + return system_settings_form($form); +} From 38fe1ee5579f806c31d71879c8db31db3f46ac59 Mon Sep 17 00:00:00 2001 From: Quitta Date: Mon, 2 Sep 2013 19:43:53 +0200 Subject: [PATCH 135/313] taking away the functions checkUser, checkEmail, checkpassword out of the module, the webuser/user handles this! --- .../ryzom_ams/ams_lib/autoload/users.php | 4 +- .../ryzommanage/autoload/webusers.php | 239 ++++++++++++++++++ .../ryzommanage/ryzommanage.module | 24 +- 3 files changed, 253 insertions(+), 14 deletions(-) create mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/autoload/webusers.php diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/users.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/users.php index 8d07283e6..e03a8fdff 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/users.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/users.php @@ -72,7 +72,7 @@ class Users{ * @takes $username * @return string Info: Returns a string based on if the username is valid, if valid then "success" is returned */ - private function checkUser( $username ) + public function checkUser( $username ) { if ( isset( $username ) ){ if ( strlen( $username ) > 12 ){ @@ -111,7 +111,7 @@ class Users{ * @takes $pass * @return string Info: Returns a string based on if the password is valid, if valid then "success" is returned */ - private function checkPassword( $pass ) + public function checkPassword( $pass ) { if ( isset( $pass ) ){ if ( strlen( $pass ) > 20 ){ diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/autoload/webusers.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/autoload/webusers.php new file mode 100644 index 000000000..7b980e79d --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/autoload/webusers.php @@ -0,0 +1,239 @@ +uId = $UId; + } + + public function set($values){ + $this->uId = $values['UId']; + $this->login = $values['Login']; + $this->email = $values['Email']; + $this->firstname = $values['FirstName']; + $this->lastname = $values['LastName']; + $this->gender = $values['Gender']; + $this->country = $values['Country']; + $this->receiveMail = $values['ReceiveMail']; + $this->language = $values['Language']; + } + + /** + * Function checkUserNameExists + * + * @takes $username + * @return string Info: Returns true or false if the user is in the web db. + */ + protected function checkUserNameExists($username){ + return db_query("SELECT COUNT(*) FROM {users} WHERE name = :name", array(':name' => $username))->fetchField(); + } + + + /** + * Function checkEmailExists + * + * @takes $username + * @return string Info: Returns true or false if the user is in the www db. + */ + protected function checkEmailExists($email){ + return db_query("SELECT COUNT(*) FROM {users} WHERE mail = :mail", array(':mail' => $email))->fetchField(); + } + + + /** + * Function checkUserPassMatch + * + * @takes $username,$password + * @return string Info: Returns true or false if a login match is found in the web db + */ + public function checkLoginMatch($username,$password){ + $dbw = new DBLayer("web"); + $statement = $dbw->execute("SELECT * FROM ams_user WHERE Login=:user", array('user' => $username)); + $row = $statement->fetch(); + + $salt = substr($row['Password'],0,2); + $hashed_input_pass = crypt($password, $salt); + if($hashed_input_pass == $row['Password']){ + return $row; + }else{ + return "fail"; + } + } + + //returns te id for a given username + public static function getId($username){ + $dbw = new DBLayer("web"); + $statement = $dbw->execute("SELECT * FROM ams_user WHERE Login=:username", array('username' => $username)); + $row = $statement->fetch(); + return $row['UId']; + } + + //returns te id for a given username + public static function getIdFromEmail($email){ + $dbw = new DBLayer("web"); + $statement = $dbw->execute("SELECT * FROM ams_user WHERE Email=:email", array('email' => $email)); + $row = $statement->fetch(); + if(!empty($row)){ + return $row['UId']; + }else{ + return "FALSE"; + } + } + + public function getUId(){ + return $this->uId; + } + + public function getUsername(){ + $dbw = new DBLayer("web"); + if(! isset($this->login) || $this->login == ""){ + $statement = $dbw->execute("SELECT * FROM ams_user WHERE UId=:id", array('id' => $this->uId)); + $row = $statement->fetch(); + $this->set($row); + } + return $this->login; + } + + public function getEmail(){ + $dbw = new DBLayer("web"); + if(! isset($this->email) || $this->email == ""){ + $statement = $dbw->execute("SELECT * FROM ams_user WHERE UId=:id", array('id' => $this->uId)); + $row = $statement->fetch(); + $this->set($row); + } + return $this->email; + } + + public function getInfo(){ + $dbw = new DBLayer("web"); + if(! (isset($this->firstname) && isset($this->lastname) && isset($this->gender) && isset($this->country) && isset($this->receiveMail) ) || + $this->firstname == "" || $this->lastname == "" || $this->gender == "" || $this->country == "" || $this->receiveMail == ""){ + $statement = $dbw->execute("SELECT * FROM ams_user WHERE UId=:id", array('id' => $this->uId)); + $row = $statement->fetch(); + $this->set($row); + } + $result = Array('FirstName' => $this->firstname, 'LastName' => $this->lastname, 'Gender' => $this->gender, 'Country' => $this->country, 'ReceiveMail' => $this->receiveMail); + return $result; + } + + public function getReceiveMail(){ + $dbw = new DBLayer("web"); + if(! isset($this->receiveMail) || $this->receiveMail == ""){ + $statement = $dbw->execute("SELECT * FROM ams_user WHERE UId=:id", array('id' => $this->uId)); + $row = $statement->fetch(); + $this->set($row); + } + return $this->receiveMail; + } + + public function getLanguage(){ + $dbw = new DBLayer("web"); + if(! isset($this->language) || $this->language == ""){ + $statement = $dbw->execute("SELECT * FROM ams_user WHERE UId=:id", array('id' => $this->uId)); + $row = $statement->fetch(); + $this->set($row); + } + return $this->language; + } + + public function isLoggedIn(){ + if(isset($_SESSION['user'])){ + return true; + } + return false; + } + + public function setPassword($user, $pass){ + $reply = WebUsers::setAmsPassword($user, $pass); + $values = Array('user' => $user, 'pass' => $pass); + try { + //make connection with and put into shard db + $dbw = new DBLayer("web"); + $dbw->execute("UPDATE ams_user SET Password = :pass WHERE Login = :user ",$values); + } + catch (PDOException $e) { + //ERROR: the web DB is offline + } + return $reply; + } + + public function setEmail($user, $mail){ + $reply = WebUsers::setAmsEmail($user, $mail); + $values = Array('user' => $user, 'mail' => $mail); + try { + //make connection with and put into shard db + $dbw = new DBLayer("web"); + $dbw->execute("UPDATE ams_user SET Email = :mail WHERE Login = :user ",$values); + } + catch (PDOException $e) { + //ERROR: the web DB is offline + } + return $reply; + } + + public static function setReceiveMail($user, $receivemail){ + $values = Array('user' => $user, 'receivemail' => $receivemail); + try { + //make connection with and put into shard db + $dbw = new DBLayer("web"); + $dbw->execute("UPDATE ams_user SET ReceiveMail = :receivemail WHERE UId = :user ",$values); + } + catch (PDOException $e) { + //ERROR: the web DB is offline + } + } + + public static function setLanguage($user, $language){ + $values = Array('user' => $user, 'language' => $language); + try { + //make connection with and put into shard db + $dbw = new DBLayer("web"); + $dbw->execute("UPDATE ams_user SET Language = :language WHERE UId = :user ",$values); + } + catch (PDOException $e) { + //ERROR: the web DB is offline + } + } + + public function getUsers(){ + $dbl = new DBLayer("web"); + $data = $dbl->executeWithoutParams("SELECT * FROM ams_user"); + return $data; + } + + public static function getAllUsersQuery(){ + return "SELECT * FROM ams_user"; + } + + public static function createWebuser($name, $pass, $mail){ + + //register account with the correct language (check if cookie is already set)! + if ( isset( $_COOKIE['Language'] ) ) { + $lang = $_COOKIE['Language']; + }else{ + global $DEFAULT_LANGUAGE; + $lang = $DEFAULT_LANGUAGE; + } + + $values = Array('name' => $name, 'pass' => $pass, 'mail' => $mail, 'lang' => $lang); + + try { + $dbw = new DBLayer("web"); + return $dbw->executeReturnId("INSERT INTO ams_user (Login, Password, Email, Language) VALUES (:name, :pass, :mail, :lang)",$values); + } + catch (PDOException $e) { + //ERROR: the web DB is offline + } + } + +} \ No newline at end of file diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module index 6684ec0c8..73670cb2a 100644 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module @@ -56,9 +56,9 @@ function ryzommanage_form_alter(&$form, &$form_state, $form_id) function _webpage_registration(&$form_state) { - - $user = checkUser($form_state['account']['name']['#value']); - $email = validEmail($form_state['account']['mail']['#value']); + $webUser = new WebUsers(); + $user = $webUser->checkUser($form_state['account']['name']['#value']); + $email = $webUser->validEmail($form_state['account']['mail']['#value']); if ($user != "success") { form_set_error('name', t($user)); @@ -71,12 +71,13 @@ function _webpage_registration(&$form_state) function _webpage_profile(&$form_state) { - $email = validEmail($form_state['account']['mail']['#value']); + $webUser = new WebUsers(); + $email = $webUser->validEmail($form_state['account']['mail']['#value']); if ($email != "success") { form_set_error('mail', t('Not a valid email address, please check it and try again.')); } - if ((checkPassword($form_state['account']['pass']['#value']['pass1']) == "success" ) and ( $form_state['account']['pass']['#value']['pass1'] == + if (($webUser->checkPassword($form_state['account']['pass']['#value']['pass1']) == "success" ) and ( $form_state['account']['pass']['#value']['pass1'] == $form_state['account']['pass']['#value']['pass2'] )) { } } @@ -329,7 +330,7 @@ function return_client_httpdata() * Info: Returns a string based on if the username is valid, if valid then "success" is returned * */ -function checkUser($username) +/*function checkUser($username) { if (isset($username)) { if (strlen($username) > 12) { @@ -349,7 +350,7 @@ function checkUser($username) return "success"; } return "fail"; -} +}*/ /** * * Function checkPassword @@ -360,7 +361,7 @@ function checkUser($username) * Info: Returns a string based on if the password is valid, if valid then "success" is returned * */ -function checkPassword($pass) +/*function checkPassword($pass) { if (isset($pass)) { if (strlen($pass) > 20) { @@ -372,7 +373,7 @@ function checkPassword($pass) } } return "fail"; -} +}*/ /** * * Function confirmPassword @@ -402,7 +403,7 @@ function confirmPassword() * * */ -function checkEmail($email) +/*function checkEmail($email) { if (isset($email)) { if (!validEmail($email)) { @@ -461,7 +462,7 @@ function validEmail($email) } } return $isValid; -} +}*/ /** * @@ -503,7 +504,6 @@ function createUser($values, $user_id) //Create the user on the shard + in case shard is offline put copy of query in query db //returns: ok, shardoffline or liboffline $result = WebUsers::createUser($params, $user_id); - echo $result; //createPermissions(array($login)); } From 2805577c81143077cb0d0fd95a7e189df0eda7b1 Mon Sep 17 00:00:00 2001 From: Quitta Date: Mon, 2 Sep 2013 21:21:41 +0200 Subject: [PATCH 136/313] auto login when calling the page from ingame (should work)... --- .../ryzommanage/autoload/webusers.php | 4 +- .../ryzommanage/ryzommanage.module | 337 +++++++----------- 2 files changed, 127 insertions(+), 214 deletions(-) diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/autoload/webusers.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/autoload/webusers.php index 7b980e79d..158eb3b29 100644 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/autoload/webusers.php +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/autoload/webusers.php @@ -72,9 +72,7 @@ class WebUsers extends Users{ //returns te id for a given username public static function getId($username){ - $dbw = new DBLayer("web"); - $statement = $dbw->execute("SELECT * FROM ams_user WHERE Login=:username", array('username' => $username)); - $row = $statement->fetch(); + $row = db_query("SELECT * FROM {users} WHERE name = :name", array(':name' => $username))->fetchField(); return $row['UId']; } diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module index 73670cb2a..6177aa3e3 100644 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module @@ -37,6 +37,7 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ //output template + function loadTemplate($template,$vars) { extract($vars); @@ -104,6 +105,16 @@ function ryzommanage_menu() 'access arguments' => array('access content'), 'type' => MENU_CALLBACK, ); + + $items['login'] = array( + 'title' => 'Login', + 'page callback' => '_collect_login', + 'page arguments' => array(1, 2), + 'access callback' => 'user_access', + 'access arguments' => array('access content'), + 'type' => MENU_CALLBACK, + ); + //main menu item $items['admin/config/ryzommanage'] = array( 'title' => 'Ryzom Server Integration', @@ -216,164 +227,49 @@ function _collect_register($nids, $collection) { syncdata(); //if not using ryzom core client show registration page - if (check_if_game_client()) { + if (Helpers::check_if_game_client()) { return_client_httpdata(); } else { //redirect to registration page header("Location: user/register"); } } + + /** * - * Function check_if_game_client - * - * @takes Nothing - * @return Boolean - * - * Info: Returns True if connecting client is ryzom core - * - */ -function check_if_game_client() -{ - //if HTTP_USER_AGENT is not set then its ryzom core - if (!isset($_SERVER['HTTP_USER_AGENT'])) { - return true; - } else { - return false; - } -} -/** - * - * Function return_client_httpdata + * Function _collect_register * * @takes - * @return + * @return Nothing * - * Info: Returns ryzom core formatted html for use in registration via client + * Info: Determins what to send back to client, if the client is ryzom core then send the http data if its a browser send to / * */ -function return_client_httpdata() +function _collect_login($nids, $collection) { - //check if values exist - if (isset($_POST["Username"]) and isset($_POST["Password"]) and isset($_POST["Email"]) ) - { - //check values - $user = checkUser($_POST["Username"]); - $pass = checkPassword($_POST["Password"]); - $cpass = confirmPassword(); - $email = checkEmail($_POST["Email"]); + $result = Helpers::check_login_ingame(); + if ($result != "FALSE") { + //handle successful ingame login + $_SESSION['user'] = $result['name']; + $_SESSION['id'] = WebUsers::getId($result['name']); + $_SESSION['ticket_user'] = Ticket_User::constr_ExternId($_SESSION['id']); + + if ($account = user_load( $_SESSION['id'])) { + global $user; + $user->uid = $_SESSION['id']; + $user->name = $account->name; + $user->timezone = $account->timezone; + user_login_finalize(); + } + header( 'Location: ams' ); } else { - $user = ""; - $pass = ""; - $cpass = ""; - $email = ""; - } - //if all are good then create user - if (($user == "success") and ($pass == "success") and ($cpass == "success") and ($email == "success") and (isset($_POST["TaC"]))) { - $edit = array( - 'name' => $_POST["Username"], - 'pass' => $_POST["Password"], - 'mail' => $_POST["Email"], - 'init' => $_POST["Email"], - 'unhashpass' => $_POST["Password"], - 'status' => 1, - 'access' => REQUEST_TIME - ); - user_save(NULL, $edit); - header('Location: email_sent.php'); - exit; - } else { - $pageElements = array( - 'GAME_NAME' => variable_get('ryzommanage_game-name', ''), - 'WELCOME_MESSAGE' => variable_get('ryzommanage_register-welcome', ''), - 'USERNAME' => $user, - 'PASSWORD' => $pass, - 'CPASSWORD' => $cpass, - 'EMAIL' => $email - ); - if ($user != "success") { - $pageElements['USERNAME_ERROR'] = 'TRUE'; - } else { - $pageElements['USERNAME_ERROR'] = 'FALSE'; - } - - if ($pass != "success") { - $pageElements['PASSWORD_ERROR'] = 'TRUE'; - } else { - $pageElements['PASSWORD_ERROR'] = 'FALSE'; - } - if ($cpass != "success") { - $pageElements['CPASSWORD_ERROR'] = 'TRUE'; - } else { - $pageElements['CPASSWORD_ERROR'] = 'FALSE'; - } - if ($email != "success") { - $pageElements['EMAIL_ERROR'] = 'TRUE'; - } else { - $pageElements['EMAIL_ERROR'] = 'FALSE'; - } - if (isset($_POST["TaC"])) { - $pageElements['TAC_ERROR'] = 'FALSE'; - } else { - $pageElements['TAC_ERROR'] = 'TRUE'; - } - loadTemplate('templates/ingame_register.phtml',$pageElements); + //redirect to registration page + header("Location: user/login"); } } -/** - * - * Function checkUser - * - * @takes $username - * @return string - * - * Info: Returns a string based on if the username is valid, if valid then "success" is returned - * - */ -/*function checkUser($username) -{ - if (isset($username)) { - if (strlen($username) > 12) { - return "Username must be no more than 12 characters."; - } elseif (strlen($username) < 5) { - return "Username must be 5 or more characters."; - } elseif (!preg_match('/^[a-z0-9\.]*$/', $username)) { - return "Username can only contain numbers and letters."; - } elseif (db_query("SELECT COUNT(*) FROM {users} WHERE name = :name", array( - ':name' => $username - ))->fetchField()) { - return "Username " . $username . " is in use."; - } else { - return "success"; - } - } else { - return "success"; - } - return "fail"; -}*/ -/** - * - * Function checkPassword - * - * @takes $pass - * @return string - * - * Info: Returns a string based on if the password is valid, if valid then "success" is returned - * - */ -/*function checkPassword($pass) -{ - if (isset($pass)) { - if (strlen($pass) > 20) { - return "Password must be no more than 20 characters."; - } elseif (strlen($pass) < 5) { - return "Password must be more than 5 characters."; - } else { - return "success"; - } - } - return "fail"; -}*/ + + /** * * Function confirmPassword @@ -393,76 +289,7 @@ function confirmPassword() } return "fail"; } -/** - * - * Function checkEmail - * - * @takes $email - * @return - * - * - * - */ -/*function checkEmail($email) -{ - if (isset($email)) { - if (!validEmail($email)) { - return "Email address is not valid."; - } elseif (db_query("SELECT COUNT(*) FROM {users} WHERE mail = :mail", array( - ':mail' => $email - ))->fetchField()) { - return "Email is in use."; - } else { - return "success"; - } - } else { - return "success"; - } - return "fail"; -} -function validEmail($email) -{ - $isValid = true; - $atIndex = strrpos($email, "@"); - if (is_bool($atIndex) && !$atIndex) { - $isValid = false; - } else { - $domain = substr($email, $atIndex + 1); - $local = substr($email, 0, $atIndex); - $localLen = strlen($local); - $domainLen = strlen($domain); - if ($localLen < 1 || $localLen > 64) { - // local part length exceeded - $isValid = false; - } else if ($domainLen < 1 || $domainLen > 255) { - // domain part length exceeded - $isValid = false; - } else if ($local[0] == '.' || $local[$localLen - 1] == '.') { - // local part starts or ends with '.' - $isValid = false; - } else if (preg_match('/\\.\\./', $local)) { - // local part has two consecutive dots - $isValid = false; - } else if (!preg_match('/^[A-Za-z0-9\\-\\.]+$/', $domain)) { - // character not valid in domain part - $isValid = false; - } else if (preg_match('/\\.\\./', $domain)) { - // domain part has two consecutive dots - $isValid = false; - } else if (!preg_match('/^(\\\\.|[A-Za-z0-9!#%&`_=\\/$\'*+?^{}|~.-])+$/', str_replace("\\\\", "", $local))) { - // character not valid in local part unless - // local part is quoted - if (!preg_match('/^"(\\\\"|[^"])+"$/', str_replace("\\\\", "", $local))) { - $isValid = false; - } - } - if ($isValid && !(checkdnsrr($domain, "MX") || checkdnsrr($domain, "A"))) { - // domain not found in DNS - $isValid = false; - } - } - return $isValid; -}*/ + /** * @@ -556,6 +383,13 @@ function createUser($values, $user_id) return true; }*/ +function ryzommanage_user_login(&$edit, $account){ + echo "You just logged in with id"; + $_SESSION['user'] = $account->name; + $_SESSION['id'] = $account->uid; + $_SESSION['ticket_user'] = Ticket_User::constr_ExternId($_SESSION['id']); +} + function login_form($login_form) { $login_form['#action'] = url(current_path(), array( @@ -972,3 +806,84 @@ function ryzommanage_admin() ); return system_settings_form($form); } + +/** + * + * Function return_client_httpdata + * + * @takes + * @return + * + * Info: Returns ryzom core formatted html for use in registration via client + * + */ +function return_client_httpdata() +{ + //needs $cpass = confirmPassword(($_POST["Password"]) != ($_POST["ConfirmPass"])); !!!!!! + //check if values exist + if (isset($_POST["Username"]) and isset($_POST["Password"]) and isset($_POST["Email"]) ) + { + //check values + $user = checkUser($_POST["Username"]); + $pass = checkPassword($_POST["Password"]); + $cpass = confirmPassword(($_POST["Password"]) != ($_POST["ConfirmPass"])); + $email = checkEmail($_POST["Email"]); + } else { + $user = ""; + $pass = ""; + $cpass = ""; + $email = ""; + } + //if all are good then create user + if (($user == "success") and ($pass == "success") and ($cpass == "success") and ($email == "success") and (isset($_POST["TaC"]))) { + $edit = array( + 'name' => $_POST["Username"], + 'pass' => $_POST["Password"], + 'mail' => $_POST["Email"], + 'init' => $_POST["Email"], + 'unhashpass' => $_POST["Password"], + 'status' => 1, + 'access' => REQUEST_TIME + ); + user_save(NULL, $edit); + header('Location: email_sent.php'); + exit; + } else { + $pageElements = array( + 'GAME_NAME' => variable_get('ryzommanage_game-name', ''), + 'WELCOME_MESSAGE' => variable_get('ryzommanage_register-welcome', ''), + 'USERNAME' => $user, + 'PASSWORD' => $pass, + 'CPASSWORD' => $cpass, + 'EMAIL' => $email + ); + if ($user != "success") { + $pageElements['USERNAME_ERROR'] = 'TRUE'; + } else { + $pageElements['USERNAME_ERROR'] = 'FALSE'; + } + + if ($pass != "success") { + $pageElements['PASSWORD_ERROR'] = 'TRUE'; + } else { + $pageElements['PASSWORD_ERROR'] = 'FALSE'; + } + if ($cpass != "success") { + $pageElements['CPASSWORD_ERROR'] = 'TRUE'; + } else { + $pageElements['CPASSWORD_ERROR'] = 'FALSE'; + } + if ($email != "success") { + $pageElements['EMAIL_ERROR'] = 'TRUE'; + } else { + $pageElements['EMAIL_ERROR'] = 'FALSE'; + } + if (isset($_POST["TaC"])) { + $pageElements['TAC_ERROR'] = 'FALSE'; + } else { + $pageElements['TAC_ERROR'] = 'TRUE'; + } + loadTemplate('templates/ingame_register.phtml',$pageElements); + } +} + From 08d21336afb53383271527693614a114704bcbee Mon Sep 17 00:00:00 2001 From: Quitta Date: Mon, 2 Sep 2013 23:52:42 +0200 Subject: [PATCH 137/313] added help hook, to make it viewable in the help menu, however still unsure if I have to make a page or block or a combination.. --- .../ryzommanage/ryzommanage.module | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module index 6177aa3e3..647cf453e 100644 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module @@ -115,6 +115,15 @@ function ryzommanage_menu() 'type' => MENU_CALLBACK, ); + $items['ams'] = array( + 'title' => 'Account Management System', + 'page callback' => '_collect_ams', + 'page arguments' => array(1, 2), + 'access callback' => 'user_access', + 'access arguments' => array('access content'), + 'type' => MENU_CALLBACK, + ); + //main menu item $items['admin/config/ryzommanage'] = array( 'title' => 'Ryzom Server Integration', @@ -384,7 +393,6 @@ function createUser($values, $user_id) }*/ function ryzommanage_user_login(&$edit, $account){ - echo "You just logged in with id"; $_SESSION['user'] = $account->name; $_SESSION['id'] = $account->uid; $_SESSION['ticket_user'] = Ticket_User::constr_ExternId($_SESSION['id']); @@ -495,6 +503,7 @@ function ryzommanage_form_user_register_form_alter(&$form, &$form_state, $form_i '#title' => t("I agree with the terms and conditions."), '#required' => TRUE, ); + } function ryzommanage_form_user_profile_form_alter(&$form, &$form_state, $form_id) { @@ -887,3 +896,20 @@ function return_client_httpdata() } } +/** +* Implements hook_help. +* +* Displays help and module information. +* +* @param path +* Which path of the site we're using to display help +* @param arg +* Array that holds the current path as returned from arg() function +*/ +function ryzommanage_help($path, $arg) { + switch ($path) { + case "admin/help#ryzommanage": + return '

    ' . t("A module that handles account registration and a ticketing service regarding ryzomcore.") . '

    '; + break; + } +} \ No newline at end of file From 6f4fb697b5f84dc104ed825fad4c2ffc7d61fca1 Mon Sep 17 00:00:00 2001 From: Quitta Date: Tue, 3 Sep 2013 04:55:51 +0200 Subject: [PATCH 138/313] added amsblock and a menu item for the ams page --- .../ryzommanage/ryzommanage.module | 37 ++++++++++++++++--- 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module index 647cf453e..4a812578a 100644 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module @@ -11,6 +11,7 @@ menu items that do stuff */ error_reporting(E_ALL); ini_set('display_errors', 'on'); +ini_set('display_startup_errors', TRUE); global $TOS_URL; global $cfg; @@ -103,7 +104,7 @@ function ryzommanage_menu() 'page arguments' => array(1, 2), 'access callback' => 'user_access', 'access arguments' => array('access content'), - 'type' => MENU_CALLBACK, + 'type' => MENU_CALLBACK ); $items['login'] = array( @@ -112,16 +113,15 @@ function ryzommanage_menu() 'page arguments' => array(1, 2), 'access callback' => 'user_access', 'access arguments' => array('access content'), - 'type' => MENU_CALLBACK, + 'type' => MENU_CALLBACK ); $items['ams'] = array( - 'title' => 'Account Management System', - 'page callback' => '_collect_ams', - 'page arguments' => array(1, 2), + 'title' => 'Ryzom Account Management System', + 'page callback' => '_ams_callback', 'access callback' => 'user_access', 'access arguments' => array('access content'), - 'type' => MENU_CALLBACK, + 'type' => MENU_NORMAL_ITEM ); //main menu item @@ -163,6 +163,12 @@ function ryzommanage_menu() } +function _ams_callback(){ + //an empty call back function, because we need an empty page! + //without this the page gets access denied, perhaps for later use.. + return array(); +} + /** * * Function ryzommanage_menu @@ -198,6 +204,15 @@ function ryzommanage_block_info() 'region' => '-1', // Not usually provided. 'visibility' => BLOCK_VISIBILITY_LISTED // Not usually provided. ); + + $blocks['ryzommanage_amsblock'] = array( + // info: The name of the block. + 'info' => t('Ryzom AMS Block'), + 'status' => TRUE, + 'region' => '-1', // Not usually provided. + 'visibility' => BLOCK_VISIBILITY_LISTED, + 'pages' => 'ams' + ); return $blocks; } /** @@ -219,6 +234,11 @@ function ryzommanage_block_view($delta = '') $block['subject'] = t("uppercase this please"); $block['content'] = top_bar(); break; + + case 'ryzommanage_amsblock': + $block['subject'] = t("uppercase this please"); + $block['content'] = ams_handler(); + break; } return $block; } @@ -467,6 +487,11 @@ function top_bar() } } +function ams_handler() +{ + return "This is a placeholder."; +} + function ryzommanage_user_presave(&$edit, $account, $category) { if (isset($edit['unhashpass'])) { From 213000c5c83baaf4857c809d02b9f21fa6f99cd0 Mon Sep 17 00:00:00 2001 From: Quitta Date: Tue, 3 Sep 2013 17:52:52 +0200 Subject: [PATCH 139/313] reworked the index.php page into the module's _ams_handler function, which will be the main function. Having some serialize/unserialize issues though.. --- .../ryzommanage/ryzommanage.module | 74 ++++++++++++++++--- 1 file changed, 65 insertions(+), 9 deletions(-) diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module index 4a812578a..e4ff17005 100644 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module @@ -16,9 +16,9 @@ ini_set('display_startup_errors', TRUE); global $TOS_URL; global $cfg; -include 'ams_lib/libinclude.php'; +require 'ams_lib/libinclude.php'; spl_autoload_register('__autoload'); -include 'config.php'; +require 'config.php'; /* Drupal 7 ryzom core module @@ -236,12 +236,71 @@ function ryzommanage_block_view($delta = '') break; case 'ryzommanage_amsblock': - $block['subject'] = t("uppercase this please"); - $block['content'] = ams_handler(); + //$block['subject'] = t("Ryzom Account Management System"); + $block['content'] = _ams_handler(); break; } return $block; } + + +function _ams_handler() +{ + + //Decide what page to load + if ( ! isset( $_GET["page"]) ){ + if(isset($_SESSION['user'])){ + if(Ticket_User::isMod(unserialize($_SESSION['ticket_user']))){ + $page = 'dashboard'; + }else{ + $page = 'show_user'; + } + }else{ + //default page + $page = 'login'; + } + }else{ + $page = $_GET["page"]; + } + + //perform an action in case one is specified + //else check if a php page is included in the inc folder, else just set page to the get param + if ( isset( $_POST["function"] ) ){ + require( "func/" . $_POST["function"] . ".php" ); + $return = $_POST["function"](); + }else{ + $filename = 'inc/' . $page . '.php'; + if(is_file($filename)){ + require_once($filename); + $return = $page(); + } + } + + //add username to the return array in case logged in. + if(isset($_SESSION['user'])){ + $return['username'] = $_SESSION['user']; + } + + //Set permission + if(isset($_SESSION['ticket_user'])){ + $return['permission'] = unserialize($_SESSION['ticket_user'])->getPermission(); + }else{ + //default permission + $return['permission'] = 0; + } + + //handle error page + if($page == 'error'){ + $return['permission'] = 0; + $return['no_visible_elements'] = 'FALSE'; + } + + return $page; + //helpers :: loadTemplate( $page , $return ); +} + + + /** * * Function _collect_register @@ -415,7 +474,7 @@ function createUser($values, $user_id) function ryzommanage_user_login(&$edit, $account){ $_SESSION['user'] = $account->name; $_SESSION['id'] = $account->uid; - $_SESSION['ticket_user'] = Ticket_User::constr_ExternId($_SESSION['id']); + $_SESSION['ticket_user'] = serialize(Ticket_User::constr_ExternId($_SESSION['id'])); } function login_form($login_form) @@ -487,10 +546,7 @@ function top_bar() } } -function ams_handler() -{ - return "This is a placeholder."; -} + function ryzommanage_user_presave(&$edit, $account, $category) { From fd368bb123d2f5a4eef589c17e9f1889ce7adb8b Mon Sep 17 00:00:00 2001 From: Quitta Date: Wed, 4 Sep 2013 17:44:43 +0200 Subject: [PATCH 140/313] can display the dashboard html atm --- .../drupal_module/ryzommanage/config.php | 4 +- .../ryzommanage/inc/dashboard.php | 35 +++++ .../ryzommanage/ryzommanage.module | 30 ++-- .../ryzommanage/templates/dashboard.tpl | 77 ++++++++++ .../ryzommanage/templates/layout.tpl | 2 + .../ryzommanage/templates/layout_admin.tpl | 2 + .../ryzommanage/templates/layout_mod.tpl | 3 + .../ryzommanage/templates/layout_user.tpl | 1 + .../ryzommanage/templates/show_user.tpl | 142 ++++++++++++++++++ .../drupal_module/ryzommanage/todo.txt | 2 + 10 files changed, 285 insertions(+), 13 deletions(-) create mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/dashboard.php create mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/dashboard.tpl create mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/layout.tpl create mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/layout_admin.tpl create mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/layout_mod.tpl create mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/layout_user.tpl create mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/show_user.tpl create mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/todo.txt diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/config.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/config.php index a2bf18c20..0037eef30 100644 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/config.php +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/config.php @@ -69,10 +69,10 @@ $ALLOW_UNKNOWN = true ; $CREATE_RING = true ; // site paths definitions -$AMS_LIB = dirname( dirname( __FILE__ ) ) . '/ams_lib'; +$AMS_LIB = dirname( __FILE__ ) . '/ams_lib'; $AMS_TRANS = $AMS_LIB . '/translations'; $AMS_CACHEDIR = $AMS_LIB . '/cache'; -$SITEBASE = dirname( __FILE__ ) . '/html/' ; +$SITEBASE = dirname( __FILE__ ); $WEBPATH ='http://localhost:40917' ; //defines the default language diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/dashboard.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/dashboard.php new file mode 100644 index 000000000..6d0ef5faf --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/dashboard.php @@ -0,0 +1,35 @@ +getTUserId(); + $result['nrToDo'] = Ticket_Queue_Handler::getNrOfTicketsToDo(unserialize($_SESSION['ticket_user'])->getTUserId()); + $result['nrAssignedWaiting'] = Ticket_Queue_Handler::getNrOfTicketsAssignedWaiting(unserialize($_SESSION['ticket_user'])->getTUserId()); + $result['nrTotalTickets'] = Ticket_Queue_Handler::getNrOfTickets(); + $ticket = Ticket_Queue_Handler::getNewestTicket(); + $result['newestTicketId'] = $ticket->getTId(); + $result['newestTicketTitle'] = $ticket->getTitle(); + $result['newestTicketAuthor'] = Ticket_User::get_username_from_id($ticket->getAuthor()); + return $result; + + }else{ + //ERROR: No access! + $_SESSION['error_code'] = "403"; + header("Location: index.php?page=error"); + exit; + + } + + }else{ + //ERROR: not logged in! + header("Location: index.php"); + exit; + } + + +} \ No newline at end of file diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module index e4ff17005..e935828fd 100644 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module @@ -14,6 +14,10 @@ ini_set('display_errors', 'on'); ini_set('display_startup_errors', TRUE); global $TOS_URL; +global $AMS_LIB; +global $SITEBASE; +global $AMS_TRANS; +global $DEFAULT_LANGUAGE; global $cfg; require 'ams_lib/libinclude.php'; @@ -257,7 +261,8 @@ function _ams_handler() } }else{ //default page - $page = 'login'; + header("Location: user/login"); + exit; } }else{ $page = $_GET["page"]; @@ -266,14 +271,17 @@ function _ams_handler() //perform an action in case one is specified //else check if a php page is included in the inc folder, else just set page to the get param if ( isset( $_POST["function"] ) ){ - require( "func/" . $_POST["function"] . ".php" ); - $return = $_POST["function"](); + $filename = dirname( __FILE__ ).'/func/' . $_POST["function"] . '.php'; + if(is_file($filename)){ + require($filename); + $return = $_POST["function"](); + } }else{ - $filename = 'inc/' . $page . '.php'; - if(is_file($filename)){ - require_once($filename); - $return = $page(); - } + $filename = dirname( __FILE__ ).'/inc/' . $page . '.php'; + if(is_file($filename)){ + require_once($filename); + $return = $page(); + } } //add username to the return array in case logged in. @@ -295,8 +303,8 @@ function _ams_handler() $return['no_visible_elements'] = 'FALSE'; } - return $page; - //helpers :: loadTemplate( $page , $return ); + //return $page; + return helpers :: loadTemplate( $page , $return, true); } @@ -341,7 +349,7 @@ function _collect_login($nids, $collection) //handle successful ingame login $_SESSION['user'] = $result['name']; $_SESSION['id'] = WebUsers::getId($result['name']); - $_SESSION['ticket_user'] = Ticket_User::constr_ExternId($_SESSION['id']); + $_SESSION['ticket_user'] = serialize(Ticket_User::constr_ExternId($_SESSION['id'])); if ($account = user_load( $_SESSION['id'])) { global $user; diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/dashboard.tpl b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/dashboard.tpl new file mode 100644 index 000000000..ea53b1665 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/dashboard.tpl @@ -0,0 +1,77 @@ +{block name=content} + + + + + + + + + + + +
    + +
    Tickets Waiting for Direct Action: + {$nrAssignedWaiting}
    +
    +
    + +
    Tickets Todo: + {$nrToDo}
    +
    +
    + + +
    Newest Ticket: + {$newestTicketTitle}
    +
    +
    + + +
    Total amount of Tickets: + {$nrTotalTickets}
    +
    +
    + +
    +
    +
    +

    {$home_title}

    +
    + + + + +
    +
    +
    +

    {$home_info}

    +

    This is the GSOC project of Daan Janssens mentored by Matthew Lagoe.

    +

    The features as admin covered in this project are:

    +
      +
    • Manage user accounts
    • +
    • Make users moderator or admin
    • +
    • browse user's tickets
    • +
    • Create a new ticket for a specific user as admin
    • +
    • Create a new support group (and link an email to it)
    • +
    • Add mods to support groups
    • +
    • Assign ticket to you
    • +
    • Forward ticket to a support group
    • +
    • Add hidden messages to a ticket only viewable by other mods
    • +
    • Browse ticket queues or create one dynamically
    • +
    • Sync changes after the game server is back up after being down for a while
    • +
    • Browse the log of a ticket
    • +
    • Browse additional info sent along when a ticket is created ingame
    • +
    • All the above can be done while ingame too
    • +
    + +
    +
    +
    +
    + + +{/block} + diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/layout.tpl b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/layout.tpl new file mode 100644 index 000000000..f889a43a6 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/layout.tpl @@ -0,0 +1,2 @@ +{block name=content}{/block} + \ No newline at end of file diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/layout_admin.tpl b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/layout_admin.tpl new file mode 100644 index 000000000..881ccb18a --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/layout_admin.tpl @@ -0,0 +1,2 @@ +{extends file="layout.tpl"} + diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/layout_mod.tpl b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/layout_mod.tpl new file mode 100644 index 000000000..c5ed6c10b --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/layout_mod.tpl @@ -0,0 +1,3 @@ +{extends file="layout.tpl"} + + diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/layout_user.tpl b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/layout_user.tpl new file mode 100644 index 000000000..62185e613 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/layout_user.tpl @@ -0,0 +1 @@ +{extends file="layout.tpl"} diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/show_user.tpl b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/show_user.tpl new file mode 100644 index 000000000..91bdc683f --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/show_user.tpl @@ -0,0 +1,142 @@ +{block name=content} +
    +
    +
    +

    Profile of {$target_name}

    +
    + + +
    +
    +
    +
    + Info + + + + + + + + + + + + {if $firstName neq ""} + + + + + {/if} + {if $lastName neq ""} + + + + + {/if} + {if $country neq ""} + + + + + {/if} + {if $gender neq 0} + + + {if $gender eq 1} + + {else if $gender eq 2} + + {/if} + + {/if} + +
    Email:{$mail}
    Role: + {if $userPermission eq 1}User{/if} + {if $userPermission eq 2}Moderator{/if} + {if $userPermission eq 3}Admin{/if} +
    Firstname:{$firstName}
    LastName:{$lastName}
    Country:{$country}
    Gender:♂♀
    +
    +
    +
    + +
    +
    +

    Actions

    +
    + + +
    +
    +
    +
    +
    + + +
    +
    +
    +
    +
    + +
    +
    +
    +

    Tickets of {$target_name}

    +
    + + +
    +
    +
    +
    + Tickets + + + + + + + + + + + + {foreach from=$ticketlist item=ticket} + + + + + + + + + {/foreach} + + +
    IDTitleTimestampCategoryStatus
    {$ticket.tId}{$ticket.title}{$ticket.timestamp}{$ticket.category}{if $ticket.status eq 0} {/if} {$ticket.statusText}
    +
    +
    +
    +
    +{/block} + diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/todo.txt b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/todo.txt new file mode 100644 index 000000000..539cf76cd --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/todo.txt @@ -0,0 +1,2 @@ +-Remove full path in autoload functions +-Make Permission www dependend, so it can be implemented in drupal with hook_permission(); \ No newline at end of file From 98e5964869fe5e0ab9e77961d93159ad9327e062 Mon Sep 17 00:00:00 2001 From: Quitta Date: Wed, 4 Sep 2013 17:49:46 +0200 Subject: [PATCH 141/313] helpers class updated! --- .../ryzom_ams/ams_lib/autoload/helpers.php | 35 ++++++++++++------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/helpers.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/helpers.php index 68f0e7917..cef808a92 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/helpers.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/helpers.php @@ -3,27 +3,28 @@ class Helpers{ - public static function loadTemplate( $template, $vars = array (), $forcelibrender = false ) + public static function loadTemplate( $template, $vars = array (), $returnHTML = false ) { global $AMS_LIB; global $SITEBASE; global $AMS_TRANS; global $INGAME_LAYOUT; - define('SMARTY_SPL_AUTOLOAD',1); + //define('SMARTY_SPL_AUTOLOAD',1); require_once $AMS_LIB . '/smarty/libs/Smarty.class.php'; spl_autoload_register('__autoload'); $smarty = new Smarty; - + $smarty->setCompileDir($SITEBASE.'/templates_c/'); + $smarty->setCacheDir($SITEBASE.'/cache/'); + $smarty -> setConfigDir($SITEBASE . '/configs/' ); // turn smarty debugging on/off - $smarty -> debugging = false; + $smarty -> debugging = false; // caching must be disabled for multi-language support - $smarty -> caching = false; + $smarty -> caching = false; $smarty -> cache_lifetime = 120; helpers :: create_folders (); - if ( helpers::check_if_game_client() or $forcelibrender = false ){ $smarty -> template_dir = $AMS_LIB . '/ingame_templates/'; $smarty -> setConfigDir( $AMS_LIB . '/configs' ); @@ -45,7 +46,7 @@ class Helpers{ foreach ( $variables[$template] as $key => $value ){ $smarty -> assign( $key, $value ); } - + if( isset($vars['permission']) && $vars['permission'] == 3 ){ $inherited = "extends:layout_admin.tpl|"; }else if( isset($vars['permission']) && $vars['permission'] == 2){ @@ -55,9 +56,15 @@ class Helpers{ }else{ $inherited =""; } - // extends:' . $inherited .'|register.tpl - $smarty -> display( $inherited . $template . '.tpl' ); - } + + + + if($returnHTML == true){ + return $smarty ->fetch($inherited . $template . '.tpl' ); + }else{ + $smarty -> display( $inherited . $template . '.tpl' ); + } + } static public function create_folders(){ global $AMS_LIB; @@ -72,8 +79,9 @@ class Helpers{ ); foreach ( $arr as & $value ){ if ( !file_exists( $value ) ){ - echo $value; - mkdir( $value); + //echo $value; + print($value); + drupal_mkdir($value); } } @@ -133,6 +141,9 @@ class Helpers{ } } + if ($_SESSION['Language'] == ""){ + $_SESSION['Language'] = $DEFAULT_LANGUAGE; + } return parse_ini_file( $AMS_TRANS . '/' . $_SESSION['Language'] . '.ini', true ); } From f795cf44d246b04c6235778e71015fc36f15084d Mon Sep 17 00:00:00 2001 From: Quitta Date: Thu, 5 Sep 2013 02:31:29 +0200 Subject: [PATCH 142/313] Creating tickets, browsing the profile, replying on tickets and forwarding them is possible! --- .../ryzommanage/autoload/webusers.php | 33 ++- .../ryzommanage/func/create_ticket.php | 52 +++++ .../ryzommanage/func/reply_on_ticket.php | 54 +++++ .../ryzommanage/inc/createticket.php | 46 +++++ .../ryzommanage/inc/show_ticket.php | 84 ++++++++ .../ryzommanage/inc/show_user.php | 44 ++++ .../ryzommanage/ryzommanage.module | 75 ++++++- .../ryzommanage/templates/createticket.tpl | 38 ++++ .../ryzommanage/templates/dashboard.tpl | 8 +- .../ryzommanage/templates/show_ticket.tpl | 188 ++++++++++++++++++ .../ryzommanage/templates/show_user.tpl | 149 +++++--------- .../drupal_module/ryzommanage/todo.txt | 3 +- 12 files changed, 651 insertions(+), 123 deletions(-) create mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/create_ticket.php create mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/reply_on_ticket.php create mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/createticket.php create mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/show_ticket.php create mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/show_user.php create mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/createticket.tpl create mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/show_ticket.tpl diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/autoload/webusers.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/autoload/webusers.php index 158eb3b29..2beb75c1b 100644 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/autoload/webusers.php +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/autoload/webusers.php @@ -17,15 +17,15 @@ class WebUsers extends Users{ } public function set($values){ - $this->uId = $values['UId']; - $this->login = $values['Login']; - $this->email = $values['Email']; - $this->firstname = $values['FirstName']; - $this->lastname = $values['LastName']; - $this->gender = $values['Gender']; - $this->country = $values['Country']; - $this->receiveMail = $values['ReceiveMail']; - $this->language = $values['Language']; + $this->uId = $values['uid']; + $this->login = $values['name']; + $this->email = $values['mail']; + //$this->firstname = $values['FirstName']; + //$this->lastname = $values['LastName']; + //$this->gender = $values['Gender']; + //$this->country = $values['Country']; + //$this->receiveMail = $values['ReceiveMail']; + //$this->language = $values['Language']; } /** @@ -72,8 +72,8 @@ class WebUsers extends Users{ //returns te id for a given username public static function getId($username){ - $row = db_query("SELECT * FROM {users} WHERE name = :name", array(':name' => $username))->fetchField(); - return $row['UId']; + $row = db_query("SELECT * FROM {users} WHERE name = :name", array(':name' => $username))->fetchAssoc(); + return $row['uid']; } //returns te id for a given username @@ -93,20 +93,17 @@ class WebUsers extends Users{ } public function getUsername(){ - $dbw = new DBLayer("web"); + if(! isset($this->login) || $this->login == ""){ - $statement = $dbw->execute("SELECT * FROM ams_user WHERE UId=:id", array('id' => $this->uId)); - $row = $statement->fetch(); - $this->set($row); + $row = db_query("SELECT * FROM {users} WHERE uid = :id", array(':id' => $this->uId))->fetchAssoc(); + $this->set($row); } return $this->login; } public function getEmail(){ - $dbw = new DBLayer("web"); if(! isset($this->email) || $this->email == ""){ - $statement = $dbw->execute("SELECT * FROM ams_user WHERE UId=:id", array('id' => $this->uId)); - $row = $statement->fetch(); + $row = db_query("SELECT * FROM {users} WHERE uid = :id", array(':id' => $this->uId))->fetchAssoc(); $this->set($row); } return $this->email; diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/create_ticket.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/create_ticket.php new file mode 100644 index 000000000..764bd6931 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/create_ticket.php @@ -0,0 +1,52 @@ +getTUserId(); + }else{ + $author= Ticket_User::constr_ExternId($_POST['target_id'])->getTUserId(); + } + $ticket_id = Ticket::create_Ticket($title, $content, $category, $author, unserialize($_SESSION['ticket_user'])->getTUserId(),0, $_POST); + header("Location: index.php?page=show_ticket&id=".$ticket_id); + exit; + + }catch (PDOException $e) { + //ERROR: LIB DB is not online! + print_r($e); + exit; + header("Location: index.php"); + exit; + } + + }else{ + //ERROR: permission denied! + $_SESSION['error_code'] = "403"; + header("Location: index.php?page=error"); + exit; + } + + }else{ + //ERROR: The form was not filled in correclty + header("Location: index.php?page=create_ticket"); + exit; + } + }else{ + //ERROR: user is not logged in + header("Location: index.php"); + exit; + } + +} + diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/reply_on_ticket.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/reply_on_ticket.php new file mode 100644 index 000000000..36fa3abeb --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/reply_on_ticket.php @@ -0,0 +1,54 @@ +load_With_TId($ticket_id); + + if(($target_ticket->getAuthor() == unserialize($_SESSION['ticket_user'])->getTUserId()) || Ticket_User::isMod(unserialize($_SESSION['ticket_user'])) ){ + + try{ + $author = unserialize($_SESSION['ticket_user'])->getTUserId(); + if(isset($_POST['Content'])){ + $content = $_POST['Content']; + }else{ + $content=""; + } + $hidden = 0; + if(isset($_POST['hidden']) && Ticket_User::isMod(unserialize($_SESSION['ticket_user']))){ + $hidden = 1; + } + Ticket::createReply($content, $author, $ticket_id, $hidden); + + if(isset($_POST['ChangeStatus']) && isset($_POST['ChangePriority']) && Ticket_User::isMod(unserialize($_SESSION['ticket_user']))){ + $newStatus = filter_var($_POST['ChangeStatus'], FILTER_SANITIZE_NUMBER_INT); + $newPriority = filter_var($_POST['ChangePriority'], FILTER_SANITIZE_NUMBER_INT); + Ticket::updateTicketStatusAndPriority($ticket_id,$newStatus, $newPriority, $author); + } + header("Location: ams?page=show_ticket&id=".$ticket_id); + exit; + + }catch (PDOException $e) { + //ERROR: LIB DB is not online! + print_r($e); + //header("Location: index.php"); + exit; + } + + }else{ + //ERROR: No access! + $_SESSION['error_code'] = "403"; + header("Location: index.php?page=error"); + exit; + } + }else{ + //ERROR: not logged in! + header("Location: index.php"); + exit; + } + +} \ No newline at end of file diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/createticket.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/createticket.php new file mode 100644 index 000000000..49e2263cf --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/createticket.php @@ -0,0 +1,46 @@ +getTUserId(); + $result['ticket_id'] = filter_var($_GET['id'], FILTER_SANITIZE_NUMBER_INT); + $target_ticket = new Ticket(); + $target_ticket->load_With_TId($result['ticket_id']); + + if(Ticket_User::isMod(unserialize($_SESSION['ticket_user'] ))){ + if(isset($_POST['action'])){ + switch($_POST['action']){ + case "forward": + $ticket_id = filter_var($_POST['ticket_id'], FILTER_SANITIZE_NUMBER_INT); + $group_id = filter_var($_POST['group'], FILTER_SANITIZE_NUMBER_INT); + $result['ACTION_RESULT'] = Ticket::forwardTicket($result['user_id'], $ticket_id, $group_id); + break; + case "assignTicket": + $ticket_id = filter_var($_POST['ticket_id'], FILTER_SANITIZE_NUMBER_INT); + $result['ACTION_RESULT'] = Ticket::assignTicket($result['user_id'] , $ticket_id); + break; + case "unAssignTicket": + $ticket_id = filter_var($_POST['ticket_id'], FILTER_SANITIZE_NUMBER_INT); + $result['ACTION_RESULT'] = Ticket::unAssignTicket($result['user_id'], $ticket_id); + break; + + } + } + } + + if(($target_ticket->getAuthor() == unserialize($_SESSION['ticket_user'])->getTUserId()) || Ticket_User::isMod(unserialize($_SESSION['ticket_user']) )){ + + $show_as_admin = false; + if(Ticket_User::isMod(unserialize($_SESSION['ticket_user']))){ + $show_as_admin = true; + } + + $entire_ticket = Ticket::getEntireTicket( $result['ticket_id'],$show_as_admin); + Ticket_Log::createLogEntry($result['ticket_id'],unserialize($_SESSION['ticket_user'])->getTUserId(), 3); + $result['ticket_tId'] = $entire_ticket['ticket_obj']->getTId(); + $result['ticket_forwardedGroupName'] = $entire_ticket['ticket_obj']->getForwardedGroupName(); + $result['ticket_forwardedGroupId'] = $entire_ticket['ticket_obj']->getForwardedGroupId(); + $result['ticket_title'] = $entire_ticket['ticket_obj']->getTitle(); + $result['ticket_timestamp'] = $entire_ticket['ticket_obj']->getTimestamp(); + $result['ticket_status'] = $entire_ticket['ticket_obj']->getStatus(); + $result['ticket_author'] = $entire_ticket['ticket_obj']->getAuthor(); + $result['ticket_prioritytext'] = $entire_ticket['ticket_obj']->getPriorityText(); + $result['ticket_priorities'] = Ticket::getPriorityArray(); + $result['ticket_priority'] = $entire_ticket['ticket_obj']->getPriority(); + $result['ticket_statustext'] = $entire_ticket['ticket_obj']->getStatusText(); + $result['ticket_lastupdate'] = Gui_Elements::time_elapsed_string(Ticket::getLatestReply($result['ticket_id'])->getTimestamp()); + $result['ticket_category'] = $entire_ticket['ticket_obj']->getCategoryName(); + $webUser = new WebUsers(Assigned::getUserAssignedToTicket($result['ticket_tId'])); + $result['ticket_assignedToText'] = $webUser->getUsername(); + $result['ticket_assignedTo'] = Assigned::getUserAssignedToTicket($result['ticket_tId']); + $result['ticket_replies'] = Gui_Elements::make_table($entire_ticket['reply_array'], Array("getTReplyId","getContent()->getContent","getTimestamp","getAuthor()->getExternId","getAuthor()->getPermission","getHidden"), Array("tReplyId","replyContent","timestamp","authorExtern","permission","hidden")); + $i = 0; + foreach( $result['ticket_replies'] as $reply){ + $webReplyUser = new WebUsers($reply['authorExtern']); + $result['ticket_replies'][$i]['author'] = $webReplyUser->getUsername(); + $i++; + } + if(Ticket_User::isMod(unserialize($_SESSION['ticket_user']))){ + $result['isMod'] = "TRUE"; + $result['statusList'] = Ticket::getStatusArray(); + $result['sGroups'] = Gui_Elements::make_table_with_key_is_id(Support_Group::getAllSupportGroups(), Array("getName"), "getSGroupId" ); + } + $result['hasInfo'] = $target_ticket->hasInfo(); + return $result; + + }else{ + //ERROR: No access! + $_SESSION['error_code'] = "403"; + header("Location: index.php?page=error"); + exit; + } + }else{ + //ERROR: not logged in! + header("Location: index.php"); + exit; + } +} \ No newline at end of file diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/show_user.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/show_user.php new file mode 100644 index 000000000..3a4b2acee --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/show_user.php @@ -0,0 +1,44 @@ +getUsername(); + $result['mail'] = $webUser->getEmail(); + //$info = $webUser->getInfo(); + //$result['firstName'] = $info['FirstName']; + //$result['lastName'] = $info['LastName']; + //$result['country'] = $info['Country']; + //$result['gender'] = $info['Gender']; + + $ticket_user = Ticket_User::constr_ExternId($result['target_id']); + $result['userPermission'] = $ticket_user->getPermission(); + if(Ticket_User::isAdmin(unserialize($_SESSION['ticket_user']))){ + $result['isAdmin'] = "TRUE"; + } + $ticketlist = Ticket::getTicketsOf($ticket_user->getTUserId()); + + $result['ticketlist'] = Gui_Elements::make_table($ticketlist, Array("getTId","getTimestamp","getTitle","getStatus","getStatusText","getStatusText","getCategoryName"), Array("tId","timestamp","title","status","statustext","statusText","category")); + return $result; + + }else{ + //ERROR: No access! + $_SESSION['error_code'] = "403"; + header("Location: index.php?page=error"); + exit; + } + }else{ + //ERROR: not logged in! + header("Location: index.php"); + exit; + } +} \ No newline at end of file diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module index e935828fd..dbba16531 100644 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module @@ -248,6 +248,8 @@ function ryzommanage_block_view($delta = '') } + + function _ams_handler() { @@ -547,7 +549,20 @@ function top_bar() $userId = $user->uid; if (user_is_logged_in()) { // Logged in user - return "
    Notepad | Mail | Wiki | Profile | Craft Recipe-Book | Occupations | News/Events | Account | Logout
    "; + //check permission, if user + if(ticket_user::isMod(unserialize($_SESSION['ticket_user']))){ + return ""; + + }else{ + return ""; + } + } else { return drupal_get_form('login_form'); // Not logged in @@ -1001,4 +1016,60 @@ function ryzommanage_help($path, $arg) { return '

    ' . t("A module that handles account registration and a ticketing service regarding ryzomcore.") . '

    '; break; } -} \ No newline at end of file +} + +function ryzommanage_enable() { + // Check if our field is not already created. + if (!field_info_field('firstname')) { + $field = array( + 'field_name' => 'firstname', + 'type' => 'text', + ); + field_create_field($field); + + // Create the instance on the bundle. + $instance = array( + 'field_name' => 'firstname', + 'entity_type' => 'user', + 'label' => 'First Name', + 'bundle' => 'user', + // If you don't set the "required" property then the field wont be required by default. + 'required' => FALSE, + 'settings' => array( + // Here you inform either or not you want this field showing up on the registration form. + + ), + 'widget' => array( + 'type' => 'textfield', + 'weight' => '1', + ), + ); + field_create_instance($instance); + } + + if (!field_info_field('secondname')) { + $field = array( + 'field_name' => 'secondname', + 'type' => 'text', + ); + field_create_field($field); + + // Create the instance on the bundle. + $instance = array( + 'field_name' => 'secondname', + 'entity_type' => 'user', + 'label' => 'Second Name', + 'bundle' => 'user', + // If you don't set the "required" property then the field wont be required by default. + 'required' => FALSE, + 'settings' => array( + // Here you inform either or not you want this field showing up on the registration form. + ), + 'widget' => array( + 'type' => 'textfield', + 'weight' => '1', + ), + ); + field_create_instance($instance); + } +} \ No newline at end of file diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/createticket.tpl b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/createticket.tpl new file mode 100644 index 000000000..c1ca81e07 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/createticket.tpl @@ -0,0 +1,38 @@ +{block name=content} + +

    Create a new Ticket

    +
    + + + + + + + + + + + + + +
    + + +
    + + +
    + + +
    + + + +
    +
    +{/block} + diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/dashboard.tpl b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/dashboard.tpl index ea53b1665..83def588a 100644 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/dashboard.tpl +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/dashboard.tpl @@ -5,27 +5,27 @@
    + href="ams?page=show_queue&get=create&userid={$user_id}&groupid=1&what=waiting_for_support&how=assigned&who=user">
    Tickets Waiting for Direct Action: {$nrAssignedWaiting}
    - +
    Tickets Todo: {$nrToDo}
    - +
    Newest Ticket: {$newestTicketTitle}
    - +
    Total amount of Tickets: {$nrTotalTickets}
    diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/show_ticket.tpl b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/show_ticket.tpl new file mode 100644 index 000000000..be00cfa92 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/show_ticket.tpl @@ -0,0 +1,188 @@ +{block name=content} +

    Title: {$ticket_title} [ID#{$ticket_tId}]

    + + + + {if isset($isMod) and $isMod eq "TRUE"} + + + {/if} + {if isset($isMod) and $isMod eq "TRUE"}{/if} + + + {if $hasInfo}{/if} + + +
    + Ticket Assigning: + {if $ticket_assignedTo eq 0} +
    + + + +
    + {else if $ticket_assignedTo eq $user_id} +
    + + + +
    + {/if} +
    + Forward to Group: +
    + + + + +
    +
    Show Ticket LogSend Other TicketShow ticket Info
    + + + + {if isset($ACTION_RESULT) and $ACTION_RESULT eq "SUCCESS_ASSIGNED"} + +

    {$success_assigned}

    +
    + {else if isset($ACTION_RESULT) and $ACTION_RESULT eq "SUCCESS_UNASSIGNED"} + +

    {$success_unassigned}

    +
    + {else if isset($ACTION_RESULT) and $ACTION_RESULT eq "TICKET_NOT_EXISTING"} + +

    {$ticket_not_existing}

    +
    + {else if isset($ACTION_RESULT) and $ACTION_RESULT eq "ALREADY_ASSIGNED"} + +

    {$ticket_already_assigned}

    +
    + {else if isset($ACTION_RESULT) and $ACTION_RESULT eq "NOT_ASSIGNED"} + +

    {$ticket_not_assigned}

    +
    + {/if} + + {if isset($ACTION_RESULT) and $ACTION_RESULT eq "INVALID_SGROUP"} + +

    {$invalid_sgroup}

    +
    + {else if isset($ACTION_RESULT) and $ACTION_RESULT eq "TICKET_NOT_EXISTING"} + +

    {$ticket_not_existing}

    +
    + {else if isset($ACTION_RESULT) and $ACTION_RESULT eq "SUCCESS_FORWARDED"} + +

    {$success_forwarded}

    +
    + {/if} + +
    + + + + + + + + + + + + + + + + +
    Original Submitted: {$ticket_timestamp}Last Updated: {$ticket_lastupdate}Status: {if $ticket_status neq 3}Open{/if} {$ticket_statustext}
    Category: {$ticket_category}Priority: {$ticket_prioritytext}Support Group: + + {if $ticket_forwardedGroupName eq "0"} + {$public_sgroup} + {else} + {$ticket_forwardedGroupName} + {/if} + +
    Assigned To: {if $ticket_assignedTo neq ""} {$ticket_assignedToText} {else} {$not_assigned} {/if}
    + + + + + {foreach from=$ticket_replies item=reply} + + + + {/foreach} + + {if $ticket_status eq 3} + + + + {/if} + + + + + + + + + + + +
    +

    + {$reply.timestamp} + {if $reply.permission eq '1'} + {if isset($isMod) and $isMod eq "TRUE"} {$reply.author}{else} {$reply.author} {/if} + {else if $reply.permission gt '1'} + {if isset($isMod) and $isMod eq "TRUE"} {$reply.author}{else} {$reply.author} {/if} + {/if} +

    +

    {if $reply.hidden eq 1}{/if}{$reply.replyContent}{if $reply.hidden eq 1}{/if}

    +
    +

    Ticket is closed.

    +
    + + {if $ticket_status neq 3} + {$t_reply}: + + + {if isset($isMod) and $isMod eq "TRUE"} + + Hide reply for user. + {/if} + {/if} +
    + {if isset($isMod) and $isMod eq "TRUE"} +
    + + +
    +
    + + +
    + {/if} +
    + + + + +
    + + + +{/block} + diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/show_user.tpl b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/show_user.tpl index 91bdc683f..3c441cb51 100644 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/show_user.tpl +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/show_user.tpl @@ -1,17 +1,8 @@ {block name=content} -
    -
    -
    +

    Profile of {$target_name}

    -
    - - -
    -
    -
    -
    - Info - + +
    @@ -21,96 +12,60 @@ - {if $firstName neq ""} - - - - - {/if} - {if $lastName neq ""} - - - - - {/if} - {if $country neq ""} - - - - - {/if} - {if $gender neq 0} - - - {if $gender eq 1} - - {else if $gender eq 2} - - {/if} - - {/if} +
    Email:
    Role: - {if $userPermission eq 1}User{/if} - {if $userPermission eq 2}Moderator{/if} - {if $userPermission eq 3}Admin{/if} + {if $userPermission eq 1}User{/if} + {if $userPermission eq 2}Moderator{/if} + {if $userPermission eq 3}Admin{/if}
    Firstname:{$firstName}
    LastName:{$lastName}
    Country:{$country}
    Gender:♂♀
    -
    -
    -
    + -
    -
    +

    Actions

    -
    - - -
    -
    -
    -
    -
    - -
    -
    -
    + Tickets - +
    @@ -124,7 +79,7 @@ {foreach from=$ticketlist item=ticket} - + @@ -134,9 +89,7 @@
    ID
    {$ticket.tId}{$ticket.title}{$ticket.title} {$ticket.timestamp} {$ticket.category}
    -
    -
    -
    -
    + + {/block} diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/todo.txt b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/todo.txt index 539cf76cd..241f7c3f2 100644 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/todo.txt +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/todo.txt @@ -1,2 +1,3 @@ -Remove full path in autoload functions --Make Permission www dependend, so it can be implemented in drupal with hook_permission(); \ No newline at end of file +-Make Permission www dependend, so it can be implemented in drupal with hook_permission(); +-in helpers make_folders mkdir($value); should be drupal_mkdir(); \ No newline at end of file From 8af7f0231138211331d8d2454f8f33c6fb77ee3f Mon Sep 17 00:00:00 2001 From: Quitta Date: Thu, 5 Sep 2013 03:29:34 +0200 Subject: [PATCH 143/313] show ticket log works + fixed timestamp issue --- .../drupal_module/ryzommanage/config.php | 2 +- .../ryzommanage/func/create_ticket.php | 2 +- .../ryzommanage/inc/show_ticket_log.php | 56 +++++++++++++++++++ .../ryzommanage/ryzommanage.module | 3 + .../ryzommanage/templates/show_ticket.tpl | 2 +- .../ryzommanage/templates/show_ticket_log.tpl | 26 +++++++++ .../ryzommanage/templates/show_user.tpl | 2 +- 7 files changed, 89 insertions(+), 4 deletions(-) create mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/show_ticket_log.php create mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/show_ticket_log.tpl diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/config.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/config.php index 0037eef30..74651b7fc 100644 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/config.php +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/config.php @@ -49,7 +49,7 @@ $cfg['mail']['host'] = "ryzomcore.com"; //Defines mailing related stuff $SUPPORT_GROUP_IMAP_CRYPTKEY = "azerty"; -$TICKET_MAILING_SUPPORT = true; +$TICKET_MAILING_SUPPORT = false; //You have to create this dir at first! $MAIL_DIR = "/tmp/mail"; diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/create_ticket.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/create_ticket.php index 764bd6931..f50dbdd6a 100644 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/create_ticket.php +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/create_ticket.php @@ -19,7 +19,7 @@ function create_ticket(){ $author= Ticket_User::constr_ExternId($_POST['target_id'])->getTUserId(); } $ticket_id = Ticket::create_Ticket($title, $content, $category, $author, unserialize($_SESSION['ticket_user'])->getTUserId(),0, $_POST); - header("Location: index.php?page=show_ticket&id=".$ticket_id); + header("Location: ams?page=show_ticket&id=".$ticket_id); exit; }catch (PDOException $e) { diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/show_ticket_log.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/show_ticket_log.php new file mode 100644 index 000000000..1974cbc66 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/show_ticket_log.php @@ -0,0 +1,56 @@ +load_With_TId($result['ticket_id']); + $result['ticket_title'] = $target_ticket->getTitle(); + $ticket_logs = Ticket_Log::getLogsOfTicket( $result['ticket_id']); + $log_action_array = Ticket_Log::getActionTextArray(); + $result['ticket_logs'] = Gui_Elements::make_table($ticket_logs, Array("getTLogId","getTimestamp","getAuthor()->getExternId","getAction","getArgument()"), Array("tLogId","timestamp","authorExtern","action","argument")); + $i = 0; + foreach( $result['ticket_logs'] as $log){ + $webUser = new WebUsers($log['authorExtern']); + $author = $webUser->getUsername(); + $result['ticket_logs'][$i]['author'] = $author; + $query_backpart = ""; + if($log['action'] == 2){ + $webUser2 = new WebUsers($log['argument']); + $query_backpart = $webUser2->getUsername(); + }else if($log['action'] == 4){ + $query_backpart = "ID#" . $log['argument'] . ""; + }else if($log['action'] == 5){ + $statusArray = Ticket::getStatusArray(); + $query_backpart = $statusArray[$log['argument'] ]; + }else if($log['action'] == 6){ + $priorityArray = Ticket::getPriorityArray(); + $query_backpart = $priorityArray[$log['argument'] ]; + }else if($log['action'] == 8){ + $query_backpart = "" . Support_Group::getGroup($log['argument'])->getName() . ""; + } + $result['ticket_logs'][$i]['query'] = $author . " " . $log_action_array[$log['action']] . " " . $query_backpart; + $result['ticket_logs'][$i]['timestamp_elapsed'] = Gui_Elements::time_elapsed_string($log['timestamp']); + $i++; + } + if(Ticket_User::isMod(unserialize($_SESSION['ticket_user']))){ + $result['isMod'] = "TRUE"; + } + return $result; + + }else{ + //ERROR: No access! + $_SESSION['error_code'] = "403"; + header("Location: index.php?page=error"); + exit; + } + }else{ + //ERROR: not logged in! + header("Location: index.php"); + exit; + } +} \ No newline at end of file diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module index dbba16531..be5bcbe8c 100644 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module @@ -19,6 +19,9 @@ global $SITEBASE; global $AMS_TRANS; global $DEFAULT_LANGUAGE; global $cfg; +global $TICKET_LOGGING; +global $TIME_FORMAT; +global $TICKET_MAILING_SUPPORT; require 'ams_lib/libinclude.php'; spl_autoload_register('__autoload'); diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/show_ticket.tpl b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/show_ticket.tpl index be00cfa92..9b83aab09 100644 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/show_ticket.tpl +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/show_ticket.tpl @@ -86,7 +86,7 @@
    Original Submitted: {$ticket_timestamp} Last Updated: {$ticket_lastupdate}Status: {if $ticket_status neq 3}Open{/if} {$ticket_statustext}Status: {if $ticket_status neq 3}Open{/if} {$ticket_statustext}
    Category: {$ticket_category}
    + + + + + + + + + {foreach from=$ticket_logs item=log} + + + + + + {/foreach} + + +
    IDTimestampQuery
    {$log.tLogId}{$log.timestamp}{$log.query}
    + +{/block} + \ No newline at end of file diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/show_user.tpl b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/show_user.tpl index 3c441cb51..0344c11b5 100644 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/show_user.tpl +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/show_user.tpl @@ -83,7 +83,7 @@ {$ticket.timestamp} {$ticket.category} - {if $ticket.status eq 0} {/if} {$ticket.statusText} + {$ticket.statusText} {/foreach} From 8f2dd7f2fdf6cb3f3e1db3e222524f0a3353aab7 Mon Sep 17 00:00:00 2001 From: Quitta Date: Thu, 5 Sep 2013 04:16:28 +0200 Subject: [PATCH 144/313] userlist works, show reply works, also possible to change the permissions of the users (though relying on the libs permission system atm..) --- .../ryzommanage/autoload/webusers.php | 2 +- .../drupal_module/ryzommanage/config.php | 6 +- .../ryzommanage/inc/change_permission.php | 40 ++++++++++++ .../ryzommanage/inc/show_reply.php | 46 ++++++++++++++ .../ryzommanage/inc/userlist.php | 28 +++++++++ .../ryzommanage/ryzommanage.module | 2 +- .../ryzommanage/templates/show_reply.tpl | 24 +++++++ .../ryzommanage/templates/show_ticket.tpl | 8 +-- .../ryzommanage/templates/show_user.tpl | 2 - .../ryzommanage/templates/userlist.tpl | 62 +++++++++++++++++++ 10 files changed, 209 insertions(+), 11 deletions(-) create mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/change_permission.php create mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/show_reply.php create mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/userlist.php create mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/show_reply.tpl create mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/userlist.tpl diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/autoload/webusers.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/autoload/webusers.php index 2beb75c1b..73d0b79a0 100644 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/autoload/webusers.php +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/autoload/webusers.php @@ -207,7 +207,7 @@ class WebUsers extends Users{ } public static function getAllUsersQuery(){ - return "SELECT * FROM ams_user"; + return "SELECT * FROM users WHERE `uid` > 0"; } public static function createWebuser($name, $pass, $mail){ diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/config.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/config.php index 74651b7fc..739ed8d02 100644 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/config.php +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/config.php @@ -8,9 +8,9 @@ $cfg['db']['web']['host'] = variable_get('ryzommanage_webserverurl', 'localhost'); $cfg['db']['web']['port'] = variable_get('ryzommanage_webmysqlport', '3306'); -$cfg['db']['web']['name'] = variable_get('ryzommanage_webdbname', 'ryzom_ams'); -$cfg['db']['web']['user'] = variable_get('ryzommanage_webusername', 'shard'); -$cfg['db']['web']['pass'] = variable_get('ryzommanage_webpassword', ''); +$cfg['db']['web']['name'] = variable_get('ryzommanage_webdbname', 'drupal'); +$cfg['db']['web']['user'] = variable_get('ryzommanage_webusername', 'root'); +$cfg['db']['web']['pass'] = variable_get('ryzommanage_webpassword', 'lol123'); $cfg['db']['lib']['host'] = variable_get('ryzommanage_libserverurl', 'localhost'); $cfg['db']['lib']['port'] = variable_get('ryzommanage_libmysqlport', '3306'); diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/change_permission.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/change_permission.php new file mode 100644 index 000000000..3830b718e --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/change_permission.php @@ -0,0 +1,40 @@ +getTUserId(), $value); + header("Location: ams?page=show_user&id=".$user_id); + exit; + + + }else{ + //ERROR: GET PARAMS not given or trying to change admin + header("Location: ams?page=show_user&id=".$user_id); + exit; + } + + }else{ + //ERROR: No access! + $_SESSION['error_code'] = "403"; + header("Location: index.php?page=error"); + exit; + + } + + }else{ + //ERROR: not logged in! + header("Location: index.php"); + exit; + } + + +} \ No newline at end of file diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/show_reply.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/show_reply.php new file mode 100644 index 000000000..94090fa88 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/show_reply.php @@ -0,0 +1,46 @@ +load_With_TReplyId($result['reply_id']); + + + $ticket = new Ticket(); + $ticket->load_With_TId($reply->getTicket()); + + if(( $ticket->getAuthor() == unserialize($_SESSION['ticket_user'])->getTUserId() && ! $reply->getHidden()) || Ticket_User::isMod(unserialize($_SESSION['ticket_user']) )){ + $content = new Ticket_Content(); + $content->load_With_TContentId($reply->getContent()); + + $author = new Ticket_User(); + $author->load_With_TUserId($reply->getAuthor()); + + $result['hidden'] = $reply->getHidden(); + $result['ticket_id'] = $reply->getTicket(); + $result['reply_timestamp'] = $reply->getTimestamp(); + $result['author_permission'] = $author->getPermission(); + $result['reply_content'] = $content->getContent(); + $result['author'] = $author->getExternId(); + $webUser = new WebUsers($author->getExternId()); + $result['authorName'] = $webUser->getUsername(); + if(Ticket_User::isMod(unserialize($_SESSION['ticket_user']))){ + $result['isMod'] = "TRUE"; + } + return $result; + + }else{ + //ERROR: No access! + $_SESSION['error_code'] = "403"; + header("Location: index.php?page=error"); + exit; + } + }else{ + //ERROR: not logged in! + header("Location: index.php"); + exit; + } +} \ No newline at end of file diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/userlist.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/userlist.php new file mode 100644 index 000000000..d8783ce32 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/userlist.php @@ -0,0 +1,28 @@ +getElements() , Array("getUId","getUsername","getEmail"), Array("id","username","email")); + $pageResult['links'] = $pagination->getLinks(5); + $pageResult['lastPage'] = $pagination->getLast(); + $pageResult['currentPage'] = $pagination->getCurrent(); + + $i = 0; + foreach( $pageResult['userlist'] as $user ){ + $pageResult['userlist'][$i]['permission'] = Ticket_User::constr_ExternId($pageResult['userlist'][$i]['id'])->getPermission(); + $i++; + } + + if (Ticket_User::isAdmin(unserialize($_SESSION['ticket_user']))){ + $pageResult['isAdmin'] = "TRUE"; + } + return $pageResult; + }else{ + //ERROR: No access! + $_SESSION['error_code'] = "403"; + header("Location: index.php?page=error"); + exit; + } +} \ No newline at end of file diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module index be5bcbe8c..20d3e1569 100644 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module @@ -556,7 +556,7 @@ function top_bar() if(ticket_user::isMod(unserialize($_SESSION['ticket_user']))){ return ""; diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/show_reply.tpl b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/show_reply.tpl new file mode 100644 index 000000000..287d59f8b --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/show_reply.tpl @@ -0,0 +1,24 @@ +{block name=content} +

    Show Reply

    + +

    Reply ID#{$reply_id} of Ticket #{$ticket_id}

    + + + + +
    +

    {$reply_timestamp} + {if $author_permission eq '1'} + {if isset($isMod) and $isMod eq "TRUE"} {$authorName}{else} {$authorName} {/if}

    + {else if $author_permission gt '1'} + {if isset($isMod) and $isMod eq "TRUE"} {$authorName}{else} {$authorName} {/if}

    + {/if} +

    {if $hidden eq 1}{/if}{$reply_content}{if $hidden eq 1}{/if}

    + +
    +
    + + + +{/block} + diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/show_ticket.tpl b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/show_ticket.tpl index 9b83aab09..c9d8c854b 100644 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/show_ticket.tpl +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/show_ticket.tpl @@ -96,13 +96,13 @@ {if $ticket_forwardedGroupName eq "0"} {$public_sgroup} {else} - {$ticket_forwardedGroupName} + {$ticket_forwardedGroupName} {/if} - Assigned To: {if $ticket_assignedTo neq ""} {$ticket_assignedToText} {else} {$not_assigned} {/if} + Assigned To: {if $ticket_assignedTo neq ""} {$ticket_assignedToText} {else} {$not_assigned} {/if} @@ -117,9 +117,9 @@

    {$reply.timestamp} {if $reply.permission eq '1'} - {if isset($isMod) and $isMod eq "TRUE"} {$reply.author}{else} {$reply.author} {/if} + {if isset($isMod) and $isMod eq "TRUE"} {$reply.author}{else} {$reply.author} {/if} {else if $reply.permission gt '1'} - {if isset($isMod) and $isMod eq "TRUE"} {$reply.author}{else} {$reply.author} {/if} + {if isset($isMod) and $isMod eq "TRUE"} {$reply.author}{else} {$reply.author} {/if} {/if}

    {if $reply.hidden eq 1}{/if}{$reply.replyContent}{if $reply.hidden eq 1}{/if}

    diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/show_user.tpl b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/show_user.tpl index 0344c11b5..a7b5a95b1 100644 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/show_user.tpl +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/show_user.tpl @@ -63,8 +63,6 @@

    Tickets of {$target_name}

    - - Tickets diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/userlist.tpl b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/userlist.tpl new file mode 100644 index 000000000..bf39cc6ae --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/userlist.tpl @@ -0,0 +1,62 @@ +{block name=content} + +
    + + + + + + + + + + + {foreach from=$userlist item=element} + + + + + {if $element.permission eq 1}{/if} + {if $element.permission eq 2}{/if} + {if $element.permission eq 3}{/if} + + + {/foreach} + + +
    IdUsernameEmailPermissionAction
    {$element.id}{$element.username}{$element.email}UserModeratorAdmin + + + + {if isset($isAdmin) and $isAdmin eq 'TRUE' and $element.id neq 1} + + {if $element.permission eq 1} + + + {else if $element.permission eq 2 } + + + {else if $element.permission eq 3 } + + + {/if} + {/if} + +
    + Show User + + Edit User + Make ModeratorMake AdminDemote to UserMake AdminDemote to UserDemote to Moderator
    +
    +
    + + « | + {foreach from=$links item=link} + {if $link == $currentPage}{/if}{$link}{if $link == $currentPage}{/if} | + {/foreach} + » + +
    + +{/block} + From dd1454804838c0d45c41625e7e877b3f33ec213d Mon Sep 17 00:00:00 2001 From: Quitta Date: Thu, 5 Sep 2013 04:29:47 +0200 Subject: [PATCH 145/313] some fixes for the real version --- .../tools/server/ryzom_ams/ams_lib/autoload/helpers.php | 2 +- .../server/ryzom_ams/ams_lib/autoload/mail_handler.php | 6 +++--- .../tools/server/ryzom_ams/ams_lib/autoload/ticket.php | 2 +- .../tools/server/ryzom_ams/ams_lib/autoload/ticket_log.php | 1 - 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/helpers.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/helpers.php index cef808a92..18f34b1d1 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/helpers.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/helpers.php @@ -81,7 +81,7 @@ class Helpers{ if ( !file_exists( $value ) ){ //echo $value; print($value); - drupal_mkdir($value); + mkdir($value); } } diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php index 3a17412e4..4434c62db 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php @@ -13,11 +13,11 @@ class Mail_Handler{ public static function send_ticketing_mail($receiver, $ticketObj, $content, $type, $sender = 0) { - global $MAIL_LOG_PATH; - error_log("Receiver: {$receiver}, content: {$content}, type: {$type}, SendingId: {$sender} \n", 3, $MAIL_LOG_PATH); + global $TICKET_MAILING_SUPPORT; if($TICKET_MAILING_SUPPORT){ - + global $MAIL_LOG_PATH; + error_log("Receiver: {$receiver}, content: {$content}, type: {$type}, SendingId: {$sender} \n", 3, $MAIL_LOG_PATH); if($sender == 0){ //if it is not forwarded (==public == which returns 0) then make it NULL which is needed to be placed in the DB. $sender = NULL; diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket.php index e263ff700..84cda6f0a 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket.php @@ -87,7 +87,7 @@ class Ticket{ //create the new ticket! $ticket = new Ticket(); - $values = array("Title" => $title, "Status"=> 1, "Queue"=> 0, "Ticket_Category" => $category, "Author" => $author, "Priority" => 0); + $values = array("Title" => $title, "Timestamp"=>0, "Status"=> 1, "Queue"=> 0, "Ticket_Category" => $category, "Author" => $author, "Priority" => 0); $ticket->set($values); $ticket->create(); $ticket_id = $ticket->getTId(); diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_log.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_log.php index d011049d7..ee6f6b9ef 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_log.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_log.php @@ -42,7 +42,6 @@ class Ticket_Log{ $instanceLog->setAuthor($instanceAuthor); $instanceLog->setTicket($ticket_id); $instanceLog->setQuery($log['Query']); - $result[] = $instanceLog; } return $result; From d951e1c45213d7378c67a6f0751dd4949a920e55 Mon Sep 17 00:00:00 2001 From: StudioEtrange Date: Thu, 5 Sep 2013 17:18:01 +0200 Subject: [PATCH 146/313] ** PCH Support for NMake with VS2012 NMAKE-VS2012 Error LNK2011 while NMAKE-VS2010 does not complain we need to link the pch.obj file see http://msdn.microsoft.com/en-us/library/3ay26wa2(v=vs.110).aspx ** PCH Support for Ninja Ninja need to add property OBJECT_DEPENDS for using PCH OBJECT_OUTPUTS for create PCH see http://public.kitware.com/pipermail/cmake-developers/2012-March/003653.html --- code/CMakeModules/PCHSupport.cmake | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/code/CMakeModules/PCHSupport.cmake b/code/CMakeModules/PCHSupport.cmake index bcca9d877..da26978c5 100644 --- a/code/CMakeModules/PCHSupport.cmake +++ b/code/CMakeModules/PCHSupport.cmake @@ -209,6 +209,9 @@ MACRO(PCH_SET_COMPILE_COMMAND _inputcpp _compile_FLAGS) IF(MSVC) GET_PDB_FILENAME(PDB_FILE ${_PCH_current_target}) SET(PCH_COMMAND ${CMAKE_CXX_COMPILER} ${pchsupport_compiler_cxx_arg1} ${_compile_FLAGS} /Yc /Fp"${PCH_OUTPUT}" ${_inputcpp} /Fd"${PDB_FILE}" /c /Fo"${PCH_OUTPUT}.obj") + # Ninja PCH Support + # http://public.kitware.com/pipermail/cmake-developers/2012-March/003653.html + SET_SOURCE_FILES_PROPERTIES(${_inputcpp} PROPERTIES OBJECT_OUTPUTS "${PCH_OUTPUT}.obj") ELSE(MSVC) SET(HEADER_FORMAT "c++-header") SET(_FLAGS "") @@ -268,6 +271,18 @@ MACRO(ADD_PRECOMPILED_HEADER_TO_TARGET _targetName) IF(MSVC) SET(_target_cflags "${oldProps} /Yu\"${PCH_INPUT}\" /FI\"${PCH_INPUT}\" /Fp\"${PCH_OUTPUT}\"") + # Ninja PCH Support + # http://public.kitware.com/pipermail/cmake-developers/2012-March/003653.html + SET_TARGET_PROPERTIES(${_targetName} PROPERTIES OBJECT_DEPENDS "${PCH_OUTPUT}") + + # NMAKE-VS2012 Error LNK2011 (NMAKE-VS2010 do not complain) + # we need to link the pch.obj file, see http://msdn.microsoft.com/en-us/library/3ay26wa2(v=vs.110).aspx + GET_TARGET_PROPERTY(DEPS ${_targetName} LINK_LIBRARIES) + if (NOT DEPS) + set (DEPS) + endif () + list (INSERT DEPS 0 "${PCH_OUTPUT}.obj") + SET_TARGET_PROPERTIES(${_targetName} PROPERTIES LINK_LIBRARIES "${DEPS}") ELSE(MSVC) # for use with distcc and gcc >4.0.1 if preprocessed files are accessible # on all remote machines set From 4691242d4d7471ef133ea767b14f969aa89266c9 Mon Sep 17 00:00:00 2001 From: Quitta Date: Thu, 5 Sep 2013 21:33:11 +0200 Subject: [PATCH 147/313] added support group functionality to the drupal module --- .../ryzommanage/func/add_sgroup.php | 40 ++++++ .../ryzommanage/func/add_user_to_sgroup.php | 43 +++++++ .../func/modify_email_of_sgroup.php | 54 ++++++++ .../ryzommanage/inc/sgroup_list.php | 31 +++++ .../ryzommanage/inc/show_sgroup.php | 58 +++++++++ .../ryzommanage/templates/sgroup_list.tpl | 105 ++++++++++++++++ .../ryzommanage/templates/show_sgroup.tpl | 118 ++++++++++++++++++ .../drupal_module/ryzommanage/todo.txt | 4 +- 8 files changed, 452 insertions(+), 1 deletion(-) create mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/add_sgroup.php create mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/add_user_to_sgroup.php create mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/modify_email_of_sgroup.php create mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/sgroup_list.php create mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/show_sgroup.php create mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/sgroup_list.tpl create mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/show_sgroup.tpl diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/add_sgroup.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/add_sgroup.php new file mode 100644 index 000000000..e2ef663f1 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/add_sgroup.php @@ -0,0 +1,40 @@ +getPermission(); + $result['no_visible_elements'] = 'FALSE'; + $result['username'] = $_SESSION['user']; + //global $SITEBASE; + //require($SITEBASE . '/inc/sgroup_list.php'); + //$result= array_merge($result, sgroup_list()); + //return helpers :: loadtemplate( 'sgroup_list', $result, true); + header("Location: ams?page=sgroup_list"); + exit; + + }else{ + //ERROR: No access! + $_SESSION['error_code'] = "403"; + header("Location: index.php?page=error"); + exit; + } + }else{ + //ERROR: not logged in! + header("Location: index.php"); + exit; + } + +} \ No newline at end of file diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/add_user_to_sgroup.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/add_user_to_sgroup.php new file mode 100644 index 000000000..924014f79 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/add_user_to_sgroup.php @@ -0,0 +1,43 @@ +getPermission()>1){ + $result['RESULT_OF_ADDING'] = Support_Group::addUserToSupportGroup($user_id, $id); + }else{ + $result['RESULT_OF_ADDING'] = "NOT_MOD_OR_ADMIN"; + } + + }else{ + $result['RESULT_OF_ADDING'] = "USER_NOT_EXISTING"; + } + $result['permission'] = unserialize($_SESSION['ticket_user'])->getPermission(); + $result['no_visible_elements'] = 'FALSE'; + $result['username'] = $_SESSION['user']; + //global $SITEBASE; + //require_once($SITEBASE . 'inc/show_sgroup.php'); + //$result= array_merge($result, show_sgroup()); + //helpers :: loadtemplate( 'show_sgroup', $result); + header("Location: ams?page=show_sgroup&id=".$id); + exit; + + }else{ + //ERROR: No access! + $_SESSION['error_code'] = "403"; + header("Location: index.php?page=error"); + exit; + } + }else{ + //ERROR: not logged in! + header("Location: index.php"); + exit; + } + +} \ No newline at end of file diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/modify_email_of_sgroup.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/modify_email_of_sgroup.php new file mode 100644 index 000000000..ade3c3af2 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/modify_email_of_sgroup.php @@ -0,0 +1,54 @@ +setGroupEmail($groupemail); + $group->setIMAP_MailServer(filter_var($_POST['IMAP_MailServer'],FILTER_SANITIZE_STRING)); + $group->setIMAP_Username(filter_var($_POST['IMAP_Username'],FILTER_SANITIZE_STRING)); + + //encrypt password! + global $cfg; + $crypter = new MyCrypt($cfg['crypt']); + $enc_password = $crypter->encrypt($password); + $group->setIMAP_Password($enc_password); + $group->update(); + $result['RESULT_OF_MODIFYING'] = "SUCCESS"; + if($password == ""){ + $result['RESULT_OF_MODIFYING'] = "NO_PASSWORD"; + } + }else{ + $result['RESULT_OF_MODIFYING'] = "EMAIL_NOT_VALID"; + } + + $result['permission'] = unserialize($_SESSION['ticket_user'])->getPermission(); + $result['no_visible_elements'] = 'FALSE'; + $result['username'] = $_SESSION['user']; + //global $SITEBASE; + //require_once($SITEBASE . 'inc/show_sgroup.php'); + //$result= array_merge($result, show_sgroup()); + //helpers :: loadtemplate( 'show_sgroup', $result); + header("Location: ams?page=show_sgroup&id=".$sgroupid); + exit; + + }else{ + //ERROR: No access! + $_SESSION['error_code'] = "403"; + header("Location: index.php?page=error"); + exit; + } + }else{ + //ERROR: not logged in! + header("Location: index.php"); + exit; + } + +} \ No newline at end of file diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/sgroup_list.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/sgroup_list.php new file mode 100644 index 000000000..546395ed8 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/sgroup_list.php @@ -0,0 +1,31 @@ +getName(); + $result['groupemail'] = $group->getGroupEmail(); + $result['imap_mailserver'] = $group->getIMAP_MailServer(); + $result['imap_username'] = $group->getIMAP_Username(); + $result['userlist'] = Gui_Elements::make_table(Support_Group::getAllUsersOfSupportGroup($result['target_id']), Array("getTUserId","getPermission","getExternId"), Array("tUserId","permission","externId")); + $i = 0; + foreach( $result['userlist'] as $user){ + $webuser = new Webusers($user['externId']); + $result['userlist'][$i]['name'] = $webuser->getUsername(); + $i++; + } + return $result; + + + }else{ + + //ERROR: No page specified! + $_SESSION['error_code'] = "404"; + header("Location: ams?page=error"); + exit; + } + + }else{ + //ERROR: No access! + $_SESSION['error_code'] = "403"; + header("Location: index.php?page=error"); + exit; + } + }else{ + //ERROR: not logged in! + header("Location: index.php"); + exit; + } + +} \ No newline at end of file diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/sgroup_list.tpl b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/sgroup_list.tpl new file mode 100644 index 000000000..231250fda --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/sgroup_list.tpl @@ -0,0 +1,105 @@ +{block name=content} + +

    List of all Support Groups

    + + + + + + + + + {if isset($isAdmin) && $isAdmin eq 'TRUE'}{/if} + + + + {foreach from=$grouplist item=group} + + + + + + {if isset($isAdmin) && $isAdmin eq 'TRUE'}{/if} + + {/foreach} + + +
    IDNameTagEmailAction
    {$group.sGroupId}{$group.name}{$group.tag}{$group.groupemail}Delete
    + + {if isset($isAdmin) && $isAdmin eq 'TRUE'} + +

    Add a support group

    + +
    + + + + + + + + + + +
    + + + + + + +
    + + + + + + + + +
    +
    + + + + + + +
    + + + + + + + + +
    +
    + + +
    + + +
    + {if isset($RESULT_OF_ADDING) and $RESULT_OF_ADDING eq "SUCCESS"} + +

    {$group_success}

    +
    + {else if isset($RESULT_OF_ADDING) and $RESULT_OF_ADDING eq "NAME_TAKEN"} + +

    {$group_name_taken}

    +
    + {else if isset($RESULT_OF_ADDING) and $RESULT_OF_ADDING eq "TAG_TAKEN"} + +

    {$group_tag_taken}

    +
    + {else if isset($RESULT_OF_ADDING) and $RESULT_OF_ADDING eq "SIZE_ERROR"} + +

    {$group_size_error}

    +
    + {/if} + + {/if} +{/block} + diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/show_sgroup.tpl b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/show_sgroup.tpl new file mode 100644 index 000000000..0f1d55d17 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/show_sgroup.tpl @@ -0,0 +1,118 @@ +{block name=content} + +

    {$groupsname} Members List

    + + + + + + {if isset($isAdmin) && $isAdmin eq 'TRUE'}{/if} + + + + + {foreach from=$userlist item=user} + + + + {if isset($isAdmin) && $isAdmin eq 'TRUE'}{/if} + + {/foreach} + + +
    IDNameAction
    {$user.tUserId}{$user.name} Delete
    + + {if isset($isAdmin) && $isAdmin eq 'TRUE'} +

    Add user to '{$groupsname}'

    + + + + +
    +
    + + + + + +
    +
    + + + + {if isset($RESULT_OF_ADDING) and $RESULT_OF_ADDING eq "SUCCESS"} + +

    {$add_to_group_success}

    +
    + {else if isset($RESULT_OF_ADDING) and $RESULT_OF_ADDING eq "ALREADY_ADDED"} + +

    {$user_already_added}

    +
    + {else if isset($RESULT_OF_ADDING) and $RESULT_OF_ADDING eq "GROUP_NOT_EXISTING"} + +

    {$group_not_existing}

    +
    + {else if isset($RESULT_OF_ADDING) and $RESULT_OF_ADDING eq "USER_NOT_EXISTING"} + +

    {$user_not_existing}

    +
    + {else if isset($RESULT_OF_ADDING) and $RESULT_OF_ADDING eq "NOT_MOD_OR_ADMIN"} + +

    {$not_mod_or_admin}

    +
    + {/if} + + +

    Modify Email Settings

    +
    + + + + + + + + + + + + + +
    + + + + + +
    + + + + + +
    + + + +
    +
    + + {if isset($RESULT_OF_MODIFYING) and $RESULT_OF_MODIFYING eq "SUCCESS"} + + {$modify_mail_of_group_success} + + {else if isset($RESULT_OF_MODIFYING) and $RESULT_OF_MODIFYING eq "EMAIL_NOT_VALID"} + + {$email_not_valid} + + {else if isset($RESULT_OF_MODIFYING) and $RESULT_OF_MODIFYING eq "NO_PASSWORD"} + + {$no_password_given} + + {/if} + + + {/if} + +{/block} + diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/todo.txt b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/todo.txt index 241f7c3f2..20bb8235b 100644 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/todo.txt +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/todo.txt @@ -1,3 +1,5 @@ -Remove full path in autoload functions -Make Permission www dependend, so it can be implemented in drupal with hook_permission(); --in helpers make_folders mkdir($value); should be drupal_mkdir(); \ No newline at end of file +-in helpers make_folders mkdir($value); should be drupal_mkdir(); +-write backwards compatible script for existing nel db! +-fix the callback in add_user_to_sgroup.php and show_sgroup.php in the func dir \ No newline at end of file From d267e60514c7b440f3aca666f0456b35b84c9fdb Mon Sep 17 00:00:00 2001 From: Quitta Date: Fri, 6 Sep 2013 04:52:37 +0200 Subject: [PATCH 148/313] show queue works and show ticket info, I think all outgame aspects are covered now! --- .../drupal_module/ryzommanage/config.php | 2 +- .../ryzommanage/inc/show_queue.php | 129 +++++++++++ .../ryzommanage/inc/show_ticket_info.php | 53 +++++ .../ryzommanage/ryzommanage.module | 1 + .../ryzommanage/templates/show_queue.tpl | 204 ++++++++++++++++++ .../templates/show_ticket_info.tpl | 79 +++++++ 6 files changed, 467 insertions(+), 1 deletion(-) create mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/show_queue.php create mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/show_ticket_info.php create mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/show_queue.tpl create mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/show_ticket_info.tpl diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/config.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/config.php index 739ed8d02..ad9a05462 100644 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/config.php +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/config.php @@ -73,7 +73,7 @@ $AMS_LIB = dirname( __FILE__ ) . '/ams_lib'; $AMS_TRANS = $AMS_LIB . '/translations'; $AMS_CACHEDIR = $AMS_LIB . '/cache'; $SITEBASE = dirname( __FILE__ ); -$WEBPATH ='http://localhost:40917' ; +$WEBPATH ='http://localhost:40917/drupal/sites/all/modules/ryzommanage/' ; //defines the default language $DEFAULT_LANGUAGE = 'en'; diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/show_queue.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/show_queue.php new file mode 100644 index 000000000..1567f8fa8 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/show_queue.php @@ -0,0 +1,129 @@ +getTUserId(); + $queueArray = array(); + $queue_handler = new Ticket_Queue_handler(); + + //Pagination Base Links + $result['pagination_base_link'] = "ams?page=show_queue&get=".$result['queue_view'] ; + + //form url to keep the getters constant + $result['getURL'] = "ams?page=show_queue&get=" . $result['queue_view']; + if(isset($_GET['pagenum'])){ + $result['getURL'] = $result['getURL'] . "&pagenum=".$_GET['pagenum']; + } + + if(isset($_GET['get']) && ($_GET['get'] == "create") && isset($_GET['userid']) && isset($_GET['groupid']) && isset($_GET['what']) && isset($_GET['how']) && isset($_GET['who'])){ + $userid = filter_var($_GET['userid'], FILTER_SANITIZE_NUMBER_INT); + $groupid = filter_var($_GET['groupid'], FILTER_SANITIZE_NUMBER_INT); + $what = filter_var($_GET['what'], FILTER_SANITIZE_STRING); + $how = filter_var($_GET['how'], FILTER_SANITIZE_STRING); + $who = filter_var($_GET['who'], FILTER_SANITIZE_STRING); + $queue_handler->CreateQueue($userid, $groupid, $what, $how, $who); + $result['pagination_base_link'] = "ams?page=show_queue&get=create&userid=".$userid."&groupid=".$groupid."&what=".$what."&how=".$how."&who=".$who; + $result['prev_created_userid'] = $userid; + $result['prev_created_groupid'] = $groupid; + $result['prev_created_what'] = $what; + $result['prev_created_how'] = $how; + $result['prev_created_who'] = $who; + + $result['getURL'] = $result['getURL'] . "&userid=".$userid."&groupid=".$groupid."&what=".$what."&how=".$how."&who=".$who; + + } + + //if an action is set + if(isset($_POST['action'])){ + switch($_POST['action']){ + case "assignTicket": + $ticket_id = filter_var($_POST['ticket_id'], FILTER_SANITIZE_NUMBER_INT); + $result['ACTION_RESULT'] = Ticket::assignTicket($user_id, $ticket_id); + break; + + case "unAssignTicket": + $ticket_id = filter_var($_POST['ticket_id'], FILTER_SANITIZE_NUMBER_INT); + $result['ACTION_RESULT'] = Ticket::unAssignTicket($user_id, $ticket_id); + break; + + case "create_queue": + $userid = filter_var($_POST['userid'], FILTER_SANITIZE_NUMBER_INT); + $groupid = filter_var($_POST['groupid'], FILTER_SANITIZE_NUMBER_INT); + $what = filter_var($_POST['what'], FILTER_SANITIZE_STRING); + $how = filter_var($_POST['how'], FILTER_SANITIZE_STRING); + $who = filter_var($_POST['who'], FILTER_SANITIZE_STRING); + $queue_handler->CreateQueue($userid, $groupid, $what, $how, $who); + $result['pagination_base_link'] = "ams?page=show_queue&get=create&userid=".$userid."&groupid=".$groupid."&what=".$what."&how=".$how."&who=".$who; + $result['prev_created_userid'] = $userid; + $result['prev_created_groupid'] = $groupid; + $result['prev_created_what'] = $what; + $result['prev_created_how'] = $how; + $result['prev_created_who'] = $who; + $result['getURL'] = $result['getURL'] . "&userid=".$userid."&groupid=".$groupid."&what=".$what."&how=".$how."&who=".$who; + + break; + + } + } + + $queueArray = $queue_handler->getTickets($result['queue_view'], $user_id); + + //pagination + $result['links'] = $queue_handler->getPagination()->getLinks(5); + $result['lastPage'] = $queue_handler->getPagination()->getLast(); + $result['currentPage'] = $queue_handler->getPagination()->getCurrent(); + + + //if queue_view is a valid parameter value + if ($queueArray != "ERROR"){ + $result['tickets'] = Gui_Elements::make_table($queueArray, Array("getTId","getTitle","getTimestamp","getAuthor()->getExternId","getTicket_Category()->getName","getStatus","getStatusText","getAssigned","getForwardedGroupName","getForwardedGroupId"), Array("tId","title","timestamp","authorExtern","category","status","statusText","assigned","forwardedGroupName","forwardedGroupId")); + $i = 0; + foreach( $result['tickets'] as $ticket){ + $web_author = new WebUsers($ticket['authorExtern']); + $result['tickets'][$i]['author'] = $web_author->getUsername(); + $web_assigned = new WebUsers($ticket['assigned']); + $result['tickets'][$i]['assignedText'] = $web_assigned->getUsername(); + $result['tickets'][$i]['timestamp_elapsed'] = Gui_Elements::time_elapsed_string($ticket['timestamp']); + $i++; + } + $result['user_id'] = unserialize($_SESSION['ticket_user'])->getTUserId(); + + //Queue creator field info + $result['grouplist'] = Gui_Elements::make_table(Support_Group::getGroups(), Array("getSGroupId","getName"), Array("sGroupId","name")); + $result['teamlist'] = Gui_Elements::make_table(Ticket_User::getModsAndAdmins(), Array("getTUserId","getExternId"), Array("tUserId","externId")); + $i = 0; + foreach( $result['teamlist'] as $member){ + $web_teammember = new Webusers($member['externId']); + $result['teamlist'][$i]['name'] = $web_teammember->getUsername(); + $i++; + } + return $result; + + }else{ + + //ERROR: Doesn't exist! + $_SESSION['error_code'] = "404"; + header("Location: ams?page=error"); + exit; + } + + }else{ + //ERROR: No access! + $_SESSION['error_code'] = "403"; + header("Location: index.php?page=error"); + exit; + } + }else{ + //ERROR: not logged in! + header("Location: index.php"); + exit; + } + +} \ No newline at end of file diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/show_ticket_info.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/show_ticket_info.php new file mode 100644 index 000000000..1750d4058 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/show_ticket_info.php @@ -0,0 +1,53 @@ +load_With_TId($result['ticket_id']); + + if( $target_ticket->hasInfo() && (($target_ticket->getAuthor() == unserialize($_SESSION['ticket_user'])->getTUserId()) || Ticket_User::isMod(unserialize($_SESSION['ticket_user']) ))){ + $result['ticket_title'] = $target_ticket->getTitle(); + $result['ticket_author'] = $target_ticket->getAuthor(); + + $ticket_info = new Ticket_Info(); + $ticket_info->load_With_Ticket($result['ticket_id']); + $result['shard_id'] = $ticket_info->getShardId(); + $result['user_position'] = $ticket_info->getUser_Position(); + $result['view_position'] = $ticket_info->getView_Position(); + $result['client_version'] = $ticket_info->getClient_Version(); + $result['patch_version'] = $ticket_info->getPatch_Version(); + $result['server_tick'] = $ticket_info->getServer_Tick(); + $result['connect_state'] = $ticket_info->getConnect_State(); + $result['local_address'] = $ticket_info->getLocal_Address(); + $result['memory'] = $ticket_info->getMemory(); + $result['os'] = $ticket_info->getOS(); + $result['processor'] = $ticket_info->getProcessor(); + $result['cpu_id'] = $ticket_info->getCPUId(); + $result['cpu_mask'] = $ticket_info->getCPU_Mask(); + $result['ht'] = $ticket_info->getHT(); + $result['nel3d'] = $ticket_info->getNel3D(); + $result['user_id'] = $ticket_info->getUser_Id(); + global $WEBPATH; + $result['WEBPATH'] = $WEBPATH; + + if(Ticket_User::isMod(unserialize($_SESSION['ticket_user']))){ + $result['isMod'] = "TRUE"; + } + return $result; + + }else{ + //ERROR: No access! + $_SESSION['error_code'] = "403"; + header("Location: index.php?page=error"); + exit; + } + }else{ + //ERROR: not logged in! + header("Location: index.php"); + exit; + } +} \ No newline at end of file diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module index 20d3e1569..04bdefb14 100644 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module @@ -22,6 +22,7 @@ global $cfg; global $TICKET_LOGGING; global $TIME_FORMAT; global $TICKET_MAILING_SUPPORT; +global $WEBPATH; require 'ams_lib/libinclude.php'; spl_autoload_register('__autoload'); diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/show_queue.tpl b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/show_queue.tpl new file mode 100644 index 000000000..2a274f47e --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/show_queue.tpl @@ -0,0 +1,204 @@ +{block name=content} +

    Ticket Queue {$queue_view}

    + + + + + + + + + + + + + + + + + +
    + + + + + + + + +
    Todo ticketsAll ticketsAll open ticketsTicket ArchiveNot Assigned Tickets
    +
    +
    + + + + + + + +
    + + + + + + + + + + + +
    + Show + + + + tickets + + + + to + + + + + + + + + + +
    +
    + + +
    +
    +
    + {if isset($ACTION_RESULT) and $ACTION_RESULT eq "SUCCESS_ASSIGNED"} + + {$success_assigned} + + {else if isset($ACTION_RESULT) and $ACTION_RESULT eq "SUCCESS_UNASSIGNED"} + + {$success_unassigned} + + {else if isset($ACTION_RESULT) and $ACTION_RESULT eq "TICKET_NOT_EXISTING"} + + {$ticket_not_existing} + + {else if isset($ACTION_RESULT) and $ACTION_RESULT eq "ALREADY_ASSIGNED"} + + {$ticket_already_assigned} + + {else if isset($ACTION_RESULT) and $ACTION_RESULT eq "NOT_ASSIGNED"} + + {$ticket_not_assigned} + + {/if} +
    + + + + + + + + + + + + + + + {foreach from=$tickets item=ticket} + + + + + + + + + + + {/foreach} + + +
    IDTitleAssignedTimestampCategoryStatusSupportGroupActions
    {$ticket.tId}{$ticket.title}{if $ticket.assignedText neq ""} {$ticket.assignedText} {else} {$not_assigned} {/if}{$ticket.timestamp}{$ticket.category} {$ticket.statusText} + + {if $ticket.forwardedGroupName eq "0"} + {$public_sgroup} + {else} + {$ticket.forwardedGroupName} + {/if} + + + {if $ticket.assigned eq 0} +
    + + + +
    + {else if $ticket.assigned eq $user_id} +
    + + + +
    + {/if} +
    + +
    + « | + {foreach from=$links item=link} + {if $link == $currentPage}{/if}{$link}{if $link == $currentPage}{/if} | + {/foreach} + » +
    +
    + + + +{/block} + diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/show_ticket_info.tpl b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/show_ticket_info.tpl new file mode 100644 index 000000000..6437c3f23 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/show_ticket_info.tpl @@ -0,0 +1,79 @@ +{block name=content} +

    [#{$ticket_id}] {$ticket_title}

    + +

    Actions

    + + + {if isset($isMod) and $isMod eq "TRUE"}{/if} + + + +
    Show Ticket LogSend Other TicketShow Ticket
    + +

    Additional Info

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Ingame related
    Shard ID: {$shard_id}
    User_Id: {$user_id}
    User Position: {$user_position}
    View Position: {$view_position}
    Client_Version: {$client_version}
    Patch_Version: {$patch_version}
    Server_Tick: {$server_tick}
    Hardware & Software related
    Memory: {$memory}
    Processor: {$processor}
    Cpu_Id: {$cpu_id}
    Cpu_Mask: {$cpu_mask}
    HT: {$ht}
    OS: {$os}
    NeL3D: {$nel3d}
    Network related
    Connect_State: {$connect_state}
    Local_Address: {$local_address}
    + +{/block} + From ff444f42fa65b118e8525ed73b88e564070e5463 Mon Sep 17 00:00:00 2001 From: kervala Date: Fri, 6 Sep 2013 08:43:44 +0200 Subject: [PATCH 149/313] Fixed: Don't use -fPIC and -fPIE together (this could occur in some cases) --- code/CMakeModules/PCHSupport.cmake | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/code/CMakeModules/PCHSupport.cmake b/code/CMakeModules/PCHSupport.cmake index bcca9d877..9e858e3fe 100644 --- a/code/CMakeModules/PCHSupport.cmake +++ b/code/CMakeModules/PCHSupport.cmake @@ -38,17 +38,16 @@ MACRO(PCH_SET_COMPILE_FLAGS _target) STRING(TOUPPER "${CMAKE_BUILD_TYPE}" _UPPER_BUILD) LIST(APPEND _FLAGS " ${CMAKE_CXX_FLAGS_${_UPPER_BUILD}}") - IF(NOT MSVC) - GET_TARGET_PROPERTY(_targetType ${_target} TYPE) - IF(${_targetType} STREQUAL SHARED_LIBRARY OR ${_targetType} STREQUAL MODULE_LIBRARY) - LIST(APPEND _FLAGS " -fPIC") - ENDIF(${_targetType} STREQUAL SHARED_LIBRARY OR ${_targetType} STREQUAL MODULE_LIBRARY) + GET_TARGET_PROPERTY(_targetType ${_target} TYPE) + IF(${_targetType} STREQUAL SHARED_LIBRARY OR ${_targetType} STREQUAL MODULE_LIBRARY) + LIST(APPEND _FLAGS " ${CMAKE_SHARED_LIBRARY_CXX_FLAGS}") + ELSE(${_targetType} STREQUAL SHARED_LIBRARY OR ${_targetType} STREQUAL MODULE_LIBRARY) GET_TARGET_PROPERTY(_pic ${_target} POSITION_INDEPENDENT_CODE) IF(_pic) - LIST(APPEND _FLAGS " -fPIE") + LIST(APPEND _FLAGS " ${CMAKE_CXX_COMPILE_OPTIONS_PIE}") ENDIF(_pic) - ENDIF(NOT MSVC) + ENDIF(${_targetType} STREQUAL SHARED_LIBRARY OR ${_targetType} STREQUAL MODULE_LIBRARY) GET_DIRECTORY_PROPERTY(DIRINC INCLUDE_DIRECTORIES) FOREACH(item ${DIRINC}) From 3c3e25d523b2ec5fbc8588972d24712f1f46be55 Mon Sep 17 00:00:00 2001 From: kervala Date: Fri, 6 Sep 2013 08:44:57 +0200 Subject: [PATCH 150/313] Changed: Allow to put a "revision" file in root directory if .hg directory is not present --- code/CMakeModules/GetRevision.cmake | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/code/CMakeModules/GetRevision.cmake b/code/CMakeModules/GetRevision.cmake index 18e997af2..21b234f74 100644 --- a/code/CMakeModules/GetRevision.cmake +++ b/code/CMakeModules/GetRevision.cmake @@ -19,6 +19,7 @@ IF(SOURCE_DIR) SET(SOURCE_DIR ${ROOT_DIR}) ENDIF(NOT SOURCE_DIR AND ROOT_DIR) ELSE(SOURCE_DIR) + SET(SOURCE_DIR ${CMAKE_SOURCE_DIR}) SET(ROOT_DIR ${CMAKE_SOURCE_DIR}) ENDIF(SOURCE_DIR) @@ -57,6 +58,15 @@ IF(EXISTS "${ROOT_DIR}/.hg/") ENDIF(MERCURIAL_FOUND) ENDIF(EXISTS "${ROOT_DIR}/.hg/") +# if processing exported sources, use "revision" file if exists +IF(SOURCE_DIR AND NOT DEFINED REVISION) + SET(REVISION_FILE ${SOURCE_DIR}/revision) + IF(EXISTS ${REVISION_FILE}) + FILE(STRINGS ${REVISION_FILE} REVISION LIMIT_COUNT 1) + MESSAGE(STATUS "Read revision ${REVISION} from file") + ENDIF(EXISTS ${REVISION_FILE}) +ENDIF(SOURCE_DIR AND NOT DEFINED REVISION) + IF(SOURCE_DIR AND DEFINED REVISION) IF(EXISTS ${SOURCE_DIR}/revision.h.in) MESSAGE(STATUS "Revision: ${REVISION}") From 30a8cc1fe31a5e3b699cc2a63b24b3f438059a5f Mon Sep 17 00:00:00 2001 From: kervala Date: Fri, 6 Sep 2013 08:58:02 +0200 Subject: [PATCH 151/313] Changed: Factorized AMD/amd --- code/CMakeModules/nel.cmake | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/code/CMakeModules/nel.cmake b/code/CMakeModules/nel.cmake index c70d6acde..7ed4a1c6c 100644 --- a/code/CMakeModules/nel.cmake +++ b/code/CMakeModules/nel.cmake @@ -387,11 +387,11 @@ MACRO(NL_SETUP_BUILD) SET(HOST_CPU ${CMAKE_HOST_SYSTEM_PROCESSOR}) - IF(HOST_CPU MATCHES "amd64|AMD64") + IF(HOST_CPU MATCHES "(amd|AMD)64") SET(HOST_CPU "x86_64") ELSEIF(HOST_CPU MATCHES "i.86") SET(HOST_CPU "x86") - ENDIF(HOST_CPU MATCHES "amd64|AMD64") + ENDIF(HOST_CPU MATCHES "(amd|AMD)64") # Determine target CPU @@ -400,11 +400,11 @@ MACRO(NL_SETUP_BUILD) SET(TARGET_CPU ${CMAKE_SYSTEM_PROCESSOR}) ENDIF(NOT TARGET_CPU) - IF(TARGET_CPU MATCHES "amd64|AMD64") + IF(TARGET_CPU MATCHES "(amd|AMD)64") SET(TARGET_CPU "x86_64") ELSEIF(TARGET_CPU MATCHES "i.86") SET(TARGET_CPU "x86") - ENDIF(TARGET_CPU MATCHES "amd64|AMD64") + ENDIF(TARGET_CPU MATCHES "(amd|AMD)64") IF(${CMAKE_CXX_COMPILER_ID} MATCHES "Clang") SET(CLANG ON) From 3833a510032001d4f7e19c69cedb65f114822b4b Mon Sep 17 00:00:00 2001 From: kervala Date: Fri, 6 Sep 2013 08:58:36 +0200 Subject: [PATCH 152/313] Changed: Display a message when compiling with NMake and define NMAKE --- code/CMakeModules/nel.cmake | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/code/CMakeModules/nel.cmake b/code/CMakeModules/nel.cmake index 7ed4a1c6c..1f3d45766 100644 --- a/code/CMakeModules/nel.cmake +++ b/code/CMakeModules/nel.cmake @@ -416,6 +416,11 @@ MACRO(NL_SETUP_BUILD) MESSAGE(STATUS "Generating Xcode project") ENDIF(CMAKE_GENERATOR MATCHES "Xcode") + IF(CMAKE_GENERATOR MATCHES "NMake") + SET(NMAKE ON) + MESSAGE(STATUS "Generating NMake project") + ENDIF(CMAKE_GENERATOR MATCHES "NMake") + # If target and host CPU are the same IF("${HOST_CPU}" STREQUAL "${TARGET_CPU}" AND NOT CMAKE_CROSSCOMPILING) # x86-compatible CPU From 5f4831f8b8b01ac750c753c7766fc1ea9f0adf48 Mon Sep 17 00:00:00 2001 From: kervala Date: Fri, 6 Sep 2013 09:00:33 +0200 Subject: [PATCH 153/313] Changed: Use PLATFORM_LINKFLAGS in *_LINKER_FLAGS --- code/CMakeModules/nel.cmake | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/code/CMakeModules/nel.cmake b/code/CMakeModules/nel.cmake index 1f3d45766..82b9ca815 100644 --- a/code/CMakeModules/nel.cmake +++ b/code/CMakeModules/nel.cmake @@ -899,20 +899,23 @@ ENDMACRO(NL_SETUP_BUILD) MACRO(NL_SETUP_BUILD_FLAGS) SET(CMAKE_C_FLAGS ${PLATFORM_CFLAGS} CACHE STRING "" FORCE) SET(CMAKE_CXX_FLAGS ${PLATFORM_CXXFLAGS} CACHE STRING "" FORCE) + SET(CMAKE_EXE_LINKER_FLAGS ${PLATFORM_LINKFLAGS} CACHE STRING "" FORCE) + SET(CMAKE_MODULE_LINKER_FLAGS ${PLATFORM_LINKFLAGS} CACHE STRING "" FORCE) + SET(CMAKE_SHARED_LINKER_FLAGS ${PLATFORM_LINKFLAGS} CACHE STRING "" FORCE) ## Debug SET(CMAKE_C_FLAGS_DEBUG ${NL_DEBUG_CFLAGS} CACHE STRING "" FORCE) SET(CMAKE_CXX_FLAGS_DEBUG ${NL_DEBUG_CFLAGS} CACHE STRING "" FORCE) - SET(CMAKE_EXE_LINKER_FLAGS_DEBUG "${PLATFORM_LINKFLAGS} ${NL_DEBUG_LINKFLAGS}" CACHE STRING "" FORCE) - SET(CMAKE_MODULE_LINKER_FLAGS_DEBUG "${PLATFORM_LINKFLAGS} ${NL_DEBUG_LINKFLAGS}" CACHE STRING "" FORCE) - SET(CMAKE_SHARED_LINKER_FLAGS_DEBUG "${PLATFORM_LINKFLAGS} ${NL_DEBUG_LINKFLAGS}" CACHE STRING "" FORCE) + SET(CMAKE_EXE_LINKER_FLAGS_DEBUG ${NL_DEBUG_LINKFLAGS} CACHE STRING "" FORCE) + SET(CMAKE_MODULE_LINKER_FLAGS_DEBUG ${NL_DEBUG_LINKFLAGS} CACHE STRING "" FORCE) + SET(CMAKE_SHARED_LINKER_FLAGS_DEBUG ${NL_DEBUG_LINKFLAGS} CACHE STRING "" FORCE) ## Release SET(CMAKE_C_FLAGS_RELEASE ${NL_RELEASE_CFLAGS} CACHE STRING "" FORCE) SET(CMAKE_CXX_FLAGS_RELEASE ${NL_RELEASE_CFLAGS} CACHE STRING "" FORCE) - SET(CMAKE_EXE_LINKER_FLAGS_RELEASE "${PLATFORM_LINKFLAGS} ${NL_RELEASE_LINKFLAGS}" CACHE STRING "" FORCE) - SET(CMAKE_MODULE_LINKER_FLAGS_RELEASE "${PLATFORM_LINKFLAGS} ${NL_RELEASE_LINKFLAGS}" CACHE STRING "" FORCE) - SET(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${PLATFORM_LINKFLAGS} ${NL_RELEASE_LINKFLAGS}" CACHE STRING "" FORCE) + SET(CMAKE_EXE_LINKER_FLAGS_RELEASE ${NL_RELEASE_LINKFLAGS} CACHE STRING "" FORCE) + SET(CMAKE_MODULE_LINKER_FLAGS_RELEASE ${NL_RELEASE_LINKFLAGS} CACHE STRING "" FORCE) + SET(CMAKE_SHARED_LINKER_FLAGS_RELEASE ${NL_RELEASE_LINKFLAGS} CACHE STRING "" FORCE) ENDMACRO(NL_SETUP_BUILD_FLAGS) # Macro to create x_ABSOLUTE_PREFIX from x_PREFIX From c87c99f6b28574a0a26f02d1133a1b7c5bf3c425 Mon Sep 17 00:00:00 2001 From: kervala Date: Fri, 6 Sep 2013 09:01:42 +0200 Subject: [PATCH 154/313] Added: MSVC module to use later --- code/CMakeModules/FindMSVC.cmake | 76 ++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 code/CMakeModules/FindMSVC.cmake diff --git a/code/CMakeModules/FindMSVC.cmake b/code/CMakeModules/FindMSVC.cmake new file mode 100644 index 000000000..822e752dd --- /dev/null +++ b/code/CMakeModules/FindMSVC.cmake @@ -0,0 +1,76 @@ +# - Find MS Visual C++ +# +# VC_INCLUDE_DIR - where to find headers +# VC_INCLUDE_DIRS - where to find headers +# VC_LIBRARY_DIR - where to find libraries +# VC_FOUND - True if MSVC found. + +MACRO(DETECT_VC_VERSION_HELPER _ROOT _VERSION) + # Software/Wow6432Node/... + GET_FILENAME_COMPONENT(VC${_VERSION}_DIR "[${_ROOT}\\SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VC7;${_VERSION}]" ABSOLUTE) + + IF(VC${_VERSION}_DIR AND VC${_VERSION}_DIR STREQUAL "/registry") + SET(VC${_VERSION}_DIR) + GET_FILENAME_COMPONENT(VC${_VERSION}_DIR "[${_ROOT}\\SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VS7;${_VERSION}]" ABSOLUTE) + IF(VC${_VERSION}_DIR AND NOT VC${_VERSION}_DIR STREQUAL "/registry") + SET(VC${_VERSION}_DIR "${VC${_VERSION}_DIR}VC/") + ENDIF(VC${_VERSION}_DIR AND NOT VC${_VERSION}_DIR STREQUAL "/registry") + ENDIF(VC${_VERSION}_DIR AND VC${_VERSION}_DIR STREQUAL "/registry") + + IF(VC${_VERSION}_DIR AND NOT VC${_VERSION}_DIR STREQUAL "/registry") + SET(VC${_VERSION}_FOUND ON) + IF(NOT MSVC_FIND_QUIETLY) + MESSAGE(STATUS "Found Visual C++ ${_VERSION} in ${VC${_VERSION}_DIR}") + ENDIF(NOT MSVC_FIND_QUIETLY) + ELSEIF(VC${_VERSION}_DIR AND NOT VC${_VERSION}_DIR STREQUAL "/registry") + SET(VC${_VERSION}_FOUND OFF) + SET(VC${_VERSION}_DIR "") + ENDIF(VC${_VERSION}_DIR AND NOT VC${_VERSION}_DIR STREQUAL "/registry") +ENDMACRO(DETECT_VC_VERSION_HELPER) + +MACRO(DETECT_VC_VERSION _VERSION) + SET(VC${_VERSION}_FOUND OFF) + DETECT_VC_VERSION_HELPER("HKEY_CURRENT_USER" ${_VERSION}) + + IF(NOT VC${_VERSION}_FOUND) + DETECT_VC_VERSION_HELPER("HKEY_LOCAL_MACHINE" ${_VERSION}) + ENDIF(NOT VC${_VERSION}_FOUND) + + IF(NOT VC${_VERSION}_FOUND) + SET(VC_FOUND ON) + SET(VC_DIR "${VC${_VERSION}_DIR}") + ENDIF(NOT VC${_VERSION}_FOUND) +ENDMACRO(DETECT_VC_VERSION) + +IF(MSVC11) + DETECT_VC_VERSION("11.0") + + IF(NOT MSVC11_REDIST_DIR) + # If you have VC++ 2012 Express, put x64/Microsoft.VC110.CRT/*.dll in ${EXTERNAL_PATH}/redist + SET(MSVC11_REDIST_DIR "${EXTERNAL_PATH}/redist") + ENDIF(NOT MSVC11_REDIST_DIR) +ELSEIF(MSVC10) + DETECT_VC_VERSION("10.0") + + IF(NOT MSVC10_REDIST_DIR) + # If you have VC++ 2010 Express, put x64/Microsoft.VC100.CRT/*.dll in ${EXTERNAL_PATH}/redist + SET(MSVC10_REDIST_DIR "${EXTERNAL_PATH}/redist") + ENDIF(NOT MSVC10_REDIST_DIR) +ELSEIF(MSVC90) + DETECT_VC_VERSION("9.0") +ELSEIF(MSVC80) + DETECT_VC_VERSION("8.0") +ENDIF(MSVC11) + +# If you plan to use VC++ compilers with WINE, set VC_DIR environment variable +IF(NOT VC_DIR) + SET(VC_DIR $ENV{VC_DIR}) +ENDIF(NOT VC_DIR) + +IF(NOT VC_DIR) + STRING(REGEX REPLACE "/bin/.+" "" VC_DIR ${CMAKE_CXX_COMPILER}) +ENDIF(NOT VC_DIR) + +SET(VC_INCLUDE_DIR "${VC_DIR}/include") +SET(VC_INCLUDE_DIRS ${VC_INCLUDE_DIR}) +INCLUDE_DIRECTORIES(${VC_INCLUDE_DIR}) From b87aac1cc002e5b2245ae7bfa86f01adda158acc Mon Sep 17 00:00:00 2001 From: kervala Date: Fri, 6 Sep 2013 09:02:48 +0200 Subject: [PATCH 155/313] Changed: Detect hg binary under Mac OS X if /opt/local/bin is not in PATH --- code/CMakeModules/FindMercurial.cmake | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/code/CMakeModules/FindMercurial.cmake b/code/CMakeModules/FindMercurial.cmake index a18e50c43..b0602cbdf 100644 --- a/code/CMakeModules/FindMercurial.cmake +++ b/code/CMakeModules/FindMercurial.cmake @@ -48,7 +48,8 @@ # License text for the above reference.) FIND_PROGRAM(Mercurial_HG_EXECUTABLE hg - DOC "mercurial command line client") + DOC "mercurial command line client" + HINTS /opt/local/bin) MARK_AS_ADVANCED(Mercurial_HG_EXECUTABLE) IF(Mercurial_HG_EXECUTABLE) @@ -58,7 +59,7 @@ IF(Mercurial_HG_EXECUTABLE) STRING(REGEX REPLACE ".*version ([\\.0-9]+).*" "\\1" Mercurial_VERSION_HG "${Mercurial_VERSION_HG}") - + MACRO(Mercurial_WC_INFO dir prefix) EXECUTE_PROCESS(COMMAND ${Mercurial_HG_EXECUTABLE} tip --template "{rev};{node};{tags};{author}" WORKING_DIRECTORY ${dir} From 9e042a5c67fcba5a0349abc6649156744100a7c0 Mon Sep 17 00:00:00 2001 From: kervala Date: Fri, 6 Sep 2013 09:26:23 +0200 Subject: [PATCH 156/313] Changed: Improved MFC module --- code/CMakeModules/FindCustomMFC.cmake | 45 +++++++++++---------------- 1 file changed, 18 insertions(+), 27 deletions(-) diff --git a/code/CMakeModules/FindCustomMFC.cmake b/code/CMakeModules/FindCustomMFC.cmake index 7dd87c15f..6f2f163d3 100644 --- a/code/CMakeModules/FindCustomMFC.cmake +++ b/code/CMakeModules/FindCustomMFC.cmake @@ -4,42 +4,35 @@ # MFC_LIBRARY_DIR, where to find libraries # MFC_INCLUDE_DIR, where to find headers +IF(CustomMFC_FIND_REQUIRED) + SET(MFC_FIND_REQUIRED TRUE) +ENDIF(CustomMFC_FIND_REQUIRED) + # Try to find MFC using official module, MFC_FOUND is set FIND_PACKAGE(MFC) -SET(CUSTOM_MFC_DIR FALSE) +IF(NOT MFC_DIR) + # If MFC have been found, remember their directory + IF(MFC_FOUND AND VC_DIR) + SET(MFC_STANDARD_DIR "${VC_DIR}/atlmfc") + ENDIF(MFC_FOUND AND VC_DIR) -# If using STLport and MFC have been found, remember its directory -IF(WITH_STLPORT AND MFC_FOUND AND VC_DIR) - SET(MFC_STANDARD_DIR "${VC_DIR}/atlmfc") -ENDIF(WITH_STLPORT AND MFC_FOUND AND VC_DIR) - -# If using STLport or MFC haven't been found, search for afxwin.h -IF(WITH_STLPORT OR NOT MFC_FOUND) FIND_PATH(MFC_DIR include/afxwin.h - PATHS + HINTS ${MFC_STANDARD_DIR} ) +ENDIF(NOT MFC_DIR) - IF(CustomMFC_FIND_REQUIRED) - SET(MFC_FIND_REQUIRED TRUE) - ENDIF(CustomMFC_FIND_REQUIRED) +# Display an error message if MFC are not found, MFC_FOUND is updated +# User will be able to update MFC_DIR to the correct directory +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(MFC DEFAULT_MSG MFC_DIR) - # Display an error message if MFC are not found, MFC_FOUND is updated - # User will be able to update MFC_DIR to the correct directory - INCLUDE(FindPackageHandleStandardArgs) - FIND_PACKAGE_HANDLE_STANDARD_ARGS(MFC DEFAULT_MSG MFC_DIR) +IF(MFC_FOUND) + SET(MFC_INCLUDE_DIR "${MFC_DIR}/include") + INCLUDE_DIRECTORIES(${MFC_INCLUDE_DIR}) - IF(MFC_FOUND) - SET(CUSTOM_MFC_DIR TRUE) - SET(MFC_INCLUDE_DIR "${MFC_DIR}/include") - INCLUDE_DIRECTORIES(${MFC_INCLUDE_DIR}) - ENDIF(MFC_FOUND) -ENDIF(WITH_STLPORT OR NOT MFC_FOUND) - -# Only if using a custom path -IF(CUSTOM_MFC_DIR) # Using 32 or 64 bits libraries IF(TARGET_X64) SET(MFC_LIBRARY_DIR "${MFC_DIR}/lib/amd64") @@ -49,9 +42,7 @@ IF(CUSTOM_MFC_DIR) # Add MFC libraries directory to default library path LINK_DIRECTORIES(${MFC_LIBRARY_DIR}) -ENDIF(CUSTOM_MFC_DIR) -IF(MFC_FOUND) # Set definitions for using MFC in DLL SET(MFC_DEFINITIONS -D_AFXDLL) ENDIF(MFC_FOUND) From 8cd8dcee5e784a29de83dfeb32c3898e654c5c14 Mon Sep 17 00:00:00 2001 From: kervala Date: Fri, 6 Sep 2013 09:30:54 +0200 Subject: [PATCH 157/313] Changed: Use /X everytime with MSVC --- code/CMakeModules/nel.cmake | 102 +++++++++--------------------------- 1 file changed, 24 insertions(+), 78 deletions(-) diff --git a/code/CMakeModules/nel.cmake b/code/CMakeModules/nel.cmake index 82b9ca815..bcd32d052 100644 --- a/code/CMakeModules/nel.cmake +++ b/code/CMakeModules/nel.cmake @@ -125,10 +125,6 @@ MACRO(NL_DEFAULT_PROPS name label) COMPILE_FLAGS "/GA" LINK_FLAGS "/VERSION:${NL_VERSION_MAJOR}.${NL_VERSION_MINOR}") ENDIF(${type} STREQUAL EXECUTABLE AND WIN32) - - IF(WITH_STLPORT AND WIN32) - SET_TARGET_PROPERTIES(${name} PROPERTIES COMPILE_FLAGS "/X") - ENDIF(WITH_STLPORT AND WIN32) ENDMACRO(NL_DEFAULT_PROPS) ### @@ -540,6 +536,9 @@ MACRO(NL_SETUP_BUILD) SET(MSVC11 ON) ENDIF(MSVC_VERSION EQUAL "1700" AND NOT MSVC11) + # Ignore default include paths + ADD_PLATFORM_FLAGS("/X") + IF(MSVC11) ADD_PLATFORM_FLAGS("/Gy- /MP") # /Ox is working with VC++ 2010, but custom optimizations don't exist @@ -1069,74 +1068,14 @@ MACRO(SETUP_EXTERNAL) IF(WIN32) FIND_PACKAGE(External REQUIRED) - IF(NOT VC_DIR) - SET(VC_DIR $ENV{VC_DIR}) - ENDIF(NOT VC_DIR) - - IF(MSVC11) - IF(NOT MSVC_REDIST_DIR) - # If you have VC++ 2012 Express, put x64/Microsoft.VC110.CRT/*.dll in ${EXTERNAL_PATH}/redist - SET(MSVC_REDIST_DIR "${EXTERNAL_PATH}/redist") - ENDIF(NOT MSVC_REDIST_DIR) - - IF(NOT VC_DIR) - IF(NOT VC_ROOT_DIR) - GET_FILENAME_COMPONENT(VC_ROOT_DIR "[HKEY_CURRENT_USER\\Software\\Microsoft\\VisualStudio\\11.0_Config;InstallDir]" ABSOLUTE) - # VC_ROOT_DIR is set to "registry" when a key is not found - IF(VC_ROOT_DIR MATCHES "registry") - GET_FILENAME_COMPONENT(VC_ROOT_DIR "[HKEY_CURRENT_USER\\Software\\Microsoft\\WDExpress\\11.0_Config\\Setup\\VC;InstallDir]" ABSOLUTE) - IF(VC_ROOT_DIR MATCHES "registry") - SET(VS110COMNTOOLS $ENV{VS110COMNTOOLS}) - IF(VS110COMNTOOLS) - FILE(TO_CMAKE_PATH ${VS110COMNTOOLS} VC_ROOT_DIR) - ENDIF(VS110COMNTOOLS) - IF(NOT VC_ROOT_DIR) - MESSAGE(FATAL_ERROR "Unable to find VC++ 2012 directory!") - ENDIF(NOT VC_ROOT_DIR) - ENDIF(VC_ROOT_DIR MATCHES "registry") - ENDIF(VC_ROOT_DIR MATCHES "registry") - ENDIF(NOT VC_ROOT_DIR) - # convert IDE fullpath to VC++ path - STRING(REGEX REPLACE "Common7/.*" "VC" VC_DIR ${VC_ROOT_DIR}) - ENDIF(NOT VC_DIR) - ELSEIF(MSVC10) - IF(NOT MSVC_REDIST_DIR) - # If you have VC++ 2010 Express, put x64/Microsoft.VC100.CRT/*.dll in ${EXTERNAL_PATH}/redist - SET(MSVC_REDIST_DIR "${EXTERNAL_PATH}/redist") - ENDIF(NOT MSVC_REDIST_DIR) - - IF(NOT VC_DIR) - IF(NOT VC_ROOT_DIR) - GET_FILENAME_COMPONENT(VC_ROOT_DIR "[HKEY_CURRENT_USER\\Software\\Microsoft\\VisualStudio\\10.0_Config;InstallDir]" ABSOLUTE) - # VC_ROOT_DIR is set to "registry" when a key is not found - IF(VC_ROOT_DIR MATCHES "registry") - GET_FILENAME_COMPONENT(VC_ROOT_DIR "[HKEY_CURRENT_USER\\Software\\Microsoft\\VCExpress\\10.0_Config;InstallDir]" ABSOLUTE) - IF(VC_ROOT_DIR MATCHES "registry") - SET(VS100COMNTOOLS $ENV{VS100COMNTOOLS}) - IF(VS100COMNTOOLS) - FILE(TO_CMAKE_PATH ${VS100COMNTOOLS} VC_ROOT_DIR) - ENDIF(VS100COMNTOOLS) - IF(NOT VC_ROOT_DIR) - MESSAGE(FATAL_ERROR "Unable to find VC++ 2010 directory!") - ENDIF(NOT VC_ROOT_DIR) - ENDIF(VC_ROOT_DIR MATCHES "registry") - ENDIF(VC_ROOT_DIR MATCHES "registry") - ENDIF(NOT VC_ROOT_DIR) - # convert IDE fullpath to VC++ path - STRING(REGEX REPLACE "Common7/.*" "VC" VC_DIR ${VC_ROOT_DIR}) - ENDIF(NOT VC_DIR) - ELSE(MSVC11) - IF(NOT VC_DIR) - IF(${CMAKE_MAKE_PROGRAM} MATCHES "Common7") - # convert IDE fullpath to VC++ path - STRING(REGEX REPLACE "Common7/.*" "VC" VC_DIR ${CMAKE_MAKE_PROGRAM}) - ELSE(${CMAKE_MAKE_PROGRAM} MATCHES "Common7") - # convert compiler fullpath to VC++ path - STRING(REGEX REPLACE "VC/bin/.+" "VC" VC_DIR ${CMAKE_CXX_COMPILER}) - ENDIF(${CMAKE_MAKE_PROGRAM} MATCHES "Common7") - ENDIF(NOT VC_DIR) - ENDIF(MSVC11) + # If using custom boost, we need to define the right variables used by official boost CMake module + IF(DEFINED BOOST_DIR) + SET(BOOST_INCLUDEDIR ${BOOST_DIR}/include) + SET(BOOST_LIBRARYDIR ${BOOST_DIR}/lib) + ENDIF(DEFINED BOOST_DIR) ELSE(WIN32) + FIND_PACKAGE(External QUIET) + IF(APPLE) IF(WITH_STATIC_EXTERNAL) SET(CMAKE_FIND_LIBRARY_SUFFIXES .a) @@ -1152,15 +1091,22 @@ MACRO(SETUP_EXTERNAL) ENDIF(APPLE) ENDIF(WIN32) + # Android and iOS have pthread + IF(ANDROID OR IOS) + SET(CMAKE_USE_PTHREADS_INIT 1) + SET(Threads_FOUND TRUE) + ELSE(ANDROID OR IOS) + FIND_PACKAGE(Threads REQUIRED) + # TODO: replace all -l by absolute path to in CMAKE_THREAD_LIBS_INIT + ENDIF(ANDROID OR IOS) + IF(WITH_STLPORT) FIND_PACKAGE(STLport REQUIRED) INCLUDE_DIRECTORIES(${STLPORT_INCLUDE_DIR}) - IF(MSVC) - SET(VC_INCLUDE_DIR "${VC_DIR}/include") - - FIND_PACKAGE(WindowsSDK REQUIRED) - # use VC++ and Windows SDK include paths - INCLUDE_DIRECTORIES(${VC_INCLUDE_DIR} ${WINSDK_INCLUDE_DIRS}) - ENDIF(MSVC) ENDIF(WITH_STLPORT) + + IF(MSVC) + FIND_PACKAGE(MSVC REQUIRED) + FIND_PACKAGE(WindowsSDK REQUIRED) + ENDIF(MSVC) ENDMACRO(SETUP_EXTERNAL) From b26bf1a265bb580ea581d4d5e9c903ccd1c6772c Mon Sep 17 00:00:00 2001 From: kervala Date: Fri, 6 Sep 2013 13:35:43 +0200 Subject: [PATCH 158/313] Changed: Allows to choose a specific Windows SDK version with WINSDK_VERSION Changed: Look for all installed Windows SDKs --- code/CMakeModules/FindWindowsSDK.cmake | 166 ++++++++++++++++++------- 1 file changed, 123 insertions(+), 43 deletions(-) diff --git a/code/CMakeModules/FindWindowsSDK.cmake b/code/CMakeModules/FindWindowsSDK.cmake index 9d9b91777..8f6ed5772 100644 --- a/code/CMakeModules/FindWindowsSDK.cmake +++ b/code/CMakeModules/FindWindowsSDK.cmake @@ -6,80 +6,160 @@ # WINSDK_LIBRARY_DIR - where to find libraries # WINSDK_FOUND - True if Windows SDK found. -IF(WINSDK_INCLUDE_DIR) - # Already in cache, be silent - SET(WindowsSDK_FIND_QUIETLY TRUE) -ENDIF(WINSDK_INCLUDE_DIR) +IF(WINSDK_FOUND) + # If Windows SDK already found, skip it + RETURN() +ENDIF(WINSDK_FOUND) -# TODO: add the possibility to use a specific Windows SDK +SET(WINSDK_VERSION "CURRENT" CACHE STRING "Windows SDK version to prefer") -IF(MSVC11) - GET_FILENAME_COMPONENT(WINSDK8_DIR "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\v8.0;InstallationFolder]" ABSOLUTE CACHE) - GET_FILENAME_COMPONENT(WINSDK8_VERSION "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\v8.0;ProductVersion]" NAME) +MACRO(DETECT_WINSDK_VERSION_HELPER _ROOT _VERSION) + GET_FILENAME_COMPONENT(WINSDK${_VERSION}_DIR "[${_ROOT}\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\v${_VERSION};InstallationFolder]" ABSOLUTE) - IF(WINSDK8_DIR) + IF(WINSDK${_VERSION}_DIR AND NOT WINSDK${_VERSION}_DIR STREQUAL "/registry") + SET(WINSDK${_VERSION}_FOUND ON) + GET_FILENAME_COMPONENT(WINSDK${_VERSION}_VERSION_FULL "[${_ROOT}\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\v${_VERSION};ProductVersion]" NAME) IF(NOT WindowsSDK_FIND_QUIETLY) - MESSAGE(STATUS "Found Windows SDK ${WINSDK8_VERSION} in ${WINSDK8_DIR}") + MESSAGE(STATUS "Found Windows SDK ${_VERSION} in ${WINSDK${_VERSION}_DIR}") ENDIF(NOT WindowsSDK_FIND_QUIETLY) - IF(TARGET_ARM) - SET(WINSDK8_SUFFIX "arm") - ELSEIF(TARGET_X64) - SET(WINSDK8_SUFFIX "x64") - ELSEIF(TARGET_X86) - SET(WINSDK8_SUFFIX "x86") - ENDIF(TARGET_ARM) - ENDIF(WINSDK8_DIR) -ENDIF(MSVC11) + ELSEIF(WINSDK${_VERSION}_DIR AND NOT WINSDK${_VERSION}_DIR STREQUAL "/registry") + SET(WINSDK${_VERSION}_DIR "") + ENDIF(WINSDK${_VERSION}_DIR AND NOT WINSDK${_VERSION}_DIR STREQUAL "/registry") +ENDMACRO(DETECT_WINSDK_VERSION_HELPER) -GET_FILENAME_COMPONENT(WINSDK71_DIR "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\v7.1;InstallationFolder]" ABSOLUTE CACHE) -GET_FILENAME_COMPONENT(WINSDK71_VERSION "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\v7.1;ProductVersion]" NAME) +MACRO(DETECT_WINSDK_VERSION _VERSION) + SET(WINSDK${_VERSION}_FOUND OFF) + DETECT_WINSDK_VERSION_HELPER("HKEY_CURRENT_USER" ${_VERSION}) + + IF(NOT WINSDK${_VERSION}_FOUND) + DETECT_WINSDK_VERSION_HELPER("HKEY_LOCAL_MACHINE" ${_VERSION}) + ENDIF(NOT WINSDK${_VERSION}_FOUND) +ENDMACRO(DETECT_WINSDK_VERSION) -IF(WINSDK71_DIR) - IF(NOT WindowsSDK_FIND_QUIETLY) - MESSAGE(STATUS "Found Windows SDK ${WINSDK71_VERSION} in ${WINSDK71_DIR}") - ENDIF(NOT WindowsSDK_FIND_QUIETLY) -ENDIF(WINSDK71_DIR) +SET(WINSDK_VERSIONS "8.0" "8.0A" "7.1" "7.0A" "6.1" "6.0" "6.0A") + +# Search all supported Windows SDKs +FOREACH(_VERSION ${WINSDK_VERSIONS}) + DETECT_WINSDK_VERSION(${_VERSION}) +ENDFOREACH(_VERSION) + +IF(TARGET_ARM) + SET(WINSDK8_SUFFIX "arm") +ELSEIF(TARGET_X64) + SET(WINSDK8_SUFFIX "x64") +ELSEIF(TARGET_X86) + SET(WINSDK8_SUFFIX "x86") +ENDIF(TARGET_ARM) -GET_FILENAME_COMPONENT(WINSDKCURRENT_DIR "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows;CurrentInstallFolder]" ABSOLUTE CACHE) GET_FILENAME_COMPONENT(WINSDKCURRENT_VERSION "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows;CurrentVersion]" NAME) -IF(WINSDKCURRENT_DIR) +IF(WINSDKCURRENT_VERSION AND NOT WINSDKCURRENT_VERSION STREQUAL "/registry") IF(NOT WindowsSDK_FIND_QUIETLY) - MESSAGE(STATUS "Found Windows SDK ${WINSDKCURRENT_VERSION} in ${WINSDKCURRENT_DIR}") +# MESSAGE(STATUS "Current version is ${WINSDKCURRENT_VERSION}") ENDIF(NOT WindowsSDK_FIND_QUIETLY) -ENDIF(WINSDKCURRENT_DIR) +ENDIF(WINSDKCURRENT_VERSION AND NOT WINSDKCURRENT_VERSION STREQUAL "/registry") +SET(WINSDKENV_DIR $ENV{WINSDK_DIR}) + +MACRO(USE_CURRENT_WINSDK) + IF(WINSDKENV_DIR) + SET(WINSDK_VERSION "") + SET(WINSDK_VERSION_FULL "") + SET(WINSDK_DIR ${WINSDKENV_DIR}) + FOREACH(_VERSION ${WINSDK_VERSIONS}) + IF(WINSDK_DIR STREQUAL WINSDK${_VERSION}_DIR) + SET(WINSDK_VERSION ${_VERSION}) + SET(WINSDK_VERSION_FULL "${WINSDK${_VERSION}_VERSION_FULL}") + BREAK() + ENDIF(WINSDK_DIR STREQUAL WINSDK${_VERSION}_DIR) + ENDFOREACH(_VERSION) + ELSE(WINSDKENV_DIR) + # Windows SDK 7.0A doesn't provide 64bits compilers, use SDK 7.1 for 64 bits + IF(WINSDKCURRENT_VERSION STREQUAL WINSDK7.0A_VERSION_FULL) + IF(TARGET_X64) + SET(WINSDK_VERSION "7.1") + SET(WINSDK_VERSION_FULL ${WINSDK7.1_VERSION_FULL}) + SET(WINSDK_DIR ${WINSDK7.1_DIR}) + ELSE(TARGET_X64) + SET(WINSDK_VERSION "7.0A") + SET(WINSDK_VERSION_FULL ${WINSDK7.0A_VERSION_FULL}) + SET(WINSDK_DIR ${WINSDK7.0A_DIR}) + ENDIF(TARGET_X64) + ELSE(WINSDKCURRENT_VERSION STREQUAL WINSDK7.0A_VERSION_FULL) + FOREACH(_VERSION ${WINSDK_VERSIONS}) + IF(WINSDKCURRENT_VERSION STREQUAL WINSDK${_VERSION}_VERSION) + SET(WINSDK_VERSION ${_VERSION}) + SET(WINSDK_VERSION_FULL "${WINSDK${_VERSION}_VERSION_FULL}") + SET(WINSDK_DIR "${WINSDK${_VERSION}_DIR}") + BREAK() + ENDIF(WINSDKCURRENT_VERSION STREQUAL WINSDK${_VERSION}_VERSION) + ENDFOREACH(_VERSION) + ENDIF(WINSDKCURRENT_VERSION STREQUAL WINSDK7.0A_VERSION_FULL) + ENDIF(WINSDKENV_DIR) +ENDMACRO(USE_CURRENT_WINSDK) + +IF(WINSDK_VERSION STREQUAL "CURRENT") + USE_CURRENT_WINSDK() +ELSE(WINSDK_VERSION STREQUAL "CURRENT") + IF(WINSDK${WINSDK_VERSION}_FOUND) + SET(WINSDK_VERSION_FULL "${WINSDK${WINSDK_VERSION}_VERSION_FULL}") + SET(WINSDK_DIR "${WINSDK${WINSDK_VERSION}_DIR}") + ELSE(WINSDK${WINSDK_VERSION}_FOUND) + USE_CURRENT_WINSDK() + ENDIF(WINSDK${WINSDK_VERSION}_FOUND) +ENDIF(WINSDK_VERSION STREQUAL "CURRENT") + +IF(WINSDK_DIR) + MESSAGE(STATUS "Using Windows SDK ${WINSDK_VERSION}") +ELSE(WINSDK_DIR) + MESSAGE(FATAL_ERROR "Unable to find Windows SDK!") +ENDIF(WINSDK_DIR) + +# directory where Win32 headers are found FIND_PATH(WINSDK_INCLUDE_DIR Windows.h HINTS - ${WINSDK8_DIR}/Include/um - ${WINSDK71_DIR}/Include - ${WINSDKCURRENT_DIR}/Include + ${WINSDK_DIR}/Include/um + ${WINSDK_DIR}/Include ) +# directory where DirectX headers are found FIND_PATH(WINSDK_SHARED_INCLUDE_DIR d3d9.h HINTS - ${WINSDK8_DIR}/Include/shared - ${WINSDK71_DIR}/Include - ${WINSDKCURRENT_DIR}/Include + ${WINSDK_DIR}/Include/shared + ${WINSDK_DIR}/Include ) +# directory where all libraries are found FIND_PATH(WINSDK_LIBRARY_DIR ComCtl32.lib HINTS - ${WINSDK8_DIR}/Lib/win8/um/${WINSDK8_SUFFIX} - ${WINSDK71_DIR}/Lib - ${WINSDKCURRENT_DIR}/Lib + ${WINSDK_DIR}/Lib/win8/um/${WINSDK8_SUFFIX} + ${WINSDK_DIR}/Lib ) +# signtool is used to sign executables FIND_PROGRAM(WINSDK_SIGNTOOL signtool HINTS - ${WINSDK8_DIR}/Bin/x86 - ${WINSDK71_DIR}/Bin - ${WINSDKCURRENT_DIR}/Bin + ${WINSDK_DIR}/Bin/x86 + ${WINSDK_DIR}/Bin +) + +# midl is used to generate IDL interfaces +FIND_PROGRAM(WINSDK_MIDL midl + HINTS + ${WINSDK_DIR}/Bin/x86 + ${WINSDK_DIR}/Bin ) IF(WINSDK_INCLUDE_DIR) - SET(WINSDK_FOUND TRUE) + SET(WINSDK_FOUND ON) SET(WINSDK_INCLUDE_DIRS ${WINSDK_INCLUDE_DIR} ${WINSDK_SHARED_INCLUDE_DIR}) + SET(CMAKE_LIBRARY_PATH ${WINSDK_LIBRARY_DIR} ${CMAKE_LIBRARY_PATH}) + INCLUDE_DIRECTORIES(${WINSDK_INCLUDE_DIRS}) + + # Fix for using Windows SDK 7.1 with Visual C++ 2012 + IF(WINSDK_VERSION STREQUAL "7.1" AND MSVC11) + ADD_DEFINITIONS(-D_USING_V110_SDK71_) + ENDIF(WINSDK_VERSION STREQUAL "7.1" AND MSVC11) ELSE(WINSDK_INCLUDE_DIR) IF(NOT WindowsSDK_FIND_QUIETLY) MESSAGE(STATUS "Warning: Unable to find Windows SDK!") From 08cf590b3077acc05c23c8a2263b8cca258bde7a Mon Sep 17 00:00:00 2001 From: kervala Date: Fri, 6 Sep 2013 13:38:19 +0200 Subject: [PATCH 159/313] Added: Toolchains for Android and iOS --- code/CMakeModules/AndroidToolChain.cmake | 149 ++++++++++++++++++ code/CMakeModules/iOSToolChain.cmake | 183 +++++++++++++++++++++++ 2 files changed, 332 insertions(+) create mode 100644 code/CMakeModules/AndroidToolChain.cmake create mode 100644 code/CMakeModules/iOSToolChain.cmake diff --git a/code/CMakeModules/AndroidToolChain.cmake b/code/CMakeModules/AndroidToolChain.cmake new file mode 100644 index 000000000..049c39adf --- /dev/null +++ b/code/CMakeModules/AndroidToolChain.cmake @@ -0,0 +1,149 @@ +IF(DEFINED CMAKE_CROSSCOMPILING) + # subsequent toolchain loading is not really needed + RETURN() +ENDIF() + +# Standard settings +SET(CMAKE_SYSTEM_NAME Linux) +SET(CMAKE_SYSTEM_VERSION 1) # TODO: determine target Linux version +SET(UNIX ON) +SET(LINUX ON) +SET(ANDROID ON) + +IF(NOT NDK_ROOT) + SET(NDK_ROOT $ENV{NDK_ROOT}) + + IF(CMAKE_HOST_WIN32) + FILE(TO_CMAKE_PATH ${NDK_ROOT} NDK_ROOT) + ENDIF(CMAKE_HOST_WIN32) +ENDIF(NOT NDK_ROOT) + +IF(NOT TARGET_CPU) + SET(TARGET_CPU "armv7") +ENDIF(NOT TARGET_CPU) + +IF(TARGET_CPU STREQUAL "armv7") + SET(LIBRARY_ARCHITECTURE "armeabi-v7a") + SET(CMAKE_SYSTEM_PROCESSOR "armv7") + SET(TOOLCHAIN_ARCH "arm") + SET(TOOLCHAIN_PREFIX "arm-linux-androideabi") + SET(TOOLCHAIN_BIN_PREFIX "arm") + SET(MINIMUM_NDK_TARGET 4) +ELSEIF(TARGET_CPU STREQUAL "armv5") + SET(LIBRARY_ARCHITECTURE "armeabi") + SET(CMAKE_SYSTEM_PROCESSOR "armv5") + SET(TOOLCHAIN_ARCH "arm") + SET(TOOLCHAIN_PREFIX "arm-linux-androideabi") + SET(TOOLCHAIN_BIN_PREFIX "arm") + SET(MINIMUM_NDK_TARGET 4) +ELSEIF(TARGET_CPU STREQUAL "x86") + SET(LIBRARY_ARCHITECTURE "x86") + SET(CMAKE_SYSTEM_PROCESSOR "x86") + SET(TOOLCHAIN_ARCH "x86") + SET(TOOLCHAIN_PREFIX "x86") + SET(TOOLCHAIN_BIN_PREFIX "i686") + SET(MINIMUM_NDK_TARGET 9) +ELSEIF(TARGET_CPU STREQUAL "mips") + SET(LIBRARY_ARCHITECTURE "mips") + SET(CMAKE_SYSTEM_PROCESSOR "mips") + SET(TOOLCHAIN_ARCH "mips") + SET(TOOLCHAIN_PREFIX "mipsel-linux-android") + SET(TOOLCHAIN_BIN_PREFIX "mipsel") + SET(MINIMUM_NDK_TARGET 9) +ENDIF(TARGET_CPU STREQUAL "armv7") + +IF(NOT NDK_TARGET) + SET(NDK_TARGET ${MINIMUM_NDK_TARGET}) +ENDIF(NOT NDK_TARGET) + +FILE(GLOB _TOOLCHAIN_VERSIONS "${NDK_ROOT}/toolchains/${TOOLCHAIN_PREFIX}-*") +IF(_TOOLCHAIN_VERSIONS) + LIST(SORT _TOOLCHAIN_VERSIONS) + LIST(REVERSE _TOOLCHAIN_VERSIONS) + FOREACH(_TOOLCHAIN_VERSION ${_TOOLCHAIN_VERSIONS}) + STRING(REGEX REPLACE ".+${TOOLCHAIN_PREFIX}-([0-9.]+)" "\\1" _TOOLCHAIN_VERSION "${_TOOLCHAIN_VERSION}") + IF(_TOOLCHAIN_VERSION MATCHES "^([0-9.]+)$") + LIST(APPEND NDK_TOOLCHAIN_VERSIONS ${_TOOLCHAIN_VERSION}) + ENDIF(_TOOLCHAIN_VERSION MATCHES "^([0-9.]+)$") + ENDFOREACH(_TOOLCHAIN_VERSION) +ENDIF(_TOOLCHAIN_VERSIONS) + +IF(NOT NDK_TOOLCHAIN_VERSIONS) + MESSAGE(FATAL_ERROR "No Android toolchain found in default search path ${NDK_ROOT}/toolchains") +ENDIF(NOT NDK_TOOLCHAIN_VERSIONS) + +IF(NDK_TOOLCHAIN_VERSION) + LIST(FIND NDK_TOOLCHAIN_VERSIONS "${NDK_TOOLCHAIN_VERSION}" _INDEX) + IF(_INDEX EQUAL -1) + LIST(GET NDK_TOOLCHAIN_VERSIONS 0 NDK_TOOLCHAIN_VERSION) + ENDIF(_INDEX EQUAL -1) +ELSE(NDK_TOOLCHAIN_VERSION) + LIST(GET NDK_TOOLCHAIN_VERSIONS 0 NDK_TOOLCHAIN_VERSION) +ENDIF(NDK_TOOLCHAIN_VERSION) + +MESSAGE(STATUS "Target Android NDK ${NDK_TARGET} and use GCC ${NDK_TOOLCHAIN_VERSION}") + +IF(CMAKE_HOST_WIN32) + SET(TOOLCHAIN_HOST "windows") + SET(TOOLCHAIN_BIN_SUFFIX ".exe") +ELSEIF(CMAKE_HOST_APPLE) + SET(TOOLCHAIN_HOST "apple") + SET(TOOLCHAIN_BIN_SUFFIX "") +ELSEIF(CMAKE_HOST_UNIX) + SET(TOOLCHAIN_HOST "linux") + SET(TOOLCHAIN_BIN_SUFFIX "") +ENDIF(CMAKE_HOST_WIN32) + +SET(TOOLCHAIN_ROOT "${NDK_ROOT}/toolchains/${TOOLCHAIN_PREFIX}-${NDK_TOOLCHAIN_VERSION}/prebuilt/${TOOLCHAIN_HOST}") +SET(PLATFORM_ROOT "${NDK_ROOT}/platforms/android-${NDK_TARGET}/arch-${TOOLCHAIN_ARCH}") + +IF(NOT EXISTS "${TOOLCHAIN_ROOT}") + FILE(GLOB _TOOLCHAIN_PREFIXES "${TOOLCHAIN_ROOT}*") + IF(_TOOLCHAIN_PREFIXES) + LIST(GET _TOOLCHAIN_PREFIXES 0 TOOLCHAIN_ROOT) + ENDIF(_TOOLCHAIN_PREFIXES) +ENDIF(NOT EXISTS "${TOOLCHAIN_ROOT}") + +MESSAGE(STATUS "Found Android toolchain in ${TOOLCHAIN_ROOT}") +MESSAGE(STATUS "Found Android platform in ${PLATFORM_ROOT}") + +# include dirs +SET(PLATFORM_INCLUDE_DIR "${PLATFORM_ROOT}/usr/include") +SET(STL_DIR "${NDK_ROOT}/sources/cxx-stl/gnu-libstdc++") + +IF(EXISTS "${STL_DIR}/${NDK_TOOLCHAIN_VERSION}") + # NDK version >= 8b + SET(STL_DIR "${STL_DIR}/${NDK_TOOLCHAIN_VERSION}") +ENDIF(EXISTS "${STL_DIR}/${NDK_TOOLCHAIN_VERSION}") + +# Determine bin prefix for toolchain +FILE(GLOB _TOOLCHAIN_BIN_PREFIXES "${TOOLCHAIN_ROOT}/bin/${TOOLCHAIN_BIN_PREFIX}-*-gcc${TOOLCHAIN_BIN_SUFFIX}") +IF(_TOOLCHAIN_BIN_PREFIXES) + LIST(GET _TOOLCHAIN_BIN_PREFIXES 0 _TOOLCHAIN_BIN_PREFIX) + STRING(REGEX REPLACE "${TOOLCHAIN_ROOT}/bin/([a-z0-9-]+)-gcc${TOOLCHAIN_BIN_SUFFIX}" "\\1" TOOLCHAIN_BIN_PREFIX "${_TOOLCHAIN_BIN_PREFIX}") +ENDIF(_TOOLCHAIN_BIN_PREFIXES) + +SET(STL_INCLUDE_DIR "${STL_DIR}/include") +SET(STL_LIBRARY_DIR "${STL_DIR}/libs/${LIBRARY_ARCHITECTURE}") +SET(STL_INCLUDE_CPU_DIR "${STL_LIBRARY_DIR}/include") +SET(STL_LIBRARY "${STL_LIBRARY_DIR}/libgnustl_static.a") + +SET(CMAKE_FIND_ROOT_PATH ${TOOLCHAIN_ROOT} ${PLATFORM_ROOT}/usr ${CMAKE_PREFIX_PATH} ${CMAKE_INSTALL_PREFIX} $ENV{EXTERNAL_ANDROID_PATH} CACHE string "Android find search path root") + +SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY) +SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + +INCLUDE_DIRECTORIES(${STL_INCLUDE_DIR} ${STL_INCLUDE_CPU_DIR}) + +MACRO(SET_TOOLCHAIN_BINARY _NAME _BINARY) + SET(${_NAME} ${TOOLCHAIN_ROOT}/bin/${TOOLCHAIN_BIN_PREFIX}-${_BINARY}${TOOLCHAIN_BIN_SUFFIX}) +ENDMACRO(SET_TOOLCHAIN_BINARY) + +SET_TOOLCHAIN_BINARY(CMAKE_C_COMPILER gcc) +SET_TOOLCHAIN_BINARY(CMAKE_CXX_COMPILER g++) + +# Force the compilers to GCC for Android +include (CMakeForceCompiler) +CMAKE_FORCE_C_COMPILER(${CMAKE_C_COMPILER} GNU) +CMAKE_FORCE_CXX_COMPILER(${CMAKE_CXX_COMPILER} GNU) diff --git a/code/CMakeModules/iOSToolChain.cmake b/code/CMakeModules/iOSToolChain.cmake new file mode 100644 index 000000000..5b419778e --- /dev/null +++ b/code/CMakeModules/iOSToolChain.cmake @@ -0,0 +1,183 @@ +# This file is based off of the Platform/Darwin.cmake and Platform/UnixPaths.cmake +# files which are included with CMake 2.8.4 +# It has been altered for iOS development +# +# Options: +# +# IOS_VERSION = last(default) or specific one (4.3, 5.0, 4.1) +# This decides if SDKS will be selected from the iPhoneOS.platform or iPhoneSimulator.platform folders +# OS - the default, used to build for iPhone and iPad physical devices, which have an arm arch. +# SIMULATOR - used to build for the Simulator platforms, which have an x86 arch. +# +# IOS_PLATFORM = OS (default) or SIMULATOR or ALL +# This decides if SDKS will be selected from the iPhoneOS.platform or iPhoneSimulator.platform folders +# OS - the default, used to build for iPhone and iPad physical devices, which have an arm arch. +# SIMULATOR - used to build for the Simulator platforms, which have an x86 arch. +# +# CMAKE_IOS_DEVELOPER_ROOT = automatic(default) or /path/to/platform/Developer folder +# By default this location is automatcially chosen based on the IOS_PLATFORM value above. +# If set manually, it will override the default location and force the user of a particular Developer Platform +# +# CMAKE_IOS_SDK_ROOT = automatic(default) or /path/to/platform/Developer/SDKs/SDK folder +# By default this location is automatcially chosen based on the CMAKE_IOS_DEVELOPER_ROOT value. +# In this case it will always be the most up-to-date SDK found in the CMAKE_IOS_DEVELOPER_ROOT path. +# If set manually, this will force the use of a specific SDK version + +IF(DEFINED CMAKE_CROSSCOMPILING) + # subsequent toolchain loading is not really needed + RETURN() +ENDIF() + +# Standard settings +SET(CMAKE_SYSTEM_NAME Darwin) +SET(CMAKE_SYSTEM_VERSION 1) # TODO: determine target Darwin version +SET(UNIX ON) +SET(APPLE ON) +SET(IOS ON) + +# Force the compilers to Clang for iOS +include (CMakeForceCompiler) +CMAKE_FORCE_C_COMPILER (clang Clang) +CMAKE_FORCE_CXX_COMPILER (clang++ Clang) + +# Setup iOS platform +if (NOT DEFINED IOS_PLATFORM) + set (IOS_PLATFORM "OS") +endif (NOT DEFINED IOS_PLATFORM) +set (IOS_PLATFORM ${IOS_PLATFORM} CACHE STRING "Type of iOS Platform") + +SET(IOS_PLATFORM_LOCATION "iPhoneOS.platform") +SET(IOS_SIMULATOR_PLATFORM_LOCATION "iPhoneSimulator.platform") + +# Check the platform selection and setup for developer root +if (${IOS_PLATFORM} STREQUAL "OS") + # This causes the installers to properly locate the output libraries + set (CMAKE_XCODE_EFFECTIVE_PLATFORMS "-iphoneos") +elseif (${IOS_PLATFORM} STREQUAL "SIMULATOR") + # This causes the installers to properly locate the output libraries + set (CMAKE_XCODE_EFFECTIVE_PLATFORMS "-iphonesimulator") +elseif (${IOS_PLATFORM} STREQUAL "ALL") + # This causes the installers to properly locate the output libraries + set (CMAKE_XCODE_EFFECTIVE_PLATFORMS "-iphonesimulator;-iphoneos") +else (${IOS_PLATFORM} STREQUAL "OS") + message (FATAL_ERROR "Unsupported IOS_PLATFORM value selected. Please choose OS or SIMULATOR") +endif (${IOS_PLATFORM} STREQUAL "OS") +set (CMAKE_XCODE_EFFECTIVE_PLATFORMS ${CMAKE_XCODE_EFFECTIVE_PLATFORMS} CACHE PATH "iOS Platform") + +# Setup iOS developer location unless specified manually with CMAKE_IOS_DEVELOPER_ROOT +# Note Xcode 4.3 changed the installation location, choose the most recent one available +SET(XCODE_POST_43_ROOT "/Applications/Xcode.app/Contents/Developer/Platforms") +SET(XCODE_PRE_43_ROOT "/Developer/Platforms") + +IF(NOT DEFINED CMAKE_IOS_DEVELOPER_ROOT) + IF(EXISTS ${XCODE_POST_43_ROOT}) + SET(CMAKE_XCODE_ROOT ${XCODE_POST_43_ROOT}) + ELSEIF(EXISTS ${XCODE_PRE_43_ROOT}) + SET(CMAKE_XCODE_ROOT ${XCODE_PRE_43_ROOT}) + ENDIF(EXISTS ${XCODE_POST_43_ROOT}) + IF(EXISTS ${CMAKE_XCODE_ROOT}/${IOS_PLATFORM_LOCATION}/Developer) + SET(CMAKE_IOS_DEVELOPER_ROOT ${CMAKE_XCODE_ROOT}/${IOS_PLATFORM_LOCATION}/Developer) + ENDIF(EXISTS ${CMAKE_XCODE_ROOT}/${IOS_PLATFORM_LOCATION}/Developer) + IF(EXISTS ${CMAKE_XCODE_ROOT}/${IOS_SIMULATOR_PLATFORM_LOCATION}/Developer) + SET(CMAKE_IOS_SIMULATOR_DEVELOPER_ROOT ${CMAKE_XCODE_ROOT}/${IOS_SIMULATOR_PLATFORM_LOCATION}/Developer) + ENDIF(EXISTS ${CMAKE_XCODE_ROOT}/${IOS_SIMULATOR_PLATFORM_LOCATION}/Developer) +ENDIF(NOT DEFINED CMAKE_IOS_DEVELOPER_ROOT) +SET(CMAKE_IOS_DEVELOPER_ROOT ${CMAKE_IOS_DEVELOPER_ROOT} CACHE PATH "Location of iOS Platform") +SET(CMAKE_IOS_SIMULATOR_DEVELOPER_ROOT ${CMAKE_IOS_SIMULATOR_DEVELOPER_ROOT} CACHE PATH "Location of iOS Simulator Platform") + +MACRO(GET_AVAILABLE_SDK_VERSIONS ROOT VERSIONS) + FILE(GLOB _CMAKE_IOS_SDKS "${ROOT}/SDKs/iPhoneOS*") + IF(_CMAKE_IOS_SDKS) + LIST(SORT _CMAKE_IOS_SDKS) + LIST(REVERSE _CMAKE_IOS_SDKS) + FOREACH(_CMAKE_IOS_SDK ${_CMAKE_IOS_SDKS}) + STRING(REGEX REPLACE ".+iPhoneOS([0-9.]+)\\.sdk" "\\1" _IOS_SDK "${_CMAKE_IOS_SDK}") + LIST(APPEND ${VERSIONS} ${_IOS_SDK}) + ENDFOREACH(_CMAKE_IOS_SDK) + ENDIF(_CMAKE_IOS_SDKS) +ENDMACRO(GET_AVAILABLE_SDK_VERSIONS) + +# Find and use the most recent iOS sdk +IF(NOT DEFINED CMAKE_IOS_SDK_ROOT) + # Search for a specific version of a SDK + GET_AVAILABLE_SDK_VERSIONS(${CMAKE_IOS_DEVELOPER_ROOT} IOS_VERSIONS) + + IF(NOT IOS_VERSIONS) + MESSAGE(FATAL_ERROR "No iOS SDK's found in default search path ${CMAKE_IOS_DEVELOPER_ROOT}. Manually set CMAKE_IOS_SDK_ROOT or install the iOS SDK.") + ENDIF(NOT IOS_VERSIONS) + + IF(IOS_VERSION) + LIST(FIND IOS_VERSIONS "${IOS_VERSION}" _INDEX) + IF(_INDEX EQUAL -1) + LIST(GET IOS_VERSIONS 0 IOS_SDK_VERSION) + ELSE(_INDEX EQUAL -1) + SET(IOS_SDK_VERSION ${IOS_VERSION}) + ENDIF(_INDEX EQUAL -1) + ELSE(IOS_VERSION) + LIST(GET IOS_VERSIONS 0 IOS_VERSION) + SET(IOS_SDK_VERSION ${IOS_VERSION}) + ENDIF(IOS_VERSION) + + MESSAGE(STATUS "Target iOS ${IOS_VERSION} and use SDK ${IOS_SDK_VERSION}") + + SET(CMAKE_IOS_SDK_ROOT ${CMAKE_IOS_DEVELOPER_ROOT}/SDKs/iPhoneOS${IOS_SDK_VERSION}.sdk) + SET(CMAKE_IOS_SIMULATOR_SDK_ROOT ${CMAKE_IOS_SIMULATOR_DEVELOPER_ROOT}/SDKs/iPhoneSimulator${IOS_SDK_VERSION}.sdk) +endif (NOT DEFINED CMAKE_IOS_SDK_ROOT) + +SET(CMAKE_IOS_SDK_ROOT ${CMAKE_IOS_SDK_ROOT} CACHE PATH "Location of the selected iOS SDK") +SET(CMAKE_IOS_SIMULATOR_SDK_ROOT ${CMAKE_IOS_SIMULATOR_SDK_ROOT} CACHE PATH "Location of the selected iOS Simulator SDK") + +SET(IOS_VERSION ${IOS_VERSION} CACHE STRING "iOS target version") + +# Set the sysroot default to the most recent SDK +SET(CMAKE_IOS_SYSROOT ${CMAKE_IOS_SDK_ROOT} CACHE PATH "Sysroot used for iOS support") +SET(CMAKE_IOS_SIMULATOR_SYSROOT ${CMAKE_IOS_SIMULATOR_SDK_ROOT} CACHE PATH "Sysroot used for iOS Simulator support") + +IF(CMAKE_GENERATOR MATCHES Xcode) + SET(ARCHS "$(ARCHS_STANDARD_32_BIT)") + IF(${IOS_PLATFORM} STREQUAL "OS") + SET(CMAKE_SYSTEM_PROCESSOR "armv7") + ELSEIF(${IOS_PLATFORM} STREQUAL "SIMULATOR") + SET(CMAKE_SYSTEM_PROCESSOR "x86") + ELSEIF(${IOS_PLATFORM} STREQUAL "ALL") + SET(CMAKE_SYSTEM_PROCESSOR "armv7") + ENDIF(${IOS_PLATFORM} STREQUAL "OS") +ELSE(CMAKE_GENERATOR MATCHES Xcode) + IF(${IOS_PLATFORM} STREQUAL "OS") + SET(ARCHS "armv7") + SET(CMAKE_SYSTEM_PROCESSOR "armv7") + ELSEIF(${IOS_PLATFORM} STREQUAL "SIMULATOR") + # iPhone simulator targets i386 + SET(ARCHS "i386") + SET(CMAKE_SYSTEM_PROCESSOR "x86") + ELSEIF(${IOS_PLATFORM} STREQUAL "ALL") + SET(ARCHS "armv7;i386") + SET(CMAKE_SYSTEM_PROCESSOR "armv7") + ENDIF(${IOS_PLATFORM} STREQUAL "OS") +ENDIF(CMAKE_GENERATOR MATCHES Xcode) + +# set the architecture for iOS - using ARCHS_STANDARD_32_BIT sets armv7,armv7s and appears to be XCode's standard. +# The other value that works is ARCHS_UNIVERSAL_IPHONE_OS but that sets armv7 only +set (CMAKE_OSX_ARCHITECTURES ${ARCHS} CACHE string "Build architecture for iOS") + +# Set the find root to the iOS developer roots and to user defined paths +set (CMAKE_FIND_ROOT_PATH ${CMAKE_IOS_DEVELOPER_ROOT} ${CMAKE_IOS_SDK_ROOT} ${CMAKE_PREFIX_PATH} ${CMAKE_INSTALL_PREFIX} $ENV{EXTERNAL_IOS_PATH} CACHE string "iOS find search path root") + +# default to searching for frameworks first +set (CMAKE_FIND_FRAMEWORK FIRST) + +# set up the default search directories for frameworks +set (CMAKE_SYSTEM_FRAMEWORK_PATH + ${CMAKE_IOS_SDK_ROOT}/System/Library/Frameworks + ${CMAKE_IOS_SDK_ROOT}/System/Library/PrivateFrameworks + ${CMAKE_IOS_SDK_ROOT}/Developer/Library/Frameworks +) + +# only search the iOS sdks, not the remainder of the host filesystem +set (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set (CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set (CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + +#SET(CMAKE_SYSTEM_INCLUDE_PATH /include /usr/include) +#SET(CMAKE_SYSTEM_LIBRARY_PATH /lib /usr/lib) +#SET(CMAKE_SYSTEM_PROGRAM_PATH /bin /usr/bin) From bfc65e70e778deb02fb285998420848e79fb3c4f Mon Sep 17 00:00:00 2001 From: kervala Date: Fri, 6 Sep 2013 14:44:03 +0200 Subject: [PATCH 160/313] Removed: DInput module because it's not used anymore --- code/CMakeModules/FindDInput.cmake | 38 ------------------------------ 1 file changed, 38 deletions(-) delete mode 100644 code/CMakeModules/FindDInput.cmake diff --git a/code/CMakeModules/FindDInput.cmake b/code/CMakeModules/FindDInput.cmake deleted file mode 100644 index 100650652..000000000 --- a/code/CMakeModules/FindDInput.cmake +++ /dev/null @@ -1,38 +0,0 @@ -# - Find DirectInput -# Find the DirectSound includes and libraries -# -# DINPUT_INCLUDE_DIR - where to find dinput.h -# DINPUT_LIBRARIES - List of libraries when using DirectInput. -# DINPUT_FOUND - True if DirectInput found. - -if(DINPUT_INCLUDE_DIR) - # Already in cache, be silent - set(DINPUT_FIND_QUIETLY TRUE) -endif(DINPUT_INCLUDE_DIR) - -find_path(DINPUT_INCLUDE_DIR dinput.h - "$ENV{DXSDK_DIR}" - "$ENV{DXSDK_DIR}/Include" -) - -find_library(DINPUT_LIBRARY - NAMES dinput dinput8 - PATHS - "$ENV{DXSDK_DIR}" - "$ENV{DXSDK_DIR}/Lib" - "$ENV{DXSDK_DIR}/Lib/x86" -) - -# Handle the QUIETLY and REQUIRED arguments and set DINPUT_FOUND to TRUE if -# all listed variables are TRUE. -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(DINPUT DEFAULT_MSG - DINPUT_INCLUDE_DIR DINPUT_LIBRARY) - -if(DINPUT_FOUND) - set(DINPUT_LIBRARIES ${DINPUT_LIBRARY}) -else(DINPUT_FOUND) - set(DINPUT_LIBRARIES) -endif(DINPUT_FOUND) - -mark_as_advanced(DINPUT_INCLUDE_DIR DINPUT_LIBRARY) From 1560df430ac9cf4d4adc0323d0673ce4ef38bf35 Mon Sep 17 00:00:00 2001 From: kervala Date: Fri, 6 Sep 2013 14:45:48 +0200 Subject: [PATCH 161/313] Changed: Replace atoi by NLMISC::fromString --- .../brick_param_extractor.cpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/code/ryzom/tools/server/brick_param_extractor/brick_param_extractor.cpp b/code/ryzom/tools/server/brick_param_extractor/brick_param_extractor.cpp index 0ff24e0f9..1a4257a99 100644 --- a/code/ryzom/tools/server/brick_param_extractor/brick_param_extractor.cpp +++ b/code/ryzom/tools/server/brick_param_extractor/brick_param_extractor.cpp @@ -490,6 +490,8 @@ void COutputFile::generateOutput() const outbuff+="/*\n"; outbuff+="\tFILE: "; outbuff+=_FileName+"\n\n"; + outbuff+="#ifndef RY_EGS_STATIC_BRICK_CPP_H\n"; + outbuff+="#define RY_EGS_STATIC_BRICK_CPP_H\n\n"; outbuff+="\tWARNING: This file is autogenerated - any modifications will be lost at next regeneration\n\n"; outbuff+="*/\n\n"; @@ -505,6 +507,8 @@ void COutputFile::generateOutput() const _Structures[i].generateOutput(outbuff); } + outbuff+="#endif\n\n"; + // read in the previous version of the output file char *inbuff=NULL; FILE *inf=fopen(_FileName.c_str(),"rb"); @@ -631,17 +635,11 @@ void COutputFile::CStruct::generateOutput(std::string &outbuff) const for (i=0;i<_Params.size();++i) { outbuff+="\t\t"; - outbuff+=_Params[i]._Name+"="; - if (_Params[i]._Type==COutputFile::INT) outbuff+="atoi("; - if (_Params[i]._Type==COutputFile::FLOAT) outbuff+="(float)atof("; - outbuff+="args["; + outbuff+="NLMISC::fromString(args["; if (i>100) outbuff+=('0'+((i/100)%10)); if (i>10) outbuff+=('0'+((i/10)%10)); outbuff+=('0'+(i%10)); - if (_Params[i]._Type==COutputFile::INT || _Params[i]._Type==COutputFile::FLOAT) - outbuff+="].c_str());\n"; - else - outbuff+="].c_str();\n"; + outbuff+="], "+_Params[i]._Name+");\n"; } outbuff+="\n"; outbuff+="\t\treturn *this;\n"; From b16d904bb275d634078f92c0d2a521edeb91b967 Mon Sep 17 00:00:00 2001 From: kervala Date: Fri, 6 Sep 2013 14:48:21 +0200 Subject: [PATCH 162/313] Fixed: Use 32 bits libraries from Windows SDK in 64 bits --- code/CMakeModules/FindWindowsSDK.cmake | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/code/CMakeModules/FindWindowsSDK.cmake b/code/CMakeModules/FindWindowsSDK.cmake index 8f6ed5772..1b2affb99 100644 --- a/code/CMakeModules/FindWindowsSDK.cmake +++ b/code/CMakeModules/FindWindowsSDK.cmake @@ -43,10 +43,13 @@ FOREACH(_VERSION ${WINSDK_VERSIONS}) DETECT_WINSDK_VERSION(${_VERSION}) ENDFOREACH(_VERSION) +SET(WINSDK_SUFFIX) + IF(TARGET_ARM) SET(WINSDK8_SUFFIX "arm") ELSEIF(TARGET_X64) SET(WINSDK8_SUFFIX "x64") + SET(WINSDK_SUFFIX "x64") ELSEIF(TARGET_X86) SET(WINSDK8_SUFFIX "x86") ENDIF(TARGET_ARM) @@ -133,7 +136,7 @@ FIND_PATH(WINSDK_SHARED_INCLUDE_DIR d3d9.h FIND_PATH(WINSDK_LIBRARY_DIR ComCtl32.lib HINTS ${WINSDK_DIR}/Lib/win8/um/${WINSDK8_SUFFIX} - ${WINSDK_DIR}/Lib + ${WINSDK_DIR}/Lib/${WINSDK_SUFFIX} ) # signtool is used to sign executables From 12cd9083de4386053f2b9103cbf3becdec239418 Mon Sep 17 00:00:00 2001 From: kervala Date: Fri, 6 Sep 2013 14:49:12 +0200 Subject: [PATCH 163/313] Changed: Cleanlooks doesn't exist anymore in Qt 5, so use default style --- code/nel/samples/3d/nel_qt/nel_qt.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/nel/samples/3d/nel_qt/nel_qt.cfg b/code/nel/samples/3d/nel_qt/nel_qt.cfg index b2c141f89..b87b4767a 100644 --- a/code/nel/samples/3d/nel_qt/nel_qt.cfg +++ b/code/nel/samples/3d/nel_qt/nel_qt.cfg @@ -6,7 +6,7 @@ GraphicsDriver = "OpenGL"; SoundDriver = "OpenAL"; SoundDevice = ""; LanguageCode = "en"; -QtStyle = "Cleanlooks"; +QtStyle = ""; FontName = "andbasr.ttf"; FontShadow = 1; BackgroundColor = { From 53d5e56a2ae1f3d0975f28330c37194a38b371c8 Mon Sep 17 00:00:00 2001 From: kervala Date: Fri, 6 Sep 2013 14:50:19 +0200 Subject: [PATCH 164/313] Changed: Don't use NLMISC includes in NLMISC PCH --- code/nel/src/misc/stdmisc.h | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/code/nel/src/misc/stdmisc.h b/code/nel/src/misc/stdmisc.h index 1c1cda1e5..432ec02b0 100644 --- a/code/nel/src/misc/stdmisc.h +++ b/code/nel/src/misc/stdmisc.h @@ -17,8 +17,6 @@ #ifndef NL_STDMISC_H #define NL_STDMISC_H -#include "nel/misc/types_nl.h" - #include #include #include @@ -44,16 +42,7 @@ #include #include -#include "nel/misc/debug.h" -#include "nel/misc/common.h" -#include "nel/misc/fast_mem.h" -#include "nel/misc/system_info.h" -#include "nel/misc/mem_displayer.h" -#include "nel/misc/stream.h" -#include "nel/misc/path.h" -#include "nel/misc/string_common.h" - -#ifdef NL_OS_WINDOWS +#ifdef _WIN32 #define NOMINMAX #include #include From 712f9ad2a0a10890f75bb5440476141e74b6ebe7 Mon Sep 17 00:00:00 2001 From: kervala Date: Fri, 6 Sep 2013 14:51:21 +0200 Subject: [PATCH 165/313] Fixed: Integer 32 bits conversion to 64 bits pointer --- .../nel_vertex_tree_paint/vertex_tree_paint.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/code/nel/tools/3d/plugin_max/nel_vertex_tree_paint/vertex_tree_paint.cpp b/code/nel/tools/3d/plugin_max/nel_vertex_tree_paint/vertex_tree_paint.cpp index 9c82f5421..a2465ba4c 100644 --- a/code/nel/tools/3d/plugin_max/nel_vertex_tree_paint/vertex_tree_paint.cpp +++ b/code/nel/tools/3d/plugin_max/nel_vertex_tree_paint/vertex_tree_paint.cpp @@ -217,7 +217,7 @@ LRESULT APIENTRY colorSwatchSubclassWndProc( case WM_LBUTTONUP: case WM_LBUTTONDBLCLK: { HWND hPanel = GetParent(hwnd); - LONG mod = GetWindowLongPtr(hPanel,GWLP_USERDATA); + LONG_PTR mod = GetWindowLongPtr(hPanel,GWLP_USERDATA); if (mod) { ((VertexPaint*)mod)->PaletteButton(hwnd); } @@ -424,9 +424,10 @@ void VertexPaint::BeginEditParams( IObjParam *ip, ULONG flags,Animatable *prev ) SendMessage(hParams, WM_POSTINIT, 0, 0); } - else { + else + { SetWindowLongPtr(hParams,GWLP_USERDATA,(LONG_PTR)this); - } + } iTint = SetupIntSpinner (hParams, IDC_TINT_SPIN, IDC_TINT, 0, 100, (int) (fTint*100.0f)); @@ -440,7 +441,7 @@ void VertexPaint::BeginEditParams( IObjParam *ip, ULONG flags,Animatable *prev ) // Force an eval to update caches. NotifyDependents(FOREVER, PART_VERTCOLOR, REFMSG_CHANGE); - } +} void VertexPaint::EndEditParams( IObjParam *ip, ULONG flags,Animatable *next) { From a886559bf6a040f99f157ff1174bc21fc16ccc6c Mon Sep 17 00:00:00 2001 From: kervala Date: Fri, 6 Sep 2013 14:53:40 +0200 Subject: [PATCH 166/313] Fixed: Define math.fmod as a function for Lua 5.0 --- code/ryzom/client/data/gamedev/interfaces_v3/player.lua | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/code/ryzom/client/data/gamedev/interfaces_v3/player.lua b/code/ryzom/client/data/gamedev/interfaces_v3/player.lua index 062f09690..3827593b1 100644 --- a/code/ryzom/client/data/gamedev/interfaces_v3/player.lua +++ b/code/ryzom/client/data/gamedev/interfaces_v3/player.lua @@ -8,6 +8,12 @@ function getDbPropU(dbEntry) return value end +if string.find(_VERSION, "Lua 5.0") then + function math.fmod(a, b) + return math.mod(a, b) + end +end + ------------------------------------------------------------------------------------------------------------ -- create the game namespace without reseting if already created in an other file. if (game==nil) then From 2a0d588cb5061cb82c19d31b4b7c679357cec076 Mon Sep 17 00:00:00 2001 From: kervala Date: Fri, 6 Sep 2013 15:00:01 +0200 Subject: [PATCH 167/313] Changed: Merged code from official client --- .../client/src/interface_v3/chat_window.cpp | 56 +++++++++---------- .../src/input_output_service/chat_manager.cpp | 12 ++++ 2 files changed, 37 insertions(+), 31 deletions(-) diff --git a/code/ryzom/client/src/interface_v3/chat_window.cpp b/code/ryzom/client/src/interface_v3/chat_window.cpp index 24b1e15c6..e74392432 100644 --- a/code/ryzom/client/src/interface_v3/chat_window.cpp +++ b/code/ryzom/client/src/interface_v3/chat_window.cpp @@ -924,33 +924,28 @@ void CChatGroupWindow::removeAllFreeTellers() void CChatGroupWindow::saveFreeTeller(NLMISC::IStream &f) { f.serialVersion(2); - - uint32 nNbFreeTellerSaved = 0; - - f.serial(nNbFreeTellerSaved); - - // Don't save the free tellers - //// Save the free teller only if it is present in the friend list to avoid the only-growing situation - //// because free tellers are never deleted in game if we save/load all the free tellers, we just create more - //// and more container. - - //uint32 i, nNbFreeTellerSaved = 0; - //for (i = 0; i < _FreeTellers.size(); ++i) - // if (PeopleInterraction.FriendList.getIndexFromName(_FreeTellers[i]->getUCTitle()) != -1) - // nNbFreeTellerSaved++; - - //f.serial(nNbFreeTellerSaved); - - //for (i = 0; i < _FreeTellers.size(); ++i) - //{ - // CGroupContainer *pGC = _FreeTellers[i]; - // if (PeopleInterraction.FriendList.getIndexFromName(pGC->getUCTitle()) != -1) - // { - // ucstring sTitle = pGC->getUCTitle(); - // f.serial(sTitle); - // } - //} + // Save the free teller only if it is present in the friend list to avoid the only-growing situation + // because free tellers are never deleted in game if we save/load all the free tellers, we just create more + // and more container. + + uint32 i, nNbFreeTellerSaved = 0; + for (i = 0; i < _FreeTellers.size(); ++i) + if (PeopleInterraction.FriendList.getIndexFromName(_FreeTellers[i]->getUCTitle()) != -1) + nNbFreeTellerSaved++; + + f.serial(nNbFreeTellerSaved); + + for (i = 0; i < _FreeTellers.size(); ++i) + { + CGroupContainer *pGC = _FreeTellers[i]; + + if (PeopleInterraction.FriendList.getIndexFromName(pGC->getUCTitle()) != -1) + { + ucstring sTitle = pGC->getUCTitle(); + f.serial(sTitle); + } + } } //================================================================================= @@ -979,12 +974,11 @@ void CChatGroupWindow::loadFreeTeller(NLMISC::IStream &f) ucstring sTitle; f.serial(sTitle); - // Don't actually create the free teller - //CGroupContainer *pGC = createFreeTeller(sTitle, ""); + CGroupContainer *pGC = createFreeTeller(sTitle, ""); - //// With version 1 all tells are active because windows information have "title based" ids and no "sID based". - //if ((ver == 1) && (pGC != NULL)) - // pGC->setActive(false); + // With version 1 all tells are active because windows information have "title based" ids and no "sID based". + if ((ver == 1) && (pGC != NULL)) + pGC->setActive(false); } } diff --git a/code/ryzom/server/src/input_output_service/chat_manager.cpp b/code/ryzom/server/src/input_output_service/chat_manager.cpp index a4765c300..50c58fce6 100644 --- a/code/ryzom/server/src/input_output_service/chat_manager.cpp +++ b/code/ryzom/server/src/input_output_service/chat_manager.cpp @@ -636,6 +636,18 @@ void CChatManager::chat( const TDataSetRow& sender, const ucstring& ucstr ) { if (session->WriteRight) // player must have the right to speak in the channel { + // If universal channel check if player muted + if (session->getChan()->UniversalChannel) + { + if(_MutedUsers.find( eid ) != _MutedUsers.end()) + { + nldebug("IOSCM: chat The player %s:%x is muted", + TheDataset.getEntityId(sender).toString().c_str(), + sender.getIndex()); + return; + } + } + if (!session->getChan()->getDontBroadcastPlayerInputs()) { // add msg to the historic From 8d27acc654ede4a07e5b8c8cdcb86aca71f87e3b Mon Sep 17 00:00:00 2001 From: kervala Date: Fri, 6 Sep 2013 15:00:35 +0200 Subject: [PATCH 168/313] Changed: Minor changes --- code/nel/src/misc/sstring.cpp | 5 +--- .../client/client_config/display_dlg.cpp | 28 +++++++++---------- code/ryzom/tools/sheets_packer/stdpch.h | 2 -- 3 files changed, 15 insertions(+), 20 deletions(-) diff --git a/code/nel/src/misc/sstring.cpp b/code/nel/src/misc/sstring.cpp index 492a00db1..fe9332328 100644 --- a/code/nel/src/misc/sstring.cpp +++ b/code/nel/src/misc/sstring.cpp @@ -37,14 +37,13 @@ namespace NLMISC return token; } - uint i; + uint i, j; CSString result; // skip leading junk for (i=0;i #include -//#include - #ifdef NL_OS_WINDOWS #define NOMINMAX #include From 67f574124325acfaaa77488438e0e3d4ca49c4f1 Mon Sep 17 00:00:00 2001 From: Quitta Date: Sat, 7 Sep 2013 04:12:47 +0200 Subject: [PATCH 169/313] Ingame version works now too more or less --- .../ryzommanage/autoload/webusers.php | 24 +--- .../drupal_module/ryzommanage/config.php | 7 +- .../drupal_module/ryzommanage/func/login.php | 36 +++++ .../ryzommanage/inc/dashboard.php | 2 + .../drupal_module/ryzommanage/inc/login.php | 21 +++ .../drupal_module/ryzommanage/inc/logout.php | 6 + .../ryzommanage/inc/show_ticket_info.php | 4 +- .../ryzommanage/ryzommanage.module | 132 ++++++++++++++++-- .../ryzommanage/templates/show_ticket.tpl | 2 +- .../templates/show_ticket_info.tpl | 32 ++--- .../ryzom_ams/www/html/inc/dashboard.php | 3 +- 11 files changed, 216 insertions(+), 53 deletions(-) create mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/login.php create mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/login.php create mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/logout.php diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/autoload/webusers.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/autoload/webusers.php index 73d0b79a0..779414de3 100644 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/autoload/webusers.php +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/autoload/webusers.php @@ -57,17 +57,11 @@ class WebUsers extends Users{ * @return string Info: Returns true or false if a login match is found in the web db */ public function checkLoginMatch($username,$password){ - $dbw = new DBLayer("web"); - $statement = $dbw->execute("SELECT * FROM ams_user WHERE Login=:user", array('user' => $username)); - $row = $statement->fetch(); - - $salt = substr($row['Password'],0,2); - $hashed_input_pass = crypt($password, $salt); - if($hashed_input_pass == $row['Password']){ - return $row; - }else{ - return "fail"; - } + if(!user_authenticate($username, $password)){ + return 'fail'; + }else{ + return db_query("SELECT * FROM {users} WHERE name = :name", array(':name' => $username))->fetchAssoc(); + } } //returns te id for a given username @@ -132,13 +126,7 @@ class WebUsers extends Users{ } public function getLanguage(){ - $dbw = new DBLayer("web"); - if(! isset($this->language) || $this->language == ""){ - $statement = $dbw->execute("SELECT * FROM ams_user WHERE UId=:id", array('id' => $this->uId)); - $row = $statement->fetch(); - $this->set($row); - } - return $this->language; + return $DEFAULT_LANGUAGE; } public function isLoggedIn(){ diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/config.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/config.php index ad9a05462..7723032f8 100644 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/config.php +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/config.php @@ -73,7 +73,11 @@ $AMS_LIB = dirname( __FILE__ ) . '/ams_lib'; $AMS_TRANS = $AMS_LIB . '/translations'; $AMS_CACHEDIR = $AMS_LIB . '/cache'; $SITEBASE = dirname( __FILE__ ); -$WEBPATH ='http://localhost:40917/drupal/sites/all/modules/ryzommanage/' ; +$BASE_WEBPATH = 'http://localhost:40917/drupal'; +$IMAGELOC_WEBPATH = $BASE_WEBPATH. '/sites/all/modules/ryzommanage/' ; +$WEBPATH = $BASE_WEBPATH .'/ams'; +$INGAME_WEBPATH = $BASE_WEBPATH . '/ingame'; +$CONFIG_PATH = dirname( __FILE__ ); //defines the default language $DEFAULT_LANGUAGE = 'en'; @@ -88,3 +92,4 @@ $TIME_FORMAT = "m-d-Y H:i:s"; $INGAME_LAYOUT = "basic"; + diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/login.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/login.php new file mode 100644 index 000000000..2731de130 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/login.php @@ -0,0 +1,36 @@ +getLanguage(); + + //go back to the index page. + header( 'Location: '. $INGAME_WEBPATH ); + exit; + }else{ + //handle login failure + $result = Array(); + $result['login_error'] = 'TRUE'; + $result['no_visible_elements'] = 'TRUE'; + helpers :: loadtemplate( 'login', $result); + exit; + } + + + }catch (PDOException $e) { + //go to error page or something, because can't access website db + print_r($e); + exit; + } + +} \ No newline at end of file diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/dashboard.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/dashboard.php index 6d0ef5faf..74475dcd1 100644 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/dashboard.php +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/dashboard.php @@ -15,6 +15,8 @@ function dashboard(){ $result['newestTicketId'] = $ticket->getTId(); $result['newestTicketTitle'] = $ticket->getTitle(); $result['newestTicketAuthor'] = Ticket_User::get_username_from_id($ticket->getAuthor()); + global $INGAME_WEBPATH; + $result['ingame_webpath'] = $INGAME_WEBPATH; return $result; }else{ diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/login.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/login.php new file mode 100644 index 000000000..fca774ddc --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/login.php @@ -0,0 +1,21 @@ +getHT(); $result['nel3d'] = $ticket_info->getNel3D(); $result['user_id'] = $ticket_info->getUser_Id(); - global $WEBPATH; - $result['WEBPATH'] = $WEBPATH; + global $IMAGELOC_WEBPATH; + $result['IMAGELOC_WEBPATH'] = $IMAGELOC_WEBPATH; if(Ticket_User::isMod(unserialize($_SESSION['ticket_user']))){ $result['isMod'] = "TRUE"; diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module index 04bdefb14..7249aded2 100644 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module @@ -22,10 +22,15 @@ global $cfg; global $TICKET_LOGGING; global $TIME_FORMAT; global $TICKET_MAILING_SUPPORT; +global $IMAGELOC_WEBPATH; global $WEBPATH; +global $INGAME_WEBPATH; +global $BASE_WEBPATH; +global $INGAME_LAYOUT; require 'ams_lib/libinclude.php'; spl_autoload_register('__autoload'); + require 'config.php'; /* @@ -132,6 +137,15 @@ function ryzommanage_menu() 'type' => MENU_NORMAL_ITEM ); + $items['ingame'] = array( + 'title' => 'Ingame AMS', + 'page callback' => '_collect_ingame_ams', + 'page arguments' => array(1, 2), + 'access callback' => 'user_access', + 'access arguments' => array('access content'), + 'type' => MENU_CALLBACK + ); + //main menu item $items['admin/config/ryzommanage'] = array( 'title' => 'Ryzom Server Integration', @@ -256,7 +270,7 @@ function ryzommanage_block_view($delta = '') function _ams_handler() { - + global $BASE_WEBPATH; //Decide what page to load if ( ! isset( $_GET["page"]) ){ if(isset($_SESSION['user'])){ @@ -267,7 +281,7 @@ function _ams_handler() } }else{ //default page - header("Location: user/login"); + header("Location: ".$BASE_WEBPATH."/user/login"); exit; } }else{ @@ -333,11 +347,99 @@ function _collect_register($nids, $collection) return_client_httpdata(); } else { //redirect to registration page - header("Location: user/register"); + header("Location: ".$BASE_WEBPATH. "/user/register"); } } + +/** + * + * Function _collect_register + * + * @takes + * @return Nothing + * + * Info: Determins what to send back to client, if the client is ryzom core then send the http data if its a browser send to / + * + */ +function _collect_ingame_ams($nids, $collection) +{ + //if not using ryzom core client show registration page + if (Helpers::check_if_game_client(true)) { + _return_ingame_httpdata(); + } else { + //redirect to registration page + global $WEBPATH; + header("Location: ". $WEBPATH); + } +} + + +function _return_ingame_httpdata(){ + + //Decide what page to load + if ( ! isset( $_GET["page"]) ){ + if(isset($_SESSION['user'])){ + if(Ticket_User::isMod(unserialize($_SESSION['ticket_user']))){ + $page = 'dashboard'; + }else{ + $page = 'show_user'; + } + }else{ + //default page + $page = 'login'; + } + }else{ + $page = $_GET["page"]; + } + + //perform an action in case one is specified + //else check if a php page is included in the inc folder, else just set page to the get param + global $SITEBASE; + if ( isset( $_POST["function"] ) ){ + $filename = $SITEBASE.'/func/' . $_POST["function"] . '.php'; + if(is_file($filename)){ + require($filename); + $return = $_POST["function"](); + } + }else{ + + $filename = $SITEBASE.'/inc/' . $page . '.php'; + if(is_file($filename)){ + require_once($filename); + $return = $page(); + } + } + + //add username to the return array in case logged in. + if(isset($_SESSION['user'])){ + $return['username'] = $_SESSION['user']; + } + + //Set permission + if(isset($_SESSION['ticket_user'])){ + $return['permission'] = unserialize($_SESSION['ticket_user'])->getPermission(); + }else{ + //default permission + $return['permission'] = 0; + } + + //hide sidebar + topbar in case of login/register + if($page == 'login' || $page == 'register' || $page == 'logout'){ + $return['no_visible_elements'] = 'TRUE'; + }else{ + $return['no_visible_elements'] = 'FALSE'; + } + + //handle error page + if($page == 'error'){ + $return['permission'] = 0; + $return['no_visible_elements'] = 'FALSE'; + } + + helpers :: loadTemplate( $page , $return ); +} /** * * Function _collect_register @@ -350,6 +452,8 @@ function _collect_register($nids, $collection) */ function _collect_login($nids, $collection) { + global $WEBPATH; + global $BASE_WEBPATH; $result = Helpers::check_login_ingame(); if ($result != "FALSE") { //handle successful ingame login @@ -364,10 +468,10 @@ function _collect_login($nids, $collection) $user->timezone = $account->timezone; user_login_finalize(); } - header( 'Location: ams' ); + header( 'Location: '.$WEBPATH ); } else { //redirect to registration page - header("Location: user/login"); + header("Location: ".$BASE_WEBPATH."/user/login"); } } @@ -550,21 +654,23 @@ function login_form($login_form) function top_bar() { global $user; + global $WEBPATH; + global $BASE_WEBPATH; $userId = $user->uid; if (user_is_logged_in()) { // Logged in user //check permission, if user if(ticket_user::isMod(unserialize($_SESSION['ticket_user']))){ - return ""; + return ""; }else{ - return ""; + return ""; } } else { diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/show_ticket.tpl b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/show_ticket.tpl index c9d8c854b..5a4963795 100644 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/show_ticket.tpl +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/show_ticket.tpl @@ -119,7 +119,7 @@ {if $reply.permission eq '1'} {if isset($isMod) and $isMod eq "TRUE"} {$reply.author}{else} {$reply.author} {/if} {else if $reply.permission gt '1'} - {if isset($isMod) and $isMod eq "TRUE"} {$reply.author}{else} {$reply.author} {/if} + {if isset($isMod) and $isMod eq "TRUE"} {$reply.author}{else} {$reply.author} {/if} {/if}

    {if $reply.hidden eq 1}{/if}{$reply.replyContent}{if $reply.hidden eq 1}{/if}

    diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/show_ticket_info.tpl b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/show_ticket_info.tpl index 6437c3f23..04384009e 100644 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/show_ticket_info.tpl +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/show_ticket_info.tpl @@ -16,61 +16,61 @@
    Ingame related
    - Shard ID: {$shard_id} + Shard ID: {$shard_id} - User_Id: {$user_id} + User_Id: {$user_id} - User Position: {$user_position} + User Position: {$user_position} - View Position: {$view_position} + View Position: {$view_position} - Client_Version: {$client_version} + Client_Version: {$client_version} - Patch_Version: {$patch_version} + Patch_Version: {$patch_version} - Server_Tick: {$server_tick} + Server_Tick: {$server_tick}
    Hardware & Software related
    - Memory: {$memory} + Memory: {$memory} - Processor: {$processor} + Processor: {$processor} - Cpu_Id: {$cpu_id} + Cpu_Id: {$cpu_id} - Cpu_Mask: {$cpu_mask} + Cpu_Mask: {$cpu_mask} - HT: {$ht} + HT: {$ht} - OS: {$os} + OS: {$os} - NeL3D: {$nel3d} + NeL3D: {$nel3d}
    Network related
    - Connect_State: {$connect_state} + Connect_State: {$connect_state} - Local_Address: {$local_address} + Local_Address: {$local_address} diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/inc/dashboard.php b/code/ryzom/tools/server/ryzom_ams/www/html/inc/dashboard.php index 752156c6c..980da3cb1 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/inc/dashboard.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/inc/dashboard.php @@ -1,8 +1,7 @@ Date: Sat, 7 Sep 2013 15:51:30 +0200 Subject: [PATCH 170/313] Move r2 plot item security off the AIS to the EGS and remove hardcoded sheet ids --- .../server/src/ai_service/nf_grp_npc.cpp | 65 +-------- .../modules/r2_mission_item.cpp | 129 +++++++++++------- 2 files changed, 79 insertions(+), 115 deletions(-) diff --git a/code/ryzom/server/src/ai_service/nf_grp_npc.cpp b/code/ryzom/server/src/ai_service/nf_grp_npc.cpp index 0672269fc..25fd7e4e0 100644 --- a/code/ryzom/server/src/ai_service/nf_grp_npc.cpp +++ b/code/ryzom/server/src/ai_service/nf_grp_npc.cpp @@ -1607,6 +1607,7 @@ Then user events are triggered on the group to inform it about what happens: - user_event_3: triggered after the player has given the mission items to the npc. Warning: this function can only be called after the event "player_target_npc". +Warning: only works on an R2 shard for R2 plot items. Arguments: s(missionItems), s(missionText), c(groupToNotify) -> @param[in] missionItems is the list of mission items, the string format is "item1:qty1;item2:qty2;...". @@ -1709,38 +1710,6 @@ void receiveMissionItems_ssc_(CStateInstance* entity, CScriptStack& stack) DEBUG_STOP; return; } - // if LD use this the function outside a ring shard - if (IsRingShard) - { - - // Here we destroy the item: so we do not want that a user create a scenario where we destroy - // other players precious items - - static std::set r2PlotItemSheetId; // :TODO: use R2Share::CRingAccess - // lazy intialisation - if (r2PlotItemSheetId.empty()) - { - for (uint32 i = 0 ; i <= 184 ; ++i) - { - r2PlotItemSheetId.insert( CSheetId( NLMISC::toString("r2_plot_item_%d.sitem", i))); - } - } - - // A npc give a mission to take an item given by another npc - // but the item instead of being a r2_plot_item is a normal item like system_mp or big armor - if ( r2PlotItemSheetId.find(sheetId) == r2PlotItemSheetId.end()) - { - nlwarning("!!!!!!!!!!!!"); - nlwarning("!!!!!!!!!!!! Someone is trying to hack us"); - nlwarning("!!!!!!!!!!!!"); - nlwarning("ERROR/HACK : an npc is trying to give to a player a item that is not a plot item SheetId='%s' sheetIdAsInt=%u",sheetId.toString().c_str(), sheetId.asInt()); - nlwarning("His ai instanceId is %u, use log to know the sessionId and the user ", msg.InstanceId ); - nlwarning("!!!!!!!!!!!!"); - nlwarning("!!!!!!!!!!!!"); - return ; - } - - } uint32 quantity; NLMISC::fromString(itemAndQty[1], quantity); @@ -1774,6 +1743,7 @@ Then user events are triggered on the group to inform it about what happens: - user_event_1: triggered after the player has received the mission items from the npc. Warning: this function can only be called after the event "player_target_npc". +Warning: only works on an R2 shard for R2 plot items. Arguments: s(missionItems), s(missionText), c(groupToNotify) -> @param[in] missionItems is the list of mission items, the string format is "item1:qty1;item2:qty2;...". @@ -1877,37 +1847,6 @@ void giveMissionItems_ssc_(CStateInstance* entity, CScriptStack& stack) return; } - - // if LD use this the function outside a ring shard - if (IsRingShard) - { - - static std::set r2PlotItemSheetId; // :TODO: use R2Share::CRingAccess - // lazy intialisation - if (r2PlotItemSheetId.empty()) - { - for (uint32 i = 0 ; i <= 184 ; ++i) - { - r2PlotItemSheetId.insert( CSheetId( NLMISC::toString("r2_plot_item_%d.sitem", i))); - } - } - - // A npc give a mission to give a item to another npc - // but the item instead of being a r2_plot_item is a normal item like system_mp or big armor - if ( r2PlotItemSheetId.find(sheetId) == r2PlotItemSheetId.end()) - { - nlwarning("!!!!!!!!!!!!"); - nlwarning("!!!!!!!!!!!! Someone is trying to hack us"); - nlwarning("!!!!!!!!!!!!"); - nlwarning("ERROR/HACK : an npc is trying to give to a player a item that is not a plot item SheetId='%s' sheetIdAsInt=%u",sheetId.toString().c_str(), sheetId.asInt()); - nlwarning("His ai instanceId is %u, use log to know the sessionId and the user ", msg.InstanceId ); - nlwarning("!!!!!!!!!!!!"); - nlwarning("!!!!!!!!!!!!"); - return ; - } - - } - uint32 quantity; NLMISC::fromString(itemAndQty[1], quantity); if (quantity == 0) diff --git a/code/ryzom/server/src/entities_game_service/modules/r2_mission_item.cpp b/code/ryzom/server/src/entities_game_service/modules/r2_mission_item.cpp index d51682761..7feea7f00 100644 --- a/code/ryzom/server/src/entities_game_service/modules/r2_mission_item.cpp +++ b/code/ryzom/server/src/entities_game_service/modules/r2_mission_item.cpp @@ -26,6 +26,7 @@ #include "player_manager/player_manager.h" #include "player_manager/character.h" #include "server_share/log_item_gen.h" +#include "egs_sheets/egs_sheets.h" using namespace std; using namespace NLMISC; @@ -155,46 +156,58 @@ void CR2MissionItem::giveMissionItem(const NLMISC::CEntityId &eid, TSessionId se std::vector< CGameItemPtr > itemDropToEgg; for( uint32 j = 0; j < items.size(); ++j ) { - CGameItemPtr item = c->createItem(1, items[j].Quantity, items[j].SheetId); - - if( item != NULL ) + const CStaticItem* sitem = CSheets::getForm(items[j].SheetId); + if (sitem == NULL) { - if( c->addItemToInventory(INVENTORIES::bag, item) ) - { -/* // check eid is registered as character have instantiated mission item for this scenario - TMissionItemInstanciatedOwner::iterator it = _OwnerOfInstanciatedItemFromScenario.find(scenarioId); - if( it == _OwnerOfInstanciatedItemFromScenario.end() ) - { - pair< TMissionItemInstanciatedOwner::iterator, bool > ret = _OwnerOfInstanciatedItemFromScenario.insert( make_pair( scenarioId, vector< CEntityId >() ) ); - if( ret.second ) - { - (*ret.first).second.push_back( eid ); - } - } - else - { - bool found = false; - for( uint32 i = 0; i < (*it).second.size(); ++ i ) - { - if( (*it).second[i] == eid ) - { - found = true; - break; - } - } - if ( ! found) { (*it).second.push_back(eid); } - } -*/ - keepR2ItemAssociation(eid, scenarioId); - } - else - { - itemDropToEgg.push_back(item); - } + nlwarning("Attempted to give deprecated sitem sheet %s to player character %s in session %i", items[j].SheetId.toString().c_str(), c->getName().toUtf8().c_str(), sessionId.asInt()); + } + else if (sitem->Family != ITEMFAMILY::SCROLL_R2) + { + nlwarning("Attempted hack to give non-R2 item %s to player character %s in session %i", items[j].SheetId.toString().c_str(), c->getName().toUtf8().c_str(), sessionId.asInt()); } else { - nlwarning("CR2MissionItem::giveMissionItem: can't create item %s", items[j].SheetId.toString().c_str()); + CGameItemPtr item = c->createItem(1, items[j].Quantity, items[j].SheetId); + + if( item != NULL ) + { + if( c->addItemToInventory(INVENTORIES::bag, item) ) + { + /* // check eid is registered as character have instantiated mission item for this scenario + TMissionItemInstanciatedOwner::iterator it = _OwnerOfInstanciatedItemFromScenario.find(scenarioId); + if( it == _OwnerOfInstanciatedItemFromScenario.end() ) + { + pair< TMissionItemInstanciatedOwner::iterator, bool > ret = _OwnerOfInstanciatedItemFromScenario.insert( make_pair( scenarioId, vector< CEntityId >() ) ); + if( ret.second ) + { + (*ret.first).second.push_back( eid ); + } + } + else + { + bool found = false; + for( uint32 i = 0; i < (*it).second.size(); ++ i ) + { + if( (*it).second[i] == eid ) + { + found = true; + break; + } + } + if ( ! found) { (*it).second.push_back(eid); } + } + */ + keepR2ItemAssociation(eid, scenarioId); + } + else + { + itemDropToEgg.push_back(item); + } + } + else + { + nlwarning("CR2MissionItem::giveMissionItem: can't create item %s", items[j].SheetId.toString().c_str()); + } } } if(itemDropToEgg.size() != 0) @@ -273,24 +286,36 @@ void CR2MissionItem::destroyMissionItem(const NLMISC::CEntityId &eid, const std: CSheetId itemSheetId = items[j].SheetId; uint32 quantity = items[j].Quantity; - CInventoryPtr inv = c->getInventory(INVENTORIES::bag); - nlassert( inv != NULL ); - _destroyMissionItem( inv, itemSheetId, quantity ); - if( quantity > 0) + const CStaticItem* sitem = CSheets::getForm(items[j].SheetId); + if (sitem == NULL) { - for( uint32 j = INVENTORIES::pet_animal; j < INVENTORIES::max_pet_animal; ++j ) - { - inv = c->getInventory((INVENTORIES::TInventory)j); - nlassert(inv != NULL); - _destroyMissionItem( inv, itemSheetId, quantity ); - if(quantity == 0) - break; - } + nlwarning("Attempted to take deprecated sitem sheet %s from player character %s", items[j].SheetId.toString().c_str(), c->getName().toUtf8().c_str()); + } + else if (sitem->Family != ITEMFAMILY::SCROLL_R2) + { + nlwarning("Attempted hack to take non-R2 item %s from player character %s", items[j].SheetId.toString().c_str(), c->getName().toUtf8().c_str()); + } + else + { + CInventoryPtr inv = c->getInventory(INVENTORIES::bag); + nlassert( inv != NULL ); + _destroyMissionItem( inv, itemSheetId, quantity ); + if( quantity > 0) + { + for( uint32 j = INVENTORIES::pet_animal; j < INVENTORIES::max_pet_animal; ++j ) + { + inv = c->getInventory((INVENTORIES::TInventory)j); + nlassert(inv != NULL); + _destroyMissionItem( inv, itemSheetId, quantity ); + if(quantity == 0) + break; + } + } + // TODO: if we can't found enough quantity of item to destroy, we need decide if we must manage that as an error + // if(quantity > 0) + // { + // } } - // TODO: if we can't found enough quantity of item to destroy, we need decide if we must manage that as an error -// if(quantity > 0) -// { -// } } } } From 1d1fb14fc8d244528ffb1407c31ac1d0bd974fa1 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Sat, 7 Sep 2013 15:53:00 +0200 Subject: [PATCH 171/313] Comment if-check around commented unused client-side speed limiting code --- code/ryzom/client/src/user_entity.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/code/ryzom/client/src/user_entity.cpp b/code/ryzom/client/src/user_entity.cpp index 5f7fc3868..c8652fd43 100644 --- a/code/ryzom/client/src/user_entity.cpp +++ b/code/ryzom/client/src/user_entity.cpp @@ -3552,12 +3552,13 @@ void CUserEntity::CSpeedFactor::update(ICDBNode *node) // virtual //nlinfo("SpeedFactor changed to %f / %"NL_I64"u", _Value, leaf->getValue64()); // clamp the value (2.0 is the egg item or the level 6 speed up power up, nothing should be faster) - if(_Value > 2.0f) - { + // commented because ring editor speed is in fact faster + //if(_Value > 2.0f) + //{ //nlwarning("HACK: you try to change the speed factor to %f", _Value); //nlstop; //_Value = 2.0f; - } + //} }// CSpeedFactor::update // From 9d3bb1c76b9eda03c9761b5d91cbcdc0a23dfe02 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Sat, 7 Sep 2013 15:53:10 +0200 Subject: [PATCH 172/313] Add distSqTo to CAIVector --- .../server/src/ai_service/ai_pos_mirror_inline.h | 15 +++++++++++++++ .../server/src/ai_service/ai_vector_mirror.h | 3 +++ code/ryzom/server/src/ai_share/ai_vector.h | 1 + 3 files changed, 19 insertions(+) diff --git a/code/ryzom/server/src/ai_service/ai_pos_mirror_inline.h b/code/ryzom/server/src/ai_service/ai_pos_mirror_inline.h index b75c54f1a..4185adafa 100644 --- a/code/ryzom/server/src/ai_service/ai_pos_mirror_inline.h +++ b/code/ryzom/server/src/ai_service/ai_pos_mirror_inline.h @@ -155,6 +155,11 @@ inline double CAIVectorMirror::distTo(const CAIPos &dest) const return (dest-CAIVector(*this)).norm(); } +inline double CAIVectorMirror::distSqTo(const CAIPos &dest) const +{ + return (dest-CAIVector(*this)).sqrnorm(); +} + inline double CAIVectorMirror::quickDistTo(const CAIPos &dest) const { double dx=fabs((dest.x()-x()).asDouble()), dy=fabs((dest.y()-y()).asDouble()); @@ -172,6 +177,11 @@ inline double CAIVectorMirror::distTo(const CAIVector &dest) const return (dest-CAIVector(*this)).norm(); } +inline double CAIVectorMirror::distSqTo(const CAIVector &dest) const +{ + return (dest-CAIVector(*this)).sqrnorm(); +} + inline double CAIVectorMirror::quickDistTo(const CAIVector &dest) const { double dx=fabs((dest.x()-x()).asDouble()), dy=fabs((dest.y()-y()).asDouble()); @@ -189,6 +199,11 @@ inline double CAIVectorMirror::distTo(const CAIVectorMirror &dest) const return (dest-*this).norm(); } +inline double CAIVectorMirror::distSqTo(const CAIVectorMirror &dest) const +{ + return (dest-*this).sqrnorm(); +} + inline double CAIVectorMirror::quickDistTo(const CAIVectorMirror &dest) const { double dx=fabs((dest.x()-x()).asDouble()), dy=fabs((dest.y()-y()).asDouble()); return (dx>dy)? (dx+dy/2): (dy+dx/2); diff --git a/code/ryzom/server/src/ai_service/ai_vector_mirror.h b/code/ryzom/server/src/ai_service/ai_vector_mirror.h index dc03305bc..0464de4fb 100644 --- a/code/ryzom/server/src/ai_service/ai_vector_mirror.h +++ b/code/ryzom/server/src/ai_service/ai_vector_mirror.h @@ -83,14 +83,17 @@ public: // Methods. // a few handy utility methods inline CAngle angleTo(const CAIPos &dest) const; inline double distTo(const CAIPos &dest) const; + inline double distSqTo(const CAIPos &dest) const; inline double quickDistTo(const CAIPos &dest) const; inline CAngle angleTo(const CAIVector &dest) const; inline double distTo(const CAIVector &dest) const; + inline double distSqTo(const CAIVector &dest) const; inline double quickDistTo(const CAIVector &dest) const; inline CAngle angleTo(const CAIVectorMirror &dest) const; inline double distTo(const CAIVectorMirror &dest) const; + inline double distSqTo(const CAIVectorMirror &dest) const; inline double quickDistTo(const CAIVectorMirror &dest) const; protected: diff --git a/code/ryzom/server/src/ai_share/ai_vector.h b/code/ryzom/server/src/ai_share/ai_vector.h index 34a82bb82..8c818b70a 100644 --- a/code/ryzom/server/src/ai_share/ai_vector.h +++ b/code/ryzom/server/src/ai_share/ai_vector.h @@ -130,6 +130,7 @@ public: // Methods. template CAngle angleTo(const V &v) const { return CAngle(atan2((v.y()-y()).asDouble(), (v.x()-x()).asDouble())); } template double distTo(const V &v) const { return (*this-v).norm(); } + template double distSqTo(const V &v) const { return (*this-v).sqrnorm(); } template double quickDistTo(const V &v) const { double dx=fabs((v.x()-x()).asDouble()), dy=fabs((v.y()-y()).asDouble()); return (dx>dy)? (dx+dy/2): (dy+dx/2); } From 576e508860817c66366283f5a682c162f85dc3c2 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Sat, 7 Sep 2013 15:53:17 +0200 Subject: [PATCH 173/313] Use distSqTo instead of distTo --- .../src/ai_service/ai_profile_fauna.cpp | 22 +++++++++++-------- .../server/src/ai_service/ai_profile_fauna.h | 6 ++--- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/code/ryzom/server/src/ai_service/ai_profile_fauna.cpp b/code/ryzom/server/src/ai_service/ai_profile_fauna.cpp index 41b0504d9..354cccdfc 100644 --- a/code/ryzom/server/src/ai_service/ai_profile_fauna.cpp +++ b/code/ryzom/server/src/ai_service/ai_profile_fauna.cpp @@ -310,9 +310,9 @@ void CWanderFaunaProfile::updateProfile(uint ticksSinceLastUpdate) CFollowPathContext fpcWanderFaunaProfileUpdate("WanderFaunaProfileUpdate"); // calculate distance from bot position to magnet point (used in all the different processes) - _magnetDist=_Bot->pos().distTo(_Bot->spawnGrp().magnetPos()); - - if (_magnetDist>_Bot->spawnGrp().magnetRadiusFar()) + _magnetDistSq=_Bot->pos().distSqTo(_Bot->spawnGrp().magnetPos()); + double grpMagnetRadiusFar=_Bot->spawnGrp().magnetRadiusFar(); + if (_magnetDistSq>(grpMagnetRadiusFar*grpMagnetRadiusFar)) { _Bot->setMode( MBEHAV::NORMAL ); @@ -405,11 +405,12 @@ void CGrazeFaunaProfile::updateProfile(uint ticksSinceLastUpdate) CFollowPathContext fpcGrazeFaunaProfileUpdate("GrazeFaunaProfileUpdate"); // calculate distance from bot position to magnet point (used in all the different processes) - _magnetDist=_Bot->pos().distTo(_Bot->spawnGrp().magnetPos()); + _magnetDistSq=_Bot->pos().distSqTo(_Bot->spawnGrp().magnetPos()); if (!_ArrivedInZone) { - if (_magnetDist>_Bot->spawnGrp().magnetRadiusFar()) + float grpMagnetRadiusFar=_Bot->spawnGrp().magnetRadiusFar(); + if (_magnetDistSq>(grpMagnetRadiusFar*grpMagnetRadiusFar)) { _Bot->setMode( MBEHAV::NORMAL ); @@ -501,7 +502,8 @@ void CGrazeFaunaProfile::updateProfile(uint ticksSinceLastUpdate) } // && _state==0 ) // means wait in movementmagnet. - if ( _magnetDist<=_Bot->spawnGrp().magnetRadiusNear() + const float grpMagnetRadiusNear=_Bot->spawnGrp().magnetRadiusNear(); + if ( _magnetDistSq<=(grpMagnetRadiusNear*grpMagnetRadiusNear) && _MovementMagnet->getMovementType()==CMovementMagnet::Movement_Anim) { if (_Bot->getPersistent().grp().getType()==AITYPES::FaunaTypePredator) @@ -564,11 +566,12 @@ void CRestFaunaProfile::updateProfile(uint ticksSinceLastUpdate) CFollowPathContext fpcRestFaunaProfileUpdate("RestFaunaProfileUpdate"); // calculate distance from bot position to magnet point (used in all the different processes) - _magnetDist=_Bot->pos().distTo(_Bot->spawnGrp().magnetPos()); + _magnetDistSq=_Bot->pos().distSqTo(_Bot->spawnGrp().magnetPos()); if (!_ArrivedInZone) { - if (_magnetDist>_Bot->spawnGrp().magnetRadiusFar()) + float grpMagnetRadiusFar=_Bot->spawnGrp().magnetRadiusFar(); + if (_magnetDistSq>(grpMagnetRadiusFar*grpMagnetRadiusFar)) { if (!_OutOfMagnet) { @@ -656,7 +659,8 @@ void CRestFaunaProfile::updateProfile(uint ticksSinceLastUpdate) break; } - if ( _magnetDist<=_Bot->spawnGrp().magnetRadiusNear() + const float grpMagnetRadiusNear=_Bot->spawnGrp().magnetRadiusNear(); + if ( _magnetDistSq<=(grpMagnetRadiusNear*grpMagnetRadiusNear) && _MovementMagnet->getMovementType()==CMovementMagnet::Movement_Anim) { _Bot->setMode(MBEHAV::REST); diff --git a/code/ryzom/server/src/ai_service/ai_profile_fauna.h b/code/ryzom/server/src/ai_service/ai_profile_fauna.h index 1c3499080..e85f2b709 100644 --- a/code/ryzom/server/src/ai_service/ai_profile_fauna.h +++ b/code/ryzom/server/src/ai_service/ai_profile_fauna.h @@ -137,7 +137,7 @@ public: protected: RYAI_MAP_CRUNCH::TAStarFlag _DenyFlags; CSpawnBotFauna* _Bot; - double _magnetDist; ///< distance from bot to his magnet at last move + double _magnetDistSq; ///< square distance from bot to his magnet at last move static CFaunaProfileFloodLogger _FloodLogger; }; @@ -166,7 +166,7 @@ private: CAITimer _CycleTimer; uint _CycleTimerBaseTime; bool _ArrivedInZone; - double _magnetDist; ///< distance from bot to his magnet at last move + double _magnetDistSq; ///< square distance from bot to his magnet at last move RYAI_MAP_CRUNCH::TAStarFlag _DenyFlags; static CFaunaProfileFloodLogger _FloodLogger; }; @@ -196,7 +196,7 @@ private: CAITimer _CycleTimer; uint _CycleTimerBaseTime; bool _ArrivedInZone; - double _magnetDist; // distance from bot to his magnet at last move + double _magnetDistSq; // square distance from bot to his magnet at last move RYAI_MAP_CRUNCH::TAStarFlag _DenyFlags; static CFaunaProfileFloodLogger _FloodLogger; }; From ddbb65bd3465d3938c598cf7d6d5692fc9334fee Mon Sep 17 00:00:00 2001 From: kaetemi Date: Sat, 7 Sep 2013 16:05:28 +0200 Subject: [PATCH 174/313] Fix compile error --- code/nel/src/misc/bitmap.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/code/nel/src/misc/bitmap.cpp b/code/nel/src/misc/bitmap.cpp index cfe8cab89..f32980508 100644 --- a/code/nel/src/misc/bitmap.cpp +++ b/code/nel/src/misc/bitmap.cpp @@ -19,6 +19,7 @@ #include "nel/misc/bitmap.h" #include "nel/misc/stream.h" #include "nel/misc/file.h" +#include "nel/misc/system_info.h" // Define this to force all bitmap white (debug) // #define NEL_ALL_BITMAP_WHITE From 6489e9e04fe9f50494427d898cb16d1e072ba283 Mon Sep 17 00:00:00 2001 From: kervala Date: Sat, 7 Sep 2013 17:01:06 +0200 Subject: [PATCH 175/313] Removed: S3TC module because not used anymore --- code/CMakeModules/FindS3TC.cmake | 50 -------------------------------- 1 file changed, 50 deletions(-) delete mode 100644 code/CMakeModules/FindS3TC.cmake diff --git a/code/CMakeModules/FindS3TC.cmake b/code/CMakeModules/FindS3TC.cmake deleted file mode 100644 index f4efb8c5e..000000000 --- a/code/CMakeModules/FindS3TC.cmake +++ /dev/null @@ -1,50 +0,0 @@ -# - Locate S3TC library -# This module defines -# S3TC_LIBRARY, the library to link against -# S3TC_FOUND, if false, do not try to link to S3TC -# S3TC_INCLUDE_DIR, where to find headers. - -IF(S3TC_LIBRARY AND S3TC_INCLUDE_DIR) - # in cache already - SET(S3TC_FIND_QUIETLY TRUE) -ENDIF(S3TC_LIBRARY AND S3TC_INCLUDE_DIR) - - -FIND_PATH(S3TC_INCLUDE_DIR - s3_intrf.h - PATHS - $ENV{S3TC_DIR}/include - /usr/local/include - /usr/include - /sw/include - /opt/local/include - /opt/csw/include - /opt/include - PATH_SUFFIXES S3TC -) - -FIND_LIBRARY(S3TC_LIBRARY - NAMES s3tc libs3tc - PATHS - $ENV{S3TC_DIR}/lib - /usr/local/lib - /usr/lib - /usr/local/X11R6/lib - /usr/X11R6/lib - /sw/lib - /opt/local/lib - /opt/csw/lib - /opt/lib - /usr/freeware/lib64 -) - -IF(S3TC_LIBRARY AND S3TC_INCLUDE_DIR) - SET(S3TC_FOUND "YES") - IF(NOT S3TC_FIND_QUIETLY) - MESSAGE(STATUS "Found S3TC: ${S3TC_LIBRARY}") - ENDIF(NOT S3TC_FIND_QUIETLY) -ELSE(S3TC_LIBRARY AND S3TC_INCLUDE_DIR) - IF(NOT S3TC_FIND_QUIETLY) - MESSAGE(STATUS "Warning: Unable to find S3TC!") - ENDIF(NOT S3TC_FIND_QUIETLY) -ENDIF(S3TC_LIBRARY AND S3TC_INCLUDE_DIR) From d54774f978d295cd7bcfa05317279df3b4b689b7 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Sat, 7 Sep 2013 18:33:18 +0200 Subject: [PATCH 176/313] Add abstract gpu program and source classes --- code/nel/include/nel/3d/gpu_program.h | 233 +++++++++++++++++++ code/nel/include/nel/3d/gpu_program_source.h | 91 ++++++++ code/nel/src/3d/CMakeLists.txt | 6 +- code/nel/src/3d/gpu_program.cpp | 191 +++++++++++++++ code/nel/src/3d/gpu_program_source.cpp | 47 ++++ 5 files changed, 567 insertions(+), 1 deletion(-) create mode 100644 code/nel/include/nel/3d/gpu_program.h create mode 100644 code/nel/include/nel/3d/gpu_program_source.h create mode 100644 code/nel/src/3d/gpu_program.cpp create mode 100644 code/nel/src/3d/gpu_program_source.cpp diff --git a/code/nel/include/nel/3d/gpu_program.h b/code/nel/include/nel/3d/gpu_program.h new file mode 100644 index 000000000..63d6eea88 --- /dev/null +++ b/code/nel/include/nel/3d/gpu_program.h @@ -0,0 +1,233 @@ +/** + * \file gpu_program.h + * \brief CGPUProgram + * \date 2013-09-07 15:00GMT + * \author Jan Boon (Kaetemi) + * IGPUProgram + */ + +/* + * Copyright (C) 2013 by authors + * + * This file is part of NL3D. + * NL3D is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * NL3D 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 Affero General + * Public License for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with NL3D. If not, see + * . + */ + +#ifndef NL3D_GPU_PROGRAM_H +#define NL3D_GPU_PROGRAM_H +#include + +// STL includes + +// NeL includes +#include + +// Project includes + +namespace NL3D { + +/** + * \brief CVertexProgramInfo + * \date 2013-09-07 15:00GMT + * \author Jan Boon (Kaetemi) + * Read-only information structure. + */ +struct CVertexProgramInfo +{ +public: + std::string DisplayName; + + enum TFeatures + { + // World + // transform + + // Lights + Ambient = 0x0001, + Sun = 0x0002, + PointLight0 = 0x0004, + PointLight1 = 0x0008, + PointLight2 = 0x0010, + + // Lights, additional parameters for user shaders + /// Adds an enabled/disabled parameter to all of the lights + DynamicLights = 0x0020, + }; + + /// Bitfield containing features used by this vertex program. + uint Features; + + /// Indices of parameters used by features. + + /// Lights, NeL supports: + /// - Ambient light + uint AmbientIdx; // (Ambient) + /// - One directional light + uint SunDirectionIdx; // (Sun) + uint SunDiffuseIdx; // (Sun) + /// - Zero to three point lights + uint PointLight0PositionIdx; // (PointLight0) + uint PointLight0DiffuseIdx; // (PointLight0) + uint PointLight1PositionIdx; // (PointLight1) + uint PointLight1DiffuseIdx; // (PointLight1) + uint PointLight2PositionIdx; // (PointLight2) + uint PointLight2DiffuseIdx; // (PointLight2) + + /// DynamicLights + uint SunEnabledIdx; // (DynamicLights && Sun) + uint PointLight0EnabledIdx; // (DynamicLights && PointLight0) + uint PointLight1EnabledIdx; // (DynamicLights && PointLight1) + uint PointLight2EnabledIdx; // (DynamicLights && PointLight2) +}; + +/** + * \brief CPixelProgramInfo + * \date 2013-09-07 15:00GMT + * \author Jan Boon (Kaetemi) + * Read-only information structure. + */ +struct CPixelProgramInfo +{ +public: + std::string DisplayName; + + enum TFeatures + { + /// Use texture stages from CMaterial as texture parameters + MaterialTextures = 0x0001, + /// Set driver fog parameters on this program + Fog = 0x0002, + /// Adds an enabled/disabled parameter to the fog, for user shaders + DynamicFog = 0x0004, + }; + + // Bitfield containing features used by this pixel program + uint Features; + + // Indices of parameters used by features + uint FogEnabledIdx; // (Fog && DynamicFog) nlFogEnabled, fog enabled + uint FogStartEndIdx; // (Fog) nlFogStartEnd, start and end of fog + uint FogColorIdx; // (Fog) nlFogColor, fog color +}; + +/** + * \brief CGeometryProgramInfo + * \date 2013-09-07 15:00GMT + * \author Jan Boon (Kaetemi) + * Read-only information structure. + */ +struct CGeometryProgramInfo +{ +public: + /*enum TFeatures + { + + };*/ + /// Bitfield containing features used by this pixel program. + // uint Features; + /// Indices of parameters used by features. +}; + +/** + * \brief CGPUProgram + * \date 2013-09-07 15:00GMT + * \author Jan Boon (Kaetemi) + * A compiled GPU program + */ +class IGPUProgram : public NLMISC::CRefCount +{ +public: + enum TProfile + { + // types + // Vertex Shader = 0x01 + // Pixel Shader = 0x02 + // Geometry Shader = 0x03 + + // nel - 0x31,type,bitfield + nelvp = 0x31010001, // VP supported by CVertexProgramParser, similar to arbvp1, can be translated to vs_1_1 + + // direct3d - 0xD9,type,major,minor + // vertex programs + vs_1_1 = 0xD9010101, + vs_2_0 = 0xD9010200, + // vs_2_sw = 0xD9010201, // not sure... + // vs_2_x = 0xD9010202, // not sure... + // vs_3_0 = 0xD9010300, // not supported + // pixel programs + ps_1_1 = 0xD9020101, + ps_1_2 = 0xD9020102, + ps_1_3 = 0xD9020103, + ps_1_4 = 0xD9020104, + ps_2_0 = 0xD9020200, + // ps_2_x = 0xD9020201, // not sure... + // ps_3_0 = 0xD9020300, // not supported + + // opengl - 0x61,type,bitfield + // vertex programs + // vp20 = 0x61010001, // NV_vertex_program1_1, outdated + arbvp1 = 0x61010002, // ARB_vertex_program + vp30 = 0x61010004, // NV_vertex_program2 + vp40 = 0x61010008, // NV_vertex_program3 + NV_fragment_program3 + gp4vp = 0x61010010, // NV_gpu_program4 + gp5vp = 0x61010020, // NV_gpu_program5 + // pixel programs + // fp20 = 0x61020001, // very limited and outdated, unnecessary + // fp30 = 0x61020002, // NV_fragment_program, now arbfp1, redundant + arbfp1 = 0x61020004, // ARB_fragment_program + fp40 = 0x61020008, // NV_fragment_program2, arbfp1 with "OPTION NV_fragment_program2;\n" + gp4fp = 0x61020010, // NV_gpu_program4 + gp5fp = 0x61020020, // NV_gpu_program5 + // geometry programs + gp4gp = 0x61030001, // NV_gpu_program4 + gp5gp = 0x61030001, // NV_gpu_program5 + + // glsl - 0x65,type,version + glsl330v = 0x65010330, // GLSL vertex program version 330 + glsl330f = 0x65020330, // GLSL fragment program version 330 + glsl330g = 0x65030330, // GLSL geometry program version 330 + }; + +public: + IGPUProgram(); + virtual ~IGPUProgram(); + + /// Get the idx of a parameter (ogl: uniform, d3d: constant, etcetera) by name. Invalid name returns ~0 + virtual uint getParamIdx(char *name) const = 0; + + void buildVPInfo(const char *displayName, uint features); + void buildPPInfo(const char *displayName, uint features); + void buildGPInfo(const char *displayName, uint features); + + inline const CVertexProgramInfo *getVPInfo() const { return Info.VertexProgram; } + inline const CPixelProgramInfo *getPPInfo() const { return Info.PixelProgram; } + inline const CGeometryProgramInfo *getGPInfo() const { return Info.GeometryProgram; } + +private: + union + { + CVertexProgramInfo *VertexProgram; + CPixelProgramInfo *PixelProgram; + CGeometryProgramInfo *GeometryProgram; + void *Ptr; + } Info; + +}; /* class IGPUProgram */ + +} /* namespace NL3D */ + +#endif /* #ifndef NL3D_GPU_PROGRAM_H */ + +/* end of file */ diff --git a/code/nel/include/nel/3d/gpu_program_source.h b/code/nel/include/nel/3d/gpu_program_source.h new file mode 100644 index 000000000..7e6ba81fa --- /dev/null +++ b/code/nel/include/nel/3d/gpu_program_source.h @@ -0,0 +1,91 @@ +/** + * \file gpu_program_source.h + * \brief CGPUProgramSource + * \date 2013-09-07 14:54GMT + * \author Jan Boon (Kaetemi) + * CGPUProgramSource + */ + +/* + * Copyright (C) 2013 by authors + * + * This file is part of NL3D. + * NL3D is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * NL3D 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 Affero General + * Public License for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with NL3D. If not, see + * . + */ + +#ifndef NL3D_GPU_PROGRAM_SOURCE_H +#define NL3D_GPU_PROGRAM_SOURCE_H +#include + +// STL includes + +// NeL includes +#include + +// Project includes +#include + +namespace NL3D { + +/** + * \brief CGPUProgramSource + * \date 2013-09-07 14:54GMT + * \author Jan Boon (Kaetemi) + * A single GPU program with a specific profile. + */ +struct CGPUProgramSource +{ +public: + std::string DisplayName; + + /// Minimal required profile for this GPU program + IGPUProgram::TProfile Profile; + + char *SourcePtr; + /// Copy the source string + inline void setSource(char *source) { SourceCopy = source; SourcePtr = &source[0]; } + /// Set pointer to source string without copying the string + inline void setSourcePtr(char *sourcePtr) { SourceCopy.clear(); SourcePtr = sourcePtr; } + + /// CVertexProgramInfo/CPixelProgramInfo/... NeL features + uint Features; + + /// Map with known parameter indices, used for assembly programs + std::map ParamIndices; + +private: + std::string SourceCopy; + +}; /* class CGPUProgramSource */ + +/** + * \brief CGPUProgramSourceCont + * \date 2013-09-07 14:54GMT + * \author Jan Boon (Kaetemi) + * Container for the source code of a single GPU program, allowing + * variations in different language profiles. + */ +struct CGPUProgramSourceCont +{ +public: + std::vector Sources; + +}; /* class CGPUProgramSourceCont */ + +} /* namespace NL3D */ + +#endif /* #ifndef NL3D_GPU_PROGRAM_SOURCE_H */ + +/* end of file */ diff --git a/code/nel/src/3d/CMakeLists.txt b/code/nel/src/3d/CMakeLists.txt index 2769d8e2d..fb5d5851a 100644 --- a/code/nel/src/3d/CMakeLists.txt +++ b/code/nel/src/3d/CMakeLists.txt @@ -166,7 +166,11 @@ SOURCE_GROUP(Driver FILES vertex_program_parse.cpp ../../include/nel/3d/vertex_program_parse.h pixel_program.cpp - ../../include/nel/3d/pixel_program.h) + ../../include/nel/3d/pixel_program.h + gpu_program.cpp + ../../include/nel/3d/gpu_program.h + gpu_program_source.cpp + ../../include/nel/3d/gpu_program_source.h) SOURCE_GROUP(Font FILES computed_string.cpp diff --git a/code/nel/src/3d/gpu_program.cpp b/code/nel/src/3d/gpu_program.cpp new file mode 100644 index 000000000..457d8b1a1 --- /dev/null +++ b/code/nel/src/3d/gpu_program.cpp @@ -0,0 +1,191 @@ +/** + * \file gpu_program.cpp + * \brief CGPUProgram + * \date 2013-09-07 15:00GMT + * \author Jan Boon (Kaetemi) + * CGPUProgram + */ + +/* + * Copyright (C) 2013 by authors + * + * This file is part of NL3D. + * NL3D is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * NL3D 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 Affero General + * Public License for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with NL3D. If not, see + * . + */ + +#include +#include + +// STL includes + +// NeL includes +// #include +#include + +// Project includes + +using namespace std; +// using namespace NLMISC; + +namespace NL3D { + +IGPUProgram::IGPUProgram() +{ + Info.Ptr = NULL; +} + +IGPUProgram::~IGPUProgram() +{ + delete Info.Ptr; + Info.Ptr = NULL; +} + +void IGPUProgram::buildVPInfo(const char *displayName, uint features) +{ + nlassert(Info.VertexProgram == NULL); + Info.VertexProgram = new CVertexProgramInfo(); + CVertexProgramInfo *info = Info.VertexProgram; + info->DisplayName = displayName; + info->Features = features; + if (features & CVertexProgramInfo::Ambient) + { + info->AmbientIdx = getParamIdx("nlAmbient"); + if (info->AmbientIdx == ~0) + { + nlwarning("Missing 'nlAmbient' in gpu program '%s', Ambient disabled", displayName); + info->Features &= ~CVertexProgramInfo::Ambient; + } + } + if (features & CVertexProgramInfo::Sun) + { + if (features & CVertexProgramInfo::DynamicLights) + { + info->SunEnabledIdx = getParamIdx("nlSunEnabled"); + if (info->SunEnabledIdx == ~0) + { + nlwarning("Missing 'nlSunEnabled' in gpu program '%s', DynamicLights disabled", displayName); + info->Features &= ~CVertexProgramInfo::DynamicLights; + } + } + info->SunDirectionIdx = getParamIdx("nlSunDirection"); + info->SunDiffuseIdx = getParamIdx("nlSunDiffuse"); + if (info->SunDirectionIdx == ~0 + || info->SunDiffuseIdx == ~0) + { + nlwarning("Missing 'nlSunDirection/nlSunDiffuse' in gpu program '%s', Sun disabled", displayName); + info->Features &= ~CVertexProgramInfo::Sun; + } + } + if (features & CVertexProgramInfo::PointLight0) + { + if (features & CVertexProgramInfo::DynamicLights) + { + info->PointLight0EnabledIdx = getParamIdx("nlPointLight0Enabled"); + if (info->PointLight0EnabledIdx == ~0) + { + nlwarning("Missing 'nlPointLight0Enabled' in gpu program '%s', DynamicLights disabled", displayName); + info->Features &= ~CVertexProgramInfo::DynamicLights; + } + } + info->PointLight0PositionIdx = getParamIdx("nlPointLight0Position"); + info->PointLight0DiffuseIdx = getParamIdx("nlPointLight0Diffuse"); + if (info->PointLight0PositionIdx == ~0 + || info->PointLight0DiffuseIdx == ~0) + { + nlwarning("Missing 'nlPointLight0Position/nlPointLight0Diffuse' in gpu program '%s', PointLight0 disabled", displayName); + info->Features &= ~CVertexProgramInfo::PointLight0; + } + } + if (features & CVertexProgramInfo::PointLight1) + { + if (features & CVertexProgramInfo::DynamicLights) + { + info->PointLight1EnabledIdx = getParamIdx("nlPointLight1Enabled"); + if (info->PointLight1EnabledIdx == ~0) + { + nlwarning("Missing 'nlPointLight1Enabled' in gpu program '%s', DynamicLights disabled", displayName); + info->Features &= ~CVertexProgramInfo::DynamicLights; + } + } + info->PointLight1PositionIdx = getParamIdx("nlPointLight1Position"); + info->PointLight1DiffuseIdx = getParamIdx("nlPointLight1Diffuse"); + if (info->PointLight1PositionIdx == ~0 + || info->PointLight1DiffuseIdx == ~0) + { + nlwarning("Missing 'nlPointLight1Position/nlPointLight1Diffuse' in gpu program '%s', PointLight1 disabled", displayName); + info->Features &= ~CVertexProgramInfo::PointLight1; + } + } + if (features & CVertexProgramInfo::PointLight2) + { + if (features & CVertexProgramInfo::DynamicLights) + { + info->PointLight2EnabledIdx = getParamIdx("nlPointLight2Enabled"); + if (info->PointLight2EnabledIdx == ~0) + { + nlwarning("Missing 'nlPointLight2Enabled' in gpu program '%s', DynamicLights disabled", displayName); + info->Features &= ~CVertexProgramInfo::DynamicLights; + } + } + info->PointLight2PositionIdx = getParamIdx("nlPointLight2Position"); + info->PointLight2DiffuseIdx = getParamIdx("nlPointLight2Diffuse"); + if (info->PointLight2PositionIdx == ~0 + || info->PointLight2DiffuseIdx == ~0) + { + nlwarning("Missing 'nlPointLight2Position/nlPointLight2Diffuse' in gpu program '%s', PointLight2 disabled", displayName); + info->Features &= ~CVertexProgramInfo::PointLight2; + } + } +} + +void IGPUProgram::buildPPInfo(const char *displayName, uint features) +{ + nlassert(Info.PixelProgram == NULL); + Info.PixelProgram = new CPixelProgramInfo(); + CPixelProgramInfo *info = Info.PixelProgram; + info->DisplayName = displayName; + info->Features = features; + if (features & CPixelProgramInfo::Fog) + { + if (features & CPixelProgramInfo::DynamicFog) + { + info->FogEnabledIdx = getParamIdx("nlFogEnabled"); + if (info->FogEnabledIdx == ~0) + { + nlwarning("Missing 'nlFogEnabled' in gpu program '%s', DynamicFog disabled", displayName); + info->Features &= ~CPixelProgramInfo::DynamicFog; + } + } + info->FogStartEndIdx = getParamIdx("nlFogStartEnd"); + info->FogColorIdx = getParamIdx("nlFogColor"); + if (info->FogStartEndIdx == ~0 + || info->FogStartEndIdx == ~0) + { + nlwarning("Missing 'nlFogStartEnd/nlFogColor' in gpu program '%s', Fog disabled", displayName); + info->Features &= ~CPixelProgramInfo::Fog; + } + } +} + +void IGPUProgram::buildGPInfo(const char *displayName, uint features) +{ + nlassert(Info.GeometryProgram == NULL); + Info.GeometryProgram = new CGeometryProgramInfo(); +} + + +} /* namespace NL3D */ + +/* end of file */ diff --git a/code/nel/src/3d/gpu_program_source.cpp b/code/nel/src/3d/gpu_program_source.cpp new file mode 100644 index 000000000..21d1cc18b --- /dev/null +++ b/code/nel/src/3d/gpu_program_source.cpp @@ -0,0 +1,47 @@ +/** + * \file gpu_program_source.cpp + * \brief CGPUProgramSource + * \date 2013-09-07 14:54GMT + * \author Jan Boon (Kaetemi) + * CGPUProgramSource + */ + +/* + * Copyright (C) 2013 by authors + * + * This file is part of NL3D. + * NL3D is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * NL3D 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 Affero General + * Public License for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with NL3D. If not, see + * . + */ + +#include +#include + +// STL includes + +// NeL includes +// #include + +// Project includes + +using namespace std; +// using namespace NLMISC; + +namespace NL3D { + +void gpu_program_source_cpp_dummy() { } + +} /* namespace NL3D */ + +/* end of file */ From 47716d7247e6479e155b5cb175f36fbb152f31ca Mon Sep 17 00:00:00 2001 From: kaetemi Date: Sat, 7 Sep 2013 20:41:07 +0200 Subject: [PATCH 177/313] Make gpu programs use abstract gpu program class --- code/nel/include/nel/3d/driver.h | 26 ++- code/nel/include/nel/3d/geometry_program.h | 83 +++++++++ code/nel/include/nel/3d/gpu_program.h | 146 ++++----------- code/nel/include/nel/3d/gpu_program_source.h | 20 +- code/nel/include/nel/3d/pixel_program.h | 99 ++++------ code/nel/include/nel/3d/shader.h | 67 ------- code/nel/include/nel/3d/vertex_program.h | 125 +++++++------ .../nel/include/nel/3d/vertex_program_parse.h | 34 ++++ code/nel/src/3d/CMakeLists.txt | 2 + code/nel/src/3d/driver.cpp | 23 ++- code/nel/src/3d/geometry_program.cpp | 59 ++++++ code/nel/src/3d/gpu_program.cpp | 171 ++++-------------- code/nel/src/3d/pixel_program.cpp | 52 ++++-- code/nel/src/3d/shader.cpp | 81 --------- code/nel/src/3d/stereo_debugger.cpp | 14 +- code/nel/src/3d/stereo_ovr.cpp | 20 +- code/nel/src/3d/vertex_program.cpp | 129 +++++++++++-- 17 files changed, 552 insertions(+), 599 deletions(-) create mode 100644 code/nel/include/nel/3d/geometry_program.h create mode 100644 code/nel/src/3d/geometry_program.cpp diff --git a/code/nel/include/nel/3d/driver.h b/code/nel/include/nel/3d/driver.h index 655a9e62b..3cb0c0349 100644 --- a/code/nel/include/nel/3d/driver.h +++ b/code/nel/include/nel/3d/driver.h @@ -151,9 +151,10 @@ protected: TVBDrvInfoPtrList _VBDrvInfos; TIBDrvInfoPtrList _IBDrvInfos; TPolygonMode _PolygonMode; - TVtxPrgDrvInfoPtrList _VtxPrgDrvInfos; - TPixelPrgDrvInfoPtrList _PixelPrgDrvInfos; - TShaderDrvInfoPtrList _ShaderDrvInfos; + TGPUPrgDrvInfoPtrList _GPUPrgDrvInfos; + // TPixelPrgDrvInfoPtrList _PixelPrgDrvInfos; + // TGeomPrgDrvInfoPtrList _GeomPrgDrvInfos; + // TShaderDrvInfoPtrList _ShaderDrvInfos; uint _ResetCounter; @@ -317,11 +318,6 @@ public: virtual bool setupMaterial(CMaterial& mat)=0; - /** - * Activate a shader, NULL to disable the current shader. - */ - virtual bool activeShader(CShader *shd)=0; - /** Special for Faster Specular Setup. Call this between lot of primitives rendered with Specular Materials. * Visual Errors may arise if you don't correctly call endSpecularBatch(). */ @@ -1303,9 +1299,10 @@ protected: friend class CTextureDrvShare; friend class ITextureDrvInfos; friend class IMaterialDrvInfos; - friend class IVertexProgramDrvInfos; - friend class IPixelProgramDrvInfos; - friend class IShaderDrvInfos; + // friend class IVertexProgramDrvInfos; + // friend class IPixelProgramDrvInfos; + // friend class IShaderDrvInfos; + friend class IGPUProgramDrvInfos; /// remove ptr from the lists in the driver. void removeVBDrvInfoPtr(ItVBDrvInfoPtrList vbDrvInfoIt); @@ -1313,9 +1310,10 @@ protected: void removeTextureDrvInfoPtr(ItTexDrvInfoPtrMap texDrvInfoIt); void removeTextureDrvSharePtr(ItTexDrvSharePtrList texDrvShareIt); void removeMatDrvInfoPtr(ItMatDrvInfoPtrList shaderIt); - void removeShaderDrvInfoPtr(ItShaderDrvInfoPtrList shaderIt); - void removeVtxPrgDrvInfoPtr(ItVtxPrgDrvInfoPtrList vtxPrgDrvInfoIt); - void removePixelPrgDrvInfoPtr(ItPixelPrgDrvInfoPtrList pixelPrgDrvInfoIt); + // void removeShaderDrvInfoPtr(ItShaderDrvInfoPtrList shaderIt); + // void removeVtxPrgDrvInfoPtr(ItVtxPrgDrvInfoPtrList vtxPrgDrvInfoIt); + // void removePixelPrgDrvInfoPtr(ItPixelPrgDrvInfoPtrList pixelPrgDrvInfoIt); + void removeGPUPrgDrvInfoPtr(ItGPUPrgDrvInfoPtrList gpuPrgDrvInfoIt); private: bool _StaticMemoryToVRAM; diff --git a/code/nel/include/nel/3d/geometry_program.h b/code/nel/include/nel/3d/geometry_program.h new file mode 100644 index 000000000..ac9451b27 --- /dev/null +++ b/code/nel/include/nel/3d/geometry_program.h @@ -0,0 +1,83 @@ +/** \file geometry_program.h + * Geometry program definition + */ + +/* Copyright, 2000, 2001 Nevrax Ltd. + * + * This file is part of NEVRAX NEL. + * NEVRAX NEL 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, or (at your option) + * any later version. + + * NEVRAX NEL 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 NEVRAX NEL; see the file COPYING. If not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#ifndef NL_GEOMETRY_PROGRAM_H +#define NL_GEOMETRY_PROGRAM_H + +#include +#include +#include +#include + +#include + +namespace NL3D { + +/** + * \brief CGeometryProgramInfo + * \date 2013-09-07 15:00GMT + * \author Jan Boon (Kaetemi) + * Read-only information structure. + */ +struct CGeometryProgramInfo +{ +public: + std::string DisplayName; + + /*enum TFeatures + { + + };*/ + + // Bitfield containing features used by this geometry program + uint Features; + + // Indices of parameters used by features + // ... +}; + +class CGeometryProgram : public IGPUProgram +{ +public: + /// Constructor + CGeometryProgram(CGPUProgramSourceCont *programSource); + /// Destructor + virtual ~CGeometryProgram (); + + /// Build feature information + void buildInfo(const char *displayName, uint features); + /// Get feature information + inline const CGeometryProgramInfo *getInfo() const { return _Info; } + +private: + + /// Feature information + CGeometryProgramInfo *_Info; +}; + +} // NL3D + + +#endif // NL_GEOMETRY_PROGRAM_H + +/* End of vertex_program.h */ diff --git a/code/nel/include/nel/3d/gpu_program.h b/code/nel/include/nel/3d/gpu_program.h index 63d6eea88..3df13ac3d 100644 --- a/code/nel/include/nel/3d/gpu_program.h +++ b/code/nel/include/nel/3d/gpu_program.h @@ -1,6 +1,6 @@ /** * \file gpu_program.h - * \brief CGPUProgram + * \brief IGPUProgram * \date 2013-09-07 15:00GMT * \author Jan Boon (Kaetemi) * IGPUProgram @@ -38,110 +38,33 @@ namespace NL3D { -/** - * \brief CVertexProgramInfo - * \date 2013-09-07 15:00GMT - * \author Jan Boon (Kaetemi) - * Read-only information structure. - */ -struct CVertexProgramInfo +// List typedef. +class IDriver; +class IGPUProgramDrvInfos; +typedef std::list TGPUPrgDrvInfoPtrList; +typedef TGPUPrgDrvInfoPtrList::iterator ItGPUPrgDrvInfoPtrList; + +// Class for interaction of vertex program with Driver. +// IGPUProgramDrvInfos represent the real data of the GPU program, stored into the driver (eg: just a GLint for opengl). +class IGPUProgramDrvInfos : public NLMISC::CRefCount { +private: + IDriver *_Driver; + ItGPUPrgDrvInfoPtrList _DriverIterator; + public: - std::string DisplayName; + IGPUProgramDrvInfos (IDriver *drv, ItGPUPrgDrvInfoPtrList it); + // The virtual dtor is important. + virtual ~IGPUProgramDrvInfos(void); - enum TFeatures - { - // World - // transform - - // Lights - Ambient = 0x0001, - Sun = 0x0002, - PointLight0 = 0x0004, - PointLight1 = 0x0008, - PointLight2 = 0x0010, - - // Lights, additional parameters for user shaders - /// Adds an enabled/disabled parameter to all of the lights - DynamicLights = 0x0020, - }; - - /// Bitfield containing features used by this vertex program. - uint Features; - - /// Indices of parameters used by features. - - /// Lights, NeL supports: - /// - Ambient light - uint AmbientIdx; // (Ambient) - /// - One directional light - uint SunDirectionIdx; // (Sun) - uint SunDiffuseIdx; // (Sun) - /// - Zero to three point lights - uint PointLight0PositionIdx; // (PointLight0) - uint PointLight0DiffuseIdx; // (PointLight0) - uint PointLight1PositionIdx; // (PointLight1) - uint PointLight1DiffuseIdx; // (PointLight1) - uint PointLight2PositionIdx; // (PointLight2) - uint PointLight2DiffuseIdx; // (PointLight2) - - /// DynamicLights - uint SunEnabledIdx; // (DynamicLights && Sun) - uint PointLight0EnabledIdx; // (DynamicLights && PointLight0) - uint PointLight1EnabledIdx; // (DynamicLights && PointLight1) - uint PointLight2EnabledIdx; // (DynamicLights && PointLight2) + virtual uint getParamIdx(char *name) const { return ~0; }; // STEREO_TODO }; -/** - * \brief CPixelProgramInfo - * \date 2013-09-07 15:00GMT - * \author Jan Boon (Kaetemi) - * Read-only information structure. - */ -struct CPixelProgramInfo -{ -public: - std::string DisplayName; - - enum TFeatures - { - /// Use texture stages from CMaterial as texture parameters - MaterialTextures = 0x0001, - /// Set driver fog parameters on this program - Fog = 0x0002, - /// Adds an enabled/disabled parameter to the fog, for user shaders - DynamicFog = 0x0004, - }; - - // Bitfield containing features used by this pixel program - uint Features; - - // Indices of parameters used by features - uint FogEnabledIdx; // (Fog && DynamicFog) nlFogEnabled, fog enabled - uint FogStartEndIdx; // (Fog) nlFogStartEnd, start and end of fog - uint FogColorIdx; // (Fog) nlFogColor, fog color -}; +class CGPUProgramSource; +class CGPUProgramSourceCont; /** - * \brief CGeometryProgramInfo - * \date 2013-09-07 15:00GMT - * \author Jan Boon (Kaetemi) - * Read-only information structure. - */ -struct CGeometryProgramInfo -{ -public: - /*enum TFeatures - { - - };*/ - /// Bitfield containing features used by this pixel program. - // uint Features; - /// Indices of parameters used by features. -}; - -/** - * \brief CGPUProgram + * \brief IGPUProgram * \date 2013-09-07 15:00GMT * \author Jan Boon (Kaetemi) * A compiled GPU program @@ -202,28 +125,23 @@ public: public: IGPUProgram(); + IGPUProgram(CGPUProgramSourceCont *programSource); virtual ~IGPUProgram(); /// Get the idx of a parameter (ogl: uniform, d3d: constant, etcetera) by name. Invalid name returns ~0 - virtual uint getParamIdx(char *name) const = 0; + inline uint getParamIdx(char *name) const { return _DrvInfo->getParamIdx(name); }; - void buildVPInfo(const char *displayName, uint features); - void buildPPInfo(const char *displayName, uint features); - void buildGPInfo(const char *displayName, uint features); + /// Get the program + inline const CGPUProgramSourceCont *getProgramSource() const { return _ProgramSource; }; - inline const CVertexProgramInfo *getVPInfo() const { return Info.VertexProgram; } - inline const CPixelProgramInfo *getPPInfo() const { return Info.PixelProgram; } - inline const CGeometryProgramInfo *getGPInfo() const { return Info.GeometryProgram; } +protected: + /// The progam source + NLMISC::CSmartPtr _ProgramSource; + +public: + /// The driver information. For the driver implementation only. + NLMISC::CRefPtr _DrvInfo; -private: - union - { - CVertexProgramInfo *VertexProgram; - CPixelProgramInfo *PixelProgram; - CGeometryProgramInfo *GeometryProgram; - void *Ptr; - } Info; - }; /* class IGPUProgram */ } /* namespace NL3D */ diff --git a/code/nel/include/nel/3d/gpu_program_source.h b/code/nel/include/nel/3d/gpu_program_source.h index 7e6ba81fa..0484b28d2 100644 --- a/code/nel/include/nel/3d/gpu_program_source.h +++ b/code/nel/include/nel/3d/gpu_program_source.h @@ -45,7 +45,7 @@ namespace NL3D { * \author Jan Boon (Kaetemi) * A single GPU program with a specific profile. */ -struct CGPUProgramSource +struct CGPUProgramSource : public NLMISC::CRefCount { public: std::string DisplayName; @@ -53,11 +53,11 @@ public: /// Minimal required profile for this GPU program IGPUProgram::TProfile Profile; - char *SourcePtr; - /// Copy the source string - inline void setSource(char *source) { SourceCopy = source; SourcePtr = &source[0]; } - /// Set pointer to source string without copying the string - inline void setSourcePtr(char *sourcePtr) { SourceCopy.clear(); SourcePtr = sourcePtr; } + const char *CodePtr; + /// Copy the source code string + inline void setCode(const char *source) { CodeCopy = source; CodePtr = &source[0]; } + /// Set pointer to source code string without copying the string + inline void setCodePtr(const char *sourcePtr) { CodeCopy.clear(); CodePtr = sourcePtr; } /// CVertexProgramInfo/CPixelProgramInfo/... NeL features uint Features; @@ -66,7 +66,7 @@ public: std::map ParamIndices; private: - std::string SourceCopy; + std::string CodeCopy; }; /* class CGPUProgramSource */ @@ -77,10 +77,10 @@ private: * Container for the source code of a single GPU program, allowing * variations in different language profiles. */ -struct CGPUProgramSourceCont +struct CGPUProgramSourceCont : public NLMISC::CRefCount { -public: - std::vector Sources; +public: + std::vector > Sources; }; /* class CGPUProgramSourceCont */ diff --git a/code/nel/include/nel/3d/pixel_program.h b/code/nel/include/nel/3d/pixel_program.h index 183ad2d83..e006844aa 100644 --- a/code/nel/include/nel/3d/pixel_program.h +++ b/code/nel/include/nel/3d/pixel_program.h @@ -1,7 +1,5 @@ /** \file pixel_program.h * Pixel program definition - * - * $Id: pixel_program.h,v 1.1.2.3 2007/07/06 15:58:45 legallo Exp $ */ /* Copyright, 2000, 2001 Nevrax Ltd. @@ -28,81 +26,62 @@ #include #include +#include +#include #include namespace NL3D { -// List typedef. -class IDriver; -class IPixelProgramDrvInfos; -typedef std::list TPixelPrgDrvInfoPtrList; -typedef TPixelPrgDrvInfoPtrList::iterator ItPixelPrgDrvInfoPtrList; - -// Class for interaction of pixel program with Driver. -// IPixelProgramDrvInfos represent the real data of the pixel program, stored into the driver. -class IPixelProgramDrvInfos : public NLMISC::CRefCount -{ -private: - IDriver *_Driver; - ItPixelPrgDrvInfoPtrList _DriverIterator; - -public: - IPixelProgramDrvInfos (IDriver *drv, ItPixelPrgDrvInfoPtrList it); - // The virtual dtor is important. - virtual ~IPixelProgramDrvInfos(void); -}; - - -//------------------------------------------------------------------------------- -class CPixelProgram : public NLMISC::CRefCount +/** + * \brief CPixelProgramInfo + * \date 2013-09-07 15:00GMT + * \author Jan Boon (Kaetemi) + * Read-only information structure. + */ +struct CPixelProgramInfo { public: - - enum TProfile - { - // TODO: - // If it's more useful, change this to a flags bitfield and - // change the d3d (and gl) code to generate the bitfield of - // supported modes instead of doing a >= version check. - - // direct3d - 0xD3D0,major,minor - ps_1_1 = 0xD3D00101, - ps_1_2 = 0xD3D00102, - ps_1_3 = 0xD3D00103, - ps_1_4 = 0xD3D00104, - ps_2_0 = 0xD3D00200, - ps_2_x = 0xD3D00201, // not sure... - ps_3_0 = 0xD3D00300, - - // opengl - 0x0610,bitfield - // fp20 = 0x061B0001, // very limited and outdated, unnecessary - // fp30 = 0x06100002, // NV_fragment_program, now arbfp1, redundant - arbfp1 = 0x06100004, // ARB_fragment_program - fp40 = 0x06100008, // NV_fragment_program2, arbfp1 with "OPTION NV_fragment_program2;\n" - gp4fp = 0x06100010, // NV_gpu_program4 - gp5fp = 0x06100020, // NV_gpu_program5 + std::string DisplayName; + + enum TFeatures + { + /// Use texture stages from CMaterial as texture parameters + MaterialTextures = 0x0001, + /// Set driver fog parameters on this program + Fog = 0x0002, + /// Adds an enabled/disabled parameter to the fog, for user shaders + DynamicFog = 0x0004, }; - /// Constructor - CPixelProgram (const char* program); + // Bitfield containing features used by this pixel program + uint Features; + // Indices of parameters used by features + uint FogEnabledIdx; // (Fog && DynamicFog) nlFogEnabled, fog enabled + uint FogStartEndIdx; // (Fog) nlFogStartEnd, start and end of fog + uint FogColorIdx; // (Fog) nlFogColor, fog color +}; + +class CPixelProgram : public IGPUProgram +{ +public: + /// Constructor + CPixelProgram(CGPUProgramSourceCont *programSource); /// Destructor virtual ~CPixelProgram (); - /// Get the program - inline const std::string& getProgram() const { return _Program; }; + /// Build feature information + void buildInfo(const char *displayName, uint features); + /// Get feature information + inline const CPixelProgramInfo *getInfo() const { return _Info; } - /// The driver informations. For the driver implementation only. - NLMISC::CRefPtr _DrvInfo; +private: -protected: - - /// The progam - std::string _Program; + /// Feature information + CPixelProgramInfo *_Info; }; - } // NL3D diff --git a/code/nel/include/nel/3d/shader.h b/code/nel/include/nel/3d/shader.h index 3377c27d4..61956e8e1 100644 --- a/code/nel/include/nel/3d/shader.h +++ b/code/nel/include/nel/3d/shader.h @@ -24,73 +24,6 @@ namespace NL3D { -using NLMISC::CRefCount; - - -class IDriver; - -// List typedef. -class IShaderDrvInfos; -typedef std::list TShaderDrvInfoPtrList; -typedef TShaderDrvInfoPtrList::iterator ItShaderDrvInfoPtrList; - -/** - * Interface for shader driver infos. - */ -class IShaderDrvInfos : public CRefCount -{ -private: - IDriver *_Driver; - ItShaderDrvInfoPtrList _DriverIterator; - -public: - IShaderDrvInfos(IDriver *drv, ItShaderDrvInfoPtrList it) {_Driver= drv; _DriverIterator= it;} - // The virtual dtor is important. - virtual ~IShaderDrvInfos(); -}; - - -/** - * Shader resource for the driver. It is just a container for a ".fx" text file. - */ -/* *** IMPORTANT ******************** - * *** IF YOU MODIFY THE STRUCTURE OF THIS CLASS, PLEASE INCREMENT IDriver::InterfaceVersion TO INVALIDATE OLD DRIVER DLL - * ********************************** - */ -// -------------------------------------------------- -class CShader -{ -public: - CShader(); - ~CShader(); - - // Load a shader file - bool loadShaderFile (const char *filename); - - // Set the shader text - void setText (const char *text); - - // Get the shader text - const char *getText () const { return _Text.c_str(); } - - // Set the shader name - void setName (const char *name); - - // Get the shader name - const char *getName () const { return _Name.c_str(); } - -public: - // Private. For Driver only. - bool _ShaderChanged; - NLMISC::CRefPtr _DrvInfo; -private: - // The shader - std::string _Text; - // The shader name - std::string _Name; -}; - - } // NL3D diff --git a/code/nel/include/nel/3d/vertex_program.h b/code/nel/include/nel/3d/vertex_program.h index 903e5ccf7..b8843e182 100644 --- a/code/nel/include/nel/3d/vertex_program.h +++ b/code/nel/include/nel/3d/vertex_program.h @@ -19,90 +19,87 @@ #include "nel/misc/types_nl.h" #include "nel/misc/smart_ptr.h" +#include "nel/3d/gpu_program.h" +#include "nel/3d/gpu_program_source.h" #include - namespace NL3D { -// List typedef. -class IDriver; -class IVertexProgramDrvInfos; -typedef std::list TVtxPrgDrvInfoPtrList; -typedef TVtxPrgDrvInfoPtrList::iterator ItVtxPrgDrvInfoPtrList; - -// Class for interaction of vertex program with Driver. -// IVertexProgramDrvInfos represent the real data of the vertex program, stored into the driver (eg: just a GLint for opengl). -class IVertexProgramDrvInfos : public NLMISC::CRefCount +/** + * \brief CVertexProgramInfo + * \date 2013-09-07 15:00GMT + * \author Jan Boon (Kaetemi) + * Read-only information structure. + */ +struct CVertexProgramInfo { -private: - IDriver *_Driver; - ItVtxPrgDrvInfoPtrList _DriverIterator; - public: - IVertexProgramDrvInfos (IDriver *drv, ItVtxPrgDrvInfoPtrList it); - // The virtual dtor is important. - virtual ~IVertexProgramDrvInfos(void); + std::string DisplayName; + + enum TFeatures + { + // World + // transform + + // Lights + Ambient = 0x0001, + Sun = 0x0002, + PointLight0 = 0x0004, + PointLight1 = 0x0008, + PointLight2 = 0x0010, + + // Lights, additional parameters for user shaders + /// Adds an enabled/disabled parameter to all of the lights + DynamicLights = 0x0020, + }; + + /// Bitfield containing features used by this vertex program. + uint Features; + + /// Indices of parameters used by features. + + /// Lights, NeL supports: + /// - Ambient light + uint AmbientIdx; // (Ambient) + /// - One directional light + uint SunDirectionIdx; // (Sun) + uint SunDiffuseIdx; // (Sun) + /// - Zero to three point lights + uint PointLight0PositionIdx; // (PointLight0) + uint PointLight0DiffuseIdx; // (PointLight0) + uint PointLight1PositionIdx; // (PointLight1) + uint PointLight1DiffuseIdx; // (PointLight1) + uint PointLight2PositionIdx; // (PointLight2) + uint PointLight2DiffuseIdx; // (PointLight2) + + /// DynamicLights + uint SunEnabledIdx; // (DynamicLights && Sun) + uint PointLight0EnabledIdx; // (DynamicLights && PointLight0) + uint PointLight1EnabledIdx; // (DynamicLights && PointLight1) + uint PointLight2EnabledIdx; // (DynamicLights && PointLight2) }; - -/** - * This class is a vertex program. - * - * D3D / OPENGL compatibility notes: - * --------------------------------- - * - * To make your program compatible with D3D and OPENGL nel drivers, please follow thoses directives to write your vertex programs - * - * - Use only v[0], v[1] etc.. syntax for input registers. Don't use v0, v1 or v[OPOS] etc.. - * - Use only c[0], c[1] etc.. syntax for constant registers. Don't use c0, c1 etc.. - * - Use only o[HPOS], o[COL0] etc.. syntax for output registers. Don't use oPos, oD0 etc.. - * - Use only uppercase for registers R1, R2 etc.. Don't use lowercase r1, r2 etc.. - * - Use a semicolon to delineate instructions. - * - Use ARL instruction to load the adress register and not MOV. - * - Don't use the NOP instruction. - * - Don't use macros. - * - * -> Thoses programs work without any change under OpenGL. - * -> Direct3D driver implementation will have to modify the syntax on the fly before the setup like this: - * - "v[0]" must be changed in "v0" etc.. - * - "o[HPOS]" must be changed in oPos etc.. - * - Semicolon must be changed in line return character. - * - ARL instruction must be changed in MOV. - * - * Behaviour of LOG may change depending on implementation: You can only expect to have dest.z = log2(abs(src.w)). - * LIT may or may not clamp the specular exponent to [-128, 128] (not done when EXT_vertex_shader is used for example ..) - * - * Depending on the implementation, some optimizations can be achieved by masking the unused output values of instructions - * as LIT, EXPP .. - * - * \author Cyril 'Hulud' Corvazier - * \author Nevrax France - * \date 2001 - */ -class CVertexProgram : public NLMISC::CRefCount +class CVertexProgram : public IGPUProgram { public: - /// Constructor - CVertexProgram (const char* program); - + CVertexProgram(CGPUProgramSourceCont *programSource); + CVertexProgram(const char *nelvp); /// Destructor virtual ~CVertexProgram (); - /// Get the program - const std::string& getProgram () const { return _Program; }; + /// Build feature information + void buildInfo(const char *displayName, uint features); + /// Get feature information + inline const CVertexProgramInfo *getInfo() const { return _Info; } private: - /// The progam - std::string _Program; -public: - /// The driver information. For the driver implementation only. - NLMISC::CRefPtr _DrvInfo; + /// Feature information + CVertexProgramInfo *_Info; }; - } // NL3D diff --git a/code/nel/include/nel/3d/vertex_program_parse.h b/code/nel/include/nel/3d/vertex_program_parse.h index cd9f414c9..88538da07 100644 --- a/code/nel/include/nel/3d/vertex_program_parse.h +++ b/code/nel/include/nel/3d/vertex_program_parse.h @@ -21,6 +21,40 @@ #include +/** + * This class is a vertex program. + * + * D3D / OPENGL compatibility notes: + * --------------------------------- + * + * To make your program compatible with D3D and OPENGL nel drivers, please follow thoses directives to write your vertex programs + * + * - Use only v[0], v[1] etc.. syntax for input registers. Don't use v0, v1 or v[OPOS] etc.. + * - Use only c[0], c[1] etc.. syntax for constant registers. Don't use c0, c1 etc.. + * - Use only o[HPOS], o[COL0] etc.. syntax for output registers. Don't use oPos, oD0 etc.. + * - Use only uppercase for registers R1, R2 etc.. Don't use lowercase r1, r2 etc.. + * - Use a semicolon to delineate instructions. + * - Use ARL instruction to load the adress register and not MOV. + * - Don't use the NOP instruction. + * - Don't use macros. + * + * -> Thoses programs work without any change under OpenGL. + * -> Direct3D driver implementation will have to modify the syntax on the fly before the setup like this: + * - "v[0]" must be changed in "v0" etc.. + * - "o[HPOS]" must be changed in oPos etc.. + * - Semicolon must be changed in line return character. + * - ARL instruction must be changed in MOV. + * + * Behaviour of LOG may change depending on implementation: You can only expect to have dest.z = log2(abs(src.w)). + * LIT may or may not clamp the specular exponent to [-128, 128] (not done when EXT_vertex_shader is used for example ..) + * + * Depending on the implementation, some optimizations can be achieved by masking the unused output values of instructions + * as LIT, EXPP .. + * + * \author Cyril 'Hulud' Corvazier + * \author Nevrax France + * \date 2001 + */ /// Swizzle of an operand in a vertex program struct CVPSwizzle diff --git a/code/nel/src/3d/CMakeLists.txt b/code/nel/src/3d/CMakeLists.txt index fb5d5851a..f469b3b62 100644 --- a/code/nel/src/3d/CMakeLists.txt +++ b/code/nel/src/3d/CMakeLists.txt @@ -167,6 +167,8 @@ SOURCE_GROUP(Driver FILES ../../include/nel/3d/vertex_program_parse.h pixel_program.cpp ../../include/nel/3d/pixel_program.h + geometry_program.cpp + ../../include/nel/3d/geometry_program.h gpu_program.cpp ../../include/nel/3d/gpu_program.h gpu_program_source.cpp diff --git a/code/nel/src/3d/driver.cpp b/code/nel/src/3d/driver.cpp index 5a08da982..25c9afb63 100644 --- a/code/nel/src/3d/driver.cpp +++ b/code/nel/src/3d/driver.cpp @@ -58,7 +58,7 @@ IDriver::~IDriver() nlassert(_MatDrvInfos.size()==0); nlassert(_VBDrvInfos.size()==0); nlassert(_IBDrvInfos.size()==0); - nlassert(_VtxPrgDrvInfos.size()==0); + nlassert(_GPUPrgDrvInfos.size()==0); } @@ -94,14 +94,14 @@ bool IDriver::release(void) // NB: at IShader deletion, this->_MatDrvInfos is updated (entry deleted); delete *itmat; } - +/* // Release Shader drv. ItShaderDrvInfoPtrList itshd; while( (itshd = _ShaderDrvInfos.begin()) != _ShaderDrvInfos.end() ) { // NB: at IShader deletion, this->_MatDrvInfos is updated (entry deleted); delete *itshd; - } + }*/ // Release VBs drv. ItVBDrvInfoPtrList itvb; @@ -119,12 +119,12 @@ bool IDriver::release(void) delete *itib; } - // Release VtxPrg drv. - ItVtxPrgDrvInfoPtrList itVtxPrg; - while( (itVtxPrg = _VtxPrgDrvInfos.begin()) != _VtxPrgDrvInfos.end() ) + // Release GPUPrg drv. + ItGPUPrgDrvInfoPtrList itGPUPrg; + while( (itGPUPrg = _GPUPrgDrvInfos.begin()) != _GPUPrgDrvInfos.end() ) { - // NB: at IVertexProgramDrvInfos deletion, this->_VtxPrgDrvInfos is updated (entry deleted); - delete *itVtxPrg; + // NB: at IVertexProgramDrvInfos deletion, this->_GPUPrgDrvInfos is updated (entry deleted); + delete *itGPUPrg; } return true; @@ -249,7 +249,7 @@ void IDriver::removeMatDrvInfoPtr(ItMatDrvInfoPtrList shaderIt) _MatDrvInfos.erase(shaderIt); } // *************************************************************************** -void IDriver::removeShaderDrvInfoPtr(ItShaderDrvInfoPtrList shaderIt) +/*void IDriver::removeShaderDrvInfoPtr(ItShaderDrvInfoPtrList shaderIt) { _ShaderDrvInfos.erase(shaderIt); } @@ -262,6 +262,11 @@ void IDriver::removeVtxPrgDrvInfoPtr(ItVtxPrgDrvInfoPtrList vtxPrgDrvInfoIt) void IDriver::removePixelPrgDrvInfoPtr(ItPixelPrgDrvInfoPtrList pixelPrgDrvInfoIt) { _PixelPrgDrvInfos.erase(pixelPrgDrvInfoIt); +}*/ +// *************************************************************************** +void IDriver::removeGPUPrgDrvInfoPtr(ItGPUPrgDrvInfoPtrList vtxPrgDrvInfoIt) +{ + _GPUPrgDrvInfos.erase(vtxPrgDrvInfoIt); } // *************************************************************************** diff --git a/code/nel/src/3d/geometry_program.cpp b/code/nel/src/3d/geometry_program.cpp new file mode 100644 index 000000000..ae73d669f --- /dev/null +++ b/code/nel/src/3d/geometry_program.cpp @@ -0,0 +1,59 @@ +/** \file geometry_program.cpp + * Geometry program definition + */ + +/* Copyright, 2000, 2001 Nevrax Ltd. + * + * This file is part of NEVRAX NEL. + * NEVRAX NEL 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, or (at your option) + * any later version. + + * NEVRAX NEL 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 NEVRAX NEL; see the file COPYING. If not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include "std3d.h" + +#include + +#include + +namespace NL3D +{ + +// *************************************************************************** + +CGeometryProgram::CGeometryProgram(CGPUProgramSourceCont *programSource) : _Info(NULL), IGPUProgram(programSource) +{ + +} + +// *************************************************************************** + +CGeometryProgram::~CGeometryProgram () +{ + delete _Info; + _Info = NULL; +} + +// *************************************************************************** + +void CGeometryProgram::buildInfo(const char *displayName, uint features) +{ + nlassert(_Info == NULL); + _Info = new CGeometryProgramInfo(); + CGeometryProgramInfo *info = _Info; + info->DisplayName = displayName; + info->Features = features; +} + +} // NL3D diff --git a/code/nel/src/3d/gpu_program.cpp b/code/nel/src/3d/gpu_program.cpp index 457d8b1a1..fc75e44e4 100644 --- a/code/nel/src/3d/gpu_program.cpp +++ b/code/nel/src/3d/gpu_program.cpp @@ -1,9 +1,9 @@ /** * \file gpu_program.cpp - * \brief CGPUProgram + * \brief IGPUProgram * \date 2013-09-07 15:00GMT * \author Jan Boon (Kaetemi) - * CGPUProgram + * IGPUProgram */ /* @@ -35,157 +35,50 @@ #include // Project includes +#include using namespace std; // using namespace NLMISC; namespace NL3D { +// *************************************************************************** + +IGPUProgramDrvInfos::IGPUProgramDrvInfos(IDriver *drv, ItGPUPrgDrvInfoPtrList it) +{ + _Driver = drv; + _DriverIterator = it; +} + +// *************************************************************************** + +IGPUProgramDrvInfos::~IGPUProgramDrvInfos () +{ + _Driver->removeGPUPrgDrvInfoPtr(_DriverIterator); +} + +// *************************************************************************** + IGPUProgram::IGPUProgram() { - Info.Ptr = NULL; + } +// *************************************************************************** + +IGPUProgram::IGPUProgram(CGPUProgramSourceCont *programSource) : _ProgramSource(programSource) +{ + +} + +// *************************************************************************** + IGPUProgram::~IGPUProgram() { - delete Info.Ptr; - Info.Ptr = NULL; + // Must kill the drv mirror of this program. + _DrvInfo.kill(); } -void IGPUProgram::buildVPInfo(const char *displayName, uint features) -{ - nlassert(Info.VertexProgram == NULL); - Info.VertexProgram = new CVertexProgramInfo(); - CVertexProgramInfo *info = Info.VertexProgram; - info->DisplayName = displayName; - info->Features = features; - if (features & CVertexProgramInfo::Ambient) - { - info->AmbientIdx = getParamIdx("nlAmbient"); - if (info->AmbientIdx == ~0) - { - nlwarning("Missing 'nlAmbient' in gpu program '%s', Ambient disabled", displayName); - info->Features &= ~CVertexProgramInfo::Ambient; - } - } - if (features & CVertexProgramInfo::Sun) - { - if (features & CVertexProgramInfo::DynamicLights) - { - info->SunEnabledIdx = getParamIdx("nlSunEnabled"); - if (info->SunEnabledIdx == ~0) - { - nlwarning("Missing 'nlSunEnabled' in gpu program '%s', DynamicLights disabled", displayName); - info->Features &= ~CVertexProgramInfo::DynamicLights; - } - } - info->SunDirectionIdx = getParamIdx("nlSunDirection"); - info->SunDiffuseIdx = getParamIdx("nlSunDiffuse"); - if (info->SunDirectionIdx == ~0 - || info->SunDiffuseIdx == ~0) - { - nlwarning("Missing 'nlSunDirection/nlSunDiffuse' in gpu program '%s', Sun disabled", displayName); - info->Features &= ~CVertexProgramInfo::Sun; - } - } - if (features & CVertexProgramInfo::PointLight0) - { - if (features & CVertexProgramInfo::DynamicLights) - { - info->PointLight0EnabledIdx = getParamIdx("nlPointLight0Enabled"); - if (info->PointLight0EnabledIdx == ~0) - { - nlwarning("Missing 'nlPointLight0Enabled' in gpu program '%s', DynamicLights disabled", displayName); - info->Features &= ~CVertexProgramInfo::DynamicLights; - } - } - info->PointLight0PositionIdx = getParamIdx("nlPointLight0Position"); - info->PointLight0DiffuseIdx = getParamIdx("nlPointLight0Diffuse"); - if (info->PointLight0PositionIdx == ~0 - || info->PointLight0DiffuseIdx == ~0) - { - nlwarning("Missing 'nlPointLight0Position/nlPointLight0Diffuse' in gpu program '%s', PointLight0 disabled", displayName); - info->Features &= ~CVertexProgramInfo::PointLight0; - } - } - if (features & CVertexProgramInfo::PointLight1) - { - if (features & CVertexProgramInfo::DynamicLights) - { - info->PointLight1EnabledIdx = getParamIdx("nlPointLight1Enabled"); - if (info->PointLight1EnabledIdx == ~0) - { - nlwarning("Missing 'nlPointLight1Enabled' in gpu program '%s', DynamicLights disabled", displayName); - info->Features &= ~CVertexProgramInfo::DynamicLights; - } - } - info->PointLight1PositionIdx = getParamIdx("nlPointLight1Position"); - info->PointLight1DiffuseIdx = getParamIdx("nlPointLight1Diffuse"); - if (info->PointLight1PositionIdx == ~0 - || info->PointLight1DiffuseIdx == ~0) - { - nlwarning("Missing 'nlPointLight1Position/nlPointLight1Diffuse' in gpu program '%s', PointLight1 disabled", displayName); - info->Features &= ~CVertexProgramInfo::PointLight1; - } - } - if (features & CVertexProgramInfo::PointLight2) - { - if (features & CVertexProgramInfo::DynamicLights) - { - info->PointLight2EnabledIdx = getParamIdx("nlPointLight2Enabled"); - if (info->PointLight2EnabledIdx == ~0) - { - nlwarning("Missing 'nlPointLight2Enabled' in gpu program '%s', DynamicLights disabled", displayName); - info->Features &= ~CVertexProgramInfo::DynamicLights; - } - } - info->PointLight2PositionIdx = getParamIdx("nlPointLight2Position"); - info->PointLight2DiffuseIdx = getParamIdx("nlPointLight2Diffuse"); - if (info->PointLight2PositionIdx == ~0 - || info->PointLight2DiffuseIdx == ~0) - { - nlwarning("Missing 'nlPointLight2Position/nlPointLight2Diffuse' in gpu program '%s', PointLight2 disabled", displayName); - info->Features &= ~CVertexProgramInfo::PointLight2; - } - } -} - -void IGPUProgram::buildPPInfo(const char *displayName, uint features) -{ - nlassert(Info.PixelProgram == NULL); - Info.PixelProgram = new CPixelProgramInfo(); - CPixelProgramInfo *info = Info.PixelProgram; - info->DisplayName = displayName; - info->Features = features; - if (features & CPixelProgramInfo::Fog) - { - if (features & CPixelProgramInfo::DynamicFog) - { - info->FogEnabledIdx = getParamIdx("nlFogEnabled"); - if (info->FogEnabledIdx == ~0) - { - nlwarning("Missing 'nlFogEnabled' in gpu program '%s', DynamicFog disabled", displayName); - info->Features &= ~CPixelProgramInfo::DynamicFog; - } - } - info->FogStartEndIdx = getParamIdx("nlFogStartEnd"); - info->FogColorIdx = getParamIdx("nlFogColor"); - if (info->FogStartEndIdx == ~0 - || info->FogStartEndIdx == ~0) - { - nlwarning("Missing 'nlFogStartEnd/nlFogColor' in gpu program '%s', Fog disabled", displayName); - info->Features &= ~CPixelProgramInfo::Fog; - } - } -} - -void IGPUProgram::buildGPInfo(const char *displayName, uint features) -{ - nlassert(Info.GeometryProgram == NULL); - Info.GeometryProgram = new CGeometryProgramInfo(); -} - - } /* namespace NL3D */ /* end of file */ diff --git a/code/nel/src/3d/pixel_program.cpp b/code/nel/src/3d/pixel_program.cpp index 320bfa541..8fbe8c0cf 100644 --- a/code/nel/src/3d/pixel_program.cpp +++ b/code/nel/src/3d/pixel_program.cpp @@ -1,7 +1,5 @@ /** \file pixel_program.cpp * Pixel program definition - * - * $Id: pixel_program.cpp,v 1.1.2.1 2007/04/27 17:35:07 legallo Exp $ */ /* Copyright, 2000, 2001 Nevrax Ltd. @@ -33,31 +31,49 @@ namespace NL3D { // *************************************************************************** -IPixelProgramDrvInfos::IPixelProgramDrvInfos (IDriver *drv, ItPixelPrgDrvInfoPtrList it) + +CPixelProgram::CPixelProgram(CGPUProgramSourceCont *programSource) : _Info(NULL), IGPUProgram(programSource) { - _Driver= drv; - _DriverIterator= it; + } - // *************************************************************************** -IPixelProgramDrvInfos::~IPixelProgramDrvInfos () + +CPixelProgram::~CPixelProgram () { - _Driver->removePixelPrgDrvInfoPtr (_DriverIterator); + delete _Info; + _Info = NULL; } - // *************************************************************************** -CPixelProgram::CPixelProgram(const char* program) : _Program(program) + +void CPixelProgram::buildInfo(const char *displayName, uint features) { - -} - - -// *************************************************************************** -CPixelProgram::~CPixelProgram() -{ - + nlassert(_Info == NULL); + _Info = new CPixelProgramInfo(); + CPixelProgramInfo *info = _Info; + info->DisplayName = displayName; + info->Features = features; + if (features & CPixelProgramInfo::Fog) + { + if (features & CPixelProgramInfo::DynamicFog) + { + info->FogEnabledIdx = getParamIdx("nlFogEnabled"); + if (info->FogEnabledIdx == ~0) + { + nlwarning("Missing 'nlFogEnabled' in gpu program '%s', DynamicFog disabled", displayName); + info->Features &= ~CPixelProgramInfo::DynamicFog; + } + } + info->FogStartEndIdx = getParamIdx("nlFogStartEnd"); + info->FogColorIdx = getParamIdx("nlFogColor"); + if (info->FogStartEndIdx == ~0 + || info->FogStartEndIdx == ~0) + { + nlwarning("Missing 'nlFogStartEnd/nlFogColor' in gpu program '%s', Fog disabled", displayName); + info->Features &= ~CPixelProgramInfo::Fog; + } + } } } // NL3D diff --git a/code/nel/src/3d/shader.cpp b/code/nel/src/3d/shader.cpp index a0d0c3172..9c08e9980 100644 --- a/code/nel/src/3d/shader.cpp +++ b/code/nel/src/3d/shader.cpp @@ -27,85 +27,4 @@ using namespace NLMISC; namespace NL3D { -// *************************************************************************** - -CShader::~CShader() -{ - // Must kill the drv mirror of this shader. - _DrvInfo.kill(); -} - -// *************************************************************************** - -CShader::CShader() -{ - _ShaderChanged = true; -} - -// *************************************************************************** - -void CShader::setText (const char *text) -{ - _Text = text; - _ShaderChanged = true; -} - -// *************************************************************************** - -void CShader::setName (const char *name) -{ - _Name = name; - _ShaderChanged = true; -} - -// *************************************************************************** - -bool CShader::loadShaderFile (const char *filename) -{ - _Text = ""; - // Lookup - string _filename = CPath::lookup(filename, false, true, true); - if (!_filename.empty()) - { - // File length - uint size = CFile::getFileSize (_filename); - _Text.reserve (size+1); - - try - { - CIFile file; - if (file.open (_filename)) - { - // Read it - while (!file.eof ()) - { - char line[512]; - file.getline (line, 512); - _Text += line; - } - - // Set the shader name - _Name = CFile::getFilename (filename); - return true; - } - else - { - nlwarning ("Can't open the file %s for reading", _filename.c_str()); - } - } - catch (const Exception &e) - { - nlwarning ("Error while reading %s : %s", _filename.c_str(), e.what()); - } - } - return false; -} - -// *************************************************************************** - -IShaderDrvInfos::~IShaderDrvInfos() -{ - _Driver->removeShaderDrvInfoPtr(_DriverIterator); -} - } // NL3D diff --git a/code/nel/src/3d/stereo_debugger.cpp b/code/nel/src/3d/stereo_debugger.cpp index 6be97d42a..17c5c8c98 100644 --- a/code/nel/src/3d/stereo_debugger.cpp +++ b/code/nel/src/3d/stereo_debugger.cpp @@ -114,7 +114,13 @@ void CStereoDebugger::setDriver(NL3D::UDriver *driver) { nlassert(!m_PixelProgram); - NL3D::IDriver *drvInternal = (static_cast(driver))->getDriver(); + NL3D::IDriver *drvInternal = (static_cast(driver))->getDriver(); + + CGPUProgramSource *source = new CGPUProgramSource(); + CGPUProgramSourceCont *sourceCont = new CGPUProgramSourceCont(); + sourceCont->Sources.push_back(source); + source->Features = CPixelProgramInfo::MaterialTextures; + /*if (drvInternal->supportPixelProgram(CPixelProgram::fp40) && drvInternal->supportBloomEffect() && drvInternal->supportNonPowerOfTwoTextures()) { nldebug("VR: fp40"); @@ -122,8 +128,10 @@ void CStereoDebugger::setDriver(NL3D::UDriver *driver) } else*/ if (drvInternal->supportPixelProgram(CPixelProgram::arbfp1) && drvInternal->supportBloomEffect() && drvInternal->supportNonPowerOfTwoTextures()) { - nldebug("VR: arbfp1"); - m_PixelProgram = new CPixelProgram(a_arbfp1); + nldebug("VR: arbfp1"); + source->Profile = IGPUProgram::arbfp1; + source->setCodePtr(a_arbfp1); + m_PixelProgram = new CPixelProgram(sourceCont); } /*else if (drvInternal->supportPixelProgram(CPixelProgram::ps_2_0)) { diff --git a/code/nel/src/3d/stereo_ovr.cpp b/code/nel/src/3d/stereo_ovr.cpp index a57a0ffcb..00db2020e 100644 --- a/code/nel/src/3d/stereo_ovr.cpp +++ b/code/nel/src/3d/stereo_ovr.cpp @@ -237,21 +237,33 @@ void CStereoOVR::setDriver(NL3D::UDriver *driver) { nlassert(!m_PixelProgram); - NL3D::IDriver *drvInternal = (static_cast(driver))->getDriver(); + NL3D::IDriver *drvInternal = (static_cast(driver))->getDriver(); + + CGPUProgramSource *source = new CGPUProgramSource(); + CGPUProgramSourceCont *sourceCont = new CGPUProgramSourceCont(); + sourceCont->Sources.push_back(source); + source->Features = CPixelProgramInfo::MaterialTextures; + if (drvInternal->supportPixelProgram(CPixelProgram::fp40) && drvInternal->supportBloomEffect() && drvInternal->supportNonPowerOfTwoTextures()) { nldebug("VR: fp40"); - m_PixelProgram = new CPixelProgram(g_StereoOVR_fp40); + source->Profile = IGPUProgram::fp40; + source->setCodePtr(g_StereoOVR_fp40); + m_PixelProgram = new CPixelProgram(sourceCont); } else if (drvInternal->supportPixelProgram(CPixelProgram::arbfp1) && drvInternal->supportBloomEffect() && drvInternal->supportNonPowerOfTwoTextures()) { nldebug("VR: arbfp1"); - m_PixelProgram = new CPixelProgram(g_StereoOVR_arbfp1); + source->Profile = IGPUProgram::arbfp1; + source->setCodePtr(g_StereoOVR_arbfp1); + m_PixelProgram = new CPixelProgram(sourceCont); } else if (drvInternal->supportPixelProgram(CPixelProgram::ps_2_0)) { nldebug("VR: ps_2_0"); - m_PixelProgram = new CPixelProgram(g_StereoOVR_ps_2_0); + source->Profile = IGPUProgram::ps_2_0; + source->setCodePtr(g_StereoOVR_ps_2_0); + m_PixelProgram = new CPixelProgram(sourceCont); } if (m_PixelProgram) diff --git a/code/nel/src/3d/vertex_program.cpp b/code/nel/src/3d/vertex_program.cpp index 9e7587069..2f70d51af 100644 --- a/code/nel/src/3d/vertex_program.cpp +++ b/code/nel/src/3d/vertex_program.cpp @@ -24,34 +24,131 @@ namespace NL3D { - // *************************************************************************** -IVertexProgramDrvInfos::IVertexProgramDrvInfos (IDriver *drv, ItVtxPrgDrvInfoPtrList it) + +CVertexProgram::CVertexProgram(CGPUProgramSourceCont *programSource) : _Info(NULL), IGPUProgram(programSource) { - _Driver= drv; - _DriverIterator= it; + } - // *************************************************************************** -IVertexProgramDrvInfos::~IVertexProgramDrvInfos () + +CVertexProgram::CVertexProgram(const char *nelvp) : _Info(NULL) { - _Driver->removeVtxPrgDrvInfoPtr (_DriverIterator); + CGPUProgramSource *source = new CGPUProgramSource(); + _ProgramSource = new CGPUProgramSourceCont(); + _ProgramSource->Sources.push_back(source); + source->Profile = IGPUProgram::nelvp; + source->setCode(nelvp); + source->Features = 0; } - // *************************************************************************** -CVertexProgram::CVertexProgram (const char* program) -{ - _Program=program; -} - -// *************************************************************************** CVertexProgram::~CVertexProgram () { - // Must kill the drv mirror of this VB. - _DrvInfo.kill(); + delete _Info; + _Info = NULL; +} + +// *************************************************************************** + +void CVertexProgram::buildInfo(const char *displayName, uint features) +{ + nlassert(_Info == NULL); + _Info = new CVertexProgramInfo(); + CVertexProgramInfo *info = _Info; + info->DisplayName = displayName; + info->Features = features; + if (features & CVertexProgramInfo::Ambient) + { + info->AmbientIdx = getParamIdx("nlAmbient"); + if (info->AmbientIdx == ~0) + { + nlwarning("Missing 'nlAmbient' in gpu program '%s', Ambient disabled", displayName); + info->Features &= ~CVertexProgramInfo::Ambient; + } + } + if (features & CVertexProgramInfo::Sun) + { + if (features & CVertexProgramInfo::DynamicLights) + { + info->SunEnabledIdx = getParamIdx("nlSunEnabled"); + if (info->SunEnabledIdx == ~0) + { + nlwarning("Missing 'nlSunEnabled' in gpu program '%s', DynamicLights disabled", displayName); + info->Features &= ~CVertexProgramInfo::DynamicLights; + } + } + info->SunDirectionIdx = getParamIdx("nlSunDirection"); + info->SunDiffuseIdx = getParamIdx("nlSunDiffuse"); + if (info->SunDirectionIdx == ~0 + || info->SunDiffuseIdx == ~0) + { + nlwarning("Missing 'nlSunDirection/nlSunDiffuse' in gpu program '%s', Sun disabled", displayName); + info->Features &= ~CVertexProgramInfo::Sun; + } + } + if (features & CVertexProgramInfo::PointLight0) + { + if (features & CVertexProgramInfo::DynamicLights) + { + info->PointLight0EnabledIdx = getParamIdx("nlPointLight0Enabled"); + if (info->PointLight0EnabledIdx == ~0) + { + nlwarning("Missing 'nlPointLight0Enabled' in gpu program '%s', DynamicLights disabled", displayName); + info->Features &= ~CVertexProgramInfo::DynamicLights; + } + } + info->PointLight0PositionIdx = getParamIdx("nlPointLight0Position"); + info->PointLight0DiffuseIdx = getParamIdx("nlPointLight0Diffuse"); + if (info->PointLight0PositionIdx == ~0 + || info->PointLight0DiffuseIdx == ~0) + { + nlwarning("Missing 'nlPointLight0Position/nlPointLight0Diffuse' in gpu program '%s', PointLight0 disabled", displayName); + info->Features &= ~CVertexProgramInfo::PointLight0; + } + } + if (features & CVertexProgramInfo::PointLight1) + { + if (features & CVertexProgramInfo::DynamicLights) + { + info->PointLight1EnabledIdx = getParamIdx("nlPointLight1Enabled"); + if (info->PointLight1EnabledIdx == ~0) + { + nlwarning("Missing 'nlPointLight1Enabled' in gpu program '%s', DynamicLights disabled", displayName); + info->Features &= ~CVertexProgramInfo::DynamicLights; + } + } + info->PointLight1PositionIdx = getParamIdx("nlPointLight1Position"); + info->PointLight1DiffuseIdx = getParamIdx("nlPointLight1Diffuse"); + if (info->PointLight1PositionIdx == ~0 + || info->PointLight1DiffuseIdx == ~0) + { + nlwarning("Missing 'nlPointLight1Position/nlPointLight1Diffuse' in gpu program '%s', PointLight1 disabled", displayName); + info->Features &= ~CVertexProgramInfo::PointLight1; + } + } + if (features & CVertexProgramInfo::PointLight2) + { + if (features & CVertexProgramInfo::DynamicLights) + { + info->PointLight2EnabledIdx = getParamIdx("nlPointLight2Enabled"); + if (info->PointLight2EnabledIdx == ~0) + { + nlwarning("Missing 'nlPointLight2Enabled' in gpu program '%s', DynamicLights disabled", displayName); + info->Features &= ~CVertexProgramInfo::DynamicLights; + } + } + info->PointLight2PositionIdx = getParamIdx("nlPointLight2Position"); + info->PointLight2DiffuseIdx = getParamIdx("nlPointLight2Diffuse"); + if (info->PointLight2PositionIdx == ~0 + || info->PointLight2DiffuseIdx == ~0) + { + nlwarning("Missing 'nlPointLight2Position/nlPointLight2Diffuse' in gpu program '%s', PointLight2 disabled", displayName); + info->Features &= ~CVertexProgramInfo::PointLight2; + } + } } } // NL3D From f4fccab548b451ff4df1def85843e3de6f1547c3 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Sat, 7 Sep 2013 21:30:40 +0200 Subject: [PATCH 178/313] Implement new gpu program interface in opengl driver --- code/nel/include/nel/3d/gpu_program.h | 2 +- code/nel/include/nel/3d/gpu_program_source.h | 10 +- code/nel/src/3d/driver.cpp | 2 +- .../src/3d/driver/opengl/driver_opengl.cpp | 8 -- code/nel/src/3d/driver/opengl/driver_opengl.h | 30 ++++-- .../opengl/driver_opengl_pixel_program.cpp | 59 ++++++++---- .../3d/driver/opengl/driver_opengl_vertex.cpp | 4 +- .../opengl/driver_opengl_vertex_program.cpp | 93 ++++++++++++++++--- code/nel/src/3d/stereo_debugger.cpp | 2 +- code/nel/src/3d/stereo_ovr.cpp | 6 +- code/nel/src/3d/vertex_program.cpp | 2 +- 11 files changed, 157 insertions(+), 61 deletions(-) diff --git a/code/nel/include/nel/3d/gpu_program.h b/code/nel/include/nel/3d/gpu_program.h index 3df13ac3d..ba0afb9e4 100644 --- a/code/nel/include/nel/3d/gpu_program.h +++ b/code/nel/include/nel/3d/gpu_program.h @@ -57,7 +57,7 @@ public: // The virtual dtor is important. virtual ~IGPUProgramDrvInfos(void); - virtual uint getParamIdx(char *name) const { return ~0; }; // STEREO_TODO + virtual uint getParamIdx(char *name) const = 0; }; class CGPUProgramSource; diff --git a/code/nel/include/nel/3d/gpu_program_source.h b/code/nel/include/nel/3d/gpu_program_source.h index 0484b28d2..c51ac67ce 100644 --- a/code/nel/include/nel/3d/gpu_program_source.h +++ b/code/nel/include/nel/3d/gpu_program_source.h @@ -53,11 +53,13 @@ public: /// Minimal required profile for this GPU program IGPUProgram::TProfile Profile; - const char *CodePtr; + const char *SourcePtr; + size_t SourceLen; /// Copy the source code string - inline void setCode(const char *source) { CodeCopy = source; CodePtr = &source[0]; } + inline void setSource(const char *source) { SourceCopy = source; SourcePtr = &SourceCopy[0]; SourceLen = SourceCopy.size(); } /// Set pointer to source code string without copying the string - inline void setCodePtr(const char *sourcePtr) { CodeCopy.clear(); CodePtr = sourcePtr; } + inline void setSourcePtr(const char *sourcePtr, size_t sourceLen) { SourceCopy.clear(); SourcePtr = sourcePtr; SourceLen = sourceLen; } + inline void setSourcePtr(const char *sourcePtr) { SourceCopy.clear(); SourcePtr = sourcePtr; SourceLen = strlen(sourcePtr); } /// CVertexProgramInfo/CPixelProgramInfo/... NeL features uint Features; @@ -66,7 +68,7 @@ public: std::map ParamIndices; private: - std::string CodeCopy; + std::string SourceCopy; }; /* class CGPUProgramSource */ diff --git a/code/nel/src/3d/driver.cpp b/code/nel/src/3d/driver.cpp index 25c9afb63..3bd354a62 100644 --- a/code/nel/src/3d/driver.cpp +++ b/code/nel/src/3d/driver.cpp @@ -33,7 +33,7 @@ namespace NL3D { // *************************************************************************** -const uint32 IDriver::InterfaceVersion = 0x6c; // pixel program interface +const uint32 IDriver::InterfaceVersion = 0x6d; // gpu program interface // *************************************************************************** IDriver::IDriver() : _SyncTexDrvInfos( "IDriver::_SyncTexDrvInfos" ) diff --git a/code/nel/src/3d/driver/opengl/driver_opengl.cpp b/code/nel/src/3d/driver/opengl/driver_opengl.cpp index 92021c65b..a93c05338 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl.cpp +++ b/code/nel/src/3d/driver/opengl/driver_opengl.cpp @@ -2573,14 +2573,6 @@ CVertexBuffer::TVertexColorType CDriverGL::getVertexColorFormat() const return CVertexBuffer::TRGBA; } -// *************************************************************************** -bool CDriverGL::activeShader(CShader * /* shd */) -{ - H_AUTO_OGL(CDriverGL_activeShader) - - return false; -} - // *************************************************************************** void CDriverGL::startBench (bool wantStandardDeviation, bool quick, bool reset) { diff --git a/code/nel/src/3d/driver/opengl/driver_opengl.h b/code/nel/src/3d/driver/opengl/driver_opengl.h index 08e950b70..5289f2a26 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl.h +++ b/code/nel/src/3d/driver/opengl/driver_opengl.h @@ -406,8 +406,6 @@ public: virtual CMatrix getViewMatrix() const; - virtual bool activeShader(CShader *shd); - virtual void forceNormalize(bool normalize) { _ForceNormalize= normalize; @@ -1349,7 +1347,7 @@ private: /// \name Pixel program implementation // @{ bool activeARBPixelProgram (CPixelProgram *program); - bool setupARBPixelProgram (const CPixelProgram *program, GLuint id/*, bool &specularWritten*/); + bool setupPixelProgram (CPixelProgram *program, GLuint id/*, bool &specularWritten*/); //@} @@ -1530,7 +1528,7 @@ private: }; // *************************************************************************** -class CVertexProgamDrvInfosGL : public IVertexProgramDrvInfos +class CVertexProgamDrvInfosGL : public IGPUProgramDrvInfos { public: // The GL Id. @@ -1551,18 +1549,36 @@ public: // The gl id is auto created here. - CVertexProgamDrvInfosGL (CDriverGL *drv, ItVtxPrgDrvInfoPtrList it); + CVertexProgamDrvInfosGL (CDriverGL *drv, ItGPUPrgDrvInfoPtrList it); + + virtual uint getParamIdx(char *name) const + { + std::map::const_iterator it = ParamIndices.find(name); + if (it != ParamIndices.end()) return it->second; + return ~0; + }; + + std::map ParamIndices; }; // *************************************************************************** -class CPixelProgamDrvInfosGL : public IPixelProgramDrvInfos +class CPixelProgamDrvInfosGL : public IGPUProgramDrvInfos { public: // The GL Id. GLuint ID; // The gl id is auto created here. - CPixelProgamDrvInfosGL (CDriverGL *drv, ItPixelPrgDrvInfoPtrList it); + CPixelProgamDrvInfosGL (CDriverGL *drv, ItGPUPrgDrvInfoPtrList it); + + virtual uint getParamIdx(char *name) const + { + std::map::const_iterator it = ParamIndices.find(name); + if (it != ParamIndices.end()) return it->second; + return ~0; + }; + + std::map ParamIndices; }; #ifdef NL_STATIC diff --git a/code/nel/src/3d/driver/opengl/driver_opengl_pixel_program.cpp b/code/nel/src/3d/driver/opengl/driver_opengl_pixel_program.cpp index 2bcfc185d..b2867a95a 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl_pixel_program.cpp +++ b/code/nel/src/3d/driver/opengl/driver_opengl_pixel_program.cpp @@ -50,7 +50,7 @@ namespace NLDRIVERGL { #endif // *************************************************************************** -CPixelProgamDrvInfosGL::CPixelProgamDrvInfosGL (CDriverGL *drv, ItPixelPrgDrvInfoPtrList it) : IPixelProgramDrvInfos (drv, it) +CPixelProgamDrvInfosGL::CPixelProgamDrvInfosGL (CDriverGL *drv, ItGPUPrgDrvInfoPtrList it) : IGPUProgramDrvInfos (drv, it) { H_AUTO_OGL(CPixelProgamDrvInfosGL_CPixelProgamDrvInfosGL) // Extension must exist @@ -109,25 +109,25 @@ bool CDriverGL::activeARBPixelProgram(CPixelProgram *program) if (program->_DrvInfo==NULL) { // Insert into driver list. (so it is deleted when driver is deleted). - ItPixelPrgDrvInfoPtrList it= _PixelPrgDrvInfos.insert(_PixelPrgDrvInfos.end(), (NL3D::IPixelProgramDrvInfos*)NULL); + ItGPUPrgDrvInfoPtrList it = _GPUPrgDrvInfos.insert(_GPUPrgDrvInfos.end(), (NL3D::IGPUProgramDrvInfos*)NULL); // Create a driver info - *it = drvInfo = new CPixelProgamDrvInfosGL (this, it); + *it = drvInfo = new CPixelProgamDrvInfosGL(this, it); // Set the pointer - program->_DrvInfo=drvInfo; + program->_DrvInfo = drvInfo; - if(!setupARBPixelProgram(program, drvInfo->ID)) + if (!setupPixelProgram(program, drvInfo->ID)) { delete drvInfo; program->_DrvInfo = NULL; - _PixelPrgDrvInfos.erase(it); + _GPUPrgDrvInfos.erase(it); return false; } } else { // Cast the driver info pointer - drvInfo=safe_cast((IPixelProgramDrvInfos*)program->_DrvInfo); + drvInfo=safe_cast((IGPUProgramDrvInfos*)program->_DrvInfo); } glEnable( GL_FRAGMENT_PROGRAM_ARB ); _PixelProgramEnabled = true; @@ -148,15 +148,31 @@ bool CDriverGL::activeARBPixelProgram(CPixelProgram *program) } // *************************************************************************** -bool CDriverGL::setupARBPixelProgram (const CPixelProgram *program, GLuint id/*, bool &specularWritten*/) +bool CDriverGL::setupPixelProgram(CPixelProgram *program, GLuint id/*, bool &specularWritten*/) { H_AUTO_OGL(CDriverGL_setupARBPixelProgram) + + CPixelProgamDrvInfosGL *drvInfo = static_cast((IGPUProgramDrvInfos *)program->_DrvInfo); - const std::string &code = program->getProgram(); + // Find a supported pixel program profile + CGPUProgramSource *source = NULL; + for (uint i = 0; i < program->getProgramSource()->Sources.size(); ++i) + { + if (supportPixelProgram(program->getProgramSource()->Sources[i]->Profile)) + { + source = program->getProgramSource()->Sources[i]; + } + } + if (!source) + { + nlwarning("No supported source profile for pixel program"); + return false; + } + // Compile the program nglBindProgramARB( GL_FRAGMENT_PROGRAM_ARB, id); glGetError(); - nglProgramStringARB( GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, code.size(), code.c_str() ); + nglProgramStringARB( GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, source->SourceLen, source->SourcePtr); GLenum err = glGetError(); if (err != GL_NO_ERROR) { @@ -165,25 +181,25 @@ bool CDriverGL::setupARBPixelProgram (const CPixelProgram *program, GLuint id/*, GLint position; glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &position); nlassert(position != -1); // there was an error.. - nlassert(position < (GLint) code.size()); + nlassert(position < (GLint) source->SourceLen); uint line = 0; - const char *lineStart = program->getProgram().c_str(); + const char *lineStart = source->SourcePtr; for(uint k = 0; k < (uint) position; ++k) { - if (code[k] == '\n') + if (source->SourcePtr[k] == '\n') { - lineStart = code.c_str() + k; + lineStart = source->SourcePtr + k; ++line; } } nlwarning("ARB fragment program parse error at line %d.", (int) line); // search end of line - const char *lineEnd = code.c_str() + code.size(); - for(uint k = position; k < code.size(); ++k) + const char *lineEnd = source->SourcePtr + source->SourceLen; + for(uint k = position; k < source->SourceLen; ++k) { - if (code[k] == '\n') + if (source->SourcePtr[k] == '\n') { - lineEnd = code.c_str() + k; + lineEnd = source->SourcePtr + k; break; } } @@ -196,6 +212,13 @@ bool CDriverGL::setupARBPixelProgram (const CPixelProgram *program, GLuint id/*, nlassert(0); return false; } + + // Set parameters for assembly programs + drvInfo->ParamIndices = source->ParamIndices; + + // Build the feature info + program->buildInfo(source->DisplayName.c_str(), source->Features); + return true; } diff --git a/code/nel/src/3d/driver/opengl/driver_opengl_vertex.cpp b/code/nel/src/3d/driver/opengl/driver_opengl_vertex.cpp index 14ed83adb..521a13baf 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl_vertex.cpp +++ b/code/nel/src/3d/driver/opengl/driver_opengl_vertex.cpp @@ -1151,7 +1151,7 @@ void CDriverGL::toggleGlArraysForEXTVertexShader() CVertexProgram *vp = _LastSetuppedVP; if (vp) { - CVertexProgamDrvInfosGL *drvInfo = NLMISC::safe_cast((IVertexProgramDrvInfos *) vp->_DrvInfo); + CVertexProgamDrvInfosGL *drvInfo = NLMISC::safe_cast((IGPUProgramDrvInfos *) vp->_DrvInfo); if (drvInfo) { // Disable all VertexAttribs. @@ -1396,7 +1396,7 @@ void CDriverGL::setupGlArraysForEXTVertexShader(CVertexBufferInfo &vb) CVertexProgram *vp = _LastSetuppedVP; if (!vp) return; - CVertexProgamDrvInfosGL *drvInfo = NLMISC::safe_cast((IVertexProgramDrvInfos *) vp->_DrvInfo); + CVertexProgamDrvInfosGL *drvInfo = NLMISC::safe_cast((IGPUProgramDrvInfos *) vp->_DrvInfo); if (!drvInfo) return; uint32 flags= vb.VertexFormat; diff --git a/code/nel/src/3d/driver/opengl/driver_opengl_vertex_program.cpp b/code/nel/src/3d/driver/opengl/driver_opengl_vertex_program.cpp index 1ddb42a7d..34ae2365a 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl_vertex_program.cpp +++ b/code/nel/src/3d/driver/opengl/driver_opengl_vertex_program.cpp @@ -41,7 +41,7 @@ namespace NLDRIVERGL { #endif // *************************************************************************** -CVertexProgamDrvInfosGL::CVertexProgamDrvInfosGL (CDriverGL *drv, ItVtxPrgDrvInfoPtrList it) : IVertexProgramDrvInfos (drv, it) +CVertexProgamDrvInfosGL::CVertexProgamDrvInfosGL(CDriverGL *drv, ItGPUPrgDrvInfoPtrList it) : IGPUProgramDrvInfos (drv, it) { H_AUTO_OGL(CVertexProgamDrvInfosGL_CVertexProgamDrvInfosGL); @@ -105,13 +105,28 @@ bool CDriverGL::activeNVVertexProgram (CVertexProgram *program) // Program setuped ? if (program->_DrvInfo==NULL) { + // Find nelvp + CGPUProgramSource *source = NULL; + for (uint i = 0; i < program->getProgramSource()->Sources.size(); ++i) + { + if (program->getProgramSource()->Sources[i]->Profile == CVertexProgram::nelvp) + { + source = program->getProgramSource()->Sources[i]; + } + } + if (!source) + { + nlwarning("OpenGL driver only supports 'nelvp' profile, vertex program cannot be used"); + return false; + } + /** Check with our parser if the program will works with other implemented extensions, too. (EXT_vertex_shader ..). * There are some incompatibilities. */ CVPParser parser; CVPParser::TProgram parsedProgram; std::string errorOutput; - bool result = parser.parse(program->getProgram().c_str(), parsedProgram, errorOutput); + bool result = parser.parse(source->SourcePtr, parsedProgram, errorOutput); if (!result) { nlwarning("Unable to parse a vertex program :"); @@ -123,7 +138,7 @@ bool CDriverGL::activeNVVertexProgram (CVertexProgram *program) } // Insert into driver list. (so it is deleted when driver is deleted). - ItVtxPrgDrvInfoPtrList it= _VtxPrgDrvInfos.insert(_VtxPrgDrvInfos.end(), (NL3D::IVertexProgramDrvInfos*)NULL); + ItGPUPrgDrvInfoPtrList it= _GPUPrgDrvInfos.insert(_GPUPrgDrvInfos.end(), (NL3D::IGPUProgramDrvInfos*)NULL); // Create a driver info *it = drvInfo = new CVertexProgamDrvInfosGL (this, it); @@ -132,7 +147,7 @@ bool CDriverGL::activeNVVertexProgram (CVertexProgram *program) program->_DrvInfo=drvInfo; // Compile the program - nglLoadProgramNV (GL_VERTEX_PROGRAM_NV, drvInfo->ID, (GLsizei)program->getProgram().length(), (const GLubyte*)program->getProgram().c_str()); + nglLoadProgramNV (GL_VERTEX_PROGRAM_NV, drvInfo->ID, (GLsizei)source->SourceLen, (const GLubyte*)source->SourcePtr); // Get loading error code GLint errorOff; @@ -142,8 +157,8 @@ bool CDriverGL::activeNVVertexProgram (CVertexProgram *program) if (errorOff>=0) { // String length - uint length = (uint)program->getProgram ().length(); - const char* sString= program->getProgram ().c_str(); + uint length = (uint)source->SourceLen; + const char* sString = source->SourcePtr; // Line count and char count uint line=1; @@ -176,13 +191,19 @@ bool CDriverGL::activeNVVertexProgram (CVertexProgram *program) return false; } + // Set parameters for assembly programs + drvInfo->ParamIndices = source->ParamIndices; + + // Build the feature info + program->buildInfo(source->DisplayName.c_str(), source->Features); + // Setup ok return true; } else { // Cast the driver info pointer - drvInfo=safe_cast((IVertexProgramDrvInfos*)program->_DrvInfo); + drvInfo=safe_cast((IGPUProgramDrvInfos*)program->_DrvInfo); } // Setup this program @@ -1503,11 +1524,26 @@ bool CDriverGL::activeARBVertexProgram (CVertexProgram *program) // Program setuped ? if (program->_DrvInfo==NULL) { + // Find nelvp + CGPUProgramSource *source = NULL; + for (uint i = 0; i < program->getProgramSource()->Sources.size(); ++i) + { + if (program->getProgramSource()->Sources[i]->Profile == CVertexProgram::nelvp) + { + source = program->getProgramSource()->Sources[i]; + } + } + if (!source) + { + nlwarning("OpenGL driver only supports 'nelvp' profile, vertex program cannot be used"); + return false; + } + // try to parse the program CVPParser parser; CVPParser::TProgram parsedProgram; std::string errorOutput; - bool result = parser.parse(program->getProgram().c_str(), parsedProgram, errorOutput); + bool result = parser.parse(source->SourcePtr, parsedProgram, errorOutput); if (!result) { nlwarning("Unable to parse a vertex program."); @@ -1517,7 +1553,7 @@ bool CDriverGL::activeARBVertexProgram (CVertexProgram *program) return false; } // Insert into driver list. (so it is deleted when driver is deleted). - ItVtxPrgDrvInfoPtrList it= _VtxPrgDrvInfos.insert(_VtxPrgDrvInfos.end(), (NL3D::IVertexProgramDrvInfos*)NULL); + ItGPUPrgDrvInfoPtrList it= _GPUPrgDrvInfos.insert(_GPUPrgDrvInfos.end(), (NL3D::IGPUProgramDrvInfos*)NULL); // Create a driver info *it = drvInfo = new CVertexProgamDrvInfosGL (this, it); @@ -1528,14 +1564,20 @@ bool CDriverGL::activeARBVertexProgram (CVertexProgram *program) { delete drvInfo; program->_DrvInfo = NULL; - _VtxPrgDrvInfos.erase(it); + _GPUPrgDrvInfos.erase(it); return false; } + + // Set parameters for assembly programs + drvInfo->ParamIndices = source->ParamIndices; + + // Build the feature info + program->buildInfo(source->DisplayName.c_str(), source->Features); } else { // Cast the driver info pointer - drvInfo=safe_cast((IVertexProgramDrvInfos*)program->_DrvInfo); + drvInfo=safe_cast((IGPUProgramDrvInfos*)program->_DrvInfo); } glEnable( GL_VERTEX_PROGRAM_ARB ); _VertexProgramEnabled = true; @@ -1577,11 +1619,26 @@ bool CDriverGL::activeEXTVertexShader (CVertexProgram *program) // Program setuped ? if (program->_DrvInfo==NULL) { + // Find nelvp + CGPUProgramSource *source = NULL; + for (uint i = 0; i < program->getProgramSource()->Sources.size(); ++i) + { + if (program->getProgramSource()->Sources[i]->Profile == CVertexProgram::nelvp) + { + source = program->getProgramSource()->Sources[i]; + } + } + if (!source) + { + nlwarning("OpenGL driver only supports 'nelvp' profile, vertex program cannot be used"); + return false; + } + // try to parse the program CVPParser parser; CVPParser::TProgram parsedProgram; std::string errorOutput; - bool result = parser.parse(program->getProgram().c_str(), parsedProgram, errorOutput); + bool result = parser.parse(source->SourcePtr, parsedProgram, errorOutput); if (!result) { nlwarning("Unable to parse a vertex program."); @@ -1603,7 +1660,7 @@ bool CDriverGL::activeEXTVertexShader (CVertexProgram *program) */ // Insert into driver list. (so it is deleted when driver is deleted). - ItVtxPrgDrvInfoPtrList it= _VtxPrgDrvInfos.insert(_VtxPrgDrvInfos.end(), (NL3D::IVertexProgramDrvInfos*)NULL); + ItGPUPrgDrvInfoPtrList it= _GPUPrgDrvInfos.insert(_GPUPrgDrvInfos.end(), (NL3D::IGPUProgramDrvInfos*)NULL); // Create a driver info *it = drvInfo = new CVertexProgamDrvInfosGL (this, it); @@ -1614,14 +1671,20 @@ bool CDriverGL::activeEXTVertexShader (CVertexProgram *program) { delete drvInfo; program->_DrvInfo = NULL; - _VtxPrgDrvInfos.erase(it); + _GPUPrgDrvInfos.erase(it); return false; } + + // Set parameters for assembly programs + drvInfo->ParamIndices = source->ParamIndices; + + // Build the feature info + program->buildInfo(source->DisplayName.c_str(), source->Features); } else { // Cast the driver info pointer - drvInfo=safe_cast((IVertexProgramDrvInfos*)program->_DrvInfo); + drvInfo=safe_cast((IGPUProgramDrvInfos*)program->_DrvInfo); } glEnable( GL_VERTEX_SHADER_EXT); diff --git a/code/nel/src/3d/stereo_debugger.cpp b/code/nel/src/3d/stereo_debugger.cpp index 17c5c8c98..509ba4769 100644 --- a/code/nel/src/3d/stereo_debugger.cpp +++ b/code/nel/src/3d/stereo_debugger.cpp @@ -130,7 +130,7 @@ void CStereoDebugger::setDriver(NL3D::UDriver *driver) { nldebug("VR: arbfp1"); source->Profile = IGPUProgram::arbfp1; - source->setCodePtr(a_arbfp1); + source->setSourcePtr(a_arbfp1); m_PixelProgram = new CPixelProgram(sourceCont); } /*else if (drvInternal->supportPixelProgram(CPixelProgram::ps_2_0)) diff --git a/code/nel/src/3d/stereo_ovr.cpp b/code/nel/src/3d/stereo_ovr.cpp index 00db2020e..8c8c6ff63 100644 --- a/code/nel/src/3d/stereo_ovr.cpp +++ b/code/nel/src/3d/stereo_ovr.cpp @@ -248,21 +248,21 @@ void CStereoOVR::setDriver(NL3D::UDriver *driver) { nldebug("VR: fp40"); source->Profile = IGPUProgram::fp40; - source->setCodePtr(g_StereoOVR_fp40); + source->setSourcePtr(g_StereoOVR_fp40); m_PixelProgram = new CPixelProgram(sourceCont); } else if (drvInternal->supportPixelProgram(CPixelProgram::arbfp1) && drvInternal->supportBloomEffect() && drvInternal->supportNonPowerOfTwoTextures()) { nldebug("VR: arbfp1"); source->Profile = IGPUProgram::arbfp1; - source->setCodePtr(g_StereoOVR_arbfp1); + source->setSourcePtr(g_StereoOVR_arbfp1); m_PixelProgram = new CPixelProgram(sourceCont); } else if (drvInternal->supportPixelProgram(CPixelProgram::ps_2_0)) { nldebug("VR: ps_2_0"); source->Profile = IGPUProgram::ps_2_0; - source->setCodePtr(g_StereoOVR_ps_2_0); + source->setSourcePtr(g_StereoOVR_ps_2_0); m_PixelProgram = new CPixelProgram(sourceCont); } diff --git a/code/nel/src/3d/vertex_program.cpp b/code/nel/src/3d/vertex_program.cpp index 2f70d51af..d0acbe775 100644 --- a/code/nel/src/3d/vertex_program.cpp +++ b/code/nel/src/3d/vertex_program.cpp @@ -39,7 +39,7 @@ CVertexProgram::CVertexProgram(const char *nelvp) : _Info(NULL) _ProgramSource = new CGPUProgramSourceCont(); _ProgramSource->Sources.push_back(source); source->Profile = IGPUProgram::nelvp; - source->setCode(nelvp); + source->setSource(nelvp); source->Features = 0; } From f4d05d25c5e326ad678fdb14b9b3f9d31b5f9b69 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Sat, 7 Sep 2013 22:00:07 +0200 Subject: [PATCH 179/313] Implement new gpu program interface in direct3d driver --- code/nel/include/nel/3d/driver.h | 11 +- code/nel/include/nel/3d/material.h | 1 - code/nel/include/nel/3d/shader.h | 32 ----- code/nel/src/3d/CMakeLists.txt | 2 - code/nel/src/3d/driver.cpp | 9 -- .../3d/driver/direct3d/driver_direct3d.cpp | 7 ++ .../src/3d/driver/direct3d/driver_direct3d.h | 111 ++++++++++++++++-- .../driver_direct3d_pixel_program.cpp | 36 ++++-- .../direct3d/driver_direct3d_shader.cpp | 91 +++++++++++++- .../driver_direct3d_vertex_program.cpp | 34 +++++- code/nel/src/3d/driver/opengl/driver_opengl.h | 1 - code/nel/src/3d/material.cpp | 1 - code/nel/src/3d/shader.cpp | 30 ----- 13 files changed, 256 insertions(+), 110 deletions(-) delete mode 100644 code/nel/include/nel/3d/shader.h delete mode 100644 code/nel/src/3d/shader.cpp diff --git a/code/nel/include/nel/3d/driver.h b/code/nel/include/nel/3d/driver.h index 3cb0c0349..2d10c5653 100644 --- a/code/nel/include/nel/3d/driver.h +++ b/code/nel/include/nel/3d/driver.h @@ -26,11 +26,11 @@ #include "nel/misc/uv.h" #include "nel/misc/hierarchical_timer.h" #include "nel/3d/texture.h" -#include "nel/3d/shader.h" #include "nel/3d/vertex_buffer.h" #include "nel/3d/index_buffer.h" #include "nel/3d/vertex_program.h" #include "nel/3d/pixel_program.h" +#include "nel/3d/geometry_program.h" #include "nel/3d/material.h" #include "nel/misc/mutex.h" #include "nel/3d/primitive_profile.h" @@ -152,9 +152,6 @@ protected: TIBDrvInfoPtrList _IBDrvInfos; TPolygonMode _PolygonMode; TGPUPrgDrvInfoPtrList _GPUPrgDrvInfos; - // TPixelPrgDrvInfoPtrList _PixelPrgDrvInfos; - // TGeomPrgDrvInfoPtrList _GeomPrgDrvInfos; - // TShaderDrvInfoPtrList _ShaderDrvInfos; uint _ResetCounter; @@ -1299,9 +1296,6 @@ protected: friend class CTextureDrvShare; friend class ITextureDrvInfos; friend class IMaterialDrvInfos; - // friend class IVertexProgramDrvInfos; - // friend class IPixelProgramDrvInfos; - // friend class IShaderDrvInfos; friend class IGPUProgramDrvInfos; /// remove ptr from the lists in the driver. @@ -1310,9 +1304,6 @@ protected: void removeTextureDrvInfoPtr(ItTexDrvInfoPtrMap texDrvInfoIt); void removeTextureDrvSharePtr(ItTexDrvSharePtrList texDrvShareIt); void removeMatDrvInfoPtr(ItMatDrvInfoPtrList shaderIt); - // void removeShaderDrvInfoPtr(ItShaderDrvInfoPtrList shaderIt); - // void removeVtxPrgDrvInfoPtr(ItVtxPrgDrvInfoPtrList vtxPrgDrvInfoIt); - // void removePixelPrgDrvInfoPtr(ItPixelPrgDrvInfoPtrList pixelPrgDrvInfoIt); void removeGPUPrgDrvInfoPtr(ItGPUPrgDrvInfoPtrList gpuPrgDrvInfoIt); private: diff --git a/code/nel/include/nel/3d/material.h b/code/nel/include/nel/3d/material.h index bf00f9741..6d9589ca5 100644 --- a/code/nel/include/nel/3d/material.h +++ b/code/nel/include/nel/3d/material.h @@ -22,7 +22,6 @@ #include "nel/misc/rgba.h" #include "nel/misc/matrix.h" #include "nel/3d/texture.h" -#include "nel/3d/shader.h" #include diff --git a/code/nel/include/nel/3d/shader.h b/code/nel/include/nel/3d/shader.h deleted file mode 100644 index 61956e8e1..000000000 --- a/code/nel/include/nel/3d/shader.h +++ /dev/null @@ -1,32 +0,0 @@ -// NeL - MMORPG Framework -// Copyright (C) 2010 Winch Gate Property Limited -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -#ifndef NL_SHADER_H -#define NL_SHADER_H - -#include "nel/misc/types_nl.h" -#include "nel/misc/smart_ptr.h" -#include - - -namespace NL3D { - -} // NL3D - - -#endif // NL_SHADER_H - -/* End of shader.h */ diff --git a/code/nel/src/3d/CMakeLists.txt b/code/nel/src/3d/CMakeLists.txt index f469b3b62..242cb754c 100644 --- a/code/nel/src/3d/CMakeLists.txt +++ b/code/nel/src/3d/CMakeLists.txt @@ -153,8 +153,6 @@ SOURCE_GROUP(Driver FILES ../../include/nel/3d/scene.h scene_group.cpp ../../include/nel/3d/scene_group.h - shader.cpp - ../../include/nel/3d/shader.h texture.cpp ../../include/nel/3d/texture.h vertex_buffer.cpp diff --git a/code/nel/src/3d/driver.cpp b/code/nel/src/3d/driver.cpp index 3bd354a62..31dd4d2ec 100644 --- a/code/nel/src/3d/driver.cpp +++ b/code/nel/src/3d/driver.cpp @@ -20,7 +20,6 @@ #include "nel/misc/types_nl.h" #include "nel/3d/driver.h" -#include "nel/3d/shader.h" #include "nel/3d/vertex_buffer.h" #include "nel/misc/algo.h" @@ -94,14 +93,6 @@ bool IDriver::release(void) // NB: at IShader deletion, this->_MatDrvInfos is updated (entry deleted); delete *itmat; } -/* - // Release Shader drv. - ItShaderDrvInfoPtrList itshd; - while( (itshd = _ShaderDrvInfos.begin()) != _ShaderDrvInfos.end() ) - { - // NB: at IShader deletion, this->_MatDrvInfos is updated (entry deleted); - delete *itshd; - }*/ // Release VBs drv. ItVBDrvInfoPtrList itvb; diff --git a/code/nel/src/3d/driver/direct3d/driver_direct3d.cpp b/code/nel/src/3d/driver/direct3d/driver_direct3d.cpp index 8f8901f5c..e7c1ba693 100644 --- a/code/nel/src/3d/driver/direct3d/driver_direct3d.cpp +++ b/code/nel/src/3d/driver/direct3d/driver_direct3d.cpp @@ -1710,6 +1710,13 @@ bool CDriverD3D::release() // Call IDriver::release() before, to destroy textures, shaders and VBs... IDriver::release(); + ItShaderDrvInfoPtrList itshd; + while( (itshd = _ShaderDrvInfos.begin()) != _ShaderDrvInfos.end() ) + { + // NB: at IShader deletion, this->_MatDrvInfos is updated (entry deleted); + delete *itshd; + } + _SwapBufferCounter = 0; if (_QuadIB) diff --git a/code/nel/src/3d/driver/direct3d/driver_direct3d.h b/code/nel/src/3d/driver/direct3d/driver_direct3d.h index 085bb7512..d329a6f56 100644 --- a/code/nel/src/3d/driver/direct3d/driver_direct3d.h +++ b/code/nel/src/3d/driver/direct3d/driver_direct3d.h @@ -35,7 +35,6 @@ #include "nel/3d/scissor.h" #include "nel/3d/driver.h" #include "nel/3d/material.h" -#include "nel/3d/shader.h" #include "nel/3d/vertex_buffer.h" #include "nel/3d/index_buffer.h" #include "nel/3d/ptr_set.h" @@ -181,6 +180,75 @@ public: }; +using NLMISC::CRefCount; + + +class IDriver; +class CDriverD3D; + +// List typedef. +class IShaderDrvInfos; +typedef std::list TShaderDrvInfoPtrList; +typedef TShaderDrvInfoPtrList::iterator ItShaderDrvInfoPtrList; + +/** + * Interface for shader driver infos. + */ +class IShaderDrvInfos : public CRefCount +{ +private: + CDriverD3D *_Driver; + ItShaderDrvInfoPtrList _DriverIterator; + +public: + IShaderDrvInfos(CDriverD3D *drv, ItShaderDrvInfoPtrList it) {_Driver= drv; _DriverIterator= it;} + // The virtual dtor is important. + virtual ~IShaderDrvInfos(); +}; + + +/** + * Shader resource for the driver. It is just a container for a ".fx" text file. + */ +/* *** IMPORTANT ******************** + * *** IF YOU MODIFY THE STRUCTURE OF THIS CLASS, PLEASE INCREMENT IDriver::InterfaceVersion TO INVALIDATE OLD DRIVER DLL + * ********************************** + */ +// -------------------------------------------------- +class CShader +{ +public: + CShader(); + ~CShader(); + + // Load a shader file + bool loadShaderFile (const char *filename); + + // Set the shader text + void setText (const char *text); + + // Get the shader text + const char *getText () const { return _Text.c_str(); } + + // Set the shader name + void setName (const char *name); + + // Get the shader name + const char *getName () const { return _Name.c_str(); } + +public: + // Private. For Driver only. + bool _ShaderChanged; + NLMISC::CRefPtr _DrvInfo; +private: + // The shader + std::string _Text; + // The shader name + std::string _Name; +}; + + + // *************************************************************************** class CTextureDrvInfosD3D : public ITextureDrvInfos { @@ -229,29 +297,48 @@ public: }; + // *************************************************************************** -class CVertexProgamDrvInfosD3D : public IVertexProgramDrvInfos +class CVertexProgamDrvInfosD3D : public IGPUProgramDrvInfos { public: // The shader IDirect3DVertexShader9 *Shader; - CVertexProgamDrvInfosD3D(IDriver *drv, ItVtxPrgDrvInfoPtrList it); + CVertexProgamDrvInfosD3D(IDriver *drv, ItGPUPrgDrvInfoPtrList it); ~CVertexProgamDrvInfosD3D(); + + virtual uint getParamIdx(char *name) const + { + std::map::const_iterator it = ParamIndices.find(name); + if (it != ParamIndices.end()) return it->second; + return ~0; + }; + + std::map ParamIndices; }; // *************************************************************************** -class CPixelProgramDrvInfosD3D : public IPixelProgramDrvInfos +class CPixelProgramDrvInfosD3D : public IGPUProgramDrvInfos { public: // The shader IDirect3DPixelShader9 *Shader; - CPixelProgramDrvInfosD3D(IDriver *drv, ItPixelPrgDrvInfoPtrList it); + CPixelProgramDrvInfosD3D(IDriver *drv, ItGPUPrgDrvInfoPtrList it); ~CPixelProgramDrvInfosD3D(); + + virtual uint getParamIdx(char *name) const + { + std::map::const_iterator it = ParamIndices.find(name); + if (it != ParamIndices.end()) return it->second; + return ~0; + }; + + std::map ParamIndices; }; @@ -346,7 +433,7 @@ public: // Scalar handles D3DXHANDLE ScalarFloatHandle[MaxShaderTexture]; - CShaderDrvInfosD3D(IDriver *drv, ItShaderDrvInfoPtrList it); + CShaderDrvInfosD3D(CDriverD3D *drv, ItShaderDrvInfoPtrList it); virtual ~CShaderDrvInfosD3D(); }; @@ -1048,7 +1135,7 @@ public: * ColorOp[n] = DISABLE; * AlphaOp[n] = DISABLE; */ - virtual bool activeShader(CShader *shd); + bool activeShader(CShader *shd); // Bench virtual void startBench (bool wantStandardDeviation = false, bool quick = false, bool reset = true); @@ -1922,7 +2009,7 @@ public: { H_AUTO_D3D(CDriverD3D_getPixelProgramD3D); CPixelProgramDrvInfosD3D* d3dPixelProgram; - d3dPixelProgram = (CPixelProgramDrvInfosD3D*)(IPixelProgramDrvInfos*)(pixelProgram._DrvInfo); + d3dPixelProgram = (CPixelProgramDrvInfosD3D*)(IGPUProgramDrvInfos*)(pixelProgram._DrvInfo); return d3dPixelProgram; } @@ -1931,7 +2018,7 @@ public: { H_AUTO_D3D(CDriverD3D_getVertexProgramD3D); CVertexProgamDrvInfosD3D* d3dVertexProgram; - d3dVertexProgram = (CVertexProgamDrvInfosD3D*)(IVertexProgramDrvInfos*)(vertexProgram._DrvInfo); + d3dVertexProgram = (CVertexProgamDrvInfosD3D*)(IGPUProgramDrvInfos*)(vertexProgram._DrvInfo); return d3dVertexProgram; } @@ -2114,6 +2201,8 @@ private: void findNearestFullscreenVideoMode(); + TShaderDrvInfoPtrList _ShaderDrvInfos; + // Windows std::string _WindowClass; HWND _HWnd; @@ -2563,6 +2652,10 @@ public: // Clip the wanted rectangle with window. return true if rect is not NULL. bool clipRect(NLMISC::CRect &rect); + friend class IShaderDrvInfos; + + void removeShaderDrvInfoPtr(ItShaderDrvInfoPtrList shaderIt); + }; #define NL_D3DCOLOR_RGBA(rgba) (D3DCOLOR_ARGB(rgba.A,rgba.R,rgba.G,rgba.B)) diff --git a/code/nel/src/3d/driver/direct3d/driver_direct3d_pixel_program.cpp b/code/nel/src/3d/driver/direct3d/driver_direct3d_pixel_program.cpp index 8234fc31d..db8763091 100644 --- a/code/nel/src/3d/driver/direct3d/driver_direct3d_pixel_program.cpp +++ b/code/nel/src/3d/driver/direct3d/driver_direct3d_pixel_program.cpp @@ -37,7 +37,7 @@ namespace NL3D // *************************************************************************** -CPixelProgramDrvInfosD3D::CPixelProgramDrvInfosD3D(IDriver *drv, ItPixelPrgDrvInfoPtrList it) : IPixelProgramDrvInfos (drv, it) +CPixelProgramDrvInfosD3D::CPixelProgramDrvInfosD3D(IDriver *drv, ItGPUPrgDrvInfoPtrList it) : IGPUProgramDrvInfos (drv, it) { H_AUTO_D3D(CPixelProgramDrvInfosD3D_CPixelProgamDrvInfosD3D) Shader = NULL; @@ -81,18 +81,32 @@ bool CDriverD3D::activePixelProgram(CPixelProgram *program) // Program setuped ? if (program->_DrvInfo==NULL) { - _PixelPrgDrvInfos.push_front (NULL); - ItPixelPrgDrvInfoPtrList itPix = _PixelPrgDrvInfos.begin(); - *itPix = new CPixelProgramDrvInfosD3D(this, itPix); + // Find a supported pixel program profile + CGPUProgramSource *source = NULL; + for (uint i = 0; i < program->getProgramSource()->Sources.size(); ++i) + { + if (supportPixelProgram(program->getProgramSource()->Sources[i]->Profile)) + { + source = program->getProgramSource()->Sources[i]; + } + } + if (!source) + { + nlwarning("No supported source profile for pixel program"); + return false; + } + + _GPUPrgDrvInfos.push_front (NULL); + ItGPUPrgDrvInfoPtrList itPix = _GPUPrgDrvInfos.begin(); + CPixelProgramDrvInfosD3D *drvInfo; + *itPix = drvInfo = new CPixelProgramDrvInfosD3D(this, itPix); // Create a driver info structure program->_DrvInfo = *itPix; - const std::string &dest = program->getProgram(); - LPD3DXBUFFER pShader; LPD3DXBUFFER pErrorMsgs; - if (D3DXAssembleShader (dest.c_str(), dest.size(), NULL, NULL, 0, &pShader, &pErrorMsgs) == D3D_OK) + if (D3DXAssembleShader(source->SourcePtr, source->SourceLen, NULL, NULL, 0, &pShader, &pErrorMsgs) == D3D_OK) { if (_DeviceInterface->CreatePixelShader((DWORD*)pShader->GetBufferPointer(), &(getPixelProgramD3D(*program)->Shader)) != D3D_OK) return false; @@ -103,13 +117,19 @@ bool CDriverD3D::activePixelProgram(CPixelProgram *program) nlwarning ((const char*)pErrorMsgs->GetBufferPointer()); return false; } + + // Set parameters for assembly programs + drvInfo->ParamIndices = source->ParamIndices; + + // Build the feature info + program->buildInfo(source->DisplayName.c_str(), source->Features); } } // Set the pixel program if (program) { - CPixelProgramDrvInfosD3D *info = static_cast((IPixelProgramDrvInfos*)program->_DrvInfo); + CPixelProgramDrvInfosD3D *info = static_cast((IGPUProgramDrvInfos*)program->_DrvInfo); setPixelShader (info->Shader); float z = 0; diff --git a/code/nel/src/3d/driver/direct3d/driver_direct3d_shader.cpp b/code/nel/src/3d/driver/direct3d/driver_direct3d_shader.cpp index b9b757de1..300cfe4b7 100644 --- a/code/nel/src/3d/driver/direct3d/driver_direct3d_shader.cpp +++ b/code/nel/src/3d/driver/direct3d/driver_direct3d_shader.cpp @@ -17,6 +17,8 @@ #include "stddirect3d.h" #include "driver_direct3d.h" +#include "nel/misc/path.h" +#include "nel/misc/file.h" using namespace std; using namespace NLMISC; @@ -24,6 +26,93 @@ using namespace NLMISC; namespace NL3D { + +// *************************************************************************** + +CShader::~CShader() +{ + // Must kill the drv mirror of this shader. + _DrvInfo.kill(); +} + +// *************************************************************************** + +CShader::CShader() +{ + _ShaderChanged = true; +} + +// *************************************************************************** + +void CShader::setText (const char *text) +{ + _Text = text; + _ShaderChanged = true; +} + +// *************************************************************************** + +void CShader::setName (const char *name) +{ + _Name = name; + _ShaderChanged = true; +} + +// *************************************************************************** + +bool CShader::loadShaderFile (const char *filename) +{ + _Text = ""; + // Lookup + string _filename = NLMISC::CPath::lookup(filename, false, true, true); + if (!_filename.empty()) + { + // File length + uint size = NLMISC::CFile::getFileSize (_filename); + _Text.reserve (size+1); + + try + { + NLMISC::CIFile file; + if (file.open (_filename)) + { + // Read it + while (!file.eof ()) + { + char line[512]; + file.getline (line, 512); + _Text += line; + } + + // Set the shader name + _Name = NLMISC::CFile::getFilename (filename); + return true; + } + else + { + nlwarning ("Can't open the file %s for reading", _filename.c_str()); + } + } + catch (const Exception &e) + { + nlwarning ("Error while reading %s : %s", _filename.c_str(), e.what()); + } + } + return false; +} + +// *************************************************************************** + +IShaderDrvInfos::~IShaderDrvInfos() +{ + _Driver->removeShaderDrvInfoPtr(_DriverIterator); +} + +void CDriverD3D::removeShaderDrvInfoPtr(ItShaderDrvInfoPtrList shaderIt) +{ + _ShaderDrvInfos.erase(shaderIt); +} + // mem allocator for state records std::allocator CStateRecord::Allocator; @@ -249,7 +338,7 @@ HRESULT CDriverD3D::SetVertexShaderConstantI(UINT StartRegister, CONST INT* pCon // *************************************************************************** -CShaderDrvInfosD3D::CShaderDrvInfosD3D(IDriver *drv, ItShaderDrvInfoPtrList it) : IShaderDrvInfos(drv, it) +CShaderDrvInfosD3D::CShaderDrvInfosD3D(CDriverD3D *drv, ItShaderDrvInfoPtrList it) : IShaderDrvInfos(drv, it) { H_AUTO_D3D(CShaderDrvInfosD3D_CShaderDrvInfosD3D) Validated = false; diff --git a/code/nel/src/3d/driver/direct3d/driver_direct3d_vertex_program.cpp b/code/nel/src/3d/driver/direct3d/driver_direct3d_vertex_program.cpp index a758aa7b3..c7b8905a1 100644 --- a/code/nel/src/3d/driver/direct3d/driver_direct3d_vertex_program.cpp +++ b/code/nel/src/3d/driver/direct3d/driver_direct3d_vertex_program.cpp @@ -26,7 +26,7 @@ namespace NL3D // *************************************************************************** -CVertexProgamDrvInfosD3D::CVertexProgamDrvInfosD3D(IDriver *drv, ItVtxPrgDrvInfoPtrList it) : IVertexProgramDrvInfos (drv, it) +CVertexProgamDrvInfosD3D::CVertexProgamDrvInfosD3D(IDriver *drv, ItGPUPrgDrvInfoPtrList it) : IGPUProgramDrvInfos (drv, it) { H_AUTO_D3D(CVertexProgamDrvInfosD3D_CVertexProgamDrvInfosD3D) Shader = NULL; @@ -274,9 +274,25 @@ bool CDriverD3D::activeVertexProgram (CVertexProgram *program) // Program setuped ? if (program->_DrvInfo==NULL) { - _VtxPrgDrvInfos.push_front (NULL); - ItVtxPrgDrvInfoPtrList itTex = _VtxPrgDrvInfos.begin(); - *itTex = new CVertexProgamDrvInfosD3D(this, itTex); + // Find nelvp + CGPUProgramSource *source = NULL; + for (uint i = 0; i < program->getProgramSource()->Sources.size(); ++i) + { + if (program->getProgramSource()->Sources[i]->Profile == CVertexProgram::nelvp) + { + source = program->getProgramSource()->Sources[i]; + } + } + if (!source) + { + nlwarning("Direct3D driver only supports 'nelvp' profile, vertex program cannot be used"); + return false; + } + + _GPUPrgDrvInfos.push_front (NULL); + ItGPUPrgDrvInfoPtrList itTex = _GPUPrgDrvInfos.begin(); + CVertexProgamDrvInfosD3D *drvInfo; + *itTex = drvInfo = new CVertexProgamDrvInfosD3D(this, itTex); // Create a driver info structure program->_DrvInfo = *itTex; @@ -287,7 +303,7 @@ bool CDriverD3D::activeVertexProgram (CVertexProgram *program) CVPParser parser; CVPParser::TProgram parsedProgram; std::string errorOutput; - bool result = parser.parse(program->getProgram().c_str(), parsedProgram, errorOutput); + bool result = parser.parse(source->SourcePtr, parsedProgram, errorOutput); if (!result) { nlwarning("Unable to parse a vertex program :"); @@ -345,13 +361,19 @@ bool CDriverD3D::activeVertexProgram (CVertexProgram *program) nlwarning ((const char*)pErrorMsgs->GetBufferPointer()); return false; } + + // Set parameters for assembly programs + drvInfo->ParamIndices = source->ParamIndices; + + // Build the feature info + program->buildInfo(source->DisplayName.c_str(), source->Features); } } // Set the vertex program if (program) { - CVertexProgamDrvInfosD3D *info = static_cast((IVertexProgramDrvInfos*)program->_DrvInfo); + CVertexProgamDrvInfosD3D *info = static_cast((IGPUProgramDrvInfos*)program->_DrvInfo); setVertexProgram (info->Shader, program); /* D3DRS_FOGSTART and D3DRS_FOGEND must be set with [1, 0] else the fog doesn't work properly on VertexShader and non-VertexShader objects diff --git a/code/nel/src/3d/driver/opengl/driver_opengl.h b/code/nel/src/3d/driver/opengl/driver_opengl.h index 5289f2a26..29fd59957 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl.h +++ b/code/nel/src/3d/driver/opengl/driver_opengl.h @@ -49,7 +49,6 @@ #include "nel/3d/driver.h" #include "nel/3d/material.h" -#include "nel/3d/shader.h" #include "nel/3d/vertex_buffer.h" #include "nel/3d/ptr_set.h" #include "nel/3d/texture_cube.h" diff --git a/code/nel/src/3d/material.cpp b/code/nel/src/3d/material.cpp index 4f4386dc3..886f45308 100644 --- a/code/nel/src/3d/material.cpp +++ b/code/nel/src/3d/material.cpp @@ -18,7 +18,6 @@ #include "nel/3d/material.h" #include "nel/3d/texture.h" -#include "nel/3d/shader.h" #include "nel/3d/driver.h" #include "nel/misc/stream.h" diff --git a/code/nel/src/3d/shader.cpp b/code/nel/src/3d/shader.cpp deleted file mode 100644 index 9c08e9980..000000000 --- a/code/nel/src/3d/shader.cpp +++ /dev/null @@ -1,30 +0,0 @@ -// NeL - MMORPG Framework -// Copyright (C) 2010 Winch Gate Property Limited -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -#include "std3d.h" - -#include "nel/3d/shader.h" -#include "nel/3d/driver.h" -#include "nel/misc/path.h" -#include "nel/misc/file.h" - -using namespace std; -using namespace NLMISC; - -namespace NL3D -{ - -} // NL3D From 58a8982ba5f3a9ab0c087d90dd64a1fd16219b74 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Sun, 8 Sep 2013 01:36:01 +0200 Subject: [PATCH 180/313] Add gpu program params storage structure --- code/nel/include/nel/3d/gpu_program_params.h | 100 +++++++++++ code/nel/src/3d/CMakeLists.txt | 4 +- code/nel/src/3d/gpu_program_params.cpp | 165 +++++++++++++++++++ 3 files changed, 268 insertions(+), 1 deletion(-) create mode 100644 code/nel/include/nel/3d/gpu_program_params.h create mode 100644 code/nel/src/3d/gpu_program_params.cpp diff --git a/code/nel/include/nel/3d/gpu_program_params.h b/code/nel/include/nel/3d/gpu_program_params.h new file mode 100644 index 000000000..a94a0ccf8 --- /dev/null +++ b/code/nel/include/nel/3d/gpu_program_params.h @@ -0,0 +1,100 @@ +/** + * \file gpu_program_params.h + * \brief CGPUProgramParams + * \date 2013-09-07 22:17GMT + * \author Jan Boon (Kaetemi) + * CGPUProgramParams + */ + +/* + * Copyright (C) 2013 by authors + * + * This file is part of NL3D. + * NL3D is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * NL3D 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 Affero General + * Public License for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with NL3D. If not, see + * . + */ + +#ifndef NL3D_GPU_PROGRAM_PARAMS_H +#define NL3D_GPU_PROGRAM_PARAMS_H +#include + +// STL includes +#include +#include + +// NeL includes + +// Project includes + +namespace NL3D { + +/** + * \brief CGPUProgramParams + * \date 2013-09-07 22:17GMT + * \author Jan Boon (Kaetemi) + * A storage for user-provided parameters for GPU programs. + * Allows for fast updating and iteration of parameters. + * NOTE TO DRIVER IMPLEMENTORS: DO NOT USE FOR STORING COPIES + * OF HARDCODED MATERIAL PARAMETERS OR DRIVER PARAMETERS!!! + */ +class CGPUProgramParams +{ +public: + enum TType { Float, Int }; + struct CMeta { uint Index, Count; TType Type; size_t Next, Prev; }; + +private: + union CVec { float F[4]; sint32 I[4]; }; + +public: + CGPUProgramParams(); + virtual ~CGPUProgramParams(); + + void set(uint index, float f0, float f1, float f2, float f3); + void set(uint index, int i0, int i1, int i2, int i3); + + // Internal + /// Allocate specified number of components if necessary + size_t allocOffset(uint index, uint count, TType type); + /// Return offset for specified index + size_t getOffset(uint index) const; + /// Remove by offset + void freeOffset(size_t offset); + + // Iteration + size_t getBegin() const { return m_Meta.size() ? m_First : s_End; } + size_t getNext(size_t offset) const { return m_Meta[offset].Next; } + size_t getEnd() const { return s_End; } + + // Data access + uint getCountByOffset(size_t offset) { return m_Meta[offset].Count; } + float *getPtrFByOffset(size_t offset) { return m_Vec[offset].F; } + int *getPtrIByOffset(size_t offset) { return m_Vec[offset].I; } + TType getTypeByOffset(size_t offset) { return m_Meta[offset].Type; } + +private: + std::vector m_Vec; + std::vector m_Meta; + std::vector m_Map; // map from index to buffer index + size_t m_First; + size_t m_Last; + static const size_t s_End = -1; + +}; /* class CGPUProgramParams */ + +} /* namespace NL3D */ + +#endif /* #ifndef NL3D_GPU_PROGRAM_PARAMS_H */ + +/* end of file */ diff --git a/code/nel/src/3d/CMakeLists.txt b/code/nel/src/3d/CMakeLists.txt index 242cb754c..5f5896ff9 100644 --- a/code/nel/src/3d/CMakeLists.txt +++ b/code/nel/src/3d/CMakeLists.txt @@ -170,7 +170,9 @@ SOURCE_GROUP(Driver FILES gpu_program.cpp ../../include/nel/3d/gpu_program.h gpu_program_source.cpp - ../../include/nel/3d/gpu_program_source.h) + ../../include/nel/3d/gpu_program_source.h + gpu_program_params.cpp + ../../include/nel/3d/gpu_program_params.h) SOURCE_GROUP(Font FILES computed_string.cpp diff --git a/code/nel/src/3d/gpu_program_params.cpp b/code/nel/src/3d/gpu_program_params.cpp new file mode 100644 index 000000000..78dcab15c --- /dev/null +++ b/code/nel/src/3d/gpu_program_params.cpp @@ -0,0 +1,165 @@ +/** + * \file gpu_program_params.cpp + * \brief CGPUProgramParams + * \date 2013-09-07 22:17GMT + * \author Jan Boon (Kaetemi) + * CGPUProgramParams + */ + +/* + * Copyright (C) 2013 by authors + * + * This file is part of NL3D. + * NL3D is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * NL3D 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 Affero General + * Public License for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with NL3D. If not, see + * . + */ + +#include +#include + +// STL includes + +// NeL includes +// #include + +// Project includes + +using namespace std; +// using namespace NLMISC; + +namespace NL3D { + +CGPUProgramParams::CGPUProgramParams() : m_First(s_End), m_Last(s_End) +{ + +} + +CGPUProgramParams::~CGPUProgramParams() +{ + +} + +void CGPUProgramParams::set(uint index, float f0, float f1, float f2, float f3) +{ + float *f = getPtrFByOffset(allocOffset(index, 4, Float)); + f[0] = f0; + f[1] = f1; + f[2] = f2; + f[3] = f3; +} + +void CGPUProgramParams::set(uint index, int i0, int i1, int i2, int i3) +{ + int *i = getPtrIByOffset(allocOffset(index, 4, Int)); + i[0] = i0; + i[1] = i1; + i[2] = i2; + i[3] = i3; +} + +/// Allocate specified number of components if necessary +size_t CGPUProgramParams::allocOffset(uint index, uint count, TType type) +{ + nlassert(count > 0); // this code will not properly handle 0 + nlassert(index < 0xFFFF); // sanity check + + size_t offset = getOffset(index); + if (offset != s_End) + { + if (getCountByOffset(offset) == count) + { + m_Meta[offset].Type = type; + return offset; + } + if (getCountByOffset(offset) > count) + { + m_Meta[offset].Type = type; + m_Meta[offset].Count = count; // reduce count + return offset; + } + if (getCountByOffset(offset) < count) + { + freeOffset(offset); + } + } + + // Allocate space + offset = m_Meta.size(); + uint blocks = (count + 3) >> 2; // per 4 components + m_Meta.resize(offset + blocks); + m_Vec.resize(offset + blocks); + + // Store offset in map + if (index >= m_Map.size()) + m_Map.resize(index + 1, s_End); + m_Map[index] = offset; + + // Fill + m_Meta[offset].Index = index; + m_Meta[offset].Count = count; + m_Meta[offset].Type = type; + m_Meta[offset].Prev = m_Last; + m_Meta[offset].Next = s_End; + + // Link + if (m_Last == s_End) + { + m_First = m_Last = offset; + } + else + { + nlassert(m_Meta[m_Last].Next == s_End); // code error otherwise + m_Meta[m_Last].Next = offset; + m_Last = offset; + } + + return offset; +} + +/// Return offset for specified index +size_t CGPUProgramParams::getOffset(uint index) const +{ + if (index >= m_Map.size()) + return s_End; + return m_Map[index]; +} + +/// Remove by offset +void CGPUProgramParams::freeOffset(size_t offset) +{ + if (offset == m_Last) + { + nlassert(m_Meta[offset].Next == s_End); + m_Last = m_Meta[offset].Prev; + } + else + { + nlassert(m_Meta[offset].Next != s_End); + m_Meta[m_Meta[offset].Next].Prev = m_Meta[offset].Prev; + } + if (offset == m_First) + { + nlassert(m_Meta[offset].Prev == s_End); + m_First = m_Meta[offset].Next; + } + else + { + nlassert(m_Meta[offset].Prev != s_End); + m_Meta[m_Meta[offset].Prev].Next = m_Meta[offset].Next; + } +} + +} /* namespace NL3D */ + +/* end of file */ From 48493b225d25a16db2aee80da07b1aa7b741271b Mon Sep 17 00:00:00 2001 From: kaetemi Date: Sun, 8 Sep 2013 01:49:34 +0200 Subject: [PATCH 181/313] Add additional set functions --- code/nel/include/nel/3d/gpu_program_params.h | 21 +++++- code/nel/src/3d/gpu_program_params.cpp | 74 +++++++++++++++++++- 2 files changed, 90 insertions(+), 5 deletions(-) diff --git a/code/nel/include/nel/3d/gpu_program_params.h b/code/nel/include/nel/3d/gpu_program_params.h index a94a0ccf8..d1a3125a2 100644 --- a/code/nel/include/nel/3d/gpu_program_params.h +++ b/code/nel/include/nel/3d/gpu_program_params.h @@ -37,6 +37,11 @@ // Project includes +namespace NLMISC { + class CVector; + class CMatrix; +} + namespace NL3D { /** @@ -61,8 +66,17 @@ public: CGPUProgramParams(); virtual ~CGPUProgramParams(); - void set(uint index, float f0, float f1, float f2, float f3); - void set(uint index, int i0, int i1, int i2, int i3); + void setF(uint index, float f0); + void setF(uint index, float f0, float f1); + void setF(uint index, float f0, float f1, float f2); + void setF(uint index, float f0, float f1, float f2, float f3); + void setI(uint index, int i0); + void setI(uint index, int i0, int i1); + void setI(uint index, int i0, int i1, int i2); + void setI(uint index, int i0, int i1, int i2, int i3); + void setF(uint index, const NLMISC::CVector& v); + void setF(uint index, const NLMISC::CMatrix& m); + void setF(uint index, uint num, const float *src); // Internal /// Allocate specified number of components if necessary @@ -83,6 +97,9 @@ public: int *getPtrIByOffset(size_t offset) { return m_Vec[offset].I; } TType getTypeByOffset(size_t offset) { return m_Meta[offset].Type; } + // Utility + static inline uint getNbRegistersByComponents(uint count) { return (count + 3) >> 2; } // vector register per 4 components + private: std::vector m_Vec; std::vector m_Meta; diff --git a/code/nel/src/3d/gpu_program_params.cpp b/code/nel/src/3d/gpu_program_params.cpp index 78dcab15c..0a03500ea 100644 --- a/code/nel/src/3d/gpu_program_params.cpp +++ b/code/nel/src/3d/gpu_program_params.cpp @@ -32,6 +32,8 @@ // NeL includes // #include +#include +#include // Project includes @@ -50,7 +52,28 @@ CGPUProgramParams::~CGPUProgramParams() } -void CGPUProgramParams::set(uint index, float f0, float f1, float f2, float f3) +void CGPUProgramParams::setF(uint index, float f0) +{ + float *f = getPtrFByOffset(allocOffset(index, 1, Float)); + f[0] = f0; +} + +void CGPUProgramParams::setF(uint index, float f0, float f1) +{ + float *f = getPtrFByOffset(allocOffset(index, 2, Float)); + f[0] = f0; + f[1] = f1; +} + +void CGPUProgramParams::setF(uint index, float f0, float f1, float f2) +{ + float *f = getPtrFByOffset(allocOffset(index, 3, Float)); + f[0] = f0; + f[1] = f1; + f[2] = f2; +} + +void CGPUProgramParams::setF(uint index, float f0, float f1, float f2, float f3) { float *f = getPtrFByOffset(allocOffset(index, 4, Float)); f[0] = f0; @@ -59,7 +82,28 @@ void CGPUProgramParams::set(uint index, float f0, float f1, float f2, float f3) f[3] = f3; } -void CGPUProgramParams::set(uint index, int i0, int i1, int i2, int i3) +void CGPUProgramParams::setI(uint index, int i0) +{ + int *i = getPtrIByOffset(allocOffset(index, 1, Int)); + i[0] = i0; +} + +void CGPUProgramParams::setI(uint index, int i0, int i1) +{ + int *i = getPtrIByOffset(allocOffset(index, 2, Int)); + i[0] = i0; + i[1] = i1; +} + +void CGPUProgramParams::setI(uint index, int i0, int i1, int i2) +{ + int *i = getPtrIByOffset(allocOffset(index, 3, Int)); + i[0] = i0; + i[1] = i1; + i[2] = i2; +} + +void CGPUProgramParams::setI(uint index, int i0, int i1, int i2, int i3) { int *i = getPtrIByOffset(allocOffset(index, 4, Int)); i[0] = i0; @@ -68,6 +112,30 @@ void CGPUProgramParams::set(uint index, int i0, int i1, int i2, int i3) i[3] = i3; } +void CGPUProgramParams::setF(uint index, const NLMISC::CVector& v) +{ + float *f = getPtrFByOffset(allocOffset(index, 3, Float)); + f[0] = v.x; + f[1] = v.y; + f[2] = v.z; +} + +void CGPUProgramParams::setF(uint index, const NLMISC::CMatrix& m) +{ + // TODO: Verify this! + float *f = getPtrFByOffset(allocOffset(index, 16, Float)); + NLMISC::CMatrix mt = m; + mt.transpose(); + mt.get(f); +} + +void CGPUProgramParams::setF(uint index, uint num, const float *src) +{ + float *f = getPtrFByOffset(allocOffset(index, num, Float)); + for (uint i = 0; i < num; ++i) + f[i] = src[i]; +} + /// Allocate specified number of components if necessary size_t CGPUProgramParams::allocOffset(uint index, uint count, TType type) { @@ -96,7 +164,7 @@ size_t CGPUProgramParams::allocOffset(uint index, uint count, TType type) // Allocate space offset = m_Meta.size(); - uint blocks = (count + 3) >> 2; // per 4 components + uint blocks = getNbRegistersByComponents(count); // per 4 components m_Meta.resize(offset + blocks); m_Vec.resize(offset + blocks); From a33288c5f2e5efa4f4e25241a1922e3dea6e258c Mon Sep 17 00:00:00 2001 From: Quitta Date: Sun, 8 Sep 2013 06:01:01 +0200 Subject: [PATCH 182/313] Huge update! ingame part should work! --- .../ams_lib/ingame_templates/createticket.tpl | 101 ++++++ .../ams_lib/ingame_templates/dashboard.tpl | 81 +++++ .../ams_lib/ingame_templates/index.tpl | 81 +++++ .../ams_lib/ingame_templates/layout.tpl | 34 ++ .../ams_lib/ingame_templates/layout_admin.tpl | 19 ++ .../ams_lib/ingame_templates/layout_mod.tpl | 18 ++ .../ams_lib/ingame_templates/layout_user.tpl | 13 + .../ams_lib/ingame_templates/login.tpl | 39 +++ .../ams_lib/ingame_templates/register.tpl | 115 +++++++ .../ams_lib/ingame_templates/settings.tpl | 161 ++++++++++ .../ams_lib/ingame_templates/sgroup_list.tpl | 146 +++++++++ .../ams_lib/ingame_templates/show_queue.tpl | 222 +++++++++++++ .../ams_lib/ingame_templates/show_reply.tpl | 88 ++++++ .../ams_lib/ingame_templates/show_sgroup.tpl | 171 ++++++++++ .../ams_lib/ingame_templates/show_ticket.tpl | 285 +++++++++++++++++ .../ingame_templates/show_ticket_info.tpl | 168 ++++++++++ .../ingame_templates/show_ticket_log.tpl | 88 ++++++ .../ams_lib/ingame_templates/show_user.tpl | 163 ++++++++++ .../ams_lib/ingame_templates/userlist.tpl | 96 ++++++ .../ryzommanage/autoload/webusers.php | 15 +- .../drupal_module/ryzommanage/config.php | 2 + .../ryzommanage/func/add_sgroup.php | 9 +- .../ryzommanage/func/add_user_to_sgroup.php | 9 +- .../ryzommanage/func/change_mail.php | 93 ++++++ .../ryzommanage/func/change_password.php | 88 ++++++ .../ryzommanage/func/create_ticket.php | 8 +- .../drupal_module/ryzommanage/func/login.php | 9 +- .../func/modify_email_of_sgroup.php | 9 +- .../ryzommanage/func/reply_on_ticket.php | 11 +- .../ryzommanage/inc/change_permission.php | 17 +- .../ryzommanage/inc/createticket.php | 2 + .../drupal_module/ryzommanage/inc/logout.php | 5 +- .../ryzommanage/inc/settings.php | 298 ++++++++++++++++++ .../ryzommanage/inc/sgroup_list.php | 10 +- .../ryzommanage/inc/show_queue.php | 34 +- .../ryzommanage/inc/show_reply.php | 2 + .../ryzommanage/inc/show_sgroup.php | 10 +- .../ryzommanage/inc/show_ticket.php | 2 + .../ryzommanage/inc/show_ticket_info.php | 2 + .../ryzommanage/inc/show_ticket_log.php | 17 +- .../ryzommanage/inc/show_user.php | 2 + .../ryzommanage/inc/userlist.php | 2 + .../ryzommanage/ryzommanage.module | 91 ++---- 43 files changed, 2731 insertions(+), 105 deletions(-) create mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/createticket.tpl create mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/dashboard.tpl create mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/index.tpl create mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/layout.tpl create mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/layout_admin.tpl create mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/layout_mod.tpl create mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/layout_user.tpl create mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/login.tpl create mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/register.tpl create mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/settings.tpl create mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/sgroup_list.tpl create mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/show_queue.tpl create mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/show_reply.tpl create mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/show_sgroup.tpl create mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/show_ticket.tpl create mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/show_ticket_info.tpl create mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/show_ticket_log.tpl create mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/show_user.tpl create mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/userlist.tpl create mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/change_mail.php create mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/change_password.php create mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/settings.php diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/createticket.tpl b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/createticket.tpl new file mode 100644 index 000000000..ec79cc661 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/createticket.tpl @@ -0,0 +1,101 @@ +{block name=content} + + + + + + + + + + + +

    Create a new ticket

    + + + + + + + + + + + + +
    + + +
    +
    + + +
    + + +
    + + +
    +
    + + + + + + + + + + + + + + + + +
    Title: + +
    Category: + +
    Description:
    + + + + + {if $ingame} + + + + + + + + + + + + + + + + + + {/if} + +
    +
    +
    +
    +
    +
    + + + +{/block} + + diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/dashboard.tpl b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/dashboard.tpl new file mode 100644 index 000000000..24b8c7878 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/dashboard.tpl @@ -0,0 +1,81 @@ +{block name=content} + + + + + + + + + + + +

    {$home_title}

    + + + + + + + + + + + + +
    + + +
    +
    + + + + +
    + + +
    + + +
    + + + + + + + + + + + + + +
    Tickets Waiting for Direct ActionTickets TodoNewest TicketTotal amount of Tickets
    {$nrAssignedWaiting}{$nrToDo}{$newestTicketTitle}{$nrTotalTickets}
    +
    +
    +
    + + +
    + + +
    +

    {$home_info}

    +

    This is the GSOC project of Daan Janssens mentored by Matthew Lagoe.

    +
    +
    +
    +
    + + + + + + + + + +{/block} + diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/index.tpl b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/index.tpl new file mode 100644 index 000000000..82495ff89 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/index.tpl @@ -0,0 +1,81 @@ +{config_load file="ams_lib.conf" section="setup"} + +
    +
    +{* bold and title are read from the config file *}
    +{if #bold#}{/if}
    +{* capitalize the first letters of each word of the title *}
    +Title: {#title#|capitalize}
    +{if #bold#}{/if}
    +
    +The current date and time is {$smarty.now|date_format:"%Y-%m-%d %H:%M:%S"}
    +
    +The value of global assigned variable $SCRIPT_NAME is {$SCRIPT_NAME}
    +
    +Example of accessing server environment variable SERVER_NAME: {$smarty.server.SERVER_NAME}
    +
    +The value of {ldelim}$Name{rdelim} is {$Name}
    +
    +variable modifier example of {ldelim}$Name|upper{rdelim}
    +
    +{$Name|upper}
    +
    +
    +An example of a section loop:
    +
    +{section name=outer 
    +loop=$FirstName}
    +{if $smarty.section.outer.index is odd by 2}
    +	{$smarty.section.outer.rownum} . {$FirstName[outer]} {$LastName[outer]}
    +{else}
    +	{$smarty.section.outer.rownum} * {$FirstName[outer]} {$LastName[outer]}
    +{/if}
    +{sectionelse}
    +	none
    +{/section}
    +
    +An example of section looped key values:
    +
    +{section name=sec1 loop=$contacts}
    +	phone: {$contacts[sec1].phone}
    + fax: {$contacts[sec1].fax}
    + cell: {$contacts[sec1].cell}
    +{/section} +

    + +testing strip tags +{strip} + + + + +
    + + This is a test + +
    +{/strip} + +

    + +This is an example of the html_select_date function: + +
    +{html_select_date start_year=1998 end_year=2010} +
    + +This is an example of the html_select_time function: + +
    +{html_select_time use_24_hours=false} +
    + +This is an example of the html_options function: + +
    + +
    + +{include file="footer.tpl"} diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/layout.tpl b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/layout.tpl new file mode 100644 index 000000000..c98768e52 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/layout.tpl @@ -0,0 +1,34 @@ + + + + + Ryzom Account Management System + + + + + + + + + {block name=content}{/block} + + + +
    + + + + + {block name=menu}{/block} + + + +
    +
    + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/layout_admin.tpl b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/layout_admin.tpl new file mode 100644 index 000000000..afe29e778 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/layout_admin.tpl @@ -0,0 +1,19 @@ +{extends file="layout.tpl"} +{block name=menu} +
    Dashboard
    + +
    Profile
    + +
    Settings
    + + | + +
    Users
    + +
    Queues
    + +
    Support Groups
    + + +{/block} + diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/layout_mod.tpl b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/layout_mod.tpl new file mode 100644 index 000000000..65d10c611 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/layout_mod.tpl @@ -0,0 +1,18 @@ +{extends file="layout.tpl"} +{block name=menu} +
    Dashboard
    + +
    Profile
    + +
    Settings
    + + | + +
    Users
    + +
    Queues
    + +
    Support Groups
    + +{/block} + diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/layout_user.tpl b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/layout_user.tpl new file mode 100644 index 000000000..233ba2ad4 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/layout_user.tpl @@ -0,0 +1,13 @@ +{extends file="layout.tpl"} +{block name=menu} +
    Profile
    + +
    Settings
    + + | + +
    Create New Ticket
    + + +{/block} + diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/login.tpl b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/login.tpl new file mode 100644 index 000000000..03d0bd7aa --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/login.tpl @@ -0,0 +1,39 @@ +

     

    + +
    +
    +

    {$login_info}

    +
    +
    +
    +

    + Username: + +

    + +

    + Password: + +

    + +

    + Remember me: +

    Remember me +

    + +

    + + +

    + +
    + + {if isset($login_error) and $login_error eq "TRUE"} +

    + {$login_error_message} +

    + {/if} +

    + {$login_register_message} {$login_register_message_here}! +

    +
    diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/register.tpl b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/register.tpl new file mode 100644 index 000000000..9c38b34a2 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/register.tpl @@ -0,0 +1,115 @@ +{config_load file="ams_lib.conf" section="setup"} +
    + {$title} +
    + +
    + {$welcome_message} +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    {$username_tag} + + {if isset($Username)}{$Username}{/if}
    {$password_tag} + + {if isset($PASSWORD_ERROR) && $PASSWORD_ERROR eq "TRUE"}{$Password}{/if}
    {$cpassword_tag} + {if isset($CPASSWORD_ERROR) && $CPASSWORD_ERROR eq "TRUE"}{$ConfirmPass}{/if}
    {$email_tag} + + {if isset($EMAIL_ERROR) && $EMAIL_ERROR eq "TRUE"}{$Email}{/if}
    {$tac_tag}{$tac_message}
    + +
    + +
    + +
    + +
    + +
    + {$username_tooltip} +
    + + +
    + {$password_message} +
    + +
    + {$cpassword_message} +
    + +
    + {$email_message} +
    + +
    diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/settings.tpl b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/settings.tpl new file mode 100644 index 000000000..4e94e633c --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/settings.tpl @@ -0,0 +1,161 @@ +{block name=content} + + + + + + + + +
    + + + + +
    + + + + + {if isset($isAdmin) and $isAdmin eq 'TRUE' and $target_id neq 1} + {if $userPermission eq 1} + + + {else if $userPermission eq 2 } + + + {else if $userPermission eq 3 } + + + {/if} + {/if} + +
    Browse UserSend TicketMake ModeratorMake AdminDemote to UserMake AdminDemote to UserDemote to Moderator
    +
    +
    + + + + + + + + + + + + +

    Change Settings of {$target_username}

    + + + + + + + + + + + + +
    + + +
    +
    + + + +
    + + +
    + + +
    + +

    Change Password

    + +
    + + {if !isset($changesOther) or $changesOther eq "FALSE"} + + + +

    + + {/if} + + + +
    + Current Password: + + + {if isset($MATCH_ERROR) and $MATCH_ERROR eq "TRUE"}The password is incorrect{/if} +
    + New Password: + + + {if isset($NEWPASSWORD_ERROR) and $NEWPASSWORD_ERROR eq "TRUE"}{$newpass_error_message}{/if} +
    + Confirm New Password: + + + {if isset($CNEWPASSWORD_ERROR) and $CNEWPASSWORD_ERROR eq "TRUE"}{$confirmnewpass_error_message}{/if} +
    + {if isset($SUCCESS_PASS) and $SUCCESS_PASS eq "OK"} +

    + The password has been changed! +

    + {/if} + + + + + +

    + +
    + +
    +
    +
    + + +
    + + +
    +

    Change Email

    + +
    + + +
    + New Email: + + + {if isset($EMAIL_ERROR) and $EMAIL_ERROR eq "TRUE"}{$EMAIL}{/if} +
    + {if isset($SUCCESS_MAIL) and $SUCCESS_MAIL eq "OK"} +

    + The email has been changed! +

    + {/if} + + + +

    + +

    + +
    +
    +
    +
    + + + +{/block} + diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/sgroup_list.tpl b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/sgroup_list.tpl new file mode 100644 index 000000000..3a6dcc52f --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/sgroup_list.tpl @@ -0,0 +1,146 @@ +{block name=content} + + + + + + + + + + + +

    Support Groups

    + + + + + + + + + + + + +
    + + +
    +
    + + {if isset($isAdmin) && $isAdmin eq 'TRUE'} + + {/if} + +
    + + +
    + + +
    +

    Add a Support group

    + +
    + + + + + + +
    + + + + + + + + + + + + + + + +
    Group name:
    Group Tag:
    Group EmailAddress:
    +
    + + + + + + + + + + + + + + + +
    IMAP MailServer IP:
    IMAP Username:
    IMAP Password:
    +
    + +

    + + + {if isset($RESULT_OF_ADDING) and $RESULT_OF_ADDING eq "SUCCESS"} +

    + {$group_success} +

    + {else if isset($RESULT_OF_ADDING) and $RESULT_OF_ADDING eq "NAME_TAKEN"} +

    + {$group_name_taken} +

    + {else if isset($RESULT_OF_ADDING) and $RESULT_OF_ADDING eq "TAG_TAKEN"} +

    + {$group_tag_taken} +

    + {else if isset($RESULT_OF_ADDING) and $RESULT_OF_ADDING eq "SIZE_ERROR"} +

    + {$group_size_error} +

    + {/if} +
    +
    +
    +
    + + +
    + + +
    +

    All groups

    + + + + + + + {if isset($isAdmin) && $isAdmin eq 'TRUE'}{/if} + + + {foreach from=$grouplist item=group} + + + + + + {if isset($isAdmin) && $isAdmin eq 'TRUE'}{/if} + + {/foreach} +
    IDNameTagEmailAction
    {$group.sGroupId}{$group.name}{$group.tag}{$group.groupemail}Delete
    +
    +
    +
    +
    + + + +{/block} + diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/show_queue.tpl b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/show_queue.tpl new file mode 100644 index 000000000..956b5e20d --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/show_queue.tpl @@ -0,0 +1,222 @@ +{block name=content} + + + + + + + + +
    + + + + +
    + + + + + + + + +
    Todo ticketsAll ticketsAll open ticketsTicket ArchiveNot Assigned Tickets
    +
    +
    + + + + + + + + + + + + +

    Ticket Queue: {$queue_view}

    + + + + + + + + + + + + +{/block} + diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/show_reply.tpl b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/show_reply.tpl new file mode 100644 index 000000000..9b0a6296a --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/show_reply.tpl @@ -0,0 +1,88 @@ +{block name=content} + + + + +{/block} + + diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/show_sgroup.tpl b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/show_sgroup.tpl new file mode 100644 index 000000000..2f0c29695 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/show_sgroup.tpl @@ -0,0 +1,171 @@ +{block name=content} + + + + +{/block} + diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/show_ticket.tpl b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/show_ticket.tpl new file mode 100644 index 000000000..97f1d3c53 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/show_ticket.tpl @@ -0,0 +1,285 @@ +{block name=content} + + + + + +{/block} + diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/show_ticket_info.tpl b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/show_ticket_info.tpl new file mode 100644 index 000000000..4431331c4 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/show_ticket_info.tpl @@ -0,0 +1,168 @@ +{block name=content} + + + + + +{/block} + diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/show_ticket_log.tpl b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/show_ticket_log.tpl new file mode 100644 index 000000000..f79735064 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/show_ticket_log.tpl @@ -0,0 +1,88 @@ +{block name=content} + + + + +{/block} + \ No newline at end of file diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/show_user.tpl b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/show_user.tpl new file mode 100644 index 000000000..51c5bb77a --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/show_user.tpl @@ -0,0 +1,163 @@ +{block name=content} + + + + + +{/block} + diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/userlist.tpl b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/userlist.tpl new file mode 100644 index 000000000..c2e226ca3 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/userlist.tpl @@ -0,0 +1,96 @@ +{block name=content} + + + +{/block} + diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/autoload/webusers.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/autoload/webusers.php index 779414de3..a99a4a295 100644 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/autoload/webusers.php +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/autoload/webusers.php @@ -87,7 +87,7 @@ class WebUsers extends Users{ } public function getUsername(){ - + if(! isset($this->login) || $this->login == ""){ $row = db_query("SELECT * FROM {users} WHERE uid = :id", array(':id' => $this->uId))->fetchAssoc(); $this->set($row); @@ -137,12 +137,13 @@ class WebUsers extends Users{ } public function setPassword($user, $pass){ - $reply = WebUsers::setAmsPassword($user, $pass); - $values = Array('user' => $user, 'pass' => $pass); + $hashpass = crypt($pass, WebUsers::generateSALT()); + $reply = WebUsers::setAmsPassword($user, $hashpass); + $drupal_pass = user_hash_password($pass); + $values = Array('user' => $user, 'pass' => $drupal_pass); try { //make connection with and put into shard db - $dbw = new DBLayer("web"); - $dbw->execute("UPDATE ams_user SET Password = :pass WHERE Login = :user ",$values); + db_query("UPDATE {users} SET pass = :pass WHERE name = :user", $values); } catch (PDOException $e) { //ERROR: the web DB is offline @@ -155,8 +156,8 @@ class WebUsers extends Users{ $values = Array('user' => $user, 'mail' => $mail); try { //make connection with and put into shard db - $dbw = new DBLayer("web"); - $dbw->execute("UPDATE ams_user SET Email = :mail WHERE Login = :user ",$values); + db_query("UPDATE {users} SET mail = :mail WHERE name = :user", $values); + } catch (PDOException $e) { //ERROR: the web DB is offline diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/config.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/config.php index 7723032f8..5cb719bc8 100644 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/config.php +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/config.php @@ -91,5 +91,7 @@ $TIME_FORMAT = "m-d-Y H:i:s"; //defines which ingame layout template should be used $INGAME_LAYOUT = "basic"; +$FORCE_INGAME = true; + diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/add_sgroup.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/add_sgroup.php index e2ef663f1..94157192b 100644 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/add_sgroup.php +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/add_sgroup.php @@ -1,7 +1,8 @@ getUsername(); + } + + $webUser = new WebUsers($_POST['target_id']); + $reply = $webUser->checkEmail($_POST['NewEmail']); + + global $SITEBASE; + require_once($SITEBASE . '/inc/settings.php'); + $result = settings(); + + if ( $reply != "success" ){ + $result['EMAIL_ERROR'] = 'TRUE'; + }else{ + $result['EMAIL_ERROR'] = 'FALSE'; + } + $result['prevNewEmail'] = filter_var($_POST["NewEmail"], FILTER_SANITIZE_EMAIL); + + if ($reply== "success"){ + $status = WebUsers::setEmail($target_username, filter_var($_POST["NewEmail"], FILTER_SANITIZE_EMAIL) ); + if($status == 'ok'){ + $result['SUCCESS_MAIL'] = "OK"; + }else if($status == 'shardoffline'){ + $result['SUCCESS_MAIL'] = "SHARDOFF"; + } + $result['permission'] = unserialize($_SESSION['ticket_user'])->getPermission(); + $result['no_visible_elements'] = 'FALSE'; + $result['username'] = $_SESSION['user']; + $result['target_id'] = $_POST['target_id']; + if(isset($_GET['id'])){ + if(Ticket_User::isMod(unserialize($_SESSION['ticket_user'])) && ($_POST['target_id'] != $_SESSION['id'])){ + $result['isMod'] = "TRUE"; + } + } + helpers :: loadtemplate( 'settings', $result); + exit; + + }else{ + $result['EMAIL'] = $reply; + $result['permission'] = unserialize($_SESSION['ticket_user'])->getPermission(); + $result['no_visible_elements'] = 'FALSE'; + $result['username'] = $_SESSION['user']; + $result['target_id'] = $_POST['target_id']; + if(isset($_GET['id'])){ + if(Ticket_User::isMod(unserialize($_SESSION['ticket_user'])) && ($_POST['target_id'] != $_SESSION['id'])){ + $result['isMod'] = "TRUE"; + } + } + helpers :: loadtemplate( 'settings', $result); + exit; + } + + }else{ + //ERROR: permission denied! + $_SESSION['error_code'] = "403"; + header("Location: index.php?page=error"); + exit; + } + + }else{ + //ERROR: The form was not filled in correclty + header("Location: index.php?page=settings"); + exit; + } + }else{ + //ERROR: user is not logged in + header("Location: index.php"); + exit; + } + + }catch (PDOException $e) { + //go to error page or something, because can't access website db + print_r($e); + exit; + } + +} + diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/change_password.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/change_password.php new file mode 100644 index 000000000..420d78103 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/change_password.php @@ -0,0 +1,88 @@ +getUsername(); + //isAdmin is true when it's the admin, but the target_id != own id + $adminChangesOther = true; + $_POST["CurrentPass"] = "dummypass"; + } + + $webUser = new WebUsers($_POST['target_id']); + $params = Array( 'user' => $target_username, 'CurrentPass' => $_POST["CurrentPass"], 'NewPass' => $_POST["NewPass"], 'ConfirmNewPass' => $_POST["ConfirmNewPass"], 'adminChangesOther' => $adminChangesOther); + $result = $webUser->check_change_password($params); + if ($result == "success"){ + //edit stuff into db + global $SITEBASE; + require_once($SITEBASE . '/inc/settings.php'); + $succresult = settings(); + $status = WebUsers::setPassword($target_username, $_POST["NewPass"]); + if($status == 'ok'){ + $succresult['SUCCESS_PASS'] = "OK"; + }else if($status == 'shardoffline'){ + $succresult['SUCCESS_PASS'] = "SHARDOFF"; + } + $succresult['permission'] = unserialize($_SESSION['ticket_user'])->getPermission(); + $succresult['no_visible_elements'] = 'FALSE'; + $succresult['username'] = $_SESSION['user']; + $succresult['target_id'] = $_POST['target_id']; + helpers :: loadtemplate( 'settings', $succresult); + exit; + + }else{ + + $result['prevCurrentPass'] = filter_var($_POST["CurrentPass"], FILTER_SANITIZE_STRING); + $result['prevNewPass'] = filter_var($_POST["NewPass"], FILTER_SANITIZE_STRING); + $result['prevConfirmNewPass'] = filter_var($_POST["ConfirmNewPass"], FILTER_SANITIZE_STRING); + $result['permission'] = unserialize($_SESSION['ticket_user'])->getPermission(); + $result['no_visible_elements'] = 'FALSE'; + $result['username'] = $_SESSION['user']; + $result['target_id'] = $_POST['target_id']; + + global $SITEBASE; + require_once($SITEBASE . '/inc/settings.php'); + $settings = settings(); + + $result = array_merge($result,$settings); + helpers :: loadtemplate( 'settings', $result); + exit; + } + + }else{ + //ERROR: permission denied! + $_SESSION['error_code'] = "403"; + header("Location: index.php?page=error"); + exit; + } + + }else{ + //ERROR: The form was not filled in correclty + header("Location: index.php?page=settings"); + exit; + } + }else{ + //ERROR: user is not logged in + header("Location: index.php"); + exit; + } + + }catch (PDOException $e) { + //go to error page or something, because can't access website db + print_r($e); + exit; + } + +} + diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/create_ticket.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/create_ticket.php index f50dbdd6a..cf497dabf 100644 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/create_ticket.php +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/create_ticket.php @@ -2,6 +2,8 @@ function create_ticket(){ //if logged in + global $INGAME_WEBPATH; + global $WEBPATH; if(WebUsers::isLoggedIn() && isset($_SESSION['ticket_user'])){ if(isset($_POST['target_id'])){ @@ -19,7 +21,11 @@ function create_ticket(){ $author= Ticket_User::constr_ExternId($_POST['target_id'])->getTUserId(); } $ticket_id = Ticket::create_Ticket($title, $content, $category, $author, unserialize($_SESSION['ticket_user'])->getTUserId(),0, $_POST); - header("Location: ams?page=show_ticket&id=".$ticket_id); + if (Helpers::check_if_game_client()) { + header("Location: ".$INGAME_WEBPATH."?page=show_ticket&id=".$ticket_id); + }else{ + header("Location: ".$WEBPATH."?page=show_ticket&id=".$ticket_id); + } exit; }catch (PDOException $e) { diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/login.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/login.php index 2731de130..54d6b0672 100644 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/login.php +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/login.php @@ -1,7 +1,8 @@ getLanguage(); //go back to the index page. - header( 'Location: '. $INGAME_WEBPATH ); + if (Helpers::check_if_game_client()) { + header( 'Location: '. $INGAME_WEBPATH ); + }else{ + header( 'Location: '. $WEBPATH ); + } exit; }else{ //handle login failure diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/modify_email_of_sgroup.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/modify_email_of_sgroup.php index ade3c3af2..9befa9ae4 100644 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/modify_email_of_sgroup.php +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/modify_email_of_sgroup.php @@ -1,7 +1,8 @@ getTUserId(), $value); - header("Location: ams?page=show_user&id=".$user_id); + if (Helpers::check_if_game_client()) { + header("Location: ".$INGAME_WEBPATH."?page=show_user&id=".$user_id); + }else{ + header("Location: ".$WEBPATH."?page=show_user&id=".$user_id); + } exit; }else{ //ERROR: GET PARAMS not given or trying to change admin - header("Location: ams?page=show_user&id=".$user_id); + if (Helpers::check_if_game_client()) { + header("Location: ".$INGAME_WEBPATH."?page=show_user&id=".$user_id); + }else{ + header("Location: ".$WEBPATH."?page=show_user&id=".$user_id); + } exit; } diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/createticket.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/createticket.php index 49e2263cf..3fdb54b84 100644 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/createticket.php +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/createticket.php @@ -35,6 +35,8 @@ function createticket(){ //create array of category id & names $catArray = Ticket_Category::getAllCategories(); $result['category'] = Gui_Elements::make_table_with_key_is_id($catArray, Array("getName"), "getTCategoryId" ); + global $INGAME_WEBPATH; + $result['ingame_webpath'] = $INGAME_WEBPATH; return $result; }else{ diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/logout.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/logout.php index 3e7454fe7..b7a13993a 100644 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/logout.php +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/logout.php @@ -2,5 +2,8 @@ function logout(){ session_unset(); - session_destroy(); + session_destroy(); + global $INGAME_WEBPATH; + header("Location: ". $INGAME_WEBPATH); + exit; } diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/settings.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/settings.php new file mode 100644 index 000000000..4b57d2e1c --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/settings.php @@ -0,0 +1,298 @@ +getInfo(); + if(Ticket_User::isMod(unserialize($_SESSION['ticket_user'])) && ($_GET['id']!= $_SESSION['id'])){ + $result['changesOther'] = "TRUE"; + } + $result['target_id'] = $_GET['id']; + $result['current_mail'] = $webUser->getEmail(); + $result['target_username'] = $webUser->getUsername(); + } + }else{ + $webUser = new Webusers($_SESSION['id']); + //$result = $webUser->getInfo(); + $result['target_id'] = $_SESSION['id']; + $result['current_mail'] = $webUser->getEmail(); + $result['target_username'] = $webUser->getUsername(); + + } + //Sanitize Data + $result['current_mail'] = filter_var($result['current_mail'], FILTER_SANITIZE_EMAIL); + $result['target_username'] = filter_var($result['target_username'], FILTER_SANITIZE_STRING); + //$result['FirstName'] = filter_var($result['FirstName'], FILTER_SANITIZE_STRING); + //$result['LastName'] = filter_var($result['LastName'], FILTER_SANITIZE_STRING); + //$result['Country'] = filter_var($result['Country'], FILTER_SANITIZE_STRING); + //$result['Gender'] = filter_var($result['Gender'], FILTER_SANITIZE_NUMBER_INT); + //$result['ReceiveMail'] = filter_var($result['ReceiveMail'], FILTER_SANITIZE_NUMBER_INT); + //$result['country_array'] = getCountryArray(); + global $INGAME_WEBPATH; + $result['ingame_webpath'] = $INGAME_WEBPATH; + return $result; + }else{ + //ERROR: not logged in! + header("Location: index.php"); + exit; + } +} + + +function getCountryArray(){ + +$countries = array( +'AA'=>'None Selected', +'AF'=>'Afghanistan', +'AL'=>'Albania', +'DZ'=>'Algeria', +'AS'=>'American Samoa', +'AD'=>'Andorra', +'AO'=>'Angola', +'AI'=>'Anguilla', +'AQ'=>'Antarctica', +'AG'=>'Antigua And Barbuda', +'AR'=>'Argentina', +'AM'=>'Armenia', +'AW'=>'Aruba', +'AU'=>'Australia', +'AT'=>'Austria', +'AZ'=>'Azerbaijan', +'BS'=>'Bahamas', +'BH'=>'Bahrain', +'BD'=>'Bangladesh', +'BB'=>'Barbados', +'BY'=>'Belarus', +'BE'=>'Belgium', +'BZ'=>'Belize', +'BJ'=>'Benin', +'BM'=>'Bermuda', +'BT'=>'Bhutan', +'BO'=>'Bolivia', +'BA'=>'Bosnia And Herzegovina', +'BW'=>'Botswana', +'BV'=>'Bouvet Island', +'BR'=>'Brazil', +'IO'=>'British Indian Ocean Territory', +'BN'=>'Brunei', +'BG'=>'Bulgaria', +'BF'=>'Burkina Faso', +'BI'=>'Burundi', +'KH'=>'Cambodia', +'CM'=>'Cameroon', +'CA'=>'Canada', +'CV'=>'Cape Verde', +'KY'=>'Cayman Islands', +'CF'=>'Central African Republic', +'TD'=>'Chad', +'CL'=>'Chile', +'CN'=>'China', +'CX'=>'Christmas Island', +'CC'=>'Cocos (Keeling) Islands', +'CO'=>'Columbia', +'KM'=>'Comoros', +'CG'=>'Congo', +'CK'=>'Cook Islands', +'CR'=>'Costa Rica', +'CI'=>'Cote D\'Ivorie (Ivory Coast)', +'HR'=>'Croatia (Hrvatska)', +'CU'=>'Cuba', +'CY'=>'Cyprus', +'CZ'=>'Czech Republic', +'CD'=>'Democratic Republic Of Congo (Zaire)', +'DK'=>'Denmark', +'DJ'=>'Djibouti', +'DM'=>'Dominica', +'DO'=>'Dominican Republic', +'TP'=>'East Timor', +'EC'=>'Ecuador', +'EG'=>'Egypt', +'SV'=>'El Salvador', +'GQ'=>'Equatorial Guinea', +'ER'=>'Eritrea', +'EE'=>'Estonia', +'ET'=>'Ethiopia', +'FK'=>'Falkland Islands (Malvinas)', +'FO'=>'Faroe Islands', +'FJ'=>'Fiji', +'FI'=>'Finland', +'FR'=>'France', +'FX'=>'France, Metropolitan', +'GF'=>'French Guinea', +'PF'=>'French Polynesia', +'TF'=>'French Southern Territories', +'GA'=>'Gabon', +'GM'=>'Gambia', +'GE'=>'Georgia', +'DE'=>'Germany', +'GH'=>'Ghana', +'GI'=>'Gibraltar', +'GR'=>'Greece', +'GL'=>'Greenland', +'GD'=>'Grenada', +'GP'=>'Guadeloupe', +'GU'=>'Guam', +'GT'=>'Guatemala', +'GN'=>'Guinea', +'GW'=>'Guinea-Bissau', +'GY'=>'Guyana', +'HT'=>'Haiti', +'HM'=>'Heard And McDonald Islands', +'HN'=>'Honduras', +'HK'=>'Hong Kong', +'HU'=>'Hungary', +'IS'=>'Iceland', +'IN'=>'India', +'ID'=>'Indonesia', +'IR'=>'Iran', +'IQ'=>'Iraq', +'IE'=>'Ireland', +'IL'=>'Israel', +'IT'=>'Italy', +'JM'=>'Jamaica', +'JP'=>'Japan', +'JO'=>'Jordan', +'KZ'=>'Kazakhstan', +'KE'=>'Kenya', +'KI'=>'Kiribati', +'KW'=>'Kuwait', +'KG'=>'Kyrgyzstan', +'LA'=>'Laos', +'LV'=>'Latvia', +'LB'=>'Lebanon', +'LS'=>'Lesotho', +'LR'=>'Liberia', +'LY'=>'Libya', +'LI'=>'Liechtenstein', +'LT'=>'Lithuania', +'LU'=>'Luxembourg', +'MO'=>'Macau', +'MK'=>'Macedonia', +'MG'=>'Madagascar', +'MW'=>'Malawi', +'MY'=>'Malaysia', +'MV'=>'Maldives', +'ML'=>'Mali', +'MT'=>'Malta', +'MH'=>'Marshall Islands', +'MQ'=>'Martinique', +'MR'=>'Mauritania', +'MU'=>'Mauritius', +'YT'=>'Mayotte', +'MX'=>'Mexico', +'FM'=>'Micronesia', +'MD'=>'Moldova', +'MC'=>'Monaco', +'MN'=>'Mongolia', +'MS'=>'Montserrat', +'MA'=>'Morocco', +'MZ'=>'Mozambique', +'MM'=>'Myanmar (Burma)', +'NA'=>'Namibia', +'NR'=>'Nauru', +'NP'=>'Nepal', +'NL'=>'Netherlands', +'AN'=>'Netherlands Antilles', +'NC'=>'New Caledonia', +'NZ'=>'New Zealand', +'NI'=>'Nicaragua', +'NE'=>'Niger', +'NG'=>'Nigeria', +'NU'=>'Niue', +'NF'=>'Norfolk Island', +'KP'=>'North Korea', +'MP'=>'Northern Mariana Islands', +'NO'=>'Norway', +'OM'=>'Oman', +'PK'=>'Pakistan', +'PW'=>'Palau', +'PA'=>'Panama', +'PG'=>'Papua New Guinea', +'PY'=>'Paraguay', +'PE'=>'Peru', +'PH'=>'Philippines', +'PN'=>'Pitcairn', +'PL'=>'Poland', +'PT'=>'Portugal', +'PR'=>'Puerto Rico', +'QA'=>'Qatar', +'RE'=>'Reunion', +'RO'=>'Romania', +'RU'=>'Russia', +'RW'=>'Rwanda', +'SH'=>'Saint Helena', +'KN'=>'Saint Kitts And Nevis', +'LC'=>'Saint Lucia', +'PM'=>'Saint Pierre And Miquelon', +'VC'=>'Saint Vincent And The Grenadines', +'SM'=>'San Marino', +'ST'=>'Sao Tome And Principe', +'SA'=>'Saudi Arabia', +'SN'=>'Senegal', +'SC'=>'Seychelles', +'SL'=>'Sierra Leone', +'SG'=>'Singapore', +'SK'=>'Slovak Republic', +'SI'=>'Slovenia', +'SB'=>'Solomon Islands', +'SO'=>'Somalia', +'ZA'=>'South Africa', +'GS'=>'South Georgia And South Sandwich Islands', +'KR'=>'South Korea', +'ES'=>'Spain', +'LK'=>'Sri Lanka', +'SD'=>'Sudan', +'SR'=>'Suriname', +'SJ'=>'Svalbard And Jan Mayen', +'SZ'=>'Swaziland', +'SE'=>'Sweden', +'CH'=>'Switzerland', +'SY'=>'Syria', +'TW'=>'Taiwan', +'TJ'=>'Tajikistan', +'TZ'=>'Tanzania', +'TH'=>'Thailand', +'TG'=>'Togo', +'TK'=>'Tokelau', +'TO'=>'Tonga', +'TT'=>'Trinidad And Tobago', +'TN'=>'Tunisia', +'TR'=>'Turkey', +'TM'=>'Turkmenistan', +'TC'=>'Turks And Caicos Islands', +'TV'=>'Tuvalu', +'UG'=>'Uganda', +'UA'=>'Ukraine', +'AE'=>'United Arab Emirates', +'UK'=>'United Kingdom', +'US'=>'United States', +'UM'=>'United States Minor Outlying Islands', +'UY'=>'Uruguay', +'UZ'=>'Uzbekistan', +'VU'=>'Vanuatu', +'VA'=>'Vatican City (Holy See)', +'VE'=>'Venezuela', +'VN'=>'Vietnam', +'VG'=>'Virgin Islands (British)', +'VI'=>'Virgin Islands (US)', +'WF'=>'Wallis And Futuna Islands', +'EH'=>'Western Sahara', +'WS'=>'Western Samoa', +'YE'=>'Yemen', +'YU'=>'Yugoslavia', +'ZM'=>'Zambia', +'ZW'=>'Zimbabwe' +); + +return $countries; + +} + diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/sgroup_list.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/sgroup_list.php index 546395ed8..0b446fc2c 100644 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/sgroup_list.php +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/sgroup_list.php @@ -1,6 +1,8 @@ CreateQueue($userid, $groupid, $what, $how, $who); - $result['pagination_base_link'] = "ams?page=show_queue&get=create&userid=".$userid."&groupid=".$groupid."&what=".$what."&how=".$how."&who=".$who; + + if (Helpers::check_if_game_client()) { + $result['pagination_base_link'] = $INGAME_WEBPATH."?page=show_queue&get=create&userid=".$userid."&groupid=".$groupid."&what=".$what."&how=".$how."&who=".$who; + }else{ + $result['pagination_base_link'] = $WEBPATH."?page=show_queue&get=create&userid=".$userid."&groupid=".$groupid."&what=".$what."&how=".$how."&who=".$who; + } + + $result['prev_created_userid'] = $userid; $result['prev_created_groupid'] = $groupid; $result['prev_created_what'] = $what; @@ -60,7 +78,11 @@ function show_queue(){ $how = filter_var($_POST['how'], FILTER_SANITIZE_STRING); $who = filter_var($_POST['who'], FILTER_SANITIZE_STRING); $queue_handler->CreateQueue($userid, $groupid, $what, $how, $who); - $result['pagination_base_link'] = "ams?page=show_queue&get=create&userid=".$userid."&groupid=".$groupid."&what=".$what."&how=".$how."&who=".$who; + if (Helpers::check_if_game_client()) { + $result['pagination_base_link'] = $INGAME_WEBPATH."?page=show_queue&get=create&userid=".$userid."&groupid=".$groupid."&what=".$what."&how=".$how."&who=".$who; + }else{ + $result['pagination_base_link'] = $WEBPATH."?page=show_queue&get=create&userid=".$userid."&groupid=".$groupid."&what=".$what."&how=".$how."&who=".$who; + } $result['prev_created_userid'] = $userid; $result['prev_created_groupid'] = $groupid; $result['prev_created_what'] = $what; @@ -104,6 +126,8 @@ function show_queue(){ $result['teamlist'][$i]['name'] = $web_teammember->getUsername(); $i++; } + global $INGAME_WEBPATH; + $result['ingame_webpath'] = $INGAME_WEBPATH; return $result; }else{ diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/show_reply.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/show_reply.php index 94090fa88..96cec0a6a 100644 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/show_reply.php +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/show_reply.php @@ -30,6 +30,8 @@ function show_reply(){ if(Ticket_User::isMod(unserialize($_SESSION['ticket_user']))){ $result['isMod'] = "TRUE"; } + global $INGAME_WEBPATH; + $result['ingame_webpath'] = $INGAME_WEBPATH; return $result; }else{ diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/show_sgroup.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/show_sgroup.php index 729c0eec8..edf1ba0a9 100644 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/show_sgroup.php +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/show_sgroup.php @@ -1,6 +1,8 @@ getUsername(); $i++; } + global $INGAME_WEBPATH; + $result['ingame_webpath'] = $INGAME_WEBPATH; return $result; diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/show_ticket.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/show_ticket.php index e354e2803..4ecddb0b7 100644 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/show_ticket.php +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/show_ticket.php @@ -68,6 +68,8 @@ function show_ticket(){ $result['sGroups'] = Gui_Elements::make_table_with_key_is_id(Support_Group::getAllSupportGroups(), Array("getName"), "getSGroupId" ); } $result['hasInfo'] = $target_ticket->hasInfo(); + global $INGAME_WEBPATH; + $result['ingame_webpath'] = $INGAME_WEBPATH; return $result; }else{ diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/show_ticket_info.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/show_ticket_info.php index 6d1cf5e0f..4e43c7aad 100644 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/show_ticket_info.php +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/show_ticket_info.php @@ -37,6 +37,8 @@ function show_ticket_info(){ if(Ticket_User::isMod(unserialize($_SESSION['ticket_user']))){ $result['isMod'] = "TRUE"; } + global $INGAME_WEBPATH; + $result['ingame_webpath'] = $INGAME_WEBPATH; return $result; }else{ diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/show_ticket_log.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/show_ticket_log.php index 1974cbc66..6d1ea9701 100644 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/show_ticket_log.php +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/show_ticket_log.php @@ -1,7 +1,8 @@ getUsername(); }else if($log['action'] == 4){ - $query_backpart = "ID#" . $log['argument'] . ""; + if (Helpers::check_if_game_client()) { + $query_backpart = "ID#" . $log['argument'] . ""; + }else{ + $query_backpart = "ID#" . $log['argument'] . ""; + } }else if($log['action'] == 5){ $statusArray = Ticket::getStatusArray(); $query_backpart = $statusArray[$log['argument'] ]; @@ -31,7 +36,11 @@ function show_ticket_log(){ $priorityArray = Ticket::getPriorityArray(); $query_backpart = $priorityArray[$log['argument'] ]; }else if($log['action'] == 8){ - $query_backpart = "" . Support_Group::getGroup($log['argument'])->getName() . ""; + if (Helpers::check_if_game_client()) { + $query_backpart = "" . Support_Group::getGroup($log['argument'])->getName() . ""; + }else{ + $query_backpart = "" . Support_Group::getGroup($log['argument'])->getName() . ""; + } } $result['ticket_logs'][$i]['query'] = $author . " " . $log_action_array[$log['action']] . " " . $query_backpart; $result['ticket_logs'][$i]['timestamp_elapsed'] = Gui_Elements::time_elapsed_string($log['timestamp']); @@ -40,6 +49,8 @@ function show_ticket_log(){ if(Ticket_User::isMod(unserialize($_SESSION['ticket_user']))){ $result['isMod'] = "TRUE"; } + global $INGAME_WEBPATH; + $result['ingame_webpath'] = $INGAME_WEBPATH; return $result; }else{ diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/show_user.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/show_user.php index 3a4b2acee..dc4548361 100644 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/show_user.php +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/show_user.php @@ -28,6 +28,8 @@ function show_user(){ $ticketlist = Ticket::getTicketsOf($ticket_user->getTUserId()); $result['ticketlist'] = Gui_Elements::make_table($ticketlist, Array("getTId","getTimestamp","getTitle","getStatus","getStatusText","getStatusText","getCategoryName"), Array("tId","timestamp","title","status","statustext","statusText","category")); + global $INGAME_WEBPATH; + $result['ingame_webpath'] = $INGAME_WEBPATH; return $result; }else{ diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/userlist.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/userlist.php index d8783ce32..2c408f683 100644 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/userlist.php +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/userlist.php @@ -18,6 +18,8 @@ function userlist(){ if (Ticket_User::isAdmin(unserialize($_SESSION['ticket_user']))){ $pageResult['isAdmin'] = "TRUE"; } + global $INGAME_WEBPATH; + $pageResult['ingame_webpath'] = $INGAME_WEBPATH; return $pageResult; }else{ //ERROR: No access! diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module index 7249aded2..deb8a5f40 100644 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module @@ -27,6 +27,7 @@ global $WEBPATH; global $INGAME_WEBPATH; global $BASE_WEBPATH; global $INGAME_LAYOUT; +global $FORCE_INGAME; require 'ams_lib/libinclude.php'; spl_autoload_register('__autoload'); @@ -268,9 +269,12 @@ function ryzommanage_block_view($delta = '') -function _ams_handler() +function _ams_handler($ingame = false) { global $BASE_WEBPATH; + global $SITEBASE; + global $INGAME_WEBPATH; + //Decide what page to load if ( ! isset( $_GET["page"]) ){ if(isset($_SESSION['user'])){ @@ -281,7 +285,11 @@ function _ams_handler() } }else{ //default page - header("Location: ".$BASE_WEBPATH."/user/login"); + if($ingame){ + header("Location: ".$INGAME_WEBPATH."?page=login"); + }else{ + header("Location: ".$BASE_WEBPATH."/user/login"); + } exit; } }else{ @@ -291,13 +299,13 @@ function _ams_handler() //perform an action in case one is specified //else check if a php page is included in the inc folder, else just set page to the get param if ( isset( $_POST["function"] ) ){ - $filename = dirname( __FILE__ ).'/func/' . $_POST["function"] . '.php'; + $filename = $SITEBASE . '/func/' . $_POST["function"] . '.php'; if(is_file($filename)){ require($filename); $return = $_POST["function"](); } }else{ - $filename = dirname( __FILE__ ).'/inc/' . $page . '.php'; + $filename = $SITEBASE . '/inc/' . $page . '.php'; if(is_file($filename)){ require_once($filename); $return = $page(); @@ -324,7 +332,12 @@ function _ams_handler() } //return $page; - return helpers :: loadTemplate( $page , $return, true); + if ($ingame){ + helpers :: loadTemplate( $page , $return ); + }else{ + return helpers :: loadTemplate( $page , $return, true); + } + } @@ -366,8 +379,8 @@ function _collect_register($nids, $collection) function _collect_ingame_ams($nids, $collection) { //if not using ryzom core client show registration page - if (Helpers::check_if_game_client(true)) { - _return_ingame_httpdata(); + if (Helpers::check_if_game_client()) { + _ams_handler(true); } else { //redirect to registration page global $WEBPATH; @@ -376,70 +389,6 @@ function _collect_ingame_ams($nids, $collection) } -function _return_ingame_httpdata(){ - - //Decide what page to load - if ( ! isset( $_GET["page"]) ){ - if(isset($_SESSION['user'])){ - if(Ticket_User::isMod(unserialize($_SESSION['ticket_user']))){ - $page = 'dashboard'; - }else{ - $page = 'show_user'; - } - }else{ - //default page - $page = 'login'; - } - }else{ - $page = $_GET["page"]; - } - - //perform an action in case one is specified - //else check if a php page is included in the inc folder, else just set page to the get param - global $SITEBASE; - if ( isset( $_POST["function"] ) ){ - $filename = $SITEBASE.'/func/' . $_POST["function"] . '.php'; - if(is_file($filename)){ - require($filename); - $return = $_POST["function"](); - } - }else{ - - $filename = $SITEBASE.'/inc/' . $page . '.php'; - if(is_file($filename)){ - require_once($filename); - $return = $page(); - } - } - - //add username to the return array in case logged in. - if(isset($_SESSION['user'])){ - $return['username'] = $_SESSION['user']; - } - - //Set permission - if(isset($_SESSION['ticket_user'])){ - $return['permission'] = unserialize($_SESSION['ticket_user'])->getPermission(); - }else{ - //default permission - $return['permission'] = 0; - } - - //hide sidebar + topbar in case of login/register - if($page == 'login' || $page == 'register' || $page == 'logout'){ - $return['no_visible_elements'] = 'TRUE'; - }else{ - $return['no_visible_elements'] = 'FALSE'; - } - - //handle error page - if($page == 'error'){ - $return['permission'] = 0; - $return['no_visible_elements'] = 'FALSE'; - } - - helpers :: loadTemplate( $page , $return ); -} /** * * Function _collect_register From d5baba13e81a912996ae547b52ec5e30b1ebbd20 Mon Sep 17 00:00:00 2001 From: kervala Date: Sun, 8 Sep 2013 10:26:23 +0200 Subject: [PATCH 183/313] Changed: Look for Debug version of freetype and use it for Debug configuration --- code/CMakeModules/FindFreeType.cmake | 59 +++++++++++++++++++++------- 1 file changed, 44 insertions(+), 15 deletions(-) diff --git a/code/CMakeModules/FindFreeType.cmake b/code/CMakeModules/FindFreeType.cmake index 4f3c84cbe..e15f55793 100644 --- a/code/CMakeModules/FindFreeType.cmake +++ b/code/CMakeModules/FindFreeType.cmake @@ -1,14 +1,13 @@ # - Locate FreeType library # This module defines -# FREETYPE_LIBRARY, the library to link against +# FREETYPE_LIBRARIES, libraries to link against # FREETYPE_FOUND, if false, do not try to link to FREETYPE # FREETYPE_INCLUDE_DIRS, where to find headers. -IF(FREETYPE_LIBRARY AND FREETYPE_INCLUDE_DIRS) +IF(FREETYPE_LIBRARIES AND FREETYPE_INCLUDE_DIRS) # in cache already - SET(FREETYPE_FIND_QUIETLY TRUE) -ENDIF(FREETYPE_LIBRARY AND FREETYPE_INCLUDE_DIRS) - + SET(Freetype_FIND_QUIETLY TRUE) +ENDIF(FREETYPE_LIBRARIES AND FREETYPE_INCLUDE_DIRS) FIND_PATH(FREETYPE_INCLUDE_DIRS freetype @@ -40,7 +39,7 @@ IF(FREETYPE_ADDITIONAL_INCLUDE_DIR) SET(FREETYPE_INCLUDE_DIRS ${FREETYPE_INCLUDE_DIRS} ${FREETYPE_ADDITIONAL_INCLUDE_DIR}) ENDIF(FREETYPE_ADDITIONAL_INCLUDE_DIR) -FIND_LIBRARY(FREETYPE_LIBRARY +FIND_LIBRARY(FREETYPE_LIBRARY_RELEASE NAMES freetype libfreetype freetype219 freetype246 PATHS $ENV{FREETYPE_DIR}/lib @@ -55,20 +54,50 @@ FIND_LIBRARY(FREETYPE_LIBRARY /usr/freeware/lib64 ) -IF(FREETYPE_LIBRARY AND FREETYPE_INCLUDE_DIRS) - SET(FREETYPE_FOUND "YES") +FIND_LIBRARY(FREETYPE_LIBRARY_DEBUG + NAMES freetyped libfreetyped freetype219d freetype246d + PATHS + $ENV{FREETYPE_DIR}/lib + /usr/local/lib + /usr/lib + /usr/local/X11R6/lib + /usr/X11R6/lib + /sw/lib + /opt/local/lib + /opt/csw/lib + /opt/lib + /usr/freeware/lib64 +) + +IF(FREETYPE_INCLUDE_DIRS) + IF(FREETYPE_LIBRARY_RELEASE AND FREETYPE_LIBRARY_DEBUG) + # Case where both Release and Debug versions are provided + SET(FREETYPE_FOUND ON) + SET(FREETYPE_LIBRARIES optimized ${FREETYPE_LIBRARY_RELEASE} debug ${FREETYPE_LIBRARY_DEBUG}) + ELSEIF(FREETYPE_LIBRARY_RELEASE) + # Normal case + SET(FREETYPE_FOUND ON) + SET(FREETYPE_LIBRARIES ${FREETYPE_LIBRARY_RELEASE}) + ELSEIF(FREETYPE_LIBRARY_DEBUG) + # Case where Freetype is compiled from sources (debug version is compiled by default) + SET(FREETYPE_FOUND ON) + SET(FREETYPE_LIBRARIES ${FREETYPE_LIBRARY_DEBUG}) + ENDIF(FREETYPE_LIBRARY_RELEASE AND FREETYPE_LIBRARY_DEBUG) +ENDIF(FREETYPE_INCLUDE_DIRS) + +IF(FREETYPE_FOUND) IF(WITH_STATIC_EXTERNAL AND APPLE) FIND_PACKAGE(BZip2) IF(BZIP2_FOUND) SET(FREETYPE_INCLUDE_DIRS ${FREETYPE_INCLUDE_DIRS} ${BZIP2_INCLUDE_DIR}) - SET(FREETYPE_LIBRARY ${FREETYPE_LIBRARY} ${BZIP2_LIBRARIES}) + SET(FREETYPE_LIBRARIES ${FREETYPE_LIBRARIES} ${BZIP2_LIBRARIES}) ENDIF(BZIP2_FOUND) ENDIF(WITH_STATIC_EXTERNAL AND APPLE) - IF(NOT FREETYPE_FIND_QUIETLY) - MESSAGE(STATUS "Found FreeType: ${FREETYPE_LIBRARY}") - ENDIF(NOT FREETYPE_FIND_QUIETLY) + IF(NOT Freetype_FIND_QUIETLY) + MESSAGE(STATUS "Found FreeType: ${FREETYPE_LIBRARIES}") + ENDIF(NOT Freetype_FIND_QUIETLY) ELSE(FREETYPE_LIBRARY AND FREETYPE_INCLUDE_DIRS) - IF(NOT FREETYPE_FIND_QUIETLY) + IF(NOT Freetype_FIND_QUIETLY) MESSAGE(STATUS "Warning: Unable to find FreeType!") - ENDIF(NOT FREETYPE_FIND_QUIETLY) -ENDIF(FREETYPE_LIBRARY AND FREETYPE_INCLUDE_DIRS) + ENDIF(NOT Freetype_FIND_QUIETLY) +ENDIF(FREETYPE_FOUND) From 811dca71eea313c08255827b774afbac6fb2ac17 Mon Sep 17 00:00:00 2001 From: kervala Date: Sun, 8 Sep 2013 12:52:00 +0200 Subject: [PATCH 184/313] Changed: Moved lua, luabind and curl FIND_PACKAGE in root CMakeLists.txt --- code/CMakeLists.txt | 32 ++++++++++++++++++++++++++++++- code/nel/src/gui/CMakeLists.txt | 7 ++----- code/ryzom/CMakeLists.txt | 34 --------------------------------- 3 files changed, 33 insertions(+), 40 deletions(-) diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index 0cd9afd54..97feb494c 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -106,7 +106,6 @@ IF(WIN32) ENDIF(WITH_MFC) ENDIF(WIN32) -FIND_PACKAGE(Threads REQUIRED) FIND_PACKAGE(LibXml2 REQUIRED) FIND_PACKAGE(PNG REQUIRED) FIND_PACKAGE(Jpeg) @@ -114,6 +113,7 @@ FIND_PACKAGE(Jpeg) IF(WITH_STATIC_LIBXML2) SET(LIBXML2_DEFINITIONS ${LIBXML2_DEFINITIONS} -DLIBXML_STATIC) ENDIF(WITH_STATIC_LIBXML2) + IF(WITH_STATIC) # libxml2 could need winsock2 library SET(LIBXML2_LIBRARIES ${LIBXML2_LIBRARIES} ${WINSOCK2_LIB}) @@ -144,6 +144,36 @@ IF(WITH_NEL) IF(WITH_GUI) FIND_PACKAGE(Libwww REQUIRED) + IF(WITH_LUA51) + FIND_PACKAGE(Lua51 REQUIRED) + ELSE(WITH_LUA51) + FIND_PACKAGE(Lua50 REQUIRED) + ENDIF(WITH_LUA51) + FIND_PACKAGE(Luabind REQUIRED) + FIND_PACKAGE(CURL REQUIRED) + + IF(WIN32 OR CURL_LIBRARIES MATCHES "\\.a") + SET(CURL_STATIC ON) + ENDIF(WIN32 OR CURL_LIBRARIES MATCHES "\\.a") + + IF(CURL_STATIC) + SET(CURL_DEFINITIONS -DCURL_STATICLIB) + + FIND_PACKAGE(OpenSSL QUIET) + + IF(OPENSSL_FOUND) + SET(CURL_INCLUDE_DIRS ${CURL_INCLUDE_DIRS} ${OPENSSL_INCLUDE_DIR}) + SET(CURL_LIBRARIES ${CURL_LIBRARIES} ${OPENSSL_LIBRARIES}) + ENDIF(OPENSSL_FOUND) + + # CURL Macports version depends on libidn, libintl and libiconv too + IF(APPLE) + FIND_LIBRARY(IDN_LIBRARY idn) + FIND_LIBRARY(INTL_LIBRARY intl) + + SET(CURL_LIBRARIES ${CURL_LIBRARIES} ${IDN_LIBRARY} ${INTL_LIBRARY}) + ENDIF(APPLE) + ENDIF(CURL_STATIC) ENDIF(WITH_GUI) INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/nel/include ${LIBXML2_INCLUDE_DIR}) diff --git a/code/nel/src/gui/CMakeLists.txt b/code/nel/src/gui/CMakeLists.txt index 3f183a2ff..e6a943594 100644 --- a/code/nel/src/gui/CMakeLists.txt +++ b/code/nel/src/gui/CMakeLists.txt @@ -1,8 +1,3 @@ -FIND_PACKAGE( Libwww REQUIRED ) -FIND_PACKAGE( CURL REQUIRED ) -FIND_PACKAGE( Lua51 REQUIRED ) -FIND_PACKAGE( Luabind REQUIRED ) - FILE(GLOB SRC *.cpp *.h) FILE(GLOB HEADERS ../../include/nel/gui/*.h) @@ -31,6 +26,8 @@ TARGET_LINK_LIBRARIES( nelgui ${CURL_LIBRARIES} ) +ADD_DEFINITIONS(${LIBXML2_DEFINITIONS} ${CURL_DEFINITIONS} ${LUABIND_DEFINITIONS}) + IF(WITH_PCH) ADD_NATIVE_PRECOMPILED_HEADER(nelgui ${CMAKE_CURRENT_SOURCE_DIR}/stdpch.h ${CMAKE_CURRENT_SOURCE_DIR}/stdpch.cpp) ENDIF(WITH_PCH) diff --git a/code/ryzom/CMakeLists.txt b/code/ryzom/CMakeLists.txt index 95ed56c46..6e0747350 100644 --- a/code/ryzom/CMakeLists.txt +++ b/code/ryzom/CMakeLists.txt @@ -14,44 +14,10 @@ IF(WITH_RYZOM_CLIENT) MESSAGE( FATAL_ERROR "The client cannot be built without the NeL GUI Library (WITH_GUI)") ENDIF(NOT WITH_GUI) - IF(WITH_LUA51) - FIND_PACKAGE(Lua51 REQUIRED) - ELSE(WITH_LUA51) - FIND_PACKAGE(Lua50 REQUIRED) - ENDIF(WITH_LUA51) - FIND_PACKAGE(Luabind REQUIRED) - FIND_PACKAGE(CURL REQUIRED) - - IF(WIN32 OR CURL_LIBRARIES MATCHES "\\.a") - SET(CURL_STATIC ON) - ENDIF(WIN32 OR CURL_LIBRARIES MATCHES "\\.a") - - IF(CURL_STATIC) - SET(CURL_DEFINITIONS -DCURL_STATICLIB) - - FIND_PACKAGE(OpenSSL QUIET) - - IF(OPENSSL_FOUND) - SET(CURL_INCLUDE_DIRS ${CURL_INCLUDE_DIRS} ${OPENSSL_INCLUDE_DIR}) - SET(CURL_LIBRARIES ${CURL_LIBRARIES} ${OPENSSL_LIBRARIES}) - ENDIF(OPENSSL_FOUND) - - # CURL Macports version depends on libidn, libintl and libiconv too - IF(APPLE) - FIND_LIBRARY(IDN_LIBRARY idn) - FIND_LIBRARY(INTL_LIBRARY intl) - - SET(CURL_LIBRARIES ${CURL_LIBRARIES} ${IDN_LIBRARY} ${INTL_LIBRARY}) - ENDIF(APPLE) - ENDIF(CURL_STATIC) - ADD_SUBDIRECTORY(client) - ELSEIF(WITH_RYZOM_TOOLS) - # Need clientsheets lib for sheets packer tool ADD_SUBDIRECTORY(client) - ENDIF(WITH_RYZOM_CLIENT) IF(WITH_RYZOM_TOOLS) From e5423d5a3a93b5c175fa1cf38afdbe6f47c1a231 Mon Sep 17 00:00:00 2001 From: kervala Date: Sun, 8 Sep 2013 12:53:35 +0200 Subject: [PATCH 185/313] Changed: Look for Debug versions of libwww libraries --- code/CMakeModules/FindLibwww.cmake | 53 +++++++++++++++++++++--------- 1 file changed, 37 insertions(+), 16 deletions(-) diff --git a/code/CMakeModules/FindLibwww.cmake b/code/CMakeModules/FindLibwww.cmake index e59d981c7..e742c6ae5 100644 --- a/code/CMakeModules/FindLibwww.cmake +++ b/code/CMakeModules/FindLibwww.cmake @@ -3,17 +3,15 @@ # # This module defines # LIBWWW_INCLUDE_DIR, where to find tiff.h, etc. -# LIBWWW_LIBRARY, where to find the LibWWW library. -# LIBWWW_FOUND, If false, do not try to use LibWWW. +# LIBWWW_LIBRARY, where to find the Libwww library. +# LIBWWW_FOUND, If false, do not try to use Libwww. OPTION(WITH_LIBWWW_STATIC "Use only static libraries for libwww" OFF) -SET(LIBWWW_FIND_QUIETLY ${Libwww_FIND_QUIETLY}) - # also defined, but not for general use are IF(LIBWWW_LIBRARIES AND LIBWWW_INCLUDE_DIR) # in cache already - SET(LIBWWW_FIND_QUIETLY TRUE) + SET(Libwww_FIND_QUIETLY TRUE) ENDIF(LIBWWW_LIBRARIES AND LIBWWW_INCLUDE_DIR) FIND_PATH(LIBWWW_INCLUDE_DIR @@ -47,14 +45,29 @@ IF(LIBWWW_ADDITIONAL_INCLUDE_DIR) ENDIF(LIBWWW_ADDITIONAL_INCLUDE_DIR) # helper to find all the libwww sub libraries -MACRO(FIND_WWW_LIBRARY MYLIBRARY OPTION) +MACRO(FIND_WWW_LIBRARY MYLIBRARY OPTION FILE) IF(WITH_LIBWWW_STATIC AND UNIX AND NOT APPLE AND NOT WITH_STATIC_EXTERNAL) SET(CMAKE_FIND_LIBRARY_SUFFIXES_OLD ${CMAKE_FIND_LIBRARY_SUFFIXES}) SET(CMAKE_FIND_LIBRARY_SUFFIXES ".a") ENDIF(WITH_LIBWWW_STATIC AND UNIX AND NOT APPLE AND NOT WITH_STATIC_EXTERNAL) - FIND_LIBRARY(${MYLIBRARY} - NAMES ${ARGN} + FIND_LIBRARY(${MYLIBRARY}_RELEASE + NAMES ${FILE} + PATHS + /usr/local/lib + /usr/lib + /usr/lib/x86_64-linux-gnu + /usr/local/X11R6/lib + /usr/X11R6/lib + /sw/lib + /opt/local/lib + /opt/csw/lib + /opt/lib + /usr/freeware/lib64 + ) + + FIND_LIBRARY(${MYLIBRARY}_DEBUG + NAMES ${FILE}d PATHS /usr/local/lib /usr/lib @@ -72,17 +85,25 @@ MACRO(FIND_WWW_LIBRARY MYLIBRARY OPTION) SET(CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES_OLD}) ENDIF(CMAKE_FIND_LIBRARY_SUFFIXES_OLD) - IF(${MYLIBRARY}) + IF(${MYLIBRARY}_RELEASE AND ${MYLIBRARY}_DEBUG) IF(${OPTION} STREQUAL REQUIRED OR WITH_STATIC OR WITH_LIBWWW_STATIC) - SET(LIBWWW_LIBRARIES ${LIBWWW_LIBRARIES} ${${MYLIBRARY}}) + SET(LIBWWW_LIBRARIES ${LIBWWW_LIBRARIES} optimized ${${MYLIBRARY}_RELEASE} debug ${${MYLIBRARY}_DEBUG}) ENDIF(${OPTION} STREQUAL REQUIRED OR WITH_STATIC OR WITH_LIBWWW_STATIC) - ELSE(${MYLIBRARY}) - IF(NOT LIBWWW_FIND_QUIETLY AND NOT WIN32) + ELSEIF(${MYLIBRARY}_RELEASE) + IF(${OPTION} STREQUAL REQUIRED OR WITH_STATIC OR WITH_LIBWWW_STATIC) + SET(LIBWWW_LIBRARIES ${LIBWWW_LIBRARIES} ${${MYLIBRARY}_RELEASE}) + ENDIF(${OPTION} STREQUAL REQUIRED OR WITH_STATIC OR WITH_LIBWWW_STATIC) + ELSEIF(${MYLIBRARY}_DEBUG) + IF(${OPTION} STREQUAL REQUIRED OR WITH_STATIC OR WITH_LIBWWW_STATIC) + SET(LIBWWW_LIBRARIES ${LIBWWW_LIBRARIES} ${${MYLIBRARY}_DEBUG}) + ENDIF(${OPTION} STREQUAL REQUIRED OR WITH_STATIC OR WITH_LIBWWW_STATIC) + ELSE(${MYLIBRARY}_RELEASE AND ${MYLIBRARY}_DEBUG) + IF(NOT Libwww_FIND_QUIETLY AND NOT WIN32) MESSAGE(STATUS "Warning: Libwww: Library not found: ${MYLIBRARY}") - ENDIF(NOT LIBWWW_FIND_QUIETLY AND NOT WIN32) - ENDIF(${MYLIBRARY}) + ENDIF(NOT Libwww_FIND_QUIETLY AND NOT WIN32) + ENDIF(${MYLIBRARY}_RELEASE AND ${MYLIBRARY}_DEBUG) - MARK_AS_ADVANCED(${MYLIBRARY}) + MARK_AS_ADVANCED(${MYLIBRARY}_RELEASE ${MYLIBRARY}_DEBUG) ENDMACRO(FIND_WWW_LIBRARY) MACRO(LINK_WWW_LIBRARY MYLIBRARY OTHERLIBRARY SYMBOL) @@ -163,7 +184,7 @@ LINK_WWW_LIBRARY(LIBWWWAPP_LIBRARY LIBREGEX_LIBRARY regexec) LINK_WWW_LIBRARY(LIBWWWAPP_LIBRARY OPENSSL_LIBRARIES SSL_new) INCLUDE(FindPackageHandleStandardArgs) -FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibWWW DEFAULT_MSG +FIND_PACKAGE_HANDLE_STANDARD_ARGS(Libwww DEFAULT_MSG LIBWWW_LIBRARIES LIBWWW_INCLUDE_DIR ) From a023ab89d718f46ae2cd26bb3eb9df39ac8e7188 Mon Sep 17 00:00:00 2001 From: kervala Date: Sun, 8 Sep 2013 12:54:05 +0200 Subject: [PATCH 186/313] Changed: Updated Freetype libraries variable --- code/nel/src/3d/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/nel/src/3d/CMakeLists.txt b/code/nel/src/3d/CMakeLists.txt index 6f632e261..2cf90852c 100644 --- a/code/nel/src/3d/CMakeLists.txt +++ b/code/nel/src/3d/CMakeLists.txt @@ -691,7 +691,7 @@ NL_TARGET_LIB(nel3d ${HEADERS} ${SRC}) INCLUDE_DIRECTORIES(${LIBXML2_INCLUDE_DIR} ${FREETYPE_INCLUDE_DIRS}) -TARGET_LINK_LIBRARIES(nel3d nelmisc ${FREETYPE_LIBRARY}) +TARGET_LINK_LIBRARIES(nel3d nelmisc ${FREETYPE_LIBRARIES}) SET_TARGET_PROPERTIES(nel3d PROPERTIES LINK_INTERFACE_LIBRARIES "") NL_DEFAULT_PROPS(nel3d "NeL, Library: NeL 3D") NL_ADD_RUNTIME_FLAGS(nel3d) From 7f6959ce39aabda0dbc075f4992398121b2d4c65 Mon Sep 17 00:00:00 2001 From: kervala Date: Sun, 8 Sep 2013 12:57:34 +0200 Subject: [PATCH 187/313] Changed: lua, luabind, libwww and libxml2 already linked to previous libraries (NLMISC and NLGUI) --- code/nel/tools/3d/CMakeLists.txt | 2 +- .../src/plugins/CMakeLists.txt | 2 +- .../src/plugins/gui_editor/CMakeLists.txt | 46 ++----------------- 3 files changed, 6 insertions(+), 44 deletions(-) diff --git a/code/nel/tools/3d/CMakeLists.txt b/code/nel/tools/3d/CMakeLists.txt index 7f4ed6b8c..58bf81f5a 100644 --- a/code/nel/tools/3d/CMakeLists.txt +++ b/code/nel/tools/3d/CMakeLists.txt @@ -62,7 +62,7 @@ IF(WITH_NEL_TOOLS) ENDIF(WITH_QT) IF(SQUISH_FOUND) - ADD_SUBDIRECTORY(s3tc_compressor_lib) + ADD_SUBDIRECTORY(s3tc_compressor_lib) ADD_SUBDIRECTORY(panoply_maker) ADD_SUBDIRECTORY(tga_2_dds) ADD_SUBDIRECTORY(hls_bank_maker) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/CMakeLists.txt b/code/nel/tools/3d/object_viewer_qt/src/plugins/CMakeLists.txt index 43900fe9e..7903114d1 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/CMakeLists.txt +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/CMakeLists.txt @@ -8,7 +8,7 @@ ADD_SUBDIRECTORY(object_viewer) ADD_SUBDIRECTORY(georges_editor) IF(WITH_GUI) -ADD_SUBDIRECTORY(gui_editor) + ADD_SUBDIRECTORY(gui_editor) ENDIF(WITH_GUI) ADD_SUBDIRECTORY(translation_manager) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/gui_editor/CMakeLists.txt b/code/nel/tools/3d/object_viewer_qt/src/plugins/gui_editor/CMakeLists.txt index 0a5d8533d..409005a6a 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/gui_editor/CMakeLists.txt +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/gui_editor/CMakeLists.txt @@ -1,40 +1,7 @@ -IF(WITH_LUA51) - FIND_PACKAGE(Lua51 REQUIRED) -ELSE(WITH_LUA51) - FIND_PACKAGE(Lua50 REQUIRED) -ENDIF(WITH_LUA51) - FIND_PACKAGE(Luabind REQUIRED) - FIND_PACKAGE(CURL REQUIRED) - -IF(WIN32 OR CURL_LIBRARIES MATCHES "\\.a") - SET(CURL_STATIC ON) -ENDIF(WIN32 OR CURL_LIBRARIES MATCHES "\\.a") - -IF(CURL_STATIC) - SET(CURL_DEFINITIONS -DCURL_STATICLIB) - FIND_PACKAGE(OpenSSL QUIET) - - IF(OPENSSL_FOUND) - SET(CURL_INCLUDE_DIRS ${CURL_INCLUDE_DIRS} ${OPENSSL_INCLUDE_DIR}) - SET(CURL_LIBRARIES ${CURL_LIBRARIES} ${OPENSSL_LIBRARIES}) - ENDIF(OPENSSL_FOUND) - - # CURL Macports version depends on libidn, libintl and libiconv too - IF(APPLE) - FIND_LIBRARY(IDN_LIBRARY idn) - FIND_LIBRARY(INTL_LIBRARY intl) - SET(CURL_LIBRARIES ${CURL_LIBRARIES} ${IDN_LIBRARY} ${INTL_LIBRARY}) - ENDIF(APPLE) -ENDIF(CURL_STATIC) - -FIND_PACKAGE(Libwww REQUIRED) - - -INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_BINARY_DIR} - ${CMAKE_CURRENT_SOURCE_DIR} - ${LIBXML2_INCLUDE_DIR} - ${LUA_INCLUDE_DIR} - ${QT_INCLUDES}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR} + ${LIBXML2_INCLUDE_DIR} + ${QT_INCLUDES}) FILE(GLOB SRC *.cpp *.h) @@ -105,11 +72,6 @@ TARGET_LINK_LIBRARIES( ${QT_LIBRARIES} ${QT_QTOPENGL_LIBRARY} qt_property_browser - ${LUA_LIBRARIES} - ${LUABIND_LIBRARIES} - ${CURL_LIBRARIES} - ${LIBWWW_LIBRARIES} - ${LIBXML2_LIBRARIES} ) NL_DEFAULT_PROPS(ovqt_plugin_gui_editor "NeL, Tools, 3D: Object Viewer Qt Plugin: GUI Editor") From 7be6891bd71ce22e6d82bcfe50e6249f421d3642 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Sun, 8 Sep 2013 21:57:27 +0200 Subject: [PATCH 188/313] Cleanup driver interface --- code/nel/include/nel/3d/driver.h | 1016 ++++++++++------- code/nel/include/nel/3d/driver_user.h | 1 - code/nel/include/nel/3d/gpu_program_params.h | 67 +- code/nel/include/nel/3d/u_driver.h | 7 - code/nel/src/3d/driver.cpp | 19 +- .../src/3d/driver/direct3d/driver_direct3d.h | 1 - .../src/3d/driver/opengl/driver_opengl.cpp | 6 - code/nel/src/3d/driver/opengl/driver_opengl.h | 2 - code/nel/src/3d/driver_user.cpp | 6 - code/nel/src/3d/gpu_program_params.cpp | 36 +- code/nel/src/3d/stereo_ovr.cpp | 18 +- 11 files changed, 661 insertions(+), 518 deletions(-) diff --git a/code/nel/include/nel/3d/driver.h b/code/nel/include/nel/3d/driver.h index 2d10c5653..2414d3df5 100644 --- a/code/nel/include/nel/3d/driver.h +++ b/code/nel/include/nel/3d/driver.h @@ -31,6 +31,7 @@ #include "nel/3d/vertex_program.h" #include "nel/3d/pixel_program.h" #include "nel/3d/geometry_program.h" +#include "nel/3d/gpu_program_params.h" #include "nel/3d/material.h" #include "nel/misc/mutex.h" #include "nel/3d/primitive_profile.h" @@ -120,9 +121,9 @@ public: static const uint32 InterfaceVersion; public: - enum TMessageBoxId { okId=0, yesId, noId, abortId, retryId, cancelId, ignoreId, idCount }; - enum TMessageBoxType { okType=0, okCancelType, yesNoType, abortRetryIgnoreType, yesNoCancelType, retryCancelType, typeCount }; - enum TMessageBoxIcon { noIcon=0, handIcon, questionIcon, exclamationIcon, asteriskIcon, warningIcon, errorIcon, informationIcon, stopIcon, iconCount }; + enum TMessageBoxId { okId = 0, yesId, noId, abortId, retryId, cancelId, ignoreId, idCount }; + enum TMessageBoxType { okType = 0, okCancelType, yesNoType, abortRetryIgnoreType, yesNoCancelType, retryCancelType, typeCount }; + enum TMessageBoxIcon { noIcon = 0, handIcon, questionIcon, exclamationIcon, asteriskIcon, warningIcon, errorIcon, informationIcon, stopIcon, iconCount }; enum TCullMode { CCW = 0, CW }; enum TStencilOp { keep = 0, zero, replace, incr, decr, invert }; enum TStencilFunc { never = 0, less, lessequal, equal, notequal, greaterequal, greater, always}; @@ -132,116 +133,218 @@ public: * * \see setPolygonMode, getPolygonMode */ - enum TPolygonMode { Filled=0, Line, Point }; - + enum TPolygonMode { Filled = 0, Line, Point }; /** * Driver Max matrix count. * Kept for backward compatibility. Suppose any Hardware VertexProgram can handle only 16 matrix * */ - enum TMatrixCount { MaxModelMatrix= 16 }; + enum TMatrixCount { MaxModelMatrix = 16 }; + + enum TMatrix + { + ModelView= 0, + Projection, + ModelViewProjection, + NumMatrix + }; + + enum TTransform + { + Identity=0, + Inverse, + Transpose, + InverseTranspose, + NumTransform + }; protected: + CSynchronized _SyncTexDrvInfos; + TTexDrvSharePtrList _TexDrvShares; + TMatDrvInfoPtrList _MatDrvInfos; + TVBDrvInfoPtrList _VBDrvInfos; + TIBDrvInfoPtrList _IBDrvInfos; + TGPUPrgDrvInfoPtrList _GPUPrgDrvInfos; - CSynchronized _SyncTexDrvInfos; - - TTexDrvSharePtrList _TexDrvShares; - TMatDrvInfoPtrList _MatDrvInfos; - TVBDrvInfoPtrList _VBDrvInfos; - TIBDrvInfoPtrList _IBDrvInfos; TPolygonMode _PolygonMode; - TGPUPrgDrvInfoPtrList _GPUPrgDrvInfos; uint _ResetCounter; public: - IDriver(void); - virtual ~IDriver(void); + IDriver(); + virtual ~IDriver(); - virtual bool init (uint windowIcon = 0, emptyProc exitFunc = 0)=0; + virtual bool init(uint windowIcon = 0, emptyProc exitFunc = 0) = 0; + + /// Deriver should calls IDriver::release() first, to destroy all driver components (textures, shaders, VBuffers). + virtual bool release(); + + /// Before rendering via a driver in a thread, must activate() (per thread). + virtual bool activate() = 0; // Test if the device is lost. Can only happen with D3D. // The calling application may skip some part of its rendering when it is the case (this is not a requirement, but may save cpu for other applications) virtual bool isLost() const = 0; + /// Return true if driver is still active. Return false else. If he user close the window, must return false. + virtual bool isActive() = 0; + + + + /** Return the driver reset counter. + * The reset counter is incremented at each driver reset. + */ + uint getResetCounter() const { return _ResetCounter; } + + // get the number of call to swapBuffer since the driver was created + virtual uint64 getSwapBufferCounter() const = 0; + + + /// \name Disable Hardware Feature /** Disable some Feature that may be supported by the Hardware * Call before setDisplay() to work properly */ // @{ - virtual void disableHardwareVertexProgram()=0; - virtual void disableHardwarePixelProgram()=0; - virtual void disableHardwareVertexArrayAGP()=0; - virtual void disableHardwareTextureShader()=0; + virtual void disableHardwareVertexProgram() = 0; + virtual void disableHardwarePixelProgram() = 0; + virtual void disableHardwareVertexArrayAGP() = 0; + virtual void disableHardwareTextureShader() = 0; // @} + + + /// \name Windowing + // @{ // first param is the associated window. // Must be a HWND for Windows (WIN32). - virtual bool setDisplay(nlWindow wnd, const GfxMode& mode, bool show = true, bool resizeable = true) throw(EBadDisplay)=0; + virtual bool setDisplay(nlWindow wnd, const GfxMode& mode, bool show = true, bool resizeable = true) throw(EBadDisplay) = 0; // Must be called after a setDisplay that initialize the mode - virtual bool setMode(const GfxMode& mode)=0; - virtual bool getModes(std::vector &modes)=0; + virtual bool setMode(const GfxMode &mode) = 0; + virtual bool getModes(std::vector &modes) = 0; /// Set the title of the NeL window - virtual void setWindowTitle(const ucstring &title)=0; - + virtual void setWindowTitle(const ucstring &title) = 0; /// Set icon(s) of the NeL window - virtual void setWindowIcon(const std::vector &bitmaps)=0; - + virtual void setWindowIcon(const std::vector &bitmaps) = 0; /// Set the position of the NeL window - virtual void setWindowPos(sint32 x, sint32 y)=0; - + virtual void setWindowPos(sint32 x, sint32 y) = 0; /// Show or hide the NeL window - virtual void showWindow(bool show)=0; + virtual void showWindow(bool show) = 0; /// return the current screen mode (if we are in windowed, return the screen mode behind the window) - virtual bool getCurrentScreenMode(GfxMode &mode)=0; + virtual bool getCurrentScreenMode(GfxMode &mode) = 0; /// enter/leave the dialog mode - virtual void beginDialogMode() =0; - virtual void endDialogMode() =0; + virtual void beginDialogMode() = 0; + virtual void endDialogMode() = 0; // Return is the associated window information. (Implementation dependent) // Must be a HWND for Windows (WIN32). - virtual nlWindow getDisplay() =0; + virtual nlWindow getDisplay() = 0; - /** - * Setup monitor color properties. - * - * Return false if setup failed. - */ - virtual bool setMonitorColorProperties (const CMonitorColorProperties &properties) = 0; + /// Setup monitor color properties. Return false if setup failed + virtual bool setMonitorColorProperties(const CMonitorColorProperties &properties) = 0; // Return is the associated default window proc for the driver. (Implementation dependent) // Must be a void GlWndProc(IDriver *driver, HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) for Windows (WIN32). virtual emptyProc getWindowProc() = 0; - /// Before rendering via a driver in a thread, must activate() (per thread). - virtual bool activate(void) = 0; + virtual NLMISC::IEventEmitter *getEventEmitter() = 0; + + /// Copy a string to system clipboard. + virtual bool copyTextToClipboard(const ucstring &text) = 0; - /// Get the number of texture stage available, for multi texturing (Normal material shaders). Valid only after setDisplay(). - virtual uint getNbTextureStages() const = 0; + /// Paste a string from system clipboard. + virtual bool pasteTextFromClipboard(ucstring &text) = 0;/// Return the depth of the driver after init(). - /** is the texture is set up in the driver - * NB: this method is thread safe. - */ - virtual bool isTextureExist(const ITexture&tex)=0; + virtual uint8 getBitPerPixel() = 0; - virtual NLMISC::IEventEmitter* getEventEmitter(void) = 0; + /** Output a system message box and print a message with an icon. This method can be call even if the driver is not initialized. + * This method is used to return internal driver problem when string can't be displayed in the driver window. + * If the driver can't open a messageBox, it should not override this method and let the IDriver class manage it with the ASCII console. + * + * \param message This is the message to display in the message box. + * \param title This is the title of the message box. + * \param type This is the type of the message box, ie number of button and label of buttons. + * \param icon This is the icon of the message box should use like warning, error etc... + */ + virtual TMessageBoxId systemMessageBox(const char *message, const char *title, TMessageBoxType type = okType, TMessageBoxIcon icon = noIcon); - /* Clear the current target surface pixels. The function ignores the viewport settings but uses the scissor. */ + /// Get the width and the height of the window + virtual void getWindowSize(uint32 &width, uint32 &height) = 0; + + /// Get the position of the window always (0,0) in fullscreen + virtual void getWindowPos(sint32 &x, sint32 &y) = 0; + // @} + + + + /// \name Framebuffer operations + // @{ + /// Clear the current target surface pixels. The function ignores the viewport settings but uses the scissor. virtual bool clear2D(CRGBA rgba) = 0; - /* Clear the current target surface zbuffer. The function ignores the viewport settings but uses the scissor. */ + /// Clear the current target surface zbuffer. The function ignores the viewport settings but uses the scissor. virtual bool clearZBuffer(float zval=1) = 0; - /* Clear the current target surface stencil buffer. The function ignores the viewport settings but uses the scissor. */ + /// Clear the current target surface stencil buffer. The function ignores the viewport settings but uses the scissor. virtual bool clearStencilBuffer(float stencilval=0) = 0; /// Set the color mask filter through where the operation done will pass - virtual void setColorMask (bool bRed, bool bGreen, bool bBlue, bool bAlpha) = 0; + virtual void setColorMask(bool bRed, bool bGreen, bool bBlue, bool bAlpha) = 0; + // @} + + + /// \name Copy framebuffer to memory + // @{ + /** get the RGBA back buffer. After swapBuffers(), the content of the back buffer is undefined. + * + * \param bitmap the buffer will be written in this bitmap + */ + virtual void getBuffer(CBitmap &bitmap) = 0; + + /** get the ZBuffer (back buffer). + * + * \param zbuffer the returned array of Z. size of getWindowSize() . + */ + virtual void getZBuffer(std::vector &zbuffer) = 0; + + /** get a part of the RGBA back buffer. After swapBuffers(), the content of the back buffer is undefined. + * NB: 0,0 is the bottom left corner of the screen. + * + * \param bitmap the buffer will be written in this bitmap + * \param rect the in/out (wanted/clipped) part of Color buffer to retrieve. + */ + virtual void getBufferPart(CBitmap &bitmap, NLMISC::CRect &rect) = 0; + + /** get a part of the ZBuffer (back buffer). + * NB: 0,0 is the bottom left corner of the screen. + * + * \param zbuffer the returned array of Z. size of rec.Width*rec.Height. + * \param rect the in/out (wanted/clipped) part of ZBuffer to retrieve. + */ + virtual void getZBufferPart(std::vector &zbuffer, NLMISC::CRect &rect) = 0; + // @} + + + + /// \name Copy memory to framebuffer + // @{ + /** fill the RGBA back buffer + * + * \param bitmap will be written in the buffer. no-op if bad size. + * \return true if success + */ + virtual bool fillBuffer(CBitmap &bitmap) = 0; + // @} + + + + /// \name Viewport depth clipping + // @{ /** Set depth range. Depth range specify a linear mapping from device z coordinates (in the [-1, 1] range) to window coordinates (in the [0, 1] range) * This mapping occurs after clipping of primitives and division by w of vertices coordinates. * Default depth range is [0, 1]. @@ -250,11 +353,20 @@ public: virtual void setDepthRange(float znear, float zfar) = 0; // Get the current depth range virtual void getDepthRange(float &znear, float &zfar) const = 0; + // @} + + + /// \name Textures + // @{ + /** is the texture is set up in the driver + * NB: this method is thread safe. + */ + virtual bool isTextureExist(const ITexture &tex) = 0; /** setup a texture, generate and upload if needed. same as setupTextureEx(tex, true, dummy); */ - virtual bool setupTexture(ITexture& tex) = 0; + virtual bool setupTexture(ITexture &tex) = 0; /** setup a texture in the driver. * \param bUpload if true the texture is created and uploaded to VRAM, if false the texture is only created @@ -267,59 +379,81 @@ public: * is only bound to tex (thus creating and uploading nothing) * NB: the texture must be at least touch()-ed for the recreate to work. */ - virtual bool setupTextureEx (ITexture& tex, bool bUpload, bool& bAllUploaded, - bool bMustRecreateSharedTexture= false) = 0; + virtual bool setupTextureEx( ITexture &tex, bool bUpload, bool &bAllUploaded, + bool bMustRecreateSharedTexture = false) = 0; /** The texture must be created or uploadTexture do nothing. * These function can be used to upload piece by piece a texture. Use it in conjunction with setupTextureEx(..., false); * For compressed textures, the rect must aligned on pixel block. (a block of pixel size is 4x4 pixels). */ - virtual bool uploadTexture (ITexture& tex, NLMISC::CRect& rect, uint8 nNumMipMap) = 0; - virtual bool uploadTextureCube (ITexture& tex, NLMISC::CRect& rect, uint8 nNumMipMap, uint8 nNumFace) = 0; + virtual bool uploadTexture(ITexture &tex, NLMISC::CRect &rect, uint8 nNumMipMap) = 0; + virtual bool uploadTextureCube(ITexture &tex, NLMISC::CRect &rect, uint8 nNumMipMap, uint8 nNumFace) = 0; /** * Invalidate shared texture */ - bool invalidateShareTexture (ITexture &); + bool invalidateShareTexture(ITexture &); /** * Get the driver share texture name */ - static void getTextureShareName (const ITexture& tex, std::string &output); + static void getTextureShareName(const ITexture &tex, std::string &output); /** if true force all the uncompressed RGBA 32 bits and RGBA 24 bits texture to be DXTC5 compressed. * Do this only during upload if ITexture::allowDegradation() is true and if ITexture::UploadFormat is "Automatic" * and if bitmap format is RGBA. */ - virtual void forceDXTCCompression(bool dxtcComp)=0; + virtual void forceDXTCCompression(bool dxtcComp) = 0; /** if different from 0, enable anisotropic filter on textures. -1 enables max value. * Default is 0. */ - virtual void setAnisotropicFilter(sint filter)=0; + virtual void setAnisotropicFilter(sint filter) = 0; /** if !=1, force mostly all the textures (but TextureFonts lightmaps, interfaces etc..) * to be divided by Divisor (2, 4, 8...) * Default is 1. * NB: this is done only on TextureFile */ - virtual void forceTextureResize(uint divisor)=0; + virtual void forceTextureResize(uint divisor) = 0; - /** Sets enforcement of native fragment programs. This is by default enabled. - * - * \param nativeOnly If set to false, fragment programs don't need to be native to stay loaded, - * otherwise (aka if true) they will be purged. + /** Get the number of texture stage available, for multi texturing (Normal material shaders). Valid only after setDisplay(). */ - virtual void forceNativeFragmentPrograms(bool nativeOnly) = 0; + virtual uint getNbTextureStages() const = 0; + + /** Get max number of per stage constant that can be used simultaneously. + * This will usually match the number of texture stages, but with a D3D driver, this feature is not available most of the time + * so it is emulated. If pixel shaders are available this will be fully supported. + * Under OpenGL this simply returns the maximum number of texture stages (getNbTextureStages) in both return values. + */ + virtual void getNumPerStageConstant(uint &lightedMaterial, uint &unlightedMaterial) const = 0; + + // [DEPRECATED] Return if this texture is a rectangle texture that requires RECT sampler (OpenGL specific pre-NPOT functionality) + virtual bool isTextureRectangle(ITexture *tex) const = 0; + + // Return true if driver support non-power of two textures + virtual bool supportNonPowerOfTwoTextures() const = 0; + // @} + + + + /// \name Texture operations + // @{ + // copy the first texture in a second one of different dimensions + virtual bool stretchRect(ITexture *srcText, NLMISC::CRect &srcRect, ITexture *destText, NLMISC::CRect &destRect) = 0; + // @} - virtual bool setupMaterial(CMaterial& mat)=0; + + /// \name Material + // @{ + virtual bool setupMaterial(CMaterial &mat) = 0; /** Special for Faster Specular Setup. Call this between lot of primitives rendered with Specular Materials. * Visual Errors may arise if you don't correctly call endSpecularBatch(). */ - virtual void startSpecularBatch()=0; - virtual void endSpecularBatch()=0; + virtual void startSpecularBatch() = 0; + virtual void endSpecularBatch() = 0; /// \name Material multipass. /** NB: setupMaterial() must be called before those methods. @@ -327,18 +461,26 @@ public: * NB: Other render calls performs the needed setup automatically */ // @{ - /// init multipass for _CurrentMaterial. return number of pass required to render this material. - virtual sint beginMaterialMultiPass() = 0; - /// active the ith pass of this material. - virtual void setupMaterialPass(uint pass) = 0; - /// end multipass for this material. - virtual void endMaterialMultiPass() = 0; + /// init multipass for _CurrentMaterial. return number of pass required to render this material. + virtual sint beginMaterialMultiPass() = 0; + /// active the ith pass of this material. + virtual void setupMaterialPass(uint pass) = 0; + /// end multipass for this material. + virtual void endMaterialMultiPass() = 0; // @} + // Does the driver support the per-pixel lighting shader ? + virtual bool supportPerPixelLighting(bool specular) const = 0; + // @} + + + + /// \name Camera + // @{ // Setup the camera mode as a perspective/ortho camera. NB: znear and zfar must be >0 (if perspective). - virtual void setFrustum(float left, float right, float bottom, float top, float znear, float zfar, bool perspective=true)=0; - virtual void setFrustumMatrix(CMatrix &frust)=0; - virtual CMatrix getFrustumMatrix()=0; + virtual void setFrustum(float left, float right, float bottom, float top, float znear, float zfar, bool perspective = true) = 0; + virtual void setFrustumMatrix(CMatrix &frust) = 0; + virtual CMatrix getFrustumMatrix() = 0; virtual float getClipSpaceZMin() const = 0; @@ -346,7 +488,7 @@ public: * * NB: you must setupViewMatrix() BEFORE setupModelMatrix(), or else undefined results. */ - virtual void setupViewMatrix(const CMatrix& mtx)=0; + virtual void setupViewMatrix(const CMatrix &mtx)=0; /** setup the view matrix (inverse of camera matrix). * Extended: give a cameraPos (mtx.Pos() is not taken into account but for getViewMatrix()), @@ -360,38 +502,40 @@ public: * \param mtx the same view matrix (still with correct "inversed" camera position) as if passed in setupViewMatrix() * \param cameraPos position of the camera (before inversion, ie mtx.getPos()!=cameraPos ). */ - virtual void setupViewMatrixEx(const CMatrix& mtx, const CVector &cameraPos)=0; + virtual void setupViewMatrixEx(const CMatrix &mtx, const CVector &cameraPos) = 0; /** setup the model matrix. * * NB: you must setupModelMatrix() AFTER setupViewMatrix(), or else undefined results. */ - virtual void setupModelMatrix(const CMatrix& mtx)=0; + virtual void setupModelMatrix(const CMatrix &mtx) = 0; - virtual CMatrix getViewMatrix(void)const=0; + virtual CMatrix getViewMatrix() const = 0; + // @} + + /// \name Fixed pipeline vertex program + // @{ /** Force input normal to be normalized by the driver. default is false. * NB: driver force the normalization himself if: * - current Model matrix has a scale. */ - virtual void forceNormalize(bool normalize)=0; + virtual void forceNormalize(bool normalize) = 0; /** return the forceNormalize() state. */ - virtual bool isForceNormalize() const =0; + virtual bool isForceNormalize() const = 0; + // @} + - /** Get max number of per stage constant that can be used simultaneously. - * This will usually match the number of texture stages, but with a D3D driver, this feature is not available most of the time - * so it is emulated. If pixel shaders are available this will be fully supported. - */ - virtual void getNumPerStageConstant(uint &lightedMaterial, uint &unlightedMaterial) const = 0; - + /// \name Vertex Buffer Hard: Features + // @{ /** return true if driver support VertexBufferHard. */ - virtual bool supportVertexBufferHard() const =0; + virtual bool supportVertexBufferHard() const = 0; /** return true if volatile vertex buffer are supported. (e.g a vertex buffer which can be created with the flag CVertexBuffer::AGPVolatile or CVertexBuffer::RAMVolatile) * If these are not supported, a RAM vb is created instead (transparent to user) @@ -405,9 +549,13 @@ public: /** return true if driver support VertexBufferHard, but vbHard->unlock() are slow (ATI-openGL). */ - virtual bool slowUnlockVertexBufferHard() const =0; + virtual bool slowUnlockVertexBufferHard() const = 0; + // @} + + /// \name Vertex Buffer Hard: Settings + // @{ /* Returns true if static vertex and index buffers must by allocated in VRAM, false in AGP. * Default is false. */ @@ -418,16 +566,15 @@ public: */ void setStaticMemoryToVRAM(bool staticMemoryToVRAM); - /** Return the driver reset counter. - * The reset counter is incremented at each driver reset. - */ - uint getResetCounter () const { return _ResetCounter; } - /** return How many vertices VertexBufferHard support */ - virtual uint getMaxVerticesByVertexBufferHard() const =0; + virtual uint getMaxVerticesByVertexBufferHard() const = 0; + // @} + + /// \name Vertex Buffer Hard + // @{ /** Allocate the initial VertexArray Memory. (no-op if !supportVertexBufferHard()). * VertexArrayRange is first reseted, so any VBhard created before will be deleted. * NB: call it after setDisplay(). But setDisplay() by default call initVertexBufferHard(16Mo, 0); @@ -437,17 +584,21 @@ public: * \param vramMem amount of VRAM Memory required. if 0, reseted. * \return false if one the Buffer has not been allocated (at least at 500K). */ - virtual bool initVertexBufferHard(uint agpMem, uint vramMem=0) =0; + virtual bool initVertexBufferHard(uint agpMem, uint vramMem = 0) = 0; /** Return the amount of AGP memory allocated by initVertexBufferHard() to store vertices. */ - virtual uint32 getAvailableVertexAGPMemory () =0; + virtual uint32 getAvailableVertexAGPMemory() = 0; /** Return the amount of video memory allocated by initVertexBufferHard() to store vertices. */ - virtual uint32 getAvailableVertexVRAMMemory () =0; + virtual uint32 getAvailableVertexVRAMMemory() = 0; + // @} + + /// \name Vertex Buffer Objects + // @{ /** active a current VB, for future render(). * This method suppose that all vertices in the VB will be used. * @@ -457,30 +608,33 @@ public: * * \see activeVertexProgram */ - virtual bool activeVertexBuffer(CVertexBuffer& VB)=0; - + virtual bool activeVertexBuffer(CVertexBuffer &VB) = 0; /** active a current IB, for future render(). * * Don't change the index buffer format/size after having activated it. * Don't lock the index buffer after having activated it. */ - virtual bool activeIndexBuffer(CIndexBuffer& IB)=0; + virtual bool activeIndexBuffer(CIndexBuffer &IB) = 0; + // @} + + /// \name Rendering + // @{ /** Render a list of indexed lines with previously setuped VertexBuffer / IndexBuffer / Matrixes. * \param mat is the material to use during this rendering * \param firstIndex is the first index in the index buffer to use as first line. * \param nlines is the number of line to render. */ - virtual bool renderLines(CMaterial& mat, uint32 firstIndex, uint32 nlines)=0; + virtual bool renderLines(CMaterial &mat, uint32 firstIndex, uint32 nlines) = 0; /** Render a list of indexed triangles with previously setuped VertexBuffer / IndexBuffer / Matrixes. * \param mat is the material to use during this rendering * \param firstIndex is the first index in the index buffer to use as first triangle. * \param ntris is the number of triangle to render. */ - virtual bool renderTriangles(CMaterial& mat, uint32 firstIndex, uint32 ntris)=0; + virtual bool renderTriangles(CMaterial &mat, uint32 firstIndex, uint32 ntris) = 0; /** Render a list of triangles with previously setuped VertexBuffer / IndexBuffer / Matrixes, AND previously setuped MATERIAL!! * This use the last material setuped. It should be a "Normal shader" material, because no multi-pass is allowed @@ -491,7 +645,7 @@ public: * \param firstIndex is the first index in the index buffer to use as first triangle. * \param ntris is the number of triangle to render. */ - virtual bool renderSimpleTriangles(uint32 firstIndex, uint32 ntris)=0; + virtual bool renderSimpleTriangles(uint32 firstIndex, uint32 ntris) = 0; /** Render points with previously setuped VertexBuffer / Matrixes. * Points are stored as a sequence in the vertex buffer. @@ -499,7 +653,7 @@ public: * \param startVertex is the first vertex to use during this rendering. * \param numPoints is the number of point to render. */ - virtual bool renderRawPoints(CMaterial& mat, uint32 startVertex, uint32 numPoints)=0; + virtual bool renderRawPoints(CMaterial &mat, uint32 startVertex, uint32 numPoints) = 0; /** Render lines with previously setuped VertexBuffer / Matrixes. * Lines are stored as a sequence in the vertex buffer. @@ -507,7 +661,7 @@ public: * \param startVertex is the first vertex to use during this rendering. * \param numLine is the number of line to render. */ - virtual bool renderRawLines(CMaterial& mat, uint32 startVertex, uint32 numTri)=0; + virtual bool renderRawLines(CMaterial &mat, uint32 startVertex, uint32 numTri) = 0; /** Render triangles with previously setuped VertexBuffer / Matrixes. * Triangles are stored as a sequence in the vertex buffer. @@ -515,15 +669,15 @@ public: * \param startVertex is the first vertex to use during this rendering. * \param numTri is the number of triangle to render. */ - virtual bool renderRawTriangles(CMaterial& mat, uint32 startVertex, uint32 numTri)=0; + virtual bool renderRawTriangles(CMaterial &mat, uint32 startVertex, uint32 numTri) = 0; /** If the driver support it, primitive can be rendered with an offset added to each index * These are the offseted version of the 'render' functions * \see supportIndexOffset */ - virtual bool renderLinesWithIndexOffset(CMaterial& mat, uint32 firstIndex, uint32 nlines, uint indexOffset)=0; - virtual bool renderTrianglesWithIndexOffset(CMaterial& mat, uint32 firstIndex, uint32 ntris, uint indexOffset)=0; - virtual bool renderSimpleTrianglesWithIndexOffset(uint32 firstIndex, uint32 ntris, uint indexOffset)=0; + virtual bool renderLinesWithIndexOffset(CMaterial &mat, uint32 firstIndex, uint32 nlines, uint indexOffset) = 0; + virtual bool renderTrianglesWithIndexOffset(CMaterial &mat, uint32 firstIndex, uint32 ntris, uint indexOffset) = 0; + virtual bool renderSimpleTrianglesWithIndexOffset(uint32 firstIndex, uint32 ntris, uint indexOffset) = 0; /** render quads with previously setuped VertexBuffer / Matrixes. @@ -540,8 +694,13 @@ public: * \param startVertex is the first vertex to use during this rendering. * \param numQuad is the number of quad to render. */ - virtual bool renderRawQuads(CMaterial& mat, uint32 startVertex, uint32 numQuads)=0; + virtual bool renderRawQuads(CMaterial &mat, uint32 startVertex, uint32 numQuads) = 0; + // @} + + + /// \name Texture coordinates fixed pipeline + // @{ /** Say what Texture Stage use what UV coord. * by default activeVertexBuffer*() methods map all stage i to UV i. You can change this behavior, * after calling activeVertexBuffer*(), by using this method. @@ -553,63 +712,58 @@ public: * Warning!: some CMaterial Shader may change automatically this behavior too when setupMaterial() * (and so render*()) is called. But Normal shader doesn't do it. */ - virtual void mapTextureStageToUV(uint stage, uint uv)=0; + virtual void mapTextureStageToUV(uint stage, uint uv) = 0; + // @} + + + /// \name Buffer swapping + // @{ /// Swap the back and front buffers. - virtual bool swapBuffers(void)=0; - - /// Copy a string to system clipboard. - virtual bool copyTextToClipboard(const ucstring &text) =0; - - /// Paste a string from system clipboard. - virtual bool pasteTextFromClipboard(ucstring &text) =0; + virtual bool swapBuffers() = 0; /** set the number of VBL wait when a swapBuffers() is issued. 0 means no synchronisation to the VBL * Default is 1. Values >1 may be clamped to 1 by the driver. */ - virtual void setSwapVBLInterval(uint interval)=0; + virtual void setSwapVBLInterval(uint interval) = 0; /// get the number of VBL wait when a swapBuffers() is issued. 0 means no synchronisation to the VBL - virtual uint getSwapVBLInterval()=0; + virtual uint getSwapVBLInterval() = 0; + // @} + + + /// \name Profiling. // @{ - - /** Get the number of primitives rendered from the last swapBuffers() call. * \param pIn the number of requested rendered primitive. * \param pOut the number of effective rendered primitive. pOut==pIn if no multi-pass material is used * (Lightmap, Specular ...). */ - virtual void profileRenderedPrimitives(CPrimitiveProfile &pIn, CPrimitiveProfile &pOut) =0; - + virtual void profileRenderedPrimitives(CPrimitiveProfile &pIn, CPrimitiveProfile &pOut) = 0; /** Return the amount of Texture memory requested. taking mipmap, compression, texture format, etc... into account. * NB: because of GeForce*, RGB888 is considered to be 32 bits. So it may be false for others cards :). */ - virtual uint32 profileAllocatedTextureMemory() =0; - + virtual uint32 profileAllocatedTextureMemory() = 0; /** Get the number of material setuped from the last swapBuffers() call. */ - virtual uint32 profileSetupedMaterials() const =0; - + virtual uint32 profileSetupedMaterials() const = 0; /** Get the number of matrix setuped from the last swapBuffers() call. */ - virtual uint32 profileSetupedModelMatrix() const =0; - + virtual uint32 profileSetupedModelMatrix() const = 0; /** Enable the sum of texture memory used since last swapBuffers() call. To retrieve the memory used call getUsedTextureMemory(). */ - virtual void enableUsedTextureMemorySum (bool enable=true) =0; - + virtual void enableUsedTextureMemorySum (bool enable = true) = 0; /** Return the amount of texture video memory used since last swapBuffers() call. Before use this method, you should enable * the sum with enableUsedTextureMemorySum(). */ - virtual uint32 getUsedTextureMemory() const =0; - + virtual uint32 getUsedTextureMemory() const = 0; /** If the driver support it, enable profile VBHard locks. * No-Op if already profiling @@ -638,179 +792,126 @@ public: /** For each texture setuped in the driver, "print" result: type, shareName, format and size (mipmap included) */ void profileTextureUsage(std::vector &result); - // @} + /// \name Fog support. // @{ - virtual bool fogEnabled()=0; - virtual void enableFog(bool enable)=0; + virtual bool fogEnabled() = 0; + virtual void enableFog(bool enable = true) = 0; /// setup fog parameters. fog must enabled to see result. start and end are distance values. - virtual void setupFog(float start, float end, CRGBA color)=0; + virtual void setupFog(float start, float end, NLMISC::CRGBA color) = 0; /// Get. - virtual float getFogStart() const =0; - virtual float getFogEnd() const =0; - virtual CRGBA getFogColor() const =0; + virtual float getFogStart() const = 0; + virtual float getFogEnd() const = 0; + virtual NLMISC::CRGBA getFogColor() const = 0; // @} - /// Deriver should calls IDriver::release() first, to destroy all driver components (textures, shaders, VBuffers). - virtual bool release(void); - /// Return true if driver is still active. Return false else. If he user close the window, must return false. - virtual bool isActive()=0; - - /// Return the depth of the driver after init(). - virtual uint8 getBitPerPixel ()=0; - - /** Output a system message box and print a message with an icon. This method can be call even if the driver is not initialized. - * This method is used to return internal driver problem when string can't be displayed in the driver window. - * If the driver can't open a messageBox, it should not override this method and let the IDriver class manage it with the ASCII console. - * - * \param message This is the message to display in the message box. - * \param title This is the title of the message box. - * \param type This is the type of the message box, ie number of button and label of buttons. - * \param icon This is the icon of the message box should use like warning, error etc... - */ - virtual TMessageBoxId systemMessageBox (const char* message, const char* title, TMessageBoxType type=okType, TMessageBoxIcon icon=noIcon); + /// \name Viewport + // @{ /** Set the current viewport * * \param viewport is a viewport to setup as current viewport. */ - virtual void setupViewport (const class CViewport& viewport)=0; + virtual void setupViewport(const class CViewport &viewport) = 0; /** Get the current viewport */ virtual void getViewport(CViewport &viewport) = 0; - /** Set the current Scissor. * \param scissor is a scissor to setup the current Scissor, in Window relative coordinate (0,1). */ - virtual void setupScissor (const class CScissor& scissor)=0; + virtual void setupScissor(const class CScissor &scissor) = 0; + // @} + + /// \name Driver information + // @{ /** * Get the driver version. Not the same than interface version. Incremented at each implementation change. * * \see InterfaceVersion */ - virtual uint32 getImplementationVersion () const = 0; + virtual uint32 getImplementationVersion() const = 0; /** * Get driver information. * get the nel name of the driver (ex: "Opengl 1.2 NeL Driver") */ - virtual const char* getDriverInformation () = 0; + virtual const char *getDriverInformation() = 0; /** * Get videocard information. * get the official name of the driver */ - virtual const char* getVideocardInformation () = 0; + virtual const char *getVideocardInformation () = 0; + // @} + + /// \name Mouse / Keyboard / Game devices // @{ - /// show cursor if b is true, or hide it if b is false - virtual void showCursor (bool b) = 0; + /// show cursor if b is true, or hide it if b is false + virtual void showCursor(bool b) = 0; - /// x and y must be between 0.0 and 1.0 - virtual void setMousePos (float x, float y) = 0; + /// x and y must be between 0.0 and 1.0 + virtual void setMousePos(float x, float y) = 0; - /** Enable / disable low level mouse. This allow to take advantage of some options (speed of the mouse, automatic wrapping) - * It returns a interface to these parameters when it is supported, or NULL otherwise - * The interface pointer is valid as long as the low level mouse is enabled. - * A call to disable the mouse returns NULL, and restore the default mouse behavior - * NB : - In this mode the mouse cursor isn't drawn. - * - Calls to showCursor have no effects - * - Calls to setCapture have no effects - */ - virtual NLMISC::IMouseDevice *enableLowLevelMouse(bool enable, bool exclusive) = 0; + /** Enable / disable low level mouse. This allow to take advantage of some options (speed of the mouse, automatic wrapping) + * It returns a interface to these parameters when it is supported, or NULL otherwise + * The interface pointer is valid as long as the low level mouse is enabled. + * A call to disable the mouse returns NULL, and restore the default mouse behavior + * NB : - In this mode the mouse cursor isn't drawn. + * - Calls to showCursor have no effects + * - Calls to setCapture have no effects + */ + virtual NLMISC::IMouseDevice *enableLowLevelMouse(bool enable, bool exclusive) = 0; - /** Enable / disable a low level keyboard. - * Such a keyboard can only send KeyDown and KeyUp event. It just consider the keyboard as a - * gamepad with lots of buttons... - * This returns a interface to some parameters when it is supported, or NULL otherwise. - * The interface pointer is valid as long as the low level keyboard is enabled. - * A call to disable the keyboard returns NULL, and restore the default keyboard behavior - */ - virtual NLMISC::IKeyboardDevice *enableLowLevelKeyboard(bool enable) = 0; + /** Enable / disable a low level keyboard. + * Such a keyboard can only send KeyDown and KeyUp event. It just consider the keyboard as a + * gamepad with lots of buttons... + * This returns a interface to some parameters when it is supported, or NULL otherwise. + * The interface pointer is valid as long as the low level keyboard is enabled. + * A call to disable the keyboard returns NULL, and restore the default keyboard behavior + */ + virtual NLMISC::IKeyboardDevice *enableLowLevelKeyboard(bool enable) = 0; - /** Get the delay in ms for mouse double clicks. - */ - virtual uint getDoubleClickDelay(bool hardwareMouse) = 0; + /** Get the delay in ms for mouse double clicks. + */ + virtual uint getDoubleClickDelay(bool hardwareMouse) = 0; - /** If true, capture the mouse to force it to stay under the window. - * NB : this has no effects if a low level mouse is used - */ - virtual void setCapture (bool b) = 0; + /** If true, capture the mouse to force it to stay under the window. + * NB : this has no effects if a low level mouse is used + */ + virtual void setCapture(bool b) = 0; - // see if system cursor is currently captured - virtual bool isSystemCursorCaptured() = 0; + // see if system cursor is currently captured + virtual bool isSystemCursorCaptured() = 0; - // Add a new cursor (name is case unsensitive) - virtual void addCursor(const std::string &name, const NLMISC::CBitmap &bitmap) = 0; + // Add a new cursor (name is case unsensitive) + virtual void addCursor(const std::string &name, const NLMISC::CBitmap &bitmap) = 0; - // Display a cursor from its name (case unsensitive) - virtual void setCursor(const std::string &name, NLMISC::CRGBA col, uint8 rot, sint hotSpotX, sint hotSpotY, bool forceRebuild = false) = 0; + // Display a cursor from its name (case unsensitive) + virtual void setCursor(const std::string &name, NLMISC::CRGBA col, uint8 rot, sint hotSpotX, sint hotSpotY, bool forceRebuild = false) = 0; - // Change default scale for all cursors - virtual void setCursorScale(float scale) = 0; + // Change default scale for all cursors + virtual void setCursorScale(float scale) = 0; - /** Check whether there is a low level device manager available, and get its interface. Return NULL if not available - * From this interface you can deal with mouse and keyboard as above, but you can also manage game device (joysticks, joypads ...) - */ - virtual NLMISC::IInputDeviceManager *getLowLevelInputDeviceManager() = 0; + /** Check whether there is a low level device manager available, and get its interface. Return NULL if not available + * From this interface you can deal with mouse and keyboard as above, but you can also manage game device (joysticks, joypads ...) + */ + virtual NLMISC::IInputDeviceManager *getLowLevelInputDeviceManager() = 0; // @} - /// Get the width and the height of the window - virtual void getWindowSize (uint32 &width, uint32 &height) = 0; - - /// Get the position of the window always (0,0) in fullscreen - virtual void getWindowPos (sint32 &x, sint32 &y) = 0; - - /** get the RGBA back buffer. After swapBuffers(), the content of the back buffer is undefined. - * - * \param bitmap the buffer will be written in this bitmap - */ - virtual void getBuffer (CBitmap &bitmap) = 0; - - /** get the ZBuffer (back buffer). - * - * \param zbuffer the returned array of Z. size of getWindowSize() . - */ - virtual void getZBuffer (std::vector &zbuffer) = 0; - - /** get a part of the RGBA back buffer. After swapBuffers(), the content of the back buffer is undefined. - * NB: 0,0 is the bottom left corner of the screen. - * - * \param bitmap the buffer will be written in this bitmap - * \param rect the in/out (wanted/clipped) part of Color buffer to retrieve. - */ - virtual void getBufferPart (CBitmap &bitmap, NLMISC::CRect &rect) = 0; - - // copy the first texture in a second one of different dimensions - virtual bool stretchRect (ITexture * srcText, NLMISC::CRect &srcRect, ITexture * destText, NLMISC::CRect &destRect) = 0; - - // is this texture a rectangle texture ? - virtual bool isTextureRectangle(ITexture * tex) const = 0; - - // return true if driver support Bloom effect. - virtual bool supportBloomEffect() const =0; - - // return true if driver support non-power of two textures - virtual bool supportNonPowerOfTwoTextures() const =0; - - /** get a part of the ZBuffer (back buffer). - * NB: 0,0 is the bottom left corner of the screen. - * - * \param zbuffer the returned array of Z. size of rec.Width*rec.Height. - * \param rect the in/out (wanted/clipped) part of ZBuffer to retrieve. - */ - virtual void getZBufferPart (std::vector &zbuffer, NLMISC::CRect &rect) = 0; + /// \name Render target // TODO: Handle Color/ZBuffer/Stencil consistently + // @{ /** Set the current render target. * * The render target can be a texture (tex pointer) or the back buffer (tex = NULL). @@ -843,17 +944,23 @@ public: * \param cubaFace the face of the cube to copy texture to. * \return true if the render target has been changed */ - virtual bool setRenderTarget (ITexture *tex, - uint32 x = 0, - uint32 y = 0, - uint32 width = 0, - uint32 height = 0, - uint32 mipmapLevel = 0, - uint32 cubeFace = 0 - ) = 0 ; + virtual bool setRenderTarget( ITexture *tex, + uint32 x = 0, + uint32 y = 0, + uint32 width = 0, + uint32 height = 0, + uint32 mipmapLevel = 0, + uint32 cubeFace = 0 + ) = 0; virtual ITexture *getRenderTarget() const = 0; + /** Retrieve the render target size. + * If the render target is the frame buffer, it returns the size of the frame buffer. + * It the render target is a texture, it returns the size of the texture mipmap selected as render target. + */ + virtual bool getRenderTargetSize (uint32 &width, uint32 &height) = 0; + /** Trick method : copy the current texture target into another texture without updating the current texture. * * This method copies the current texture into another texture. @@ -880,30 +987,21 @@ public: * \param height height of the renderable area to copy, if 0, use the whole size. * \param mipmapLevel the mipmap to copy texture to. */ - virtual bool copyTargetToTexture (ITexture *tex, - uint32 offsetx = 0, - uint32 offsety = 0, - uint32 x = 0, - uint32 y = 0, - uint32 width = 0, - uint32 height = 0, - uint32 mipmapLevel = 0 + virtual bool copyTargetToTexture( ITexture *tex, + uint32 offsetx = 0, + uint32 offsety = 0, + uint32 x = 0, + uint32 y = 0, + uint32 width = 0, + uint32 height = 0, + uint32 mipmapLevel = 0 ) = 0; - - /** Retrieve the render target size. - * If the render target is the frame buffer, it returns the size of the frame buffer. - * It the render target is a texture, it returns the size of the texture mipmap selected as render target. - */ - virtual bool getRenderTargetSize (uint32 &width, uint32 &height) = 0; - - /** fill the RGBA back buffer - * - * \param bitmap will be written in the buffer. no-op if bad size. - * \return true if success - */ - virtual bool fillBuffer (CBitmap &bitmap) = 0; + // @} + + /// \name Render state: Polygon mode + // @{ /** Set the global polygon mode. Can be filled, line or point. The implementation driver must * call IDriver::setPolygonMode and active this mode. * @@ -915,13 +1013,27 @@ public: _PolygonMode=mode; } + /** Get the global polygon mode. + * + * \param polygon mode choose in this driver. + * \see setPolygonMode(), TPolygonMode + */ + TPolygonMode getPolygonMode () + { + return _PolygonMode; + } + // @} + + + /// \name Fixed pipeline lights + // @{ /** * return the number of light supported by driver. typically 8. * * \see enableLight() setLight() */ - virtual uint getMaxLight () const = 0; + virtual uint getMaxLight() const = 0; /** * Setup a light. @@ -932,7 +1044,7 @@ public: * \param light is a light to set in this slot. * \see enableLight() */ - virtual void setLight (uint8 num, const CLight& light) = 0; + virtual void setLight(uint8 num, const CLight &light) = 0; /** * Enable / disable light. @@ -943,7 +1055,7 @@ public: * \param enable is true to enable the light, false to disable it. * \see setLight() */ - virtual void enableLight (uint8 num, bool enable=true) = 0; + virtual void enableLight(uint8 num, bool enable = true) = 0; /** * Set ambient. @@ -951,112 +1063,134 @@ public: * \param color is the new global ambient color for the scene. * \see setLight(), enableLight() */ - virtual void setAmbientColor (CRGBA color) = 0; + virtual void setAmbientColor(NLMISC::CRGBA color) = 0; /** Setup the light used for per pixel lighting. The given values should have been modulated by the material diffuse and specular. * This is only useful for material that have their shader set as 'PerPixelLighting' * \param the light used for per pixel lighting */ - virtual void setPerPixelLightingLight(CRGBA diffuse, CRGBA specular, float shininess) = 0; + virtual void setPerPixelLightingLight(NLMISC::CRGBA diffuse, NLMISC::CRGBA specular, float shininess) = 0; /** Setup the unique light used for Lightmap Shader. * Lightmaped primitives are lit per vertex with this light (should be local attenuated for maximum efficiency) * This is only useful for material that have their shader set as 'LightMap' * \param the light used for per pixel lighting */ - virtual void setLightMapDynamicLight (bool enable, const CLight& light) = 0; + virtual void setLightMapDynamicLight(bool enable, const CLight &light) = 0; + // @} - /** Get the global polygon mode. - * - * \param polygon mode choose in this driver. - * \see setPolygonMode(), TPolygonMode - */ - TPolygonMode getPolygonMode () - { - return _PolygonMode; - } - /// \name Vertex program interface + + /// \name Vertex Program // @{ - enum TMatrix - { - ModelView= 0, - Projection, - ModelViewProjection, - NumMatrix - }; - - enum TTransform - { - Identity=0, - Inverse, - Transpose, - InverseTranspose, - NumTransform - }; - - /** - * Does the driver supports vertex programs ? - */ - virtual bool supportVertexProgram () const =0; + // Order of preference + // - activeVertexProgram + // - CMaterial pass[n] VP (uses activeVertexProgram, but does not override if one already set by code) + // - default generic VP that mimics fixed pipeline / no VP with fixed pipeline /** * Does the driver supports vertex program, but emulated by CPU ? */ - virtual bool isVertexProgramEmulated () const =0; + virtual bool isVertexProgramEmulated() const = 0; - /** - * Does the driver supports pixel programs ? + /** Return true if the driver supports the specified vertex program profile. */ - virtual bool supportPixelProgram() const =0; - virtual bool supportPixelProgram(CPixelProgram::TProfile profile) const =0; + virtual bool supportVertexProgram(CVertexProgram::TProfile profile = CVertexProgram::nelvp) const = 0; - - - /** - * Activate / disactivate a vertex program - * - * \param program is a pointer on a vertex program. Can be NULL to disable the current vertex program. - * - * \return true if setup/unsetup succeeded, false else. + /** Compile the given vertex program, return if successful. Error information is returned as a string. */ - virtual bool activeVertexProgram (CVertexProgram *program) =0; + virtual bool compileVertexProgram(CVertexProgram *program, std::string &error) const = 0; - /** - * Activate / disactivate a pixel program - * - * \param program is a pointer on a pixel program. Can be NULL to disable the current pixel program. - * - * \return true if setup/unsetup successed, false else. - */ - virtual bool activePixelProgram (CPixelProgram *program) =0; - - /** - * Setup vertex program constant (uniform) values. + /** Set the active vertex program. This will override vertex programs specified in CMaterial render calls. + * Also used internally by setupMaterial(CMaterial) when getVertexProgram returns NULL. + * The vertex program is activated immediately. */ - virtual void setConstant (uint index, float, float, float, float) =0; - virtual void setConstant (uint index, double, double, double, double) =0; - virtual void setConstant (uint index, const NLMISC::CVector& value) =0; - virtual void setConstant (uint index, const NLMISC::CVectorD& value) =0; + virtual bool activeVertexProgram(CVertexProgram *program) = 0; + + /** Get the currently active vertex program. + */ + virtual CVertexProgram *getActiveVertexProgram() const = 0; + + // Set parameters + virtual void setVertexProgram1f(uint index, float f0) = 0; + virtual void setVertexProgram2f(uint index, float f0, float f1) = 0; + virtual void setVertexProgram3f(uint index, float f0, float f1, float f2) = 0; + virtual void setVertexProgram4f(uint index, float f0, float f1, float f2, float f3) = 0; + virtual void setVertexProgram1i(uint index, sint32 i0) = 0; + virtual void setVertexProgram2i(uint index, sint32 i0, sint32 i1) = 0; + virtual void setVertexProgram3i(uint index, sint32 i0, sint32 i1, sint32 i2) = 0; + virtual void setVertexProgram4i(uint index, sint32 i0, sint32 i1, sint32 i2, sint32 i3) = 0; + virtual void setVertexProgram3f(uint index, const NLMISC::CVector& v) = 0; + virtual void setVertexProgram4f(uint index, const NLMISC::CVector& v, float f3) = 0; + virtual void setVertexProgram4x4f(uint index, const NLMISC::CMatrix& m) = 0; + virtual void setVertexProgram1fv(uint index, size_t num, const float *src) = 0; + virtual void setVertexProgram2fv(uint index, size_t num, const float *src) = 0; + virtual void setVertexProgram3fv(uint index, size_t num, const float *src) = 0; + virtual void setVertexProgram4fv(uint index, size_t num, const float *src) = 0; + // @} + + + + /// \name Pixel Program + // @{ + + // Order of preference + // - activePixelProgram + // - CMaterial pass[n] PP (uses activePixelProgram, but does not override if one already set by code) + // - PP generated from CMaterial (uses activePixelProgram, but does not override if one already set by code) + + /** Return true if the driver supports the specified pixel program profile. + */ + virtual bool supportPixelProgram(CPixelProgram::TProfile profile = CPixelProgram::nelvp) const = 0; + + /** Compile the given pixel program, return if successful. Error information is returned as a string. + */ + virtual bool compilePixelProgram(CPixelProgram *program, std::string &error) const = 0; + + /** Set the active pixel program. This will override pixel programs specified in CMaterial render calls. + * Also used internally by setupMaterial(CMaterial) when getPixelProgram returns NULL. + * The pixel program is activated immediately. + */ + virtual bool activePixelProgram(CPixelProgram *program) = 0; + + /** Get the currently active pixel program. + */ + virtual CPixelProgram *getActivePixelProgram() const = 0; + + // Set parameters + virtual void setPixelProgram1f(uint index, float f0) = 0; + virtual void setPixelProgram2f(uint index, float f0, float f1) = 0; + virtual void setPixelProgram3f(uint index, float f0, float f1, float f2) = 0; + virtual void setPixelProgram4f(uint index, float f0, float f1, float f2, float f3) = 0; + virtual void setPixelProgram1i(uint index, sint32 i0) = 0; + virtual void setPixelProgram2i(uint index, sint32 i0, sint32 i1) = 0; + virtual void setPixelProgram3i(uint index, sint32 i0, sint32 i1, sint32 i2) = 0; + virtual void setPixelProgram4i(uint index, sint32 i0, sint32 i1, sint32 i2, sint32 i3) = 0; + virtual void setPixelProgram3f(uint index, const NLMISC::CVector& v) = 0; + virtual void setPixelProgram4f(uint index, const NLMISC::CVector& v, float f3) = 0; + virtual void setPixelProgram4x4f(uint index, const NLMISC::CMatrix& m) = 0; + virtual void setPixelProgram1fv(uint index, size_t num, const float *src) = 0; + virtual void setPixelProgram2fv(uint index, size_t num, const float *src) = 0; + virtual void setPixelProgram3fv(uint index, size_t num, const float *src) = 0; + virtual void setPixelProgram4fv(uint index, size_t num, const float *src) = 0; + // @} + + + + /// \name Legacy vertex program parameter setters + // @{ + /** + * Setup constant values. + */ + inline void setConstant(uint index, float f0, float f1, float f2, float f3) { setVertexProgram4f(index, f0, f1, f2, f3); } + inline void setConstant(uint index, double d0, double d1, double d2, double d3) { setVertexProgram4f(index, (float)d0, (float)d1, (float)d2, (float)d3); } + inline void setConstant(uint index, const NLMISC::CVector &value) { setVertexProgram4f(index, value, 0.f); } + inline void setConstant(uint index, const NLMISC::CVectorD &value) { setVertexProgram4f(index, (float)value.x, (float)value.y, (float)value.z, 0.f); } /// setup several 4 float csts taken from the given tab - virtual void setConstant (uint index, uint num, const float *src) =0; - /// setup several 4 double csts taken from the given tab - virtual void setConstant (uint index, uint num, const double *src) =0; - // TODO: rename to setVertexProgramConstant - - /** - * Setup pixel program constant (uniform) values. - */ - virtual void setPixelProgramConstant (uint index, float, float, float, float) =0; - virtual void setPixelProgramConstant (uint index, double, double, double, double) =0; - virtual void setPixelProgramConstant (uint index, const NLMISC::CVector& value) =0; - virtual void setPixelProgramConstant (uint index, const NLMISC::CVectorD& value) =0; - /// setup several 4 float csts taken from the given tab - virtual void setPixelProgramConstant (uint index, uint num, const float *src) =0; - /// setup several 4 double csts taken from the given tab - virtual void setPixelProgramConstant (uint index, uint num, const double *src) =0; - // TODO: uint32 and sint32 uniform types supported in opengl from gp4fp and gp5fp and sint32 in d3d + inline void setConstant(uint index, uint num, const float *src) { setVertexProgram4fv(index, num, src); } + /// setup several 4 double csts taken from the given tab (convert to float) + virtual void setConstant(uint index, uint num, const double *src) = 0; /** * Setup constants with a current matrix. @@ -1069,20 +1203,7 @@ public: * \param transform is the transformation to apply to the matrix before store it in the constants. * */ - virtual void setPixelProgramConstantMatrix (uint index, TMatrix matrix, TTransform transform) =0; - - /** - * Setup constants with a current matrix. - * - * This call must be done after setFrustum(), setupViewMatrix() or setupModelMatrix() to get correct - * results. - * - * \param index is the base constant index where to store the matrix. This index must be a multiple of 4. - * \param matrix is the matrix id to store in the constants - * \param transform is the transformation to apply to the matrix before store it in the constants. - * - */ - virtual void setConstantMatrix (uint index, TMatrix matrix, TTransform transform) =0; + virtual void setConstantMatrix(uint index, TMatrix matrix, TTransform transform) = 0; /** * Setup the constant with the fog vector. This vector must be used to get the final fog value in a vertex shader. @@ -1096,14 +1217,26 @@ public: * \param index is the index where to store the vector. * */ - virtual void setConstantFog (uint index) =0; + virtual void setConstantFog(uint index) = 0; + // @} - /// Check if the driver support double sided colors vertex programs - virtual bool supportVertexProgramDoubleSidedColor() const = 0; + + /// \name Legacy effects + // @{ // test if support for cloud render in a single pass virtual bool supportCloudRenderSinglePass() const = 0; + // [FIXME] Return true if driver support Bloom effect // FIXME: This is terrible + virtual bool supportBloomEffect() const = 0; + // @} + + + + /// \name Backface color + // @{ + /// Check if the driver support double sided colors vertex programs + virtual bool supportVertexProgramDoubleSidedColor() const = 0; /** * Activate VertexProgram 2Sided Color mode. In 2Sided mode, the BackFace (if material 2Sided enabled) read the * result from o[BFC0], and not o[COL0]. @@ -1111,27 +1244,29 @@ public: * NB: no-op if not supported by driver */ virtual void enableVertexProgramDoubleSidedColor(bool doubleSided) =0; - // @} + + /// \name Texture addressing modes aka textures/pixels shaders // @{ - /// test whether the device supports some form of texture shader. (could be limited to DX6 EMBM for example) - virtual bool supportTextureShaders() const = 0; - // Is the shader water supported ? If not, the driver caller should implement its own version - virtual bool supportWaterShader() const = 0; - // - /// test whether a texture addressing mode is supported - virtual bool supportTextureAddrMode(CMaterial::TTexAddressingMode mode) const = 0; - /** setup the 2D matrix for the OffsetTexture, OffsetTextureScale and OffsetTexture addressing mode - * It should be stored as the following - * [a0 a1] - * [a2 a3] - */ - virtual void setMatrix2DForTextureOffsetAddrMode(const uint stage, const float mat[4]) = 0; + /// test whether the device supports some form of texture shader. (could be limited to DX6 EMBM for example) + virtual bool supportTextureShaders() const = 0; + // Is the shader water supported ? If not, the driver caller should implement its own version + virtual bool supportWaterShader() const = 0; + // + /// test whether a texture addressing mode is supported + virtual bool supportTextureAddrMode(CMaterial::TTexAddressingMode mode) const = 0; + /** setup the 2D matrix for the OffsetTexture, OffsetTextureScale and OffsetTexture addressing mode + * It should be stored as the following + * [a0 a1] + * [a2 a3] + */ + virtual void setMatrix2DForTextureOffsetAddrMode(const uint stage, const float mat[4]) = 0; //@} + /** \name EMBM support. If texture shaders are present, this is not available, must use them instead. * EMBM is a color op of CMaterial. * NB : EMBM is the equivalent of the CMaterial::OffsetTexture addressing mode. However, it is both a texture @@ -1143,38 +1278,35 @@ public: */ // @{ - // Test if EMBM is supported. - virtual bool supportEMBM() const = 0; - // Test if EMBM is supported for the given stage - virtual bool isEMBMSupportedAtStage(uint stage) const = 0; - // set the matrix used for EMBM addressing - virtual void setEMBMMatrix(const uint stage, const float mat[4]) = 0; + // Test if EMBM is supported. + virtual bool supportEMBM() const = 0; + // Test if EMBM is supported for the given stage + virtual bool isEMBMSupportedAtStage(uint stage) const = 0; + // set the matrix used for EMBM addressing + virtual void setEMBMMatrix(const uint stage, const float mat[4]) = 0; // @} - // Does the driver support the per-pixel lighting shader ? - virtual bool supportPerPixelLighting(bool specular) const = 0; /// \name Misc // @{ - /** Does the driver support Blend Constant Color ??? If yes CMaterial::blendConstant* enum can be used * for blend Src ord Dst factor. If no, using these enum will have undefined results. */ - virtual bool supportBlendConstantColor() const =0; + virtual bool supportBlendConstantColor() const = 0; /** see supportBlendConstantColor(). Set the current Blend Constant Color. */ - virtual void setBlendConstantColor(NLMISC::CRGBA col)=0; + virtual void setBlendConstantColor(NLMISC::CRGBA col) = 0; /** see supportBlendConstantColor(). Get the current Blend Constant Color. */ - virtual NLMISC::CRGBA getBlendConstantColor() const =0; + virtual NLMISC::CRGBA getBlendConstantColor() const = 0; /** force the driver to flush all command. glFinish() in opengl. * Interesting only for debug and profiling purpose. */ - virtual void finish() =0; + virtual void finish() = 0; // Flush command queue an immediately returns virtual void flush() = 0; @@ -1183,27 +1315,27 @@ public: * See GL_POLYGON_SMOOTH help, and GL_SRC_ALPHA_SATURATE OpenGL doc (not yet implemented now since * used only for alpha part in ShadowMap gen) */ - virtual void enablePolygonSmoothing(bool smooth) =0; + virtual void enablePolygonSmoothing(bool smooth) = 0; /// see enablePolygonSmoothing() - virtual bool isPolygonSmoothingEnabled() const =0; - + virtual bool isPolygonSmoothingEnabled() const = 0; // @} + /** Special method to internally swap the Driver handle of 2 textures. * USE IT WITH CARE (eg: may have Size problems, mipmap problems, format problems ...) * Actually, it is used only by CAsyncTextureManager, to manage Lods of DXTC CTextureFile. * NB: internally, all textures slots are disabled. */ - virtual void swapTextureHandle(ITexture &tex0, ITexture &tex1) =0; + virtual void swapTextureHandle(ITexture &tex0, ITexture &tex1) = 0; /** Advanced usage. Get the texture Handle.Useful for texture sorting for instance * NB: if the texture is not setuped in the driver, 0 is returned. * NB: if implementation does not support it, 0 may be returned. OpenGL ones return the Texture ID. * NB: unlike isTextureExist(), this method is not thread safe. */ - virtual uint getTextureHandle(const ITexture&tex)=0; + virtual uint getTextureHandle(const ITexture&tex) = 0; // see if the Multiply-Add Tex Env operator is supported (see CMaterial::Mad) virtual bool supportMADOperator() const = 0; @@ -1224,16 +1356,16 @@ public: }; // Get the number of hardware renderer available on the client platform. - virtual uint getNumAdapter() const=0; + virtual uint getNumAdapter() const = 0; // Get a hardware renderer description. - virtual bool getAdapter(uint adapter, CAdapter &desc) const=0; + virtual bool getAdapter(uint adapter, CAdapter &desc) const = 0; /** Choose the hardware renderer. * Call it before the setDisplay and enumModes methods * Choose adapter = 0xffffffff for the default one. */ - virtual bool setAdapter(uint adapter)=0; + virtual bool setAdapter(uint adapter) = 0; /** Tell if the vertex color memory format is RGBA (openGL) or BGRA (directx) * BGRA : @@ -1247,20 +1379,22 @@ public: */ virtual CVertexBuffer::TVertexColorType getVertexColorFormat() const =0; + + /// \name Bench // @{ - // Start the bench. See CHTimer::startBench(); - virtual void startBench (bool wantStandardDeviation = false, bool quick = false, bool reset = true) =0; + virtual void startBench(bool wantStandardDeviation = false, bool quick = false, bool reset = true) =0; // End the bench. See CHTimer::endBench(); - virtual void endBench () =0; + virtual void endBench () =0; // Display the bench result - virtual void displayBench (class NLMISC::CLog *log) =0; - + virtual void displayBench (class NLMISC::CLog *log) =0; // @} + + /// \name Occlusion query mechanism // @{ // Test whether this device supports the occlusion query mechanism @@ -1273,8 +1407,8 @@ public: virtual void deleteOcclusionQuery(IOcclusionQuery *oq) = 0; // @} - // get the number of call to swapBuffer since the driver was created - virtual uint64 getSwapBufferCounter() const = 0; + + /** Set cull mode * Useful for mirrors / cube map rendering or when the scene must be rendered upside down @@ -1291,23 +1425,25 @@ public: virtual void stencilMask(uint mask) = 0; protected: - friend class IVBDrvInfos; - friend class IIBDrvInfos; - friend class CTextureDrvShare; - friend class ITextureDrvInfos; - friend class IMaterialDrvInfos; - friend class IGPUProgramDrvInfos; + friend class IVBDrvInfos; + friend class IIBDrvInfos; + friend class CTextureDrvShare; + friend class ITextureDrvInfos; + friend class IMaterialDrvInfos; + friend class IGPUProgramDrvInfos; + friend class IGPUProgramParamsDrvInfos; /// remove ptr from the lists in the driver. - void removeVBDrvInfoPtr(ItVBDrvInfoPtrList vbDrvInfoIt); - void removeIBDrvInfoPtr(ItIBDrvInfoPtrList ibDrvInfoIt); - void removeTextureDrvInfoPtr(ItTexDrvInfoPtrMap texDrvInfoIt); - void removeTextureDrvSharePtr(ItTexDrvSharePtrList texDrvShareIt); - void removeMatDrvInfoPtr(ItMatDrvInfoPtrList shaderIt); - void removeGPUPrgDrvInfoPtr(ItGPUPrgDrvInfoPtrList gpuPrgDrvInfoIt); + void removeVBDrvInfoPtr(ItVBDrvInfoPtrList vbDrvInfoIt); + void removeIBDrvInfoPtr(ItIBDrvInfoPtrList ibDrvInfoIt); + void removeTextureDrvInfoPtr(ItTexDrvInfoPtrMap texDrvInfoIt); + void removeTextureDrvSharePtr(ItTexDrvSharePtrList texDrvShareIt); + void removeMatDrvInfoPtr(ItMatDrvInfoPtrList shaderIt); + void removeGPUPrgDrvInfoPtr(ItGPUPrgDrvInfoPtrList gpuPrgDrvInfoIt); private: - bool _StaticMemoryToVRAM; + bool _StaticMemoryToVRAM; + }; // -------------------------------------------------- diff --git a/code/nel/include/nel/3d/driver_user.h b/code/nel/include/nel/3d/driver_user.h index fff83d6d5..30ea98c65 100644 --- a/code/nel/include/nel/3d/driver_user.h +++ b/code/nel/include/nel/3d/driver_user.h @@ -474,7 +474,6 @@ public: virtual void forceDXTCCompression(bool dxtcComp); virtual void setAnisotropicFilter(sint filter); virtual void forceTextureResize(uint divisor); - virtual void forceNativeFragmentPrograms(bool nativeOnly); virtual bool setMonitorColorProperties (const CMonitorColorProperties &properties); // @} diff --git a/code/nel/include/nel/3d/gpu_program_params.h b/code/nel/include/nel/3d/gpu_program_params.h index d1a3125a2..08d8010d4 100644 --- a/code/nel/include/nel/3d/gpu_program_params.h +++ b/code/nel/include/nel/3d/gpu_program_params.h @@ -48,10 +48,10 @@ namespace NL3D { * \brief CGPUProgramParams * \date 2013-09-07 22:17GMT * \author Jan Boon (Kaetemi) - * A storage for user-provided parameters for GPU programs. + * A storage for USERCODE-PROVIDED parameters for GPU programs. * Allows for fast updating and iteration of parameters. * NOTE TO DRIVER IMPLEMENTORS: DO NOT USE FOR STORING COPIES - * OF HARDCODED MATERIAL PARAMETERS OR DRIVER PARAMETERS!!! + * OF HARDCODED DRIVER MATERIAL PARAMETERS OR DRIVER PARAMETERS!!! */ class CGPUProgramParams { @@ -66,36 +66,70 @@ public: CGPUProgramParams(); virtual ~CGPUProgramParams(); - void setF(uint index, float f0); - void setF(uint index, float f0, float f1); - void setF(uint index, float f0, float f1, float f2); - void setF(uint index, float f0, float f1, float f2, float f3); - void setI(uint index, int i0); - void setI(uint index, int i0, int i1); - void setI(uint index, int i0, int i1, int i2); - void setI(uint index, int i0, int i1, int i2, int i3); - void setF(uint index, const NLMISC::CVector& v); - void setF(uint index, const NLMISC::CMatrix& m); - void setF(uint index, uint num, const float *src); + // Copy from another params storage + void copy(CGPUProgramParams *params); + + // Set by index, available only when the associated program has been compiled + void set1f(uint index, float f0); + void set2f(uint index, float f0, float f1); + void set3f(uint index, float f0, float f1, float f2); + void set4f(uint index, float f0, float f1, float f2, float f3); + void set1i(uint index, sint32 i0); + void set2i(uint index, sint32 i0, sint32 i1); + void set3i(uint index, sint32 i0, sint32 i1, sint32 i2); + void set4i(uint index, sint32 i0, sint32 i1, sint32 i2, sint32 i3); + void set3f(uint index, const NLMISC::CVector& v); + void set4f(uint index, const NLMISC::CVector& v, float f3); + void set4x4f(uint index, const NLMISC::CMatrix& m); + void set1fv(uint index, size_t num, const float *src); + void set2fv(uint index, size_t num, const float *src); + void set3fv(uint index, size_t num, const float *src); + void set4fv(uint index, size_t num, const float *src); + void unset(uint index); + + // Set by name, it is recommended to use index when repeatedly setting an element + void set1f(const std::string &name, float f0); + void set2f(const std::string &name, float f0, float f1); + void set3f(const std::string &name, float f0, float f1, float f2); + void set4f(const std::string &name, float f0, float f1, float f2, float f3); + void set1i(const std::string &name, sint32 i0); + void set2i(const std::string &name, sint32 i0, sint32 i1); + void set3i(const std::string &name, sint32 i0, sint32 i1, sint32 i2); + void set4i(const std::string &name, sint32 i0, sint32 i1, sint32 i2, sint32 i3); + void set3f(const std::string &name, const NLMISC::CVector& v); + void set4f(const std::string &name, const NLMISC::CVector& v, float f3); + void set4x4f(const std::string &name, const NLMISC::CMatrix& m); + void set1fv(const std::string &name, size_t num, const float *src); + void set2fv(const std::string &name, size_t num, const float *src); + void set3fv(const std::string &name, size_t num, const float *src); + void set4fv(const std::string &name, size_t num, const float *src); + void unset(const std::string &name); + + /// Maps the given name to the given index, on duplicate entry the data set by name will be prefered as it can be assumed to have been set after the data set by index + void map(uint index, const std::string &name); // Internal /// Allocate specified number of components if necessary size_t allocOffset(uint index, uint count, TType type); + size_t allocOffset(const std::string &name, uint count, TType type); /// Return offset for specified index size_t getOffset(uint index) const; + size_t getOffset(const std::string &name) const; /// Remove by offset void freeOffset(size_t offset); - // Iteration + // Iteration (returns the offsets for access using getFooByOffset) size_t getBegin() const { return m_Meta.size() ? m_First : s_End; } size_t getNext(size_t offset) const { return m_Meta[offset].Next; } size_t getEnd() const { return s_End; } // Data access - uint getCountByOffset(size_t offset) { return m_Meta[offset].Count; } + uint getCountByOffset(size_t offset) { return m_Meta[offset].Count; } // component count (number of floats or ints) float *getPtrFByOffset(size_t offset) { return m_Vec[offset].F; } int *getPtrIByOffset(size_t offset) { return m_Vec[offset].I; } TType getTypeByOffset(size_t offset) { return m_Meta[offset].Type; } + uint getIndexByOffset(size_t offset) { return m_Meta[offset].Index; } + const std::string *getNameByOffset(size_t offset); // non-optimized for dev tools only, may return NULL if name unknown // Utility static inline uint getNbRegistersByComponents(uint count) { return (count + 3) >> 2; } // vector register per 4 components @@ -103,7 +137,8 @@ public: private: std::vector m_Vec; std::vector m_Meta; - std::vector m_Map; // map from index to buffer index + std::vector m_Map; // map from index to offset + std::map m_MapName; // map from name to offset size_t m_First; size_t m_Last; static const size_t s_End = -1; diff --git a/code/nel/include/nel/3d/u_driver.h b/code/nel/include/nel/3d/u_driver.h index 1397c9c6e..2e74ae3fe 100644 --- a/code/nel/include/nel/3d/u_driver.h +++ b/code/nel/include/nel/3d/u_driver.h @@ -673,13 +673,6 @@ public: */ virtual void forceTextureResize(uint divisor)=0; - /** Sets enforcement of native fragment programs. This is by default enabled. - * - * \param nativeOnly If set to false, fragment programs don't need to be native to stay loaded, - * otherwise (aka if true) they will be purged. - */ - virtual void forceNativeFragmentPrograms(bool nativeOnly) = 0; - /** Setup monitor color properties. * * Return false if setup failed. diff --git a/code/nel/src/3d/driver.cpp b/code/nel/src/3d/driver.cpp index 31dd4d2ec..2bfb0ea1f 100644 --- a/code/nel/src/3d/driver.cpp +++ b/code/nel/src/3d/driver.cpp @@ -240,24 +240,9 @@ void IDriver::removeMatDrvInfoPtr(ItMatDrvInfoPtrList shaderIt) _MatDrvInfos.erase(shaderIt); } // *************************************************************************** -/*void IDriver::removeShaderDrvInfoPtr(ItShaderDrvInfoPtrList shaderIt) +void IDriver::removeGPUPrgDrvInfoPtr(ItGPUPrgDrvInfoPtrList gpuPrgDrvInfoIt) { - _ShaderDrvInfos.erase(shaderIt); -} -// *************************************************************************** -void IDriver::removeVtxPrgDrvInfoPtr(ItVtxPrgDrvInfoPtrList vtxPrgDrvInfoIt) -{ - _VtxPrgDrvInfos.erase(vtxPrgDrvInfoIt); -} -// *************************************************************************** -void IDriver::removePixelPrgDrvInfoPtr(ItPixelPrgDrvInfoPtrList pixelPrgDrvInfoIt) -{ - _PixelPrgDrvInfos.erase(pixelPrgDrvInfoIt); -}*/ -// *************************************************************************** -void IDriver::removeGPUPrgDrvInfoPtr(ItGPUPrgDrvInfoPtrList vtxPrgDrvInfoIt) -{ - _GPUPrgDrvInfos.erase(vtxPrgDrvInfoIt); + _GPUPrgDrvInfos.erase(gpuPrgDrvInfoIt); } // *************************************************************************** diff --git a/code/nel/src/3d/driver/direct3d/driver_direct3d.h b/code/nel/src/3d/driver/direct3d/driver_direct3d.h index d329a6f56..3494812cc 100644 --- a/code/nel/src/3d/driver/direct3d/driver_direct3d.h +++ b/code/nel/src/3d/driver/direct3d/driver_direct3d.h @@ -880,7 +880,6 @@ public: virtual void forceDXTCCompression(bool dxtcComp); virtual void setAnisotropicFilter(sint filter); virtual void forceTextureResize(uint divisor); - virtual void forceNativeFragmentPrograms(bool /* nativeOnly */) {} // ignored // Driver information virtual uint getNumAdapter() const; diff --git a/code/nel/src/3d/driver/opengl/driver_opengl.cpp b/code/nel/src/3d/driver/opengl/driver_opengl.cpp index a93c05338..3eff7eebf 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl.cpp +++ b/code/nel/src/3d/driver/opengl/driver_opengl.cpp @@ -1993,12 +1993,6 @@ static void fetchPerturbedEnvMapR200() #endif } -// *************************************************************************** -void CDriverGL::forceNativeFragmentPrograms(bool nativeOnly) -{ - _ForceNativeFragmentPrograms = nativeOnly; -} - // *************************************************************************** void CDriverGL::initFragmentShaders() { diff --git a/code/nel/src/3d/driver/opengl/driver_opengl.h b/code/nel/src/3d/driver/opengl/driver_opengl.h index 29fd59957..89b71c931 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl.h +++ b/code/nel/src/3d/driver/opengl/driver_opengl.h @@ -370,8 +370,6 @@ public: virtual void forceTextureResize(uint divisor); - virtual void forceNativeFragmentPrograms(bool nativeOnly); - /// Setup texture env functions. Used by setupMaterial void setTextureEnvFunction(uint stage, CMaterial& mat); diff --git a/code/nel/src/3d/driver_user.cpp b/code/nel/src/3d/driver_user.cpp index 83c7343ec..e5d814755 100644 --- a/code/nel/src/3d/driver_user.cpp +++ b/code/nel/src/3d/driver_user.cpp @@ -1496,12 +1496,6 @@ void CDriverUser::forceTextureResize(uint divisor) _Driver->forceTextureResize(divisor); } -void CDriverUser::forceNativeFragmentPrograms(bool nativeOnly) -{ - NL3D_HAUTO_UI_DRIVER; - - _Driver->forceNativeFragmentPrograms(nativeOnly); -} bool CDriverUser::setMonitorColorProperties (const CMonitorColorProperties &properties) { NL3D_HAUTO_UI_DRIVER; diff --git a/code/nel/src/3d/gpu_program_params.cpp b/code/nel/src/3d/gpu_program_params.cpp index 0a03500ea..217a9683e 100644 --- a/code/nel/src/3d/gpu_program_params.cpp +++ b/code/nel/src/3d/gpu_program_params.cpp @@ -36,6 +36,7 @@ #include // Project includes +#include using namespace std; // using namespace NLMISC; @@ -52,20 +53,20 @@ CGPUProgramParams::~CGPUProgramParams() } -void CGPUProgramParams::setF(uint index, float f0) +void CGPUProgramParams::set(uint index, float f0) { float *f = getPtrFByOffset(allocOffset(index, 1, Float)); f[0] = f0; } -void CGPUProgramParams::setF(uint index, float f0, float f1) +void CGPUProgramParams::set(uint index, float f0, float f1) { float *f = getPtrFByOffset(allocOffset(index, 2, Float)); f[0] = f0; f[1] = f1; } -void CGPUProgramParams::setF(uint index, float f0, float f1, float f2) +void CGPUProgramParams::set(uint index, float f0, float f1, float f2) { float *f = getPtrFByOffset(allocOffset(index, 3, Float)); f[0] = f0; @@ -73,7 +74,7 @@ void CGPUProgramParams::setF(uint index, float f0, float f1, float f2) f[2] = f2; } -void CGPUProgramParams::setF(uint index, float f0, float f1, float f2, float f3) +void CGPUProgramParams::set(uint index, float f0, float f1, float f2, float f3) { float *f = getPtrFByOffset(allocOffset(index, 4, Float)); f[0] = f0; @@ -82,20 +83,20 @@ void CGPUProgramParams::setF(uint index, float f0, float f1, float f2, float f3) f[3] = f3; } -void CGPUProgramParams::setI(uint index, int i0) +void CGPUProgramParams::set(uint index, int i0) { int *i = getPtrIByOffset(allocOffset(index, 1, Int)); i[0] = i0; } -void CGPUProgramParams::setI(uint index, int i0, int i1) +void CGPUProgramParams::set(uint index, int i0, int i1) { int *i = getPtrIByOffset(allocOffset(index, 2, Int)); i[0] = i0; i[1] = i1; } -void CGPUProgramParams::setI(uint index, int i0, int i1, int i2) +void CGPUProgramParams::set(uint index, int i0, int i1, int i2) { int *i = getPtrIByOffset(allocOffset(index, 3, Int)); i[0] = i0; @@ -103,7 +104,7 @@ void CGPUProgramParams::setI(uint index, int i0, int i1, int i2) i[2] = i2; } -void CGPUProgramParams::setI(uint index, int i0, int i1, int i2, int i3) +void CGPUProgramParams::set(uint index, int i0, int i1, int i2, int i3) { int *i = getPtrIByOffset(allocOffset(index, 4, Int)); i[0] = i0; @@ -112,7 +113,7 @@ void CGPUProgramParams::setI(uint index, int i0, int i1, int i2, int i3) i[3] = i3; } -void CGPUProgramParams::setF(uint index, const NLMISC::CVector& v) +void CGPUProgramParams::set(uint index, const NLMISC::CVector& v) { float *f = getPtrFByOffset(allocOffset(index, 3, Float)); f[0] = v.x; @@ -120,7 +121,7 @@ void CGPUProgramParams::setF(uint index, const NLMISC::CVector& v) f[2] = v.z; } -void CGPUProgramParams::setF(uint index, const NLMISC::CMatrix& m) +void CGPUProgramParams::set(uint index, const NLMISC::CMatrix& m) { // TODO: Verify this! float *f = getPtrFByOffset(allocOffset(index, 16, Float)); @@ -129,11 +130,18 @@ void CGPUProgramParams::setF(uint index, const NLMISC::CMatrix& m) mt.get(f); } -void CGPUProgramParams::setF(uint index, uint num, const float *src) +void CGPUProgramParams::set(uint index, const float *arr, size_t sz) { - float *f = getPtrFByOffset(allocOffset(index, num, Float)); - for (uint i = 0; i < num; ++i) - f[i] = src[i]; + float *f = getPtrFByOffset(allocOffset(index, sz, Float)); + for (uint c = 0; c < sz; ++c) + f[c] = arr[c]; +} + +void CGPUProgramParams::set(uint index, const sint32 *arr, size_t sz) +{ + int *i = getPtrIByOffset(allocOffset(index, sz, Int)); + for (uint c = 0; c < sz; ++c) + i[c] = arr[c]; } /// Allocate specified number of components if necessary diff --git a/code/nel/src/3d/stereo_ovr.cpp b/code/nel/src/3d/stereo_ovr.cpp index 8c8c6ff63..e02ccd50c 100644 --- a/code/nel/src/3d/stereo_ovr.cpp +++ b/code/nel/src/3d/stereo_ovr.cpp @@ -563,6 +563,7 @@ bool CStereoOVR::endRenderTarget() NL3D::IDriver *drvInternal = (static_cast(m_Driver))->getDriver(); NL3D::CMaterial *barrelMat = m_BarrelMat.getObjectPtr(); barrelMat->setTexture(0, m_BarrelTex); + drvInternal->activePixelProgram(m_PixelProgram); float w = float(m_BarrelQuadLeft.V1.x),// / float(width), @@ -582,20 +583,21 @@ bool CStereoOVR::endRenderTarget() float scaleY = (h / 2); float scaleInX = (2 / w); float scaleInY = (2 / h); - drvInternal->setPixelProgramConstant(0, lensCenterX, lensCenterY, 0.f, 0.f); - drvInternal->setPixelProgramConstant(1, screenCenterX, screenCenterY, 0.f, 0.f); - drvInternal->setPixelProgramConstant(2, scaleX, scaleY, 0.f, 0.f); - drvInternal->setPixelProgramConstant(3, scaleInX, scaleInY, 0.f, 0.f); - drvInternal->setPixelProgramConstant(4, 1, m_DevicePtr->HMDInfo.DistortionK); - + + drvInternal->setPixelProgram2f(0, lensCenterX, lensCenterY); + drvInternal->setPixelProgram2f(1, screenCenterX, screenCenterY); + drvInternal->setPixelProgram2f(2, scaleX, scaleY); + drvInternal->setPixelProgram2f(3, scaleInX, scaleInY); + drvInternal->setPixelProgram4fv(4, 1, m_DevicePtr->HMDInfo.DistortionK); m_Driver->drawQuad(m_BarrelQuadLeft, m_BarrelMat); x = w; lensCenterX = x + (w - lensViewportShift * 0.5f) * 0.5f; screenCenterX = x + w * 0.5f; - drvInternal->setPixelProgramConstant(0, lensCenterX, lensCenterY, 0.f, 0.f); - drvInternal->setPixelProgramConstant(1, screenCenterX, screenCenterY, 0.f, 0.f); + + drvInternal->setPixelProgram2f(0, lensCenterX, lensCenterY); + drvInternal->setPixelProgram2f(1, screenCenterX, screenCenterY); m_Driver->drawQuad(m_BarrelQuadRight, m_BarrelMat); From e01c0c09135cf85404abb2bb46993e55647b4aa2 Mon Sep 17 00:00:00 2001 From: Quitta Date: Sun, 8 Sep 2013 22:49:39 +0200 Subject: [PATCH 189/313] Soem fixes + also using the createPermissions() function of the module atm! --- .../server/ryzom_ams/drupal_module/ryzommanage/config.php | 2 +- .../drupal_module/ryzommanage/ryzommanage.module | 8 ++++---- .../tools/server/ryzom_ams/www/html/func/change_mail.php | 1 + .../server/ryzom_ams/www/html/func/change_password.php | 1 + 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/config.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/config.php index 5cb719bc8..ef2459117 100644 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/config.php +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/config.php @@ -91,7 +91,7 @@ $TIME_FORMAT = "m-d-Y H:i:s"; //defines which ingame layout template should be used $INGAME_LAYOUT = "basic"; -$FORCE_INGAME = true; +$FORCE_INGAME = false; diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module index deb8a5f40..e094aeceb 100644 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module @@ -486,10 +486,10 @@ function createUser($values, $user_id) //Create the user on the shard + in case shard is offline put copy of query in query db //returns: ok, shardoffline or liboffline $result = WebUsers::createUser($params, $user_id); - //createPermissions(array($login)); + createPermissions(array($login)); } -/*function createPermissions($values) { +function createPermissions($values) { try { $hostname = variable_get('ryzommanage_serverurl', 'localhost'); @@ -536,7 +536,7 @@ function createUser($values, $user_id) } return true; -}*/ +} function ryzommanage_user_login(&$edit, $account){ $_SESSION['user'] = $account->name; @@ -610,7 +610,7 @@ function top_bar() // Logged in user //check permission, if user if(ticket_user::isMod(unserialize($_SESSION['ticket_user']))){ - return "
    Profile | + return "
    Dashboard | Profile | Create Ticket | Settings | Users | Queues | diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/func/change_mail.php b/code/ryzom/tools/server/ryzom_ams/www/html/func/change_mail.php index ac24fa5b0..f807e46b5 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/func/change_mail.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/func/change_mail.php @@ -91,3 +91,4 @@ function change_mail(){ } + diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/func/change_password.php b/code/ryzom/tools/server/ryzom_ams/www/html/func/change_password.php index b3971202d..ab4a71892 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/func/change_password.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/func/change_password.php @@ -87,3 +87,4 @@ function change_password(){ } + From c512dfbb3dcf6be8d833670e806631a75c305887 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Mon, 9 Sep 2013 01:31:15 +0200 Subject: [PATCH 190/313] Separate count and size --- code/nel/include/nel/3d/driver.h | 6 -- code/nel/include/nel/3d/gpu_program_params.h | 39 +++++----- code/nel/src/3d/gpu_program_params.cpp | 75 +++++++++----------- 3 files changed, 52 insertions(+), 68 deletions(-) diff --git a/code/nel/include/nel/3d/driver.h b/code/nel/include/nel/3d/driver.h index 2414d3df5..3e6b1e856 100644 --- a/code/nel/include/nel/3d/driver.h +++ b/code/nel/include/nel/3d/driver.h @@ -1124,9 +1124,6 @@ public: virtual void setVertexProgram3f(uint index, const NLMISC::CVector& v) = 0; virtual void setVertexProgram4f(uint index, const NLMISC::CVector& v, float f3) = 0; virtual void setVertexProgram4x4f(uint index, const NLMISC::CMatrix& m) = 0; - virtual void setVertexProgram1fv(uint index, size_t num, const float *src) = 0; - virtual void setVertexProgram2fv(uint index, size_t num, const float *src) = 0; - virtual void setVertexProgram3fv(uint index, size_t num, const float *src) = 0; virtual void setVertexProgram4fv(uint index, size_t num, const float *src) = 0; // @} @@ -1170,9 +1167,6 @@ public: virtual void setPixelProgram3f(uint index, const NLMISC::CVector& v) = 0; virtual void setPixelProgram4f(uint index, const NLMISC::CVector& v, float f3) = 0; virtual void setPixelProgram4x4f(uint index, const NLMISC::CMatrix& m) = 0; - virtual void setPixelProgram1fv(uint index, size_t num, const float *src) = 0; - virtual void setPixelProgram2fv(uint index, size_t num, const float *src) = 0; - virtual void setPixelProgram3fv(uint index, size_t num, const float *src) = 0; virtual void setPixelProgram4fv(uint index, size_t num, const float *src) = 0; // @} diff --git a/code/nel/include/nel/3d/gpu_program_params.h b/code/nel/include/nel/3d/gpu_program_params.h index 08d8010d4..ce454b2ed 100644 --- a/code/nel/include/nel/3d/gpu_program_params.h +++ b/code/nel/include/nel/3d/gpu_program_params.h @@ -56,11 +56,11 @@ namespace NL3D { class CGPUProgramParams { public: - enum TType { Float, Int }; - struct CMeta { uint Index, Count; TType Type; size_t Next, Prev; }; + enum TType { Float, Int, UInt }; + struct CMeta { uint Index, Size, Count; TType Type; size_t Next, Prev; }; // size is element size, count is nb of elements private: - union CVec { float F[4]; sint32 I[4]; }; + union CVec { float F[4]; sint32 I[4]; uint32 UI[4]; }; public: CGPUProgramParams(); @@ -81,9 +81,6 @@ public: void set3f(uint index, const NLMISC::CVector& v); void set4f(uint index, const NLMISC::CVector& v, float f3); void set4x4f(uint index, const NLMISC::CMatrix& m); - void set1fv(uint index, size_t num, const float *src); - void set2fv(uint index, size_t num, const float *src); - void set3fv(uint index, size_t num, const float *src); void set4fv(uint index, size_t num, const float *src); void unset(uint index); @@ -99,9 +96,6 @@ public: void set3f(const std::string &name, const NLMISC::CVector& v); void set4f(const std::string &name, const NLMISC::CVector& v, float f3); void set4x4f(const std::string &name, const NLMISC::CMatrix& m); - void set1fv(const std::string &name, size_t num, const float *src); - void set2fv(const std::string &name, size_t num, const float *src); - void set3fv(const std::string &name, size_t num, const float *src); void set4fv(const std::string &name, size_t num, const float *src); void unset(const std::string &name); @@ -110,8 +104,8 @@ public: // Internal /// Allocate specified number of components if necessary - size_t allocOffset(uint index, uint count, TType type); - size_t allocOffset(const std::string &name, uint count, TType type); + size_t allocOffset(uint index, uint size, uint count, TType type); + size_t allocOffset(const std::string &name, uint size, uint count, TType type); /// Return offset for specified index size_t getOffset(uint index) const; size_t getOffset(const std::string &name) const; @@ -119,20 +113,23 @@ public: void freeOffset(size_t offset); // Iteration (returns the offsets for access using getFooByOffset) - size_t getBegin() const { return m_Meta.size() ? m_First : s_End; } - size_t getNext(size_t offset) const { return m_Meta[offset].Next; } - size_t getEnd() const { return s_End; } + inline size_t getBegin() const { return m_Meta.size() ? m_First : s_End; } + inline size_t getNext(size_t offset) const { return m_Meta[offset].Next; } + inline size_t getEnd() const { return s_End; } // Data access - uint getCountByOffset(size_t offset) { return m_Meta[offset].Count; } // component count (number of floats or ints) - float *getPtrFByOffset(size_t offset) { return m_Vec[offset].F; } - int *getPtrIByOffset(size_t offset) { return m_Vec[offset].I; } - TType getTypeByOffset(size_t offset) { return m_Meta[offset].Type; } - uint getIndexByOffset(size_t offset) { return m_Meta[offset].Index; } - const std::string *getNameByOffset(size_t offset); // non-optimized for dev tools only, may return NULL if name unknown + inline uint getSizeByOffset(size_t offset) const { return m_Meta[offset].Size; } // size of element (4 for float4) + inline uint getCountByOffset(size_t offset) const { return m_Meta[offset].Count; } // number of elements (usually 1) + inline uint getNbComponentsByOffset(size_t offset) const { return m_Meta[offset].Size * m_Meta[offset].Count; } // nb of components (size * count) + inline float *getPtrFByOffset(size_t offset) { return m_Vec[offset].F; } + inline sint32 *getPtrIByOffset(size_t offset) { return m_Vec[offset].I; } + inline uint32 *getPtrUIByOffset(size_t offset) { return m_Vec[offset].UI; } + inline TType getTypeByOffset(size_t offset) const { return m_Meta[offset].Type; } + inline uint getIndexByOffset(size_t offset) const { return m_Meta[offset].Index; } + const std::string *getNameByOffset(size_t offset) const; // non-optimized for dev tools only, may return NULL if name unknown // Utility - static inline uint getNbRegistersByComponents(uint count) { return (count + 3) >> 2; } // vector register per 4 components + static inline uint getNbRegistersByComponents(uint nbComponents) { return (nbComponents + 3) >> 2; } // vector register per 4 components private: std::vector m_Vec; diff --git a/code/nel/src/3d/gpu_program_params.cpp b/code/nel/src/3d/gpu_program_params.cpp index 217a9683e..bfcc4d32c 100644 --- a/code/nel/src/3d/gpu_program_params.cpp +++ b/code/nel/src/3d/gpu_program_params.cpp @@ -53,118 +53,110 @@ CGPUProgramParams::~CGPUProgramParams() } -void CGPUProgramParams::set(uint index, float f0) +void CGPUProgramParams::set1f(uint index, float f0) { - float *f = getPtrFByOffset(allocOffset(index, 1, Float)); + float *f = getPtrFByOffset(allocOffset(index, 1, 1, Float)); f[0] = f0; } -void CGPUProgramParams::set(uint index, float f0, float f1) +void CGPUProgramParams::set2f(uint index, float f0, float f1) { - float *f = getPtrFByOffset(allocOffset(index, 2, Float)); + float *f = getPtrFByOffset(allocOffset(index, 2, 1, Float)); f[0] = f0; f[1] = f1; } -void CGPUProgramParams::set(uint index, float f0, float f1, float f2) +void CGPUProgramParams::set3f(uint index, float f0, float f1, float f2) { - float *f = getPtrFByOffset(allocOffset(index, 3, Float)); + float *f = getPtrFByOffset(allocOffset(index, 3, 1, Float)); f[0] = f0; f[1] = f1; f[2] = f2; } -void CGPUProgramParams::set(uint index, float f0, float f1, float f2, float f3) +void CGPUProgramParams::set4f(uint index, float f0, float f1, float f2, float f3) { - float *f = getPtrFByOffset(allocOffset(index, 4, Float)); + float *f = getPtrFByOffset(allocOffset(index, 4, 1, Float)); f[0] = f0; f[1] = f1; f[2] = f2; f[3] = f3; } -void CGPUProgramParams::set(uint index, int i0) +void CGPUProgramParams::set1i(uint index, int i0) { - int *i = getPtrIByOffset(allocOffset(index, 1, Int)); + int *i = getPtrIByOffset(allocOffset(index, 1, 1, Int)); i[0] = i0; } -void CGPUProgramParams::set(uint index, int i0, int i1) +void CGPUProgramParams::set2i(uint index, int i0, int i1) { - int *i = getPtrIByOffset(allocOffset(index, 2, Int)); + int *i = getPtrIByOffset(allocOffset(index, 2, 1, Int)); i[0] = i0; i[1] = i1; } -void CGPUProgramParams::set(uint index, int i0, int i1, int i2) +void CGPUProgramParams::set3i(uint index, int i0, int i1, int i2) { - int *i = getPtrIByOffset(allocOffset(index, 3, Int)); + int *i = getPtrIByOffset(allocOffset(index, 3, 1, Int)); i[0] = i0; i[1] = i1; i[2] = i2; } -void CGPUProgramParams::set(uint index, int i0, int i1, int i2, int i3) +void CGPUProgramParams::set4i(uint index, int i0, int i1, int i2, int i3) { - int *i = getPtrIByOffset(allocOffset(index, 4, Int)); + int *i = getPtrIByOffset(allocOffset(index, 4, 1, Int)); i[0] = i0; i[1] = i1; i[2] = i2; i[3] = i3; } -void CGPUProgramParams::set(uint index, const NLMISC::CVector& v) +void CGPUProgramParams::set3f(uint index, const NLMISC::CVector& v) { - float *f = getPtrFByOffset(allocOffset(index, 3, Float)); + float *f = getPtrFByOffset(allocOffset(index, 3, 1, Float)); f[0] = v.x; f[1] = v.y; f[2] = v.z; } -void CGPUProgramParams::set(uint index, const NLMISC::CMatrix& m) +void CGPUProgramParams::set4x4f(uint index, const NLMISC::CMatrix& m) { // TODO: Verify this! - float *f = getPtrFByOffset(allocOffset(index, 16, Float)); + float *f = getPtrFByOffset(allocOffset(index, 4, 4, Float)); NLMISC::CMatrix mt = m; mt.transpose(); mt.get(f); } -void CGPUProgramParams::set(uint index, const float *arr, size_t sz) +void CGPUProgramParams::set4fv(uint index, size_t num, const float *src) { - float *f = getPtrFByOffset(allocOffset(index, sz, Float)); - for (uint c = 0; c < sz; ++c) - f[c] = arr[c]; -} - -void CGPUProgramParams::set(uint index, const sint32 *arr, size_t sz) -{ - int *i = getPtrIByOffset(allocOffset(index, sz, Int)); - for (uint c = 0; c < sz; ++c) - i[c] = arr[c]; + float *f = getPtrFByOffset(allocOffset(index, 4, num, Float)); + size_t nb = 4 * num; + for (uint c = 0; c < nb; ++c) + f[c] = src[c]; } /// Allocate specified number of components if necessary -size_t CGPUProgramParams::allocOffset(uint index, uint count, TType type) +size_t CGPUProgramParams::allocOffset(uint index, uint size, uint count, TType type) { nlassert(count > 0); // this code will not properly handle 0 + nlassert(size > 0); // this code will not properly handle 0 nlassert(index < 0xFFFF); // sanity check + uint nbComponents = size * count; size_t offset = getOffset(index); if (offset != s_End) { - if (getCountByOffset(offset) == count) + if (getCountByOffset(offset) >= nbComponents) { m_Meta[offset].Type = type; + m_Meta[offset].Size = size; + m_Meta[offset].Count = count; return offset; } - if (getCountByOffset(offset) > count) - { - m_Meta[offset].Type = type; - m_Meta[offset].Count = count; // reduce count - return offset; - } - if (getCountByOffset(offset) < count) + if (getCountByOffset(offset) < nbComponents) { freeOffset(offset); } @@ -172,7 +164,7 @@ size_t CGPUProgramParams::allocOffset(uint index, uint count, TType type) // Allocate space offset = m_Meta.size(); - uint blocks = getNbRegistersByComponents(count); // per 4 components + uint blocks = getNbRegistersByComponents(nbComponents); // per 4 components m_Meta.resize(offset + blocks); m_Vec.resize(offset + blocks); @@ -183,6 +175,7 @@ size_t CGPUProgramParams::allocOffset(uint index, uint count, TType type) // Fill m_Meta[offset].Index = index; + m_Meta[offset].Size = size; m_Meta[offset].Count = count; m_Meta[offset].Type = type; m_Meta[offset].Prev = m_Last; From 060bd3035e3fc08f82bd51919b084adc7df60f6d Mon Sep 17 00:00:00 2001 From: kaetemi Date: Mon, 9 Sep 2013 01:53:02 +0200 Subject: [PATCH 191/313] Builtin parameter set functions --- code/nel/include/nel/3d/driver.h | 12 ++++++++---- code/nel/include/nel/3d/gpu_program_params.h | 3 +++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/code/nel/include/nel/3d/driver.h b/code/nel/include/nel/3d/driver.h index 3e6b1e856..540b17962 100644 --- a/code/nel/include/nel/3d/driver.h +++ b/code/nel/include/nel/3d/driver.h @@ -1125,6 +1125,9 @@ public: virtual void setVertexProgram4f(uint index, const NLMISC::CVector& v, float f3) = 0; virtual void setVertexProgram4x4f(uint index, const NLMISC::CMatrix& m) = 0; virtual void setVertexProgram4fv(uint index, size_t num, const float *src) = 0; + // Set builtin parameters + virtual void setVertexProgramMatrix(uint index, TMatrix matrix, TTransform transform) = 0; + virtual void setVertexProgramFog(uint index) = 0; // @} @@ -1168,6 +1171,9 @@ public: virtual void setPixelProgram4f(uint index, const NLMISC::CVector& v, float f3) = 0; virtual void setPixelProgram4x4f(uint index, const NLMISC::CMatrix& m) = 0; virtual void setPixelProgram4fv(uint index, size_t num, const float *src) = 0; + // Set builtin parameters + virtual void setPixelProgramMatrix(uint index, TMatrix matrix, TTransform transform) = 0; + virtual void setPixelProgramFog(uint index) = 0; // @} @@ -1183,8 +1189,6 @@ public: inline void setConstant(uint index, const NLMISC::CVectorD &value) { setVertexProgram4f(index, (float)value.x, (float)value.y, (float)value.z, 0.f); } /// setup several 4 float csts taken from the given tab inline void setConstant(uint index, uint num, const float *src) { setVertexProgram4fv(index, num, src); } - /// setup several 4 double csts taken from the given tab (convert to float) - virtual void setConstant(uint index, uint num, const double *src) = 0; /** * Setup constants with a current matrix. @@ -1197,7 +1201,7 @@ public: * \param transform is the transformation to apply to the matrix before store it in the constants. * */ - virtual void setConstantMatrix(uint index, TMatrix matrix, TTransform transform) = 0; + inline void setConstantMatrix(uint index, TMatrix matrix, TTransform transform) { setVertexProgramMatrix(index, matrix, transform); }; /** * Setup the constant with the fog vector. This vector must be used to get the final fog value in a vertex shader. @@ -1211,7 +1215,7 @@ public: * \param index is the index where to store the vector. * */ - virtual void setConstantFog(uint index) = 0; + inline void setConstantFog(uint index) { setVertexProgramFog(index); }; // @} diff --git a/code/nel/include/nel/3d/gpu_program_params.h b/code/nel/include/nel/3d/gpu_program_params.h index ce454b2ed..270c6cff5 100644 --- a/code/nel/include/nel/3d/gpu_program_params.h +++ b/code/nel/include/nel/3d/gpu_program_params.h @@ -52,6 +52,9 @@ namespace NL3D { * Allows for fast updating and iteration of parameters. * NOTE TO DRIVER IMPLEMENTORS: DO NOT USE FOR STORING COPIES * OF HARDCODED DRIVER MATERIAL PARAMETERS OR DRIVER PARAMETERS!!! + * The 4-component alignment that is done in this storage + * class is necessary to simplify support for register-based + * assembly shaders, which require setting per 4 components. */ class CGPUProgramParams { From 0cf5dbab8a854c20d848247b9e22f54b59185bd7 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Mon, 9 Sep 2013 02:03:40 +0200 Subject: [PATCH 192/313] Reduce function duplication --- code/nel/include/nel/3d/driver.h | 71 +++++++++++++++----------------- code/nel/src/3d/stereo_ovr.cpp | 14 +++---- 2 files changed, 40 insertions(+), 45 deletions(-) diff --git a/code/nel/include/nel/3d/driver.h b/code/nel/include/nel/3d/driver.h index 540b17962..0fe4c6511 100644 --- a/code/nel/include/nel/3d/driver.h +++ b/code/nel/include/nel/3d/driver.h @@ -159,6 +159,13 @@ public: NumTransform }; + enum TProgram + { + VertexProgram, + PixelProgram, + GeometryProgram + }; + protected: CSynchronized _SyncTexDrvInfos; TTexDrvSharePtrList _TexDrvShares; @@ -1111,23 +1118,6 @@ public: /** Get the currently active vertex program. */ virtual CVertexProgram *getActiveVertexProgram() const = 0; - - // Set parameters - virtual void setVertexProgram1f(uint index, float f0) = 0; - virtual void setVertexProgram2f(uint index, float f0, float f1) = 0; - virtual void setVertexProgram3f(uint index, float f0, float f1, float f2) = 0; - virtual void setVertexProgram4f(uint index, float f0, float f1, float f2, float f3) = 0; - virtual void setVertexProgram1i(uint index, sint32 i0) = 0; - virtual void setVertexProgram2i(uint index, sint32 i0, sint32 i1) = 0; - virtual void setVertexProgram3i(uint index, sint32 i0, sint32 i1, sint32 i2) = 0; - virtual void setVertexProgram4i(uint index, sint32 i0, sint32 i1, sint32 i2, sint32 i3) = 0; - virtual void setVertexProgram3f(uint index, const NLMISC::CVector& v) = 0; - virtual void setVertexProgram4f(uint index, const NLMISC::CVector& v, float f3) = 0; - virtual void setVertexProgram4x4f(uint index, const NLMISC::CMatrix& m) = 0; - virtual void setVertexProgram4fv(uint index, size_t num, const float *src) = 0; - // Set builtin parameters - virtual void setVertexProgramMatrix(uint index, TMatrix matrix, TTransform transform) = 0; - virtual void setVertexProgramFog(uint index) = 0; // @} @@ -1157,23 +1147,28 @@ public: /** Get the currently active pixel program. */ virtual CPixelProgram *getActivePixelProgram() const = 0; + // @} + + + /// \name Program parameters + // @{ // Set parameters - virtual void setPixelProgram1f(uint index, float f0) = 0; - virtual void setPixelProgram2f(uint index, float f0, float f1) = 0; - virtual void setPixelProgram3f(uint index, float f0, float f1, float f2) = 0; - virtual void setPixelProgram4f(uint index, float f0, float f1, float f2, float f3) = 0; - virtual void setPixelProgram1i(uint index, sint32 i0) = 0; - virtual void setPixelProgram2i(uint index, sint32 i0, sint32 i1) = 0; - virtual void setPixelProgram3i(uint index, sint32 i0, sint32 i1, sint32 i2) = 0; - virtual void setPixelProgram4i(uint index, sint32 i0, sint32 i1, sint32 i2, sint32 i3) = 0; - virtual void setPixelProgram3f(uint index, const NLMISC::CVector& v) = 0; - virtual void setPixelProgram4f(uint index, const NLMISC::CVector& v, float f3) = 0; - virtual void setPixelProgram4x4f(uint index, const NLMISC::CMatrix& m) = 0; - virtual void setPixelProgram4fv(uint index, size_t num, const float *src) = 0; + virtual void setUniform1f(TProgram program, uint index, float f0) = 0; + virtual void setUniform2f(TProgram program, uint index, float f0, float f1) = 0; + virtual void setUniform3f(TProgram program, uint index, float f0, float f1, float f2) = 0; + virtual void setUniform4f(TProgram program, uint index, float f0, float f1, float f2, float f3) = 0; + virtual void setUniform1i(TProgram program, uint index, sint32 i0) = 0; + virtual void setUniform2i(TProgram program, uint index, sint32 i0, sint32 i1) = 0; + virtual void setUniform3i(TProgram program, uint index, sint32 i0, sint32 i1, sint32 i2) = 0; + virtual void setUniform4i(TProgram program, uint index, sint32 i0, sint32 i1, sint32 i2, sint32 i3) = 0; + virtual void setUniform3f(TProgram program, uint index, const NLMISC::CVector& v) = 0; + virtual void setUniform4f(TProgram program, uint index, const NLMISC::CVector& v, float f3) = 0; + virtual void setUniform4x4f(TProgram program, uint index, const NLMISC::CMatrix& m) = 0; + virtual void setUniform4fv(TProgram program, uint index, size_t num, const float *src) = 0; // Set builtin parameters - virtual void setPixelProgramMatrix(uint index, TMatrix matrix, TTransform transform) = 0; - virtual void setPixelProgramFog(uint index) = 0; + virtual void setUniformMatrix(TProgram program, uint index, TMatrix matrix, TTransform transform) = 0; + virtual void setUniformFog(TProgram program, uint index) = 0; // @} @@ -1183,12 +1178,12 @@ public: /** * Setup constant values. */ - inline void setConstant(uint index, float f0, float f1, float f2, float f3) { setVertexProgram4f(index, f0, f1, f2, f3); } - inline void setConstant(uint index, double d0, double d1, double d2, double d3) { setVertexProgram4f(index, (float)d0, (float)d1, (float)d2, (float)d3); } - inline void setConstant(uint index, const NLMISC::CVector &value) { setVertexProgram4f(index, value, 0.f); } - inline void setConstant(uint index, const NLMISC::CVectorD &value) { setVertexProgram4f(index, (float)value.x, (float)value.y, (float)value.z, 0.f); } + inline void setConstant(uint index, float f0, float f1, float f2, float f3) { setUniform4f(VertexProgram, index, f0, f1, f2, f3); } + inline void setConstant(uint index, double d0, double d1, double d2, double d3) { setUniform4f(VertexProgram, index, (float)d0, (float)d1, (float)d2, (float)d3); } + inline void setConstant(uint index, const NLMISC::CVector &value) { setUniform4f(VertexProgram, index, value, 0.f); } + inline void setConstant(uint index, const NLMISC::CVectorD &value) { setUniform4f(VertexProgram, index, (float)value.x, (float)value.y, (float)value.z, 0.f); } /// setup several 4 float csts taken from the given tab - inline void setConstant(uint index, uint num, const float *src) { setVertexProgram4fv(index, num, src); } + inline void setConstant(uint index, uint num, const float *src) { setUniform4fv(VertexProgram, index, num, src); } /** * Setup constants with a current matrix. @@ -1201,7 +1196,7 @@ public: * \param transform is the transformation to apply to the matrix before store it in the constants. * */ - inline void setConstantMatrix(uint index, TMatrix matrix, TTransform transform) { setVertexProgramMatrix(index, matrix, transform); }; + inline void setConstantMatrix(uint index, TMatrix matrix, TTransform transform) { setUniformMatrix(VertexProgram, index, matrix, transform); }; /** * Setup the constant with the fog vector. This vector must be used to get the final fog value in a vertex shader. @@ -1215,7 +1210,7 @@ public: * \param index is the index where to store the vector. * */ - inline void setConstantFog(uint index) { setVertexProgramFog(index); }; + inline void setConstantFog(uint index) { setUniformFog(VertexProgram, index); }; // @} diff --git a/code/nel/src/3d/stereo_ovr.cpp b/code/nel/src/3d/stereo_ovr.cpp index e02ccd50c..b89783cb9 100644 --- a/code/nel/src/3d/stereo_ovr.cpp +++ b/code/nel/src/3d/stereo_ovr.cpp @@ -584,11 +584,11 @@ bool CStereoOVR::endRenderTarget() float scaleInX = (2 / w); float scaleInY = (2 / h); - drvInternal->setPixelProgram2f(0, lensCenterX, lensCenterY); - drvInternal->setPixelProgram2f(1, screenCenterX, screenCenterY); - drvInternal->setPixelProgram2f(2, scaleX, scaleY); - drvInternal->setPixelProgram2f(3, scaleInX, scaleInY); - drvInternal->setPixelProgram4fv(4, 1, m_DevicePtr->HMDInfo.DistortionK); + drvInternal->setUniform2f(IDriver::PixelProgram, 0, lensCenterX, lensCenterY); + drvInternal->setUniform2f(IDriver::PixelProgram, 1, screenCenterX, screenCenterY); + drvInternal->setUniform2f(IDriver::PixelProgram, 2, scaleX, scaleY); + drvInternal->setUniform2f(IDriver::PixelProgram, 3, scaleInX, scaleInY); + drvInternal->setUniform4fv(IDriver::PixelProgram, 4, 1, m_DevicePtr->HMDInfo.DistortionK); m_Driver->drawQuad(m_BarrelQuadLeft, m_BarrelMat); @@ -596,8 +596,8 @@ bool CStereoOVR::endRenderTarget() lensCenterX = x + (w - lensViewportShift * 0.5f) * 0.5f; screenCenterX = x + w * 0.5f; - drvInternal->setPixelProgram2f(0, lensCenterX, lensCenterY); - drvInternal->setPixelProgram2f(1, screenCenterX, screenCenterY); + drvInternal->setUniform2f(IDriver::PixelProgram, 0, lensCenterX, lensCenterY); + drvInternal->setUniform2f(IDriver::PixelProgram, 1, screenCenterX, screenCenterY); m_Driver->drawQuad(m_BarrelQuadRight, m_BarrelMat); From 91c9c04dc328ff4ac514e8fd649e70ab89f6f662 Mon Sep 17 00:00:00 2001 From: Quitta Date: Mon, 9 Sep 2013 03:47:32 +0200 Subject: [PATCH 193/313] Biggest push ever! :D Kinda made it possible to only need a few files in the inc/func of the drupal module, all others can be copied of the www version --- .../ryzom_ams/ams_lib/autoload/helpers.php | 9 +- .../ams_lib/ingame_templates/createticket.tpl | 2 +- .../ams_lib/ingame_templates/dashboard.tpl | 8 +- .../ams_lib/ingame_templates/layout_admin.tpl | 12 +- .../ams_lib/ingame_templates/layout_mod.tpl | 12 +- .../ams_lib/ingame_templates/layout_user.tpl | 6 +- .../ams_lib/ingame_templates/login.tpl | 5 +- .../ams_lib/ingame_templates/register.tpl | 2 +- .../ams_lib/ingame_templates/settings.tpl | 26 +- .../ams_lib/ingame_templates/sgroup_list.tpl | 6 +- .../ams_lib/ingame_templates/show_queue.tpl | 22 +- .../ams_lib/ingame_templates/show_reply.tpl | 6 +- .../ams_lib/ingame_templates/show_sgroup.tpl | 8 +- .../ams_lib/ingame_templates/show_ticket.tpl | 18 +- .../ingame_templates/show_ticket_info.tpl | 40 +- .../ingame_templates/show_ticket_log.tpl | 4 +- .../ams_lib/ingame_templates/show_user.tpl | 18 +- .../ams_lib/ingame_templates/userlist.tpl | 24 +- .../server/ryzom_ams/ams_lib/libinclude.php | 10 +- .../ams_lib/ingame_templates/createticket.tpl | 101 - .../ams_lib/ingame_templates/dashboard.tpl | 81 - .../ams_lib/ingame_templates/index.tpl | 81 - .../ams_lib/ingame_templates/layout.tpl | 34 - .../ams_lib/ingame_templates/layout_admin.tpl | 19 - .../ams_lib/ingame_templates/layout_mod.tpl | 18 - .../ams_lib/ingame_templates/layout_user.tpl | 13 - .../ams_lib/ingame_templates/login.tpl | 39 - .../ams_lib/ingame_templates/register.tpl | 115 - .../ams_lib/ingame_templates/sgroup_list.tpl | 146 -- .../ams_lib/ingame_templates/show_queue.tpl | 222 -- .../ams_lib/ingame_templates/show_reply.tpl | 88 - .../ams_lib/ingame_templates/show_sgroup.tpl | 171 -- .../ams_lib/ingame_templates/show_ticket.tpl | 285 --- .../ingame_templates/show_ticket_info.tpl | 168 -- .../ingame_templates/show_ticket_log.tpl | 88 - .../ams_lib/ingame_templates/show_user.tpl | 163 -- .../ams_lib/ingame_templates/userlist.tpl | 96 - .../ryzommanage/func/add_sgroup.php | 45 - .../ryzommanage/func/add_user_to_sgroup.php | 48 - .../ryzommanage/func/change_mail.php | 93 - .../ryzommanage/func/change_password.php | 88 - .../ryzommanage/func/create_ticket.php | 58 - .../drupal_module/ryzommanage/func/login.php | 41 - .../func/modify_email_of_sgroup.php | 59 - .../ryzommanage/func/reply_on_ticket.php | 59 - .../ryzommanage/inc/change_permission.php | 49 - .../ryzommanage/inc/createticket.php | 48 - .../ryzommanage/inc/dashboard.php | 37 - .../drupal_module/ryzommanage/inc/login.php | 21 - .../ryzommanage/inc/sgroup_list.php | 39 - .../ryzommanage/inc/show_queue.php | 153 -- .../ryzommanage/inc/show_reply.php | 48 - .../ryzommanage/inc/show_sgroup.php | 66 - .../ryzommanage/inc/show_ticket.php | 86 - .../ryzommanage/inc/show_ticket_info.php | 55 - .../ryzommanage/inc/show_ticket_log.php | 67 - .../ryzommanage/inc/userlist.php | 30 - .../ryzommanage/templates/userlist.tpl | 2 +- .../{drupal_module/ryzommanage => }/todo.txt | 4 +- .../tools/server/ryzom_ams/www/config.php | 9 +- .../ryzom_ams/www/html/autoload/webusers.php | 2 +- .../tools/server/ryzom_ams/www/html/blank.php | 33 - .../server/ryzom_ams/www/html/calendar.php | 45 - .../tools/server/ryzom_ams/www/html/chart.php | 120 -- .../ryzom_ams/www/html/file-manager.php | 36 - .../server/ryzom_ams/www/html/footer.php | 121 -- .../tools/server/ryzom_ams/www/html/form.php | 333 --- .../ryzom_ams/www/html/func/add_sgroup.php | 22 +- .../ryzom_ams/www/html/func/add_user.php | 4 +- .../www/html/func/add_user_to_sgroup.php | 20 +- .../ryzom_ams/www/html/func/change_info.php | 10 +- .../ryzom_ams/www/html/func/change_mail.php | 13 +- .../www/html/func/change_password.php | 14 +- .../www/html/func/change_receivemail.php | 10 +- .../ryzom_ams/www/html/func/create_ticket.php | 14 +- .../server/ryzom_ams/www/html/func/login.php | 16 +- .../www/html/func/modify_email_of_sgroup.php | 20 +- .../www/html/func/reply_on_ticket.php | 26 +- .../server/ryzom_ams/www/html/gallery.php | 41 - .../tools/server/ryzom_ams/www/html/grid.php | 240 --- .../server/ryzom_ams/www/html/header.php | 158 -- .../tools/server/ryzom_ams/www/html/icon.php | 355 ---- .../www/html/inc/change_permission.php | 19 +- .../ryzom_ams/www/html/inc/createticket.php | 4 +- .../ryzom_ams/www/html/inc/dashboard.php | 13 +- .../server/ryzom_ams/www/html/inc/login.php | 13 +- .../ryzom_ams/www/html/inc/settings.php | 12 +- .../ryzom_ams/www/html/inc/sgroup_list.php | 16 +- .../ryzom_ams/www/html/inc/show_queue.php | 42 +- .../ryzom_ams/www/html/inc/show_reply.php | 6 +- .../ryzom_ams/www/html/inc/show_sgroup.php | 18 +- .../ryzom_ams/www/html/inc/show_ticket.php | 14 +- .../www/html/inc/show_ticket_info.php | 10 +- .../www/html/inc/show_ticket_log.php | 21 +- .../ryzom_ams/www/html/inc/show_user.php | 9 +- .../ryzom_ams/www/html/inc/userlist.php | 10 +- .../tools/server/ryzom_ams/www/html/index.php | 4 +- .../ryzom_ams/www/html/index_charisma.php | 329 --- .../tools/server/ryzom_ams/www/html/table.php | 1844 ----------------- .../www/html/templates/show_ticket_info.tpl | 32 +- .../tools/server/ryzom_ams/www/html/tour.php | 32 - .../server/ryzom_ams/www/html/typography.php | 176 -- .../tools/server/ryzom_ams/www/html/ui.php | 287 --- 103 files changed, 407 insertions(+), 7528 deletions(-) delete mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/createticket.tpl delete mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/dashboard.tpl delete mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/index.tpl delete mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/layout.tpl delete mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/layout_admin.tpl delete mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/layout_mod.tpl delete mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/layout_user.tpl delete mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/login.tpl delete mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/register.tpl delete mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/sgroup_list.tpl delete mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/show_queue.tpl delete mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/show_reply.tpl delete mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/show_sgroup.tpl delete mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/show_ticket.tpl delete mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/show_ticket_info.tpl delete mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/show_ticket_log.tpl delete mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/show_user.tpl delete mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/userlist.tpl delete mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/add_sgroup.php delete mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/add_user_to_sgroup.php delete mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/change_mail.php delete mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/change_password.php delete mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/create_ticket.php delete mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/login.php delete mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/modify_email_of_sgroup.php delete mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/reply_on_ticket.php delete mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/change_permission.php delete mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/createticket.php delete mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/dashboard.php delete mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/login.php delete mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/sgroup_list.php delete mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/show_queue.php delete mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/show_reply.php delete mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/show_sgroup.php delete mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/show_ticket.php delete mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/show_ticket_info.php delete mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/show_ticket_log.php delete mode 100644 code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/userlist.php rename code/ryzom/tools/server/ryzom_ams/{drupal_module/ryzommanage => }/todo.txt (69%) delete mode 100644 code/ryzom/tools/server/ryzom_ams/www/html/blank.php delete mode 100644 code/ryzom/tools/server/ryzom_ams/www/html/calendar.php delete mode 100644 code/ryzom/tools/server/ryzom_ams/www/html/chart.php delete mode 100644 code/ryzom/tools/server/ryzom_ams/www/html/file-manager.php delete mode 100644 code/ryzom/tools/server/ryzom_ams/www/html/footer.php delete mode 100644 code/ryzom/tools/server/ryzom_ams/www/html/form.php delete mode 100644 code/ryzom/tools/server/ryzom_ams/www/html/gallery.php delete mode 100644 code/ryzom/tools/server/ryzom_ams/www/html/grid.php delete mode 100644 code/ryzom/tools/server/ryzom_ams/www/html/header.php delete mode 100644 code/ryzom/tools/server/ryzom_ams/www/html/icon.php delete mode 100644 code/ryzom/tools/server/ryzom_ams/www/html/index_charisma.php delete mode 100644 code/ryzom/tools/server/ryzom_ams/www/html/table.php delete mode 100644 code/ryzom/tools/server/ryzom_ams/www/html/tour.php delete mode 100644 code/ryzom/tools/server/ryzom_ams/www/html/typography.php delete mode 100644 code/ryzom/tools/server/ryzom_ams/www/html/ui.php diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/helpers.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/helpers.php index 18f34b1d1..9bf7f03e1 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/helpers.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/helpers.php @@ -5,7 +5,6 @@ class Helpers{ public static function loadTemplate( $template, $vars = array (), $returnHTML = false ) { - global $AMS_LIB; global $SITEBASE; global $AMS_TRANS; @@ -25,7 +24,8 @@ class Helpers{ $smarty -> cache_lifetime = 120; helpers :: create_folders (); - if ( helpers::check_if_game_client() or $forcelibrender = false ){ + global $FORCE_INGAME; + if ( helpers::check_if_game_client() or $FORCE_INGAME ){ $smarty -> template_dir = $AMS_LIB . '/ingame_templates/'; $smarty -> setConfigDir( $AMS_LIB . '/configs' ); $variables = parse_ini_file( $AMS_LIB . '/configs/ingame_layout.ini', true ); @@ -58,7 +58,7 @@ class Helpers{ } - + if($returnHTML == true){ return $smarty ->fetch($inherited . $template . '.tpl' ); }else{ @@ -90,7 +90,8 @@ class Helpers{ static public function check_if_game_client() { // if HTTP_USER_AGENT is not set then its ryzom core - if ( strpos($_SERVER['HTTP_USER_AGENT'],"Ryzom") === 0){ + global $FORCE_INGAME; + if (( strpos($_SERVER['HTTP_USER_AGENT'],"Ryzom") === 0) || $FORCE_INGAME){ return true; }else{ return false; diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/createticket.tpl b/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/createticket.tpl index 4445fca39..ec79cc661 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/createticket.tpl +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/createticket.tpl @@ -32,7 +32,7 @@
    onmouseover="return searchBox.OnSearchSelectShow()" onmouseout="return searchBox.OnSearchSelectHide()" onkeydown="return searchBox.OnSearchSelectKey(event)"> - All Classes Functions Variables + All Data Structures Files Functions Variables
    @@ -457,7 +520,7 @@ Private Attributes diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/classDBLayer.html b/code/ryzom/tools/server/ryzom_ams_docs/html/classDBLayer.html index e5f879bce..c90fbd0b8 100644 --- a/code/ryzom/tools/server/ryzom_ams_docs/html/classDBLayer.html +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/classDBLayer.html @@ -25,10 +25,12 @@
    + + @@ -48,8 +50,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
    + + +
    +
    + + + +
    + + + {if isset($ACTION_RESULT)} + +
    + + + +
    +

    Tickets

    +

    +

    + + + + + + + + + + + + + + + + +
    Show + + tickets + + to + + : + + or + + + + +
    +
    +

    +

    + + + + + + + + + + + + {foreach from=$tickets item=ticket} + + + + + + + + + + + {/foreach} +
    IDTitleAssignedTimestampCategoryStatusSupportGroupActions
    {$ticket.tId}{$ticket.title}{if $ticket.assignedText neq ""} {$ticket.assignedText}{else} {$not_assigned} {/if}{$ticket.timestamp}{$ticket.category}{if $ticket.status eq 0}{else if $ticket.status eq 1}{else if $ticket.status eq 2}{/if}{$ticket.statusText} + + {if $ticket.forwardedGroupName eq "0"} + {$public_sgroup} + {else} + {$ticket.forwardedGroupName} + {/if} + + + {if $ticket.assigned eq 0} +
    + + + +
    + {else if $ticket.assigned eq $user_id} +
    + + + +
    + {/if} +
    +

    +
    + + + + {foreach from=$links item=link} + + {/foreach} + + +
    «{$link}»
    +
    +
    + + + + + + +
    + {if isset($ACTION_RESULT) and $ACTION_RESULT eq "SUCCESS_ASSIGNED"} +

    + {$success_assigned} +

    + {else if isset($ACTION_RESULT) and $ACTION_RESULT eq "SUCCESS_UNASSIGNED"} +

    + {$success_unassigned} +

    + {else if isset($ACTION_RESULT) and $ACTION_RESULT eq "TICKET_NOT_EXISTING"} +

    + {$ticket_not_existing} +

    + {else if isset($ACTION_RESULT) and $ACTION_RESULT eq "ALREADY_ASSIGNED"} +

    + {$ticket_already_assigned} +

    + {else if isset($ACTION_RESULT) and $ACTION_RESULT eq "NOT_ASSIGNED"} +

    + {$ticket_not_assigned} +

    + {/if} +
    + + {/if} +
    +
    +
    + +
    + + + + + + + +
    + + + + +
    + + + + + +
    Show TicketShow Ticket Log
    +
    +
    +
    + + + + + + + + + +

    Reply ID#{$reply_id} of Ticket #{$ticket_id}

    +
    + + + + + + + + + + +
    + + +
    +
    + + +
    + + +
    + + +
    +

    Reply:

    +

    + + + + +
    +

    + {$reply_timestamp} + {if $author_permission eq '1'} + {if isset($isMod) and $isMod eq "TRUE"} {$authorName}{else} {$authorName} {/if} + {else if $reply.permission gt '1'} + {if isset($isMod) and $isMod eq "TRUE"} {$authorName}{else} {$authorName} {/if} + {/if}

    +

    {$reply_content}

    +
    +

    +
    +
    +
    +
    + +
    + + + + + + + + + +

    Support Group: {$groupsname}

    +
    + + + + + + + + + + +
    + + +
    +
    + + + + +
    + + +
    + + +
    +

    Add user to the list

    + {if isset($isAdmin) && $isAdmin eq 'TRUE'} +
    + + + + + +
    Username:
    + + + +

    + +

    + + {if isset($RESULT_OF_ADDING) and $RESULT_OF_ADDING eq "SUCCESS"} +

    + {$add_to_group_success} +

    + {else if isset($RESULT_OF_ADDING) and $RESULT_OF_ADDING eq "ALREADY_ADDED"} +

    + {$user_already_added} +

    + {else if isset($RESULT_OF_ADDING) and $RESULT_OF_ADDING eq "GROUP_NOT_EXISTING"} +

    + {$group_not_existing} +

    + {else if isset($RESULT_OF_ADDING) and $RESULT_OF_ADDING eq "USER_NOT_EXISTING"} +

    + {$user_not_existing} +

    + {else if isset($RESULT_OF_ADDING) and $RESULT_OF_ADDING eq "NOT_MOD_OR_ADMIN"} +

    + {$not_mod_or_admin} +

    + {/if} +
    + {/if} +
    +
    +
    + + +
    + + +
    +

    All members

    + + + + + {if isset($isAdmin) && $isAdmin eq 'TRUE'}{/if} + + + {foreach from=$userlist item=user} + + + + {if isset($isAdmin) && $isAdmin eq 'TRUE'}{/if} + + {/foreach} +
    IDNameAction
    {$user.tUserId}{$user.name}Delete
    +
    +
    +
    + + +
    + + +
    +

    Mail settings

    +
    + + + + + + + + + + + + + + + + + + + + + + +
    Group Email:
    IMAP Mail Server:
    IMAP Username:
    IMAP Password:
    + + + + +

    + +

    + + {if isset($RESULT_OF_MODIFYING) and $RESULT_OF_MODIFYING eq "SUCCESS"} +

    + {$modify_mail_of_group_success} +

    + {else if isset($RESULT_OF_MODIFYING) and $RESULT_OF_MODIFYING eq "EMAIL_NOT_VALID"} +

    + {$email_not_valid} +

    + {else if isset($RESULT_OF_MODIFYING) and $RESULT_OF_MODIFYING eq "NO_PASSWORD"} +

    + {$no_password_given} +

    + {/if} + +
    +
    +
    +
    +
    + +
    + + + + + + + +
    + + + + +
    + + + {if isset($isMod) and $isMod eq "TRUE"}{/if} + + {if $hasInfo}{/if} + +
    Show Ticket LogSend Other TicketShow Additional Info
    +
    +
    +
    + + + + + + + + + +

    [{$t_title}-#{$ticket_tId}] {$ticket_title}

    +
    + + + + + + + + + + +
    + + +
    +
    + + +
    + + +
    + + +
    + + + + + {if isset($isMod) and $isMod eq "TRUE"} + +
    + + +
    + + + + + + + + + + + + + + + + +
    Submitted: {$ticket_timestamp}Last Updated: {$ticket_lastupdate}Status: {if $ticket_status neq 3}Open{/if} {if $ticket_status eq 3} {$ticket_statustext}{else}{$ticket_statustext} {/if}
    Category: {$ticket_category}Priority {$ticket_prioritytext}Support Group: + + {if $ticket_forwardedGroupName eq "0"} + {$public_sgroup} + {else} + {$ticket_forwardedGroupName} + {/if} + +
    Assigned To: {if $ticket_assignedTo neq ""} {$ticket_assignedToText}{else} {$not_assigned} {/if}
    +
    +
    + + {foreach from=$ticket_replies item=reply} + + + + {/foreach} + + {if $ticket_status eq 3} + + + + {/if} + + + + +
    + + +
    +

    + {$reply.timestamp} + {if $reply.permission eq '1'} + {if isset($isMod) and $isMod eq "TRUE"} {$reply.author}{else} {$reply.author} {/if} + {else if $reply.permission gt '1'} + {if isset($isMod) and $isMod eq "TRUE"} {$reply.author}{else} {$reply.author} {/if} + {/if} +

    +

    {$reply.replyContent}

    +
    +
    + + +
    +

    [Ticket is closed]

    +
    +
    + +
    + + + + + + {if $ticket_status neq 3} + + + + {if isset($isMod) and $isMod eq "TRUE"} + + + + {/if} + {/if} + + + + + + +
    +

    {$t_reply}:

    +
    Hide reply for user.
    + {if isset($isMod) and $isMod eq "TRUE"} + + + + + +
    + Change status to + + + Change priority to + +
    + {/if} +
    + + + +
    +
    +
    +
    + + +
    + + + + + +
    +

    + Ticket Assigning: + {if $ticket_assignedTo eq 0} +

    + + + +
    + {else if $ticket_assignedTo eq $user_id} +
    + + + +
    + {/if} +

    + {if isset($ACTION_RESULT) and $ACTION_RESULT eq "SUCCESS_ASSIGNED"} +

    + {$success_assigned} +

    + {else if isset($ACTION_RESULT) and $ACTION_RESULT eq "SUCCESS_UNASSIGNED"} +

    + {$success_unassigned} +

    + {else if isset($ACTION_RESULT) and $ACTION_RESULT eq "TICKET_NOT_EXISTING"} +

    + {$ticket_not_existing} +

    + {else if isset($ACTION_RESULT) and $ACTION_RESULT eq "ALREADY_ASSIGNED"} +

    + {$ticket_already_assigned} +

    + {else if isset($ACTION_RESULT) and $ACTION_RESULT eq "NOT_ASSIGNED"} +

    + {$ticket_not_assigned} +

    + {/if} + + +
    +

    + Forward to Group: +

    + + + + + +
    +

    + {if isset($ACTION_RESULT) and $ACTION_RESULT eq "INVALID_SGROUP"} +

    + {$invalid_sgroup} +

    + {else if isset($ACTION_RESULT) and $ACTION_RESULT eq "TICKET_NOT_EXISTING"} +

    + {$ticket_not_existing} +

    + {else if isset($ACTION_RESULT) and $ACTION_RESULT eq "SUCCESS_FORWARDED"} +

    + {$success_forwarded} +

    + {/if} +
    +
    +
    + {/if} + +
    +
    +
    +
    +
    + +
    + + + + + + + +
    + + + + +
    + + + {if isset($isMod) and $isMod eq "TRUE"}{/if} + + + +
    Show Ticket LogSend Other TicketShow Ticket
    +
    +
    +
    + + + + + + + + + +

    Additional Info For Ticket [#{$ticket_id}]

    +
    + + + + + + + + + + +
    + + +
    +
    + + +
    + + +
    + + +
    + + + + +
    + + +
    +

    Ingame related

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Shard ID: {$shard_id}
    User_Id: {$user_id}
    User Position: {$user_position}
    View Position: {$view_position}
    Client_Version: {$client_version}
    Patch_Version: {$patch_version}
    Server_Tick: {$server_tick}
    +
    +
    + + +
    +

    Hardware & Software related

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Memory: {$memory}
    Processor: {$processor}
    Cpu_Id: {$cpu_id}
    Cpu_Mask: {$cpu_mask}
    HT: {$ht}
    OS: {$os}
    NeL3D: {$nel3d}
    +
    +
    + + +
    +

    Network related

    + + + + + + + + + + +
    Connect_State: {$connect_state}
    Local_Address: {$local_address}
    +
    +
    +
    +
    +
    +
    + +
    + + + + + + + +
    + + + + +
    + + + + +
    Show Ticket
    +
    +
    +
    + + + + + + + + + +

    Log of Ticket #{$ticket_id}

    +
    + + + + + + + + + + +
    + + +
    +
    + + +
    + + +
    + + +
    +

    Title: {$ticket_title}

    +

    + + + + + + + + {foreach from=$ticket_logs item=log} + + + + + + {/foreach} + +
    IDTimestampQuery
    {$log.tLogId}{$log.timestamp}{$log.query}
    +

    +
    +
    +
    +
    + +
    + + + + + + + +
    + + + + +
    + + + + + {if isset($isAdmin) and $isAdmin eq 'TRUE' and $target_id neq 1} + {if $userPermission eq 1} + + + {else if $userPermission eq 2 } + + + {else if $userPermission eq 3 } + + + {/if} + {/if} + +
    Edit UserSend TicketMake ModeratorMake AdminDemote to UserMake AdminDemote to UserDemote to Moderator
    +
    +
    +
    + + + + + + + + + +

    Profile of {$target_name}

    +
    + + + + + + + + + + +
    + + +
    +
    + + + +
    + + +
    + + +
    + +

    Info

    + + + + + + + + + + + + {if $firstName neq ""} + + + + + {/if} + {if $lastName neq ""} + + + + + {/if} + {if $country neq ""} + + + + + {/if} + {if $gender neq 0} + + + {if $gender eq 1} + + {else if $gender eq 2} + + {/if} + + {/if} + +
    Email:{$mail}
    Role: + {if $userPermission eq 1}User{/if} + {if $userPermission eq 2}Moderator{/if} + {if $userPermission eq 3}Admin{/if} +
    Firstname:{$firstName}
    LastName:{$lastName}
    Country:{$country}
    Gender:♂♀
    +
    +
    +
    + + +
    + + +
    +

    Tickets

    + + + + + + + + + + + {foreach from=$ticketlist item=ticket} + + + + + + + + + {/foreach} + +
    IDTitleTimestampCategoryStatus
    {$ticket.tId}{$ticket.title}{$ticket.timestamp}{$ticket.category}{if $ticket.status eq 0} {/if} {$ticket.statusText}
    +
    +
    +
    +
    + +
    + + + + + + + + + +

    Members

    +
    + + + + + + + + + + +
    + + +
    +
    + + +
    + + +
    + + + +
    +

    All Acounts

    + + + + + + + + + + {foreach from=$userlist item=element} + + + + + {if $element.permission eq 1}{/if} + {if $element.permission eq 2}{/if} + {if $element.permission eq 3}{/if} + + + + {/foreach} +
    IdUsernameEmailPermissionAction
    {$element.id}{$element.username}{$element.email}UserModeratorAdmin + Show User + Edit User + {if isset($isAdmin) and $isAdmin eq 'TRUE' and $element.id neq 1} + {if $element.permission eq 1} + Make Moderator + Make Admin + {else if $element.permission eq 2 } + Demote to User + Make Admin + {else if $element.permission eq 3 } + Demote to User + Demote to Moderator + {/if} + {/if} +
    +
    + + + + {foreach from=$links item=link} + + {/foreach} + + +
    «{$link}»
    +
    +
    +
    +
    + +
    diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/layout_admin.tpl b/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/layout_admin.tpl index b3ce83d86..afe29e778 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/layout_admin.tpl +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/layout_admin.tpl @@ -1,18 +1,18 @@ {extends file="layout.tpl"} {block name=menu} - + - + - + - + - + - + {/block} diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/layout_mod.tpl b/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/layout_mod.tpl index 34911226f..65d10c611 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/layout_mod.tpl +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/layout_mod.tpl @@ -1,18 +1,18 @@ {extends file="layout.tpl"} {block name=menu} - + - + - + - + - + - + {/block} diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/layout_user.tpl b/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/layout_user.tpl index af2f23f07..233ba2ad4 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/layout_user.tpl +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/layout_user.tpl @@ -1,12 +1,12 @@ {extends file="layout.tpl"} {block name=menu} - + - + - + {/block} diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/login.tpl b/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/login.tpl index ad6f48e0d..03d0bd7aa 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/login.tpl +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/login.tpl @@ -3,14 +3,13 @@

    {$login_info}

    - +

    Username:

    -

    Password: @@ -34,7 +33,7 @@

    {/if}

    - {$login_register_message} {$login_register_message_here}! + {$login_register_message} {$login_register_message_here}!

    -
    + diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/dashboard.tpl b/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/dashboard.tpl index af87d13aa..24b8c7878 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/dashboard.tpl +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/dashboard.tpl @@ -40,10 +40,10 @@ - - - - + + + +
    Title: Total amount of Tickets
    {$nrAssignedWaiting}{$nrToDo}{$newestTicketTitle}{$nrTotalTickets}{$nrAssignedWaiting}{$nrToDo}{$newestTicketTitle}{$nrTotalTickets}
    Dashboard
    Dashboard
    Profile
    Profile
    Settings
    Settings
    |
    Users
    Users
    Queues
    Queues
    Support Groups
    Support Groups
    Dashboard
    Dashboard
    Profile
    Profile
    Settings
    Settings
    |
    Users
    Users
    Queues
    Queues
    Support Groups
    Support Groups
    Profile
    Profile
    Settings
    Settings
    |
    Create New Ticket
    Create New Ticket
    diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/register.tpl b/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/register.tpl index 03cb7c9d3..9c38b34a2 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/register.tpl +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/register.tpl @@ -6,7 +6,7 @@
    {$welcome_message}
    -click hereeeee + diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/settings.tpl b/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/settings.tpl index 048675a9e..e256e4429 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/settings.tpl +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/settings.tpl @@ -8,18 +8,18 @@
    - - + + {if isset($isAdmin) and $isAdmin eq 'TRUE' and $target_id neq 1} {if $userPermission eq 1} - - + + {else if $userPermission eq 2 } - - + + {else if $userPermission eq 3 } - - + + {/if} {/if} @@ -40,7 +40,7 @@ - + @@ -70,7 +70,7 @@

    Change Password

    - +
    Browse UserSend TicketBrowse UserSend TicketMake ModeratorMake AdminMake ModeratorMake AdminDemote to UserMake AdminDemote to UserMake AdminDemote to UserDemote to ModeratorDemote to UserDemote to Moderator

    Change Settings

    Change Settings of {$target_username}

    {if !isset($changesOther) or $changesOther eq "FALSE"} @@ -124,7 +124,7 @@ + + @@ -48,8 +50,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');

    Change Email

    - + + + @@ -48,8 +50,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
    New Email: @@ -156,7 +156,7 @@ "&&!R?L.childNodes:[];for(var M=N.length-1;M>=0;--M){if(o.nodeName(N[M],"tbody")&&!N[M].childNodes.length){N[M].parentNode.removeChild(N[M])}}}if(!o.support.leadingWhitespace&&/^\s/.test(S)){L.insertBefore(K.createTextNode(S.match(/^\s*/)[0]),L.firstChild)}S=o.makeArray(L.childNodes)}if(S.nodeType){G.push(S)}else{G=o.merge(G,S)}});if(I){for(var J=0;G[J];J++){if(o.nodeName(G[J],"script")&&(!G[J].type||G[J].type.toLowerCase()==="text/javascript")){E.push(G[J].parentNode?G[J].parentNode.removeChild(G[J]):G[J])}else{if(G[J].nodeType===1){G.splice.apply(G,[J+1,0].concat(o.makeArray(G[J].getElementsByTagName("script"))))}I.appendChild(G[J])}}return E}return G},attr:function(J,G,K){if(!J||J.nodeType==3||J.nodeType==8){return g}var H=!o.isXMLDoc(J),L=K!==g;G=H&&o.props[G]||G;if(J.tagName){var F=/href|src|style/.test(G);if(G=="selected"&&J.parentNode){J.parentNode.selectedIndex}if(G in J&&H&&!F){if(L){if(G=="type"&&o.nodeName(J,"input")&&J.parentNode){throw"type property can't be changed"}J[G]=K}if(o.nodeName(J,"form")&&J.getAttributeNode(G)){return J.getAttributeNode(G).nodeValue}if(G=="tabIndex"){var I=J.getAttributeNode("tabIndex");return I&&I.specified?I.value:J.nodeName.match(/(button|input|object|select|textarea)/i)?0:J.nodeName.match(/^(a|area)$/i)&&J.href?0:g}return J[G]}if(!o.support.style&&H&&G=="style"){return o.attr(J.style,"cssText",K)}if(L){J.setAttribute(G,""+K)}var E=!o.support.hrefNormalized&&H&&F?J.getAttribute(G,2):J.getAttribute(G);return E===null?g:E}if(!o.support.opacity&&G=="opacity"){if(L){J.zoom=1;J.filter=(J.filter||"").replace(/alpha\([^)]*\)/,"")+(parseInt(K)+""=="NaN"?"":"alpha(opacity="+K*100+")")}return J.filter&&J.filter.indexOf("opacity=")>=0?(parseFloat(J.filter.match(/opacity=([^)]*)/)[1])/100)+"":""}G=G.replace(/-([a-z])/ig,function(M,N){return N.toUpperCase()});if(L){J[G]=K}return J[G]},trim:function(E){return(E||"").replace(/^\s+|\s+$/g,"")},makeArray:function(G){var E=[];if(G!=null){var F=G.length;if(F==null||typeof G==="string"||o.isFunction(G)||G.setInterval){E[0]=G}else{while(F){E[--F]=G[F]}}}return E},inArray:function(G,H){for(var E=0,F=H.length;E0?this.clone(true):this).get();o.fn[F].apply(o(L[K]),I);J=J.concat(I)}return this.pushStack(J,E,G)}});o.each({removeAttr:function(E){o.attr(this,E,"");if(this.nodeType==1){this.removeAttribute(E)}},addClass:function(E){o.className.add(this,E)},removeClass:function(E){o.className.remove(this,E)},toggleClass:function(F,E){if(typeof E!=="boolean"){E=!o.className.has(this,F)}o.className[E?"add":"remove"](this,F)},remove:function(E){if(!E||o.filter(E,[this]).length){o("*",this).add([this]).each(function(){o.event.remove(this);o.removeData(this)});if(this.parentNode){this.parentNode.removeChild(this)}}},empty:function(){o(this).children().remove();while(this.firstChild){this.removeChild(this.firstChild)}}},function(E,F){o.fn[E]=function(){return this.each(F,arguments)}});function j(E,F){return E[0]&&parseInt(o.curCSS(E[0],F,true),10)||0}var h="jQuery"+e(),v=0,A={};o.extend({cache:{},data:function(F,E,G){F=F==l?A:F;var H=F[h];if(!H){H=F[h]=++v}if(E&&!o.cache[H]){o.cache[H]={}}if(G!==g){o.cache[H][E]=G}return E?o.cache[H][E]:H},removeData:function(F,E){F=F==l?A:F;var H=F[h];if(E){if(o.cache[H]){delete o.cache[H][E];E="";for(E in o.cache[H]){break}if(!E){o.removeData(F)}}}else{try{delete F[h]}catch(G){if(F.removeAttribute){F.removeAttribute(h)}}delete o.cache[H]}},queue:function(F,E,H){if(F){E=(E||"fx")+"queue";var G=o.data(F,E);if(!G||o.isArray(H)){G=o.data(F,E,o.makeArray(H))}else{if(H){G.push(H)}}}return G},dequeue:function(H,G){var E=o.queue(H,G),F=E.shift();if(!G||G==="fx"){F=E[0]}if(F!==g){F.call(H)}}});o.fn.extend({data:function(E,G){var H=E.split(".");H[1]=H[1]?"."+H[1]:"";if(G===g){var F=this.triggerHandler("getData"+H[1]+"!",[H[0]]);if(F===g&&this.length){F=o.data(this[0],E)}return F===g&&H[1]?this.data(H[0]):F}else{return this.trigger("setData"+H[1]+"!",[H[0],G]).each(function(){o.data(this,E,G)})}},removeData:function(E){return this.each(function(){o.removeData(this,E)})},queue:function(E,F){if(typeof E!=="string"){F=E;E="fx"}if(F===g){return o.queue(this[0],E)}return this.each(function(){var G=o.queue(this,E,F);if(E=="fx"&&G.length==1){G[0].call(this)}})},dequeue:function(E){return this.each(function(){o.dequeue(this,E)})}}); +/* + * Sizzle CSS Selector Engine - v0.9.3 + * Copyright 2009, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * More information: http://sizzlejs.com/ + */ +(function(){var R=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,L=0,H=Object.prototype.toString;var F=function(Y,U,ab,ac){ab=ab||[];U=U||document;if(U.nodeType!==1&&U.nodeType!==9){return[]}if(!Y||typeof Y!=="string"){return ab}var Z=[],W,af,ai,T,ad,V,X=true;R.lastIndex=0;while((W=R.exec(Y))!==null){Z.push(W[1]);if(W[2]){V=RegExp.rightContext;break}}if(Z.length>1&&M.exec(Y)){if(Z.length===2&&I.relative[Z[0]]){af=J(Z[0]+Z[1],U)}else{af=I.relative[Z[0]]?[U]:F(Z.shift(),U);while(Z.length){Y=Z.shift();if(I.relative[Y]){Y+=Z.shift()}af=J(Y,af)}}}else{var ae=ac?{expr:Z.pop(),set:E(ac)}:F.find(Z.pop(),Z.length===1&&U.parentNode?U.parentNode:U,Q(U));af=F.filter(ae.expr,ae.set);if(Z.length>0){ai=E(af)}else{X=false}while(Z.length){var ah=Z.pop(),ag=ah;if(!I.relative[ah]){ah=""}else{ag=Z.pop()}if(ag==null){ag=U}I.relative[ah](ai,ag,Q(U))}}if(!ai){ai=af}if(!ai){throw"Syntax error, unrecognized expression: "+(ah||Y)}if(H.call(ai)==="[object Array]"){if(!X){ab.push.apply(ab,ai)}else{if(U.nodeType===1){for(var aa=0;ai[aa]!=null;aa++){if(ai[aa]&&(ai[aa]===true||ai[aa].nodeType===1&&K(U,ai[aa]))){ab.push(af[aa])}}}else{for(var aa=0;ai[aa]!=null;aa++){if(ai[aa]&&ai[aa].nodeType===1){ab.push(af[aa])}}}}}else{E(ai,ab)}if(V){F(V,U,ab,ac);if(G){hasDuplicate=false;ab.sort(G);if(hasDuplicate){for(var aa=1;aa":function(Z,U,aa){var X=typeof U==="string";if(X&&!/\W/.test(U)){U=aa?U:U.toUpperCase();for(var V=0,T=Z.length;V=0)){if(!V){T.push(Y)}}else{if(V){U[X]=false}}}}return false},ID:function(T){return T[1].replace(/\\/g,"")},TAG:function(U,T){for(var V=0;T[V]===false;V++){}return T[V]&&Q(T[V])?U[1]:U[1].toUpperCase()},CHILD:function(T){if(T[1]=="nth"){var U=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(T[2]=="even"&&"2n"||T[2]=="odd"&&"2n+1"||!/\D/.test(T[2])&&"0n+"+T[2]||T[2]);T[2]=(U[1]+(U[2]||1))-0;T[3]=U[3]-0}T[0]=L++;return T},ATTR:function(X,U,V,T,Y,Z){var W=X[1].replace(/\\/g,"");if(!Z&&I.attrMap[W]){X[1]=I.attrMap[W]}if(X[2]==="~="){X[4]=" "+X[4]+" "}return X},PSEUDO:function(X,U,V,T,Y){if(X[1]==="not"){if(X[3].match(R).length>1||/^\w/.test(X[3])){X[3]=F(X[3],null,null,U)}else{var W=F.filter(X[3],U,V,true^Y);if(!V){T.push.apply(T,W)}return false}}else{if(I.match.POS.test(X[0])||I.match.CHILD.test(X[0])){return true}}return X},POS:function(T){T.unshift(true);return T}},filters:{enabled:function(T){return T.disabled===false&&T.type!=="hidden"},disabled:function(T){return T.disabled===true},checked:function(T){return T.checked===true},selected:function(T){T.parentNode.selectedIndex;return T.selected===true},parent:function(T){return !!T.firstChild},empty:function(T){return !T.firstChild},has:function(V,U,T){return !!F(T[3],V).length},header:function(T){return/h\d/i.test(T.nodeName)},text:function(T){return"text"===T.type},radio:function(T){return"radio"===T.type},checkbox:function(T){return"checkbox"===T.type},file:function(T){return"file"===T.type},password:function(T){return"password"===T.type},submit:function(T){return"submit"===T.type},image:function(T){return"image"===T.type},reset:function(T){return"reset"===T.type},button:function(T){return"button"===T.type||T.nodeName.toUpperCase()==="BUTTON"},input:function(T){return/input|select|textarea|button/i.test(T.nodeName)}},setFilters:{first:function(U,T){return T===0},last:function(V,U,T,W){return U===W.length-1},even:function(U,T){return T%2===0},odd:function(U,T){return T%2===1},lt:function(V,U,T){return UT[3]-0},nth:function(V,U,T){return T[3]-0==U},eq:function(V,U,T){return T[3]-0==U}},filter:{PSEUDO:function(Z,V,W,aa){var U=V[1],X=I.filters[U];if(X){return X(Z,W,V,aa)}else{if(U==="contains"){return(Z.textContent||Z.innerText||"").indexOf(V[3])>=0}else{if(U==="not"){var Y=V[3];for(var W=0,T=Y.length;W=0)}}},ID:function(U,T){return U.nodeType===1&&U.getAttribute("id")===T},TAG:function(U,T){return(T==="*"&&U.nodeType===1)||U.nodeName===T},CLASS:function(U,T){return(" "+(U.className||U.getAttribute("class"))+" ").indexOf(T)>-1},ATTR:function(Y,W){var V=W[1],T=I.attrHandle[V]?I.attrHandle[V](Y):Y[V]!=null?Y[V]:Y.getAttribute(V),Z=T+"",X=W[2],U=W[4];return T==null?X==="!=":X==="="?Z===U:X==="*="?Z.indexOf(U)>=0:X==="~="?(" "+Z+" ").indexOf(U)>=0:!U?Z&&T!==false:X==="!="?Z!=U:X==="^="?Z.indexOf(U)===0:X==="$="?Z.substr(Z.length-U.length)===U:X==="|="?Z===U||Z.substr(0,U.length+1)===U+"-":false},POS:function(X,U,V,Y){var T=U[2],W=I.setFilters[T];if(W){return W(X,V,U,Y)}}}};var M=I.match.POS;for(var O in I.match){I.match[O]=RegExp(I.match[O].source+/(?![^\[]*\])(?![^\(]*\))/.source)}var E=function(U,T){U=Array.prototype.slice.call(U);if(T){T.push.apply(T,U);return T}return U};try{Array.prototype.slice.call(document.documentElement.childNodes)}catch(N){E=function(X,W){var U=W||[];if(H.call(X)==="[object Array]"){Array.prototype.push.apply(U,X)}else{if(typeof X.length==="number"){for(var V=0,T=X.length;V";var T=document.documentElement;T.insertBefore(U,T.firstChild);if(!!document.getElementById(V)){I.find.ID=function(X,Y,Z){if(typeof Y.getElementById!=="undefined"&&!Z){var W=Y.getElementById(X[1]);return W?W.id===X[1]||typeof W.getAttributeNode!=="undefined"&&W.getAttributeNode("id").nodeValue===X[1]?[W]:g:[]}};I.filter.ID=function(Y,W){var X=typeof Y.getAttributeNode!=="undefined"&&Y.getAttributeNode("id");return Y.nodeType===1&&X&&X.nodeValue===W}}T.removeChild(U)})();(function(){var T=document.createElement("div");T.appendChild(document.createComment(""));if(T.getElementsByTagName("*").length>0){I.find.TAG=function(U,Y){var X=Y.getElementsByTagName(U[1]);if(U[1]==="*"){var W=[];for(var V=0;X[V];V++){if(X[V].nodeType===1){W.push(X[V])}}X=W}return X}}T.innerHTML="";if(T.firstChild&&typeof T.firstChild.getAttribute!=="undefined"&&T.firstChild.getAttribute("href")!=="#"){I.attrHandle.href=function(U){return U.getAttribute("href",2)}}})();if(document.querySelectorAll){(function(){var T=F,U=document.createElement("div");U.innerHTML="

    ";if(U.querySelectorAll&&U.querySelectorAll(".TEST").length===0){return}F=function(Y,X,V,W){X=X||document;if(!W&&X.nodeType===9&&!Q(X)){try{return E(X.querySelectorAll(Y),V)}catch(Z){}}return T(Y,X,V,W)};F.find=T.find;F.filter=T.filter;F.selectors=T.selectors;F.matches=T.matches})()}if(document.getElementsByClassName&&document.documentElement.getElementsByClassName){(function(){var T=document.createElement("div");T.innerHTML="
    ";if(T.getElementsByClassName("e").length===0){return}T.lastChild.className="e";if(T.getElementsByClassName("e").length===1){return}I.order.splice(1,0,"CLASS");I.find.CLASS=function(U,V,W){if(typeof V.getElementsByClassName!=="undefined"&&!W){return V.getElementsByClassName(U[1])}}})()}function P(U,Z,Y,ad,aa,ac){var ab=U=="previousSibling"&&!ac;for(var W=0,V=ad.length;W0){X=T;break}}}T=T[U]}ad[W]=X}}}var K=document.compareDocumentPosition?function(U,T){return U.compareDocumentPosition(T)&16}:function(U,T){return U!==T&&(U.contains?U.contains(T):true)};var Q=function(T){return T.nodeType===9&&T.documentElement.nodeName!=="HTML"||!!T.ownerDocument&&Q(T.ownerDocument)};var J=function(T,aa){var W=[],X="",Y,V=aa.nodeType?[aa]:aa;while((Y=I.match.PSEUDO.exec(T))){X+=Y[0];T=T.replace(I.match.PSEUDO,"")}T=I.relative[T]?T+"*":T;for(var Z=0,U=V.length;Z0||T.offsetHeight>0};F.selectors.filters.animated=function(T){return o.grep(o.timers,function(U){return T===U.elem}).length};o.multiFilter=function(V,T,U){if(U){V=":not("+V+")"}return F.matches(V,T)};o.dir=function(V,U){var T=[],W=V[U];while(W&&W!=document){if(W.nodeType==1){T.push(W)}W=W[U]}return T};o.nth=function(X,T,V,W){T=T||1;var U=0;for(;X;X=X[V]){if(X.nodeType==1&&++U==T){break}}return X};o.sibling=function(V,U){var T=[];for(;V;V=V.nextSibling){if(V.nodeType==1&&V!=U){T.push(V)}}return T};return;l.Sizzle=F})();o.event={add:function(I,F,H,K){if(I.nodeType==3||I.nodeType==8){return}if(I.setInterval&&I!=l){I=l}if(!H.guid){H.guid=this.guid++}if(K!==g){var G=H;H=this.proxy(G);H.data=K}var E=o.data(I,"events")||o.data(I,"events",{}),J=o.data(I,"handle")||o.data(I,"handle",function(){return typeof o!=="undefined"&&!o.event.triggered?o.event.handle.apply(arguments.callee.elem,arguments):g});J.elem=I;o.each(F.split(/\s+/),function(M,N){var O=N.split(".");N=O.shift();H.type=O.slice().sort().join(".");var L=E[N];if(o.event.specialAll[N]){o.event.specialAll[N].setup.call(I,K,O)}if(!L){L=E[N]={};if(!o.event.special[N]||o.event.special[N].setup.call(I,K,O)===false){if(I.addEventListener){I.addEventListener(N,J,false)}else{if(I.attachEvent){I.attachEvent("on"+N,J)}}}}L[H.guid]=H;o.event.global[N]=true});I=null},guid:1,global:{},remove:function(K,H,J){if(K.nodeType==3||K.nodeType==8){return}var G=o.data(K,"events"),F,E;if(G){if(H===g||(typeof H==="string"&&H.charAt(0)==".")){for(var I in G){this.remove(K,I+(H||""))}}else{if(H.type){J=H.handler;H=H.type}o.each(H.split(/\s+/),function(M,O){var Q=O.split(".");O=Q.shift();var N=RegExp("(^|\\.)"+Q.slice().sort().join(".*\\.")+"(\\.|$)");if(G[O]){if(J){delete G[O][J.guid]}else{for(var P in G[O]){if(N.test(G[O][P].type)){delete G[O][P]}}}if(o.event.specialAll[O]){o.event.specialAll[O].teardown.call(K,Q)}for(F in G[O]){break}if(!F){if(!o.event.special[O]||o.event.special[O].teardown.call(K,Q)===false){if(K.removeEventListener){K.removeEventListener(O,o.data(K,"handle"),false)}else{if(K.detachEvent){K.detachEvent("on"+O,o.data(K,"handle"))}}}F=null;delete G[O]}}})}for(F in G){break}if(!F){var L=o.data(K,"handle");if(L){L.elem=null}o.removeData(K,"events");o.removeData(K,"handle")}}},trigger:function(I,K,H,E){var G=I.type||I;if(!E){I=typeof I==="object"?I[h]?I:o.extend(o.Event(G),I):o.Event(G);if(G.indexOf("!")>=0) +{I.type=G=G.slice(0,-1);I.exclusive=true}if(!H){I.stopPropagation();if(this.global[G]){o.each(o.cache,function(){if(this.events&&this.events[G]){o.event.trigger(I,K,this.handle.elem)}})}}if(!H||H.nodeType==3||H.nodeType==8){return g}I.result=g;I.target=H;K=o.makeArray(K);K.unshift(I)}I.currentTarget=H;var J=o.data(H,"handle");if(J){J.apply(H,K)}if((!H[G]||(o.nodeName(H,"a")&&G=="click"))&&H["on"+G]&&H["on"+G].apply(H,K)===false){I.result=false}if(!E&&H[G]&&!I.isDefaultPrevented()&&!(o.nodeName(H,"a")&&G=="click")){this.triggered=true;try{H[G]()}catch(L){}}this.triggered=false;if(!I.isPropagationStopped()){var F=H.parentNode||H.ownerDocument;if(F){o.event.trigger(I,K,F,true)}}},handle:function(K){var J,E;K=arguments[0]=o.event.fix(K||l.event);K.currentTarget=this;var L=K.type.split(".");K.type=L.shift();J=!L.length&&!K.exclusive;var I=RegExp("(^|\\.)"+L.slice().sort().join(".*\\.")+"(\\.|$)");E=(o.data(this,"events")||{})[K.type];for(var G in E){var H=E[G];if(J||I.test(H.type)){K.handler=H;K.data=H.data;var F=H.apply(this,arguments);if(F!==g){K.result=F;if(F===false){K.preventDefault();K.stopPropagation()}}if(K.isImmediatePropagationStopped()){break}}}},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),fix:function(H){if(H[h]){return H}var F=H;H=o.Event(F);for(var G=this.props.length,J;G;){J=this.props[--G];H[J]=F[J]}if(!H.target){H.target=H.srcElement||document}if(H.target.nodeType==3){H.target=H.target.parentNode}if(!H.relatedTarget&&H.fromElement){H.relatedTarget=H.fromElement==H.target?H.toElement:H.fromElement}if(H.pageX==null&&H.clientX!=null){var I=document.documentElement,E=document.body;H.pageX=H.clientX+(I&&I.scrollLeft||E&&E.scrollLeft||0)-(I.clientLeft||0);H.pageY=H.clientY+(I&&I.scrollTop||E&&E.scrollTop||0)-(I.clientTop||0)}if(!H.which&&((H.charCode||H.charCode===0)?H.charCode:H.keyCode)){H.which=H.charCode||H.keyCode}if(!H.metaKey&&H.ctrlKey){H.metaKey=H.ctrlKey}if(!H.which&&H.button){H.which=(H.button&1?1:(H.button&2?3:(H.button&4?2:0)))}return H},proxy:function(F,E){E=E||function(){return F.apply(this,arguments)};E.guid=F.guid=F.guid||E.guid||this.guid++;return E},special:{ready:{setup:B,teardown:function(){}}},specialAll:{live:{setup:function(E,F){o.event.add(this,F[0],c)},teardown:function(G){if(G.length){var E=0,F=RegExp("(^|\\.)"+G[0]+"(\\.|$)");o.each((o.data(this,"events").live||{}),function(){if(F.test(this.type)){E++}});if(E<1){o.event.remove(this,G[0],c)}}}}}};o.Event=function(E){if(!this.preventDefault){return new o.Event(E)}if(E&&E.type){this.originalEvent=E;this.type=E.type}else{this.type=E}this.timeStamp=e();this[h]=true};function k(){return false}function u(){return true}o.Event.prototype={preventDefault:function(){this.isDefaultPrevented=u;var E=this.originalEvent;if(!E){return}if(E.preventDefault){E.preventDefault()}E.returnValue=false},stopPropagation:function(){this.isPropagationStopped=u;var E=this.originalEvent;if(!E){return}if(E.stopPropagation){E.stopPropagation()}E.cancelBubble=true},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=u;this.stopPropagation()},isDefaultPrevented:k,isPropagationStopped:k,isImmediatePropagationStopped:k};var a=function(F){var E=F.relatedTarget;while(E&&E!=this){try{E=E.parentNode}catch(G){E=this}}if(E!=this){F.type=F.data;o.event.handle.apply(this,arguments)}};o.each({mouseover:"mouseenter",mouseout:"mouseleave"},function(F,E){o.event.special[E]={setup:function(){o.event.add(this,F,a,E)},teardown:function(){o.event.remove(this,F,a)}}});o.fn.extend({bind:function(F,G,E){return F=="unload"?this.one(F,G,E):this.each(function(){o.event.add(this,F,E||G,E&&G)})},one:function(G,H,F){var E=o.event.proxy(F||H,function(I){o(this).unbind(I,E);return(F||H).apply(this,arguments)});return this.each(function(){o.event.add(this,G,E,F&&H)})},unbind:function(F,E){return this.each(function(){o.event.remove(this,F,E)})},trigger:function(E,F){return this.each(function(){o.event.trigger(E,F,this)})},triggerHandler:function(E,G){if(this[0]){var F=o.Event(E);F.preventDefault();F.stopPropagation();o.event.trigger(F,G,this[0]);return F.result}},toggle:function(G){var E=arguments,F=1;while(F=0){var E=G.slice(I,G.length);G=G.slice(0,I)}var H="GET";if(J){if(o.isFunction(J)){K=J;J=null}else{if(typeof J==="object"){J=o.param(J);H="POST"}}}var F=this;o.ajax({url:G,type:H,dataType:"html",data:J,complete:function(M,L){if(L=="success"||L=="notmodified"){F.html(E?o("
    ").append(M.responseText.replace(//g,"")).find(E):M.responseText)}if(K){F.each(K,[M.responseText,L,M])}}});return this},serialize:function(){return o.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?o.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||/select|textarea/i.test(this.nodeName)||/text|hidden|password|search/i.test(this.type))}).map(function(E,F){var G=o(this).val();return G==null?null:o.isArray(G)?o.map(G,function(I,H){return{name:F.name,value:I}}):{name:F.name,value:G}}).get()}});o.each("ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","),function(E,F){o.fn[F]=function(G){return this.bind(F,G)}});var r=e();o.extend({get:function(E,G,H,F){if(o.isFunction(G)){H=G;G=null}return o.ajax({type:"GET",url:E,data:G,success:H,dataType:F})},getScript:function(E,F){return o.get(E,null,F,"script")},getJSON:function(E,F,G){return o.get(E,F,G,"json")},post:function(E,G,H,F){if(o.isFunction(G)){H=G;G={}}return o.ajax({type:"POST",url:E,data:G,success:H,dataType:F})},ajaxSetup:function(E){o.extend(o.ajaxSettings,E)},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:function(){return l.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest()},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},ajax:function(M){M=o.extend(true,M,o.extend(true,{},o.ajaxSettings,M));var W,F=/=\?(&|$)/g,R,V,G=M.type.toUpperCase();if(M.data&&M.processData&&typeof M.data!=="string"){M.data=o.param(M.data)}if(M.dataType=="jsonp"){if(G=="GET"){if(!M.url.match(F)){M.url+=(M.url.match(/\?/)?"&":"?")+(M.jsonp||"callback")+"=?"}}else{if(!M.data||!M.data.match(F)){M.data=(M.data?M.data+"&":"")+(M.jsonp||"callback")+"=?"}}M.dataType="json"}if(M.dataType=="json"&&(M.data&&M.data.match(F)||M.url.match(F))){W="jsonp"+r++;if(M.data){M.data=(M.data+"").replace(F,"="+W+"$1")}M.url=M.url.replace(F,"="+W+"$1");M.dataType="script";l[W]=function(X){V=X;I();L();l[W]=g;try{delete l[W]}catch(Y){}if(H){H.removeChild(T)}}}if(M.dataType=="script"&&M.cache==null){M.cache=false}if(M.cache===false&&G=="GET"){var E=e();var U=M.url.replace(/(\?|&)_=.*?(&|$)/,"$1_="+E+"$2");M.url=U+((U==M.url)?(M.url.match(/\?/)?"&":"?")+"_="+E:"")}if(M.data&&G=="GET"){M.url+=(M.url.match(/\?/)?"&":"?")+M.data;M.data=null}if(M.global&&!o.active++){o.event.trigger("ajaxStart")}var Q=/^(\w+:)?\/\/([^\/?#]+)/.exec(M.url);if(M.dataType=="script"&&G=="GET"&&Q&&(Q[1]&&Q[1]!=location.protocol||Q[2]!=location.host)){var H=document.getElementsByTagName("head")[0];var T=document.createElement("script");T.src=M.url;if(M.scriptCharset){T.charset=M.scriptCharset}if(!W){var O=false;T.onload=T.onreadystatechange=function(){if(!O&&(!this.readyState||this.readyState=="loaded"||this.readyState=="complete")){O=true;I();L();T.onload=T.onreadystatechange=null;H.removeChild(T)}}}H.appendChild(T);return g}var K=false;var J=M.xhr();if(M.username){J.open(G,M.url,M.async,M.username,M.password)}else{J.open(G,M.url,M.async)}try{if(M.data){J.setRequestHeader("Content-Type",M.contentType)}if(M.ifModified){J.setRequestHeader("If-Modified-Since",o.lastModified[M.url]||"Thu, 01 Jan 1970 00:00:00 GMT")}J.setRequestHeader("X-Requested-With","XMLHttpRequest");J.setRequestHeader("Accept",M.dataType&&M.accepts[M.dataType]?M.accepts[M.dataType]+", */*":M.accepts._default)}catch(S){}if(M.beforeSend&&M.beforeSend(J,M)===false){if(M.global&&!--o.active){o.event.trigger("ajaxStop")}J.abort();return false}if(M.global){o.event.trigger("ajaxSend",[J,M])}var N=function(X){if(J.readyState==0){if(P){clearInterval(P);P=null;if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}}else{if(!K&&J&&(J.readyState==4||X=="timeout")){K=true;if(P){clearInterval(P);P=null}R=X=="timeout"?"timeout":!o.httpSuccess(J)?"error":M.ifModified&&o.httpNotModified(J,M.url)?"notmodified":"success";if(R=="success"){try{V=o.httpData(J,M.dataType,M)}catch(Z){R="parsererror"}}if(R=="success"){var Y;try{Y=J.getResponseHeader("Last-Modified")}catch(Z){}if(M.ifModified&&Y){o.lastModified[M.url]=Y}if(!W){I()}}else{o.handleError(M,J,R)}L();if(X){J.abort()}if(M.async){J=null}}}};if(M.async){var P=setInterval(N,13);if(M.timeout>0){setTimeout(function(){if(J&&!K){N("timeout")}},M.timeout)}}try{J.send(M.data)}catch(S){o.handleError(M,J,null,S)}if(!M.async){N()}function I(){if(M.success){M.success(V,R)}if(M.global){o.event.trigger("ajaxSuccess",[J,M])}}function L(){if(M.complete){M.complete(J,R)}if(M.global){o.event.trigger("ajaxComplete",[J,M])}if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}return J},handleError:function(F,H,E,G){if(F.error){F.error(H,E,G)}if(F.global){o.event.trigger("ajaxError",[H,F,G])}},active:0,httpSuccess:function(F){try{return !F.status&&location.protocol=="file:"||(F.status>=200&&F.status<300)||F.status==304||F.status==1223}catch(E){}return false},httpNotModified:function(G,E){try{var H=G.getResponseHeader("Last-Modified");return G.status==304||H==o.lastModified[E]}catch(F){}return false},httpData:function(J,H,G){var F=J.getResponseHeader("content-type"),E=H=="xml"||!H&&F&&F.indexOf("xml")>=0,I=E?J.responseXML:J.responseText;if(E&&I.documentElement.tagName=="parsererror"){throw"parsererror"}if(G&&G.dataFilter){I=G.dataFilter(I,H)}if(typeof I==="string"){if(H=="script"){o.globalEval(I)}if(H=="json"){I=l["eval"]("("+I+")")}}return I},param:function(E){var G=[];function H(I,J){G[G.length]=encodeURIComponent(I)+"="+encodeURIComponent(J)}if(o.isArray(E)||E.jquery){o.each(E,function(){H(this.name,this.value)})}else{for(var F in E){if(o.isArray(E[F])){o.each(E[F],function(){H(F,this)})}else{H(F,o.isFunction(E[F])?E[F]():E[F])}}}return G.join("&").replace(/%20/g,"+")}});var m={},n,d=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];function t(F,E){var G={};o.each(d.concat.apply([],d.slice(0,E)),function() +{G[this]=F});return G}o.fn.extend({show:function(J,L){if(J){return this.animate(t("show",3),J,L)}else{for(var H=0,F=this.length;H").appendTo("body");K=I.css("display");if(K==="none"){K="block"}I.remove();m[G]=K}o.data(this[H],"olddisplay",K)}}for(var H=0,F=this.length;H=0;H--){if(G[H].elem==this){if(E){G[H](true)}G.splice(H,1)}}});if(!E){this.dequeue()}return this}});o.each({slideDown:t("show",1),slideUp:t("hide",1),slideToggle:t("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(E,F){o.fn[E]=function(G,H){return this.animate(F,G,H)}});o.extend({speed:function(G,H,F){var E=typeof G==="object"?G:{complete:F||!F&&H||o.isFunction(G)&&G,duration:G,easing:F&&H||H&&!o.isFunction(H)&&H};E.duration=o.fx.off?0:typeof E.duration==="number"?E.duration:o.fx.speeds[E.duration]||o.fx.speeds._default;E.old=E.complete;E.complete=function(){if(E.queue!==false){o(this).dequeue()}if(o.isFunction(E.old)){E.old.call(this)}};return E},easing:{linear:function(G,H,E,F){return E+F*G},swing:function(G,H,E,F){return((-Math.cos(G*Math.PI)/2)+0.5)*F+E}},timers:[],fx:function(F,E,G){this.options=E;this.elem=F;this.prop=G;if(!E.orig){E.orig={}}}});o.fx.prototype={update:function(){if(this.options.step){this.options.step.call(this.elem,this.now,this)}(o.fx.step[this.prop]||o.fx.step._default)(this);if((this.prop=="height"||this.prop=="width")&&this.elem.style){this.elem.style.display="block"}},cur:function(F){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null)){return this.elem[this.prop]}var E=parseFloat(o.css(this.elem,this.prop,F));return E&&E>-10000?E:parseFloat(o.curCSS(this.elem,this.prop))||0},custom:function(I,H,G){this.startTime=e();this.start=I;this.end=H;this.unit=G||this.unit||"px";this.now=this.start;this.pos=this.state=0;var E=this;function F(J){return E.step(J)}F.elem=this.elem;if(F()&&o.timers.push(F)&&!n){n=setInterval(function(){var K=o.timers;for(var J=0;J=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;var E=true;for(var F in this.options.curAnim){if(this.options.curAnim[F]!==true){E=false}}if(E){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;this.elem.style.display=this.options.display;if(o.css(this.elem,"display")=="none"){this.elem.style.display="block"}}if(this.options.hide){o(this.elem).hide()}if(this.options.hide||this.options.show){for(var I in this.options.curAnim){o.attr(this.elem.style,I,this.options.orig[I])}}this.options.complete.call(this.elem)}return false}else{var J=G-this.startTime;this.state=J/this.options.duration;this.pos=o.easing[this.options.easing||(o.easing.swing?"swing":"linear")](this.state,J,0,1,this.options.duration);this.now=this.start+((this.end-this.start)*this.pos);this.update()}return true}};o.extend(o.fx,{speeds:{slow:600,fast:200,_default:400},step:{opacity:function(E){o.attr(E.elem.style,"opacity",E.now)},_default:function(E){if(E.elem.style&&E.elem.style[E.prop]!=null){E.elem.style[E.prop]=E.now+E.unit}else{E.elem[E.prop]=E.now}}}});if(document.documentElement.getBoundingClientRect){o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}var G=this[0].getBoundingClientRect(),J=this[0].ownerDocument,F=J.body,E=J.documentElement,L=E.clientTop||F.clientTop||0,K=E.clientLeft||F.clientLeft||0,I=G.top+(self.pageYOffset||o.boxModel&&E.scrollTop||F.scrollTop)-L,H=G.left+(self.pageXOffset||o.boxModel&&E.scrollLeft||F.scrollLeft)-K;return{top:I,left:H}}}else{o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}o.offset.initialized||o.offset.initialize();var J=this[0],G=J.offsetParent,F=J,O=J.ownerDocument,M,H=O.documentElement,K=O.body,L=O.defaultView,E=L.getComputedStyle(J,null),N=J.offsetTop,I=J.offsetLeft;while((J=J.parentNode)&&J!==K&&J!==H){M=L.getComputedStyle(J,null);N-=J.scrollTop,I-=J.scrollLeft;if(J===G){N+=J.offsetTop,I+=J.offsetLeft;if(o.offset.doesNotAddBorder&&!(o.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(J.tagName))){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}F=G,G=J.offsetParent}if(o.offset.subtractsBorderForOverflowNotVisible&&M.overflow!=="visible"){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}E=M}if(E.position==="relative"||E.position==="static"){N+=K.offsetTop,I+=K.offsetLeft}if(E.position==="fixed"){N+=Math.max(H.scrollTop,K.scrollTop),I+=Math.max(H.scrollLeft,K.scrollLeft)}return{top:N,left:I}}}o.offset={initialize:function(){if(this.initialized){return}var L=document.body,F=document.createElement("div"),H,G,N,I,M,E,J=L.style.marginTop,K='

    Change Info

    - + @@ -212,7 +212,7 @@
    Firstname:
    ","

    Ticket-Update Mail Settings

    - + @@ -60,7 +60,7 @@ ","
    Receive ticket updates diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/sgroup_list.tpl b/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/sgroup_list.tpl index 322323d03..3a6dcc52f 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/sgroup_list.tpl +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/sgroup_list.tpl @@ -35,7 +35,7 @@

    Add a Support group

    - + - + - {if isset($isAdmin) && $isAdmin eq 'TRUE'}{/if} + {if isset($isAdmin) && $isAdmin eq 'TRUE'}{/if} {/foreach}
    @@ -123,10 +123,10 @@ {foreach from=$grouplist item=group}
    {$group.sGroupId}{$group.name}{$group.name} {$group.tag} {$group.groupemail}DeleteDelete
    diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/show_queue.tpl b/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/show_queue.tpl index eeda5813e..956b5e20d 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/show_queue.tpl +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/show_queue.tpl @@ -8,11 +8,11 @@
    - - - - - + + + + +
    Todo ticketsAll ticketsAll open ticketsTicket ArchiveNot Assigned TicketsTodo ticketsAll ticketsAll open ticketsTicket ArchiveNot Assigned Tickets

    Tickets

    - + @@ -129,8 +129,8 @@ {foreach from=$tickets item=ticket} - - + + @@ -139,19 +139,19 @@ {if $ticket.forwardedGroupName eq "0"} {$public_sgroup} {else} - {$ticket.forwardedGroupName} + {$ticket.forwardedGroupName} {/if} @@ -27,7 +27,7 @@ - + diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/show_sgroup.tpl b/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/show_sgroup.tpl index 7888996ed..2f0c29695 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/show_sgroup.tpl +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/show_sgroup.tpl @@ -34,7 +34,7 @@ ","
    {$ticket.tId}{$ticket.title}{if $ticket.assignedText neq ""} {$ticket.assignedText}{else} {$not_assigned} {/if}{$ticket.title}{if $ticket.assignedText neq ""} {$ticket.assignedText}{else} {$not_assigned} {/if} {$ticket.timestamp} {$ticket.category} {if $ticket.status eq 0}{else if $ticket.status eq 1}{else if $ticket.status eq 2}{/if}{$ticket.statusText} {if $ticket.assigned eq 0} - + {else if $ticket.assigned eq $user_id} -
    + diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/show_reply.tpl b/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/show_reply.tpl index b6aa90cb2..9b0a6296a 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/show_reply.tpl +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/show_reply.tpl @@ -8,8 +8,8 @@
    - - + +
    Show TicketShow Ticket LogShow TicketShow Ticket Log

    Reply ID#{$reply_id} of Ticket #{$ticket_id}

    Reply ID#{$reply_id} of Ticket #{$ticket_id}

    Add user to the list

    {if isset($isAdmin) && $isAdmin eq 'TRUE'} - + @@ -92,8 +92,8 @@ {foreach from=$userlist item=user} - - {if isset($isAdmin) && $isAdmin eq 'TRUE'}{/if} + + {if isset($isAdmin) && $isAdmin eq 'TRUE'}{/if} {/foreach}
    Username:
    {$user.tUserId}{$user.name}Delete{$user.name}Delete
    @@ -108,7 +108,7 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - +

    Mail settings

    - + diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/show_ticket.tpl b/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/show_ticket.tpl index fe27b2ce7..97f1d3c53 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/show_ticket.tpl +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/show_ticket.tpl @@ -8,9 +8,9 @@ @@ -74,13 +74,13 @@ {if $ticket_forwardedGroupName eq "0"} {$public_sgroup} {else} - {$ticket_forwardedGroupName} + {$ticket_forwardedGroupName} {/if} - + @@ -125,7 +125,7 @@ @@ -53,7 +53,7 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - +
    - {if isset($isMod) and $isMod eq "TRUE"}{/if} - - {if $hasInfo}{/if} + {if isset($isMod) and $isMod eq "TRUE"}{/if} + + {if $hasInfo}{/if}
    Show Ticket LogSend Other TicketShow Additional InfoShow Ticket LogSend Other TicketShow Additional Info
    Assigned To: {if $ticket_assignedTo neq ""} {$ticket_assignedToText}{else} {$not_assigned} {/if}Assigned To: {if $ticket_assignedTo neq ""} {$ticket_assignedToText}{else} {$not_assigned} {/if}
    - + @@ -193,13 +193,13 @@

    Ticket Assigning: {if $ticket_assignedTo eq 0} - + {else if $ticket_assignedTo eq $user_id} -
    + @@ -233,7 +233,7 @@

    @@ -29,7 +29,7 @@ - + @@ -64,31 +64,31 @@

    Forward to Group: - +

    - {if isset($isMod) and $isMod eq "TRUE"}{/if} - - + {if isset($isMod) and $isMod eq "TRUE"}{/if} + +
    Show Ticket LogSend Other TicketShow TicketShow Ticket LogSend Other TicketShow Ticket

    Additional Info For Ticket [#{$ticket_id}]

    Additional Info For Ticket [#{$ticket_id}]

    - + - + - + - + - + - + - +
    Shard ID: {$shard_id}
    User_Id: {$user_id}
    User Position: {$user_position}
    View Position: {$view_position}
    Client_Version: {$client_version}
    Patch_Version: {$patch_version}
    Server_Tick: {$server_tick}
    @@ -102,31 +102,31 @@ - + - + - + - + - + - + - +
    Memory: {$memory}
    Processor: {$processor}
    Cpu_Id: {$cpu_id}
    Cpu_Mask: {$cpu_mask}
    HT: {$ht}
    OS: {$os}
    NeL3D: {$nel3d}
    @@ -140,11 +140,11 @@ - + - +
    Connect_State: {$connect_state}
    Local_Address: {$local_address}
    diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/show_ticket_log.tpl b/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/show_ticket_log.tpl index 57dff23b6..f79735064 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/show_ticket_log.tpl +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/show_ticket_log.tpl @@ -8,7 +8,7 @@
    - +
    Show TicketShow Ticket
    - - -{/block} - diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/show_queue.tpl b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/show_queue.tpl deleted file mode 100644 index 956b5e20d..000000000 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/show_queue.tpl +++ /dev/null @@ -1,222 +0,0 @@ -{block name=content} - - - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - +
    -

    Title: {$ticket_title}

    +

    Title: {$ticket_title}

    diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/show_user.tpl b/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/show_user.tpl index 93110b895..51c5bb77a 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/show_user.tpl +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/show_user.tpl @@ -8,18 +8,18 @@ - - - - - - - - - - - - - - -{/block} - diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/layout_mod.tpl b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/layout_mod.tpl deleted file mode 100644 index 65d10c611..000000000 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/layout_mod.tpl +++ /dev/null @@ -1,18 +0,0 @@ -{extends file="layout.tpl"} -{block name=menu} - - - - - - - - - - - - - - -{/block} - diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/layout_user.tpl b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/layout_user.tpl deleted file mode 100644 index 233ba2ad4..000000000 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/layout_user.tpl +++ /dev/null @@ -1,13 +0,0 @@ -{extends file="layout.tpl"} -{block name=menu} - - - - - - - - - -{/block} - diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/login.tpl b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/login.tpl deleted file mode 100644 index 03d0bd7aa..000000000 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/login.tpl +++ /dev/null @@ -1,39 +0,0 @@ -

     

    -
    - - + + {if isset($isAdmin) and $isAdmin eq 'TRUE' and $target_id neq 1} {if $userPermission eq 1} - - + + {else if $userPermission eq 2 } - - + + {else if $userPermission eq 3 } - - + + {/if} {/if} @@ -138,7 +138,7 @@ {foreach from=$ticketlist item=ticket} - + diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/userlist.tpl b/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/userlist.tpl index 4e5f2a4ae..c2e226ca3 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/userlist.tpl +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/userlist.tpl @@ -44,24 +44,24 @@ {foreach from=$userlist item=element} - + {if $element.permission eq 1}{/if} {if $element.permission eq 2}{/if} {if $element.permission eq 3}{/if} @@ -73,11 +73,11 @@ diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/libinclude.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/libinclude.php index e5deb6030..b0031de73 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/libinclude.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/libinclude.php @@ -3,10 +3,14 @@ // Base include file for library functions for AMS // *********************************************** function __autoload( $className ){ - if(file_exists( '/home/daan/ryzom/ryzomcore/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/' . strtolower ( $className ) . '.php')){ + global $AMS_LIB; + global $SITEBASE; + if(file_exists( $AMS_LIB.'/autoload/' . strtolower ( $className ) . '.php')){ require_once 'autoload/' . strtolower ( $className ) . '.php'; } - if($className == "WebUsers") - require_once '/home/daan/ryzom/ryzomcore/code/ryzom/tools/server/ryzom_ams/www/html/autoload/' . strtolower ( $className ) . '.php'; + if($className == "WebUsers"){ + require_once $SITEBASE.'/autoload/' . strtolower ( $className ) . '.php'; } +} + diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/createticket.tpl b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/createticket.tpl deleted file mode 100644 index ec79cc661..000000000 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/createticket.tpl +++ /dev/null @@ -1,101 +0,0 @@ -{block name=content} - - - - -{/block} - - diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/dashboard.tpl b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/dashboard.tpl deleted file mode 100644 index 24b8c7878..000000000 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/dashboard.tpl +++ /dev/null @@ -1,81 +0,0 @@ -{block name=content} - - - - - - - - - - -{/block} - diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/index.tpl b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/index.tpl deleted file mode 100644 index 82495ff89..000000000 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/index.tpl +++ /dev/null @@ -1,81 +0,0 @@ -{config_load file="ams_lib.conf" section="setup"} - -
    -
    -{* bold and title are read from the config file *}
    -{if #bold#}{/if}
    -{* capitalize the first letters of each word of the title *}
    -Title: {#title#|capitalize}
    -{if #bold#}{/if}
    -
    -The current date and time is {$smarty.now|date_format:"%Y-%m-%d %H:%M:%S"}
    -
    -The value of global assigned variable $SCRIPT_NAME is {$SCRIPT_NAME}
    -
    -Example of accessing server environment variable SERVER_NAME: {$smarty.server.SERVER_NAME}
    -
    -The value of {ldelim}$Name{rdelim} is {$Name}
    -
    -variable modifier example of {ldelim}$Name|upper{rdelim}
    -
    -{$Name|upper}
    -
    -
    -An example of a section loop:
    -
    -{section name=outer 
    -loop=$FirstName}
    -{if $smarty.section.outer.index is odd by 2}
    -	{$smarty.section.outer.rownum} . {$FirstName[outer]} {$LastName[outer]}
    -{else}
    -	{$smarty.section.outer.rownum} * {$FirstName[outer]} {$LastName[outer]}
    -{/if}
    -{sectionelse}
    -	none
    -{/section}
    -
    -An example of section looped key values:
    -
    -{section name=sec1 loop=$contacts}
    -	phone: {$contacts[sec1].phone}
    - fax: {$contacts[sec1].fax}
    - cell: {$contacts[sec1].cell}
    -{/section} -

    - -testing strip tags -{strip} -

    Edit UserSend TicketEdit UserSend TicketMake ModeratorMake AdminMake ModeratorMake AdminDemote to UserMake AdminDemote to UserMake AdminDemote to UserDemote to ModeratorDemote to UserDemote to Moderator
    {$ticket.tId}{$ticket.title}{$ticket.title} {$ticket.timestamp} {$ticket.category}
    {$element.id}{$element.username}{$element.username} {$element.email}UserModeratorAdmin - Show User - Edit User + Show User + Edit User {if isset($isAdmin) and $isAdmin eq 'TRUE' and $element.id neq 1} {if $element.permission eq 1} - Make Moderator - Make Admin + Make Moderator + Make Admin {else if $element.permission eq 2 } - Demote to User - Make Admin + Demote to User + Make Admin {else if $element.permission eq 3 } - Demote to User - Demote to Moderator + Demote to User + Demote to Moderator {/if} {/if}
    - + {foreach from=$links item=link} - + {/foreach} - +
    ««{$link}{$link}»»
    - - - - - - - - - -

    Create a new ticket

    -
    - - - - - - - - - - -
    - - -
    -
    - - -
    - - -
    - - -
    - - - - - - - - - - - - - - - - - -
    Title: - -
    Category: - -
    Description:
    - - - - - {if $ingame} - - - - - - - - - - - - - - - - - - {/if} - -
    - -
    -
    -
    -
    - -
    - - - - - - - - - -

    {$home_title}

    -
    - - - - - - - - - - -
    - - -
    -
    - - - - -
    - - -
    - - -
    - - - - - - - - - - - - - -
    Tickets Waiting for Direct ActionTickets TodoNewest TicketTotal amount of Tickets
    {$nrAssignedWaiting}{$nrToDo}{$newestTicketTitle}{$nrTotalTickets}
    -
    -
    -
    - - -
    - - -
    -

    {$home_info}

    -

    This is the GSOC project of Daan Janssens mentored by Matthew Lagoe.

    -
    -
    -
    -
    - -
    - - - -
    - - This is a test - -
    -{/strip} - - - -This is an example of the html_select_date function: - -
    -{html_select_date start_year=1998 end_year=2010} -
    - -This is an example of the html_select_time function: - -
    -{html_select_time use_24_hours=false} -
    - -This is an example of the html_options function: - -
    - -
    - -{include file="footer.tpl"} diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/layout.tpl b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/layout.tpl deleted file mode 100644 index c98768e52..000000000 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/layout.tpl +++ /dev/null @@ -1,34 +0,0 @@ - - - - - Ryzom Account Management System - - - - - - - - - {block name=content}{/block} - - - -
    - - - - - {block name=menu}{/block} - - - -
    -
    - - - - - - diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/layout_admin.tpl b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/layout_admin.tpl deleted file mode 100644 index afe29e778..000000000 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/layout_admin.tpl +++ /dev/null @@ -1,19 +0,0 @@ -{extends file="layout.tpl"} -{block name=menu} -
    Dashboard
    Profile
    Settings
    |
    Users
    Queues
    Support Groups
    Dashboard
    Profile
    Settings
    |
    Users
    Queues
    Support Groups
    Profile
    Settings
    |
    Create New Ticket
    -
    -
    -

    {$login_info}

    -
    -
    -
    -

    - Username: - -

    - -

    - Password: - -

    - -

    - Remember me: -

    Remember me -

    - -

    - - -

    - -
    - - {if isset($login_error) and $login_error eq "TRUE"} -

    - {$login_error_message} -

    - {/if} -

    - {$login_register_message} {$login_register_message_here}! -

    -
    diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/register.tpl b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/register.tpl deleted file mode 100644 index 9c38b34a2..000000000 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/register.tpl +++ /dev/null @@ -1,115 +0,0 @@ -{config_load file="ams_lib.conf" section="setup"} -

    - {$title} -
    - -
    - {$welcome_message} -
    - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    {$username_tag} - - {if isset($Username)}{$Username}{/if}
    {$password_tag} - - {if isset($PASSWORD_ERROR) && $PASSWORD_ERROR eq "TRUE"}{$Password}{/if}
    {$cpassword_tag} - {if isset($CPASSWORD_ERROR) && $CPASSWORD_ERROR eq "TRUE"}{$ConfirmPass}{/if}
    {$email_tag} - - {if isset($EMAIL_ERROR) && $EMAIL_ERROR eq "TRUE"}{$Email}{/if}
    {$tac_tag}{$tac_message}
    - -
    - -
    - -
    - -
    - -
    - {$username_tooltip} -
    - - -
    - {$password_message} -
    - -
    - {$cpassword_message} -
    - -
    - {$email_message} -
    - -
    diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/sgroup_list.tpl b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/sgroup_list.tpl deleted file mode 100644 index 3a6dcc52f..000000000 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/sgroup_list.tpl +++ /dev/null @@ -1,146 +0,0 @@ -{block name=content} - -
    - - - - - - - - - -

    Support Groups

    -
    - - - - - - - - - - -
    - - -
    -
    - - {if isset($isAdmin) && $isAdmin eq 'TRUE'} - - {/if} - -
    - - -
    - - -
    -

    Add a Support group

    - -
    - - - - - - -
    - - - - - - - - - - - - - - - -
    Group name:
    Group Tag:
    Group EmailAddress:
    -
    - - - - - - - - - - - - - - - -
    IMAP MailServer IP:
    IMAP Username:
    IMAP Password:
    -
    - -

    - - - {if isset($RESULT_OF_ADDING) and $RESULT_OF_ADDING eq "SUCCESS"} -

    - {$group_success} -

    - {else if isset($RESULT_OF_ADDING) and $RESULT_OF_ADDING eq "NAME_TAKEN"} -

    - {$group_name_taken} -

    - {else if isset($RESULT_OF_ADDING) and $RESULT_OF_ADDING eq "TAG_TAKEN"} -

    - {$group_tag_taken} -

    - {else if isset($RESULT_OF_ADDING) and $RESULT_OF_ADDING eq "SIZE_ERROR"} -

    - {$group_size_error} -

    - {/if} -
    -
    -
    -
    - - -
    - - -
    -

    All groups

    - - - - - - - {if isset($isAdmin) && $isAdmin eq 'TRUE'}{/if} - - - {foreach from=$grouplist item=group} - - - - - - {if isset($isAdmin) && $isAdmin eq 'TRUE'}{/if} - - {/foreach} -
    IDNameTagEmailAction
    {$group.sGroupId}{$group.name}{$group.tag}{$group.groupemail}Delete
    -
    -
    -
    -
    - -
    - - - - - - - -
    - - - - -
    - - - - - - - - -
    Todo ticketsAll ticketsAll open ticketsTicket ArchiveNot Assigned Tickets
    -
    -
    -
    - - - - - - - - - -

    Ticket Queue: {$queue_view}

    -
    - - - - - - - - - - -{/block} - diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/show_reply.tpl b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/show_reply.tpl deleted file mode 100644 index 9b0a6296a..000000000 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/show_reply.tpl +++ /dev/null @@ -1,88 +0,0 @@ -{block name=content} - - - - -{/block} - - diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/show_sgroup.tpl b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/show_sgroup.tpl deleted file mode 100644 index 2f0c29695..000000000 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/show_sgroup.tpl +++ /dev/null @@ -1,171 +0,0 @@ -{block name=content} - - - - -{/block} - diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/show_ticket.tpl b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/show_ticket.tpl deleted file mode 100644 index 97f1d3c53..000000000 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/show_ticket.tpl +++ /dev/null @@ -1,285 +0,0 @@ -{block name=content} - - - - - -{/block} - diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/show_ticket_info.tpl b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/show_ticket_info.tpl deleted file mode 100644 index 4431331c4..000000000 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/show_ticket_info.tpl +++ /dev/null @@ -1,168 +0,0 @@ -{block name=content} - - - - - -{/block} - diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/show_ticket_log.tpl b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/show_ticket_log.tpl deleted file mode 100644 index f79735064..000000000 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/show_ticket_log.tpl +++ /dev/null @@ -1,88 +0,0 @@ -{block name=content} - - - - -{/block} - \ No newline at end of file diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/show_user.tpl b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/show_user.tpl deleted file mode 100644 index 51c5bb77a..000000000 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/show_user.tpl +++ /dev/null @@ -1,163 +0,0 @@ -{block name=content} - - - - - -{/block} - diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/userlist.tpl b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/userlist.tpl deleted file mode 100644 index c2e226ca3..000000000 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/ingame_templates/userlist.tpl +++ /dev/null @@ -1,96 +0,0 @@ -{block name=content} - - - -{/block} - diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/add_sgroup.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/add_sgroup.php deleted file mode 100644 index 94157192b..000000000 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/add_sgroup.php +++ /dev/null @@ -1,45 +0,0 @@ -getPermission(); - $result['no_visible_elements'] = 'FALSE'; - $result['username'] = $_SESSION['user']; - //global $SITEBASE; - //require($SITEBASE . '/inc/sgroup_list.php'); - //$result= array_merge($result, sgroup_list()); - //return helpers :: loadtemplate( 'sgroup_list', $result, true); - if (Helpers::check_if_game_client()) { - header("Location: ".$INGAME_WEBPATH."?page=sgroup_list"); - }else{ - header("Location: ".$WEBPATH."?page=sgroup_list"); - } - exit; - - }else{ - //ERROR: No access! - $_SESSION['error_code'] = "403"; - header("Location: index.php?page=error"); - exit; - } - }else{ - //ERROR: not logged in! - header("Location: index.php"); - exit; - } - -} \ No newline at end of file diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/add_user_to_sgroup.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/add_user_to_sgroup.php deleted file mode 100644 index e4d46e610..000000000 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/add_user_to_sgroup.php +++ /dev/null @@ -1,48 +0,0 @@ -getPermission()>1){ - $result['RESULT_OF_ADDING'] = Support_Group::addUserToSupportGroup($user_id, $id); - }else{ - $result['RESULT_OF_ADDING'] = "NOT_MOD_OR_ADMIN"; - } - - }else{ - $result['RESULT_OF_ADDING'] = "USER_NOT_EXISTING"; - } - $result['permission'] = unserialize($_SESSION['ticket_user'])->getPermission(); - $result['no_visible_elements'] = 'FALSE'; - $result['username'] = $_SESSION['user']; - //global $SITEBASE; - //require_once($SITEBASE . 'inc/show_sgroup.php'); - //$result= array_merge($result, show_sgroup()); - //helpers :: loadtemplate( 'show_sgroup', $result); - if (Helpers::check_if_game_client()) { - header("Location: ".$INGAME_WEBPATH."?page=show_sgroup&id=".$id); - }else{ - header("Location: ".$WEBPATH."?page=show_sgroup&id=".$id); - } - exit; - - }else{ - //ERROR: No access! - $_SESSION['error_code'] = "403"; - header("Location: index.php?page=error"); - exit; - } - }else{ - //ERROR: not logged in! - header("Location: index.php"); - exit; - } - -} \ No newline at end of file diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/change_mail.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/change_mail.php deleted file mode 100644 index f04648343..000000000 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/change_mail.php +++ /dev/null @@ -1,93 +0,0 @@ -getUsername(); - } - - $webUser = new WebUsers($_POST['target_id']); - $reply = $webUser->checkEmail($_POST['NewEmail']); - - global $SITEBASE; - require_once($SITEBASE . '/inc/settings.php'); - $result = settings(); - - if ( $reply != "success" ){ - $result['EMAIL_ERROR'] = 'TRUE'; - }else{ - $result['EMAIL_ERROR'] = 'FALSE'; - } - $result['prevNewEmail'] = filter_var($_POST["NewEmail"], FILTER_SANITIZE_EMAIL); - - if ($reply== "success"){ - $status = WebUsers::setEmail($target_username, filter_var($_POST["NewEmail"], FILTER_SANITIZE_EMAIL) ); - if($status == 'ok'){ - $result['SUCCESS_MAIL'] = "OK"; - }else if($status == 'shardoffline'){ - $result['SUCCESS_MAIL'] = "SHARDOFF"; - } - $result['permission'] = unserialize($_SESSION['ticket_user'])->getPermission(); - $result['no_visible_elements'] = 'FALSE'; - $result['username'] = $_SESSION['user']; - $result['target_id'] = $_POST['target_id']; - if(isset($_GET['id'])){ - if(Ticket_User::isMod(unserialize($_SESSION['ticket_user'])) && ($_POST['target_id'] != $_SESSION['id'])){ - $result['isMod'] = "TRUE"; - } - } - helpers :: loadtemplate( 'settings', $result); - exit; - - }else{ - $result['EMAIL'] = $reply; - $result['permission'] = unserialize($_SESSION['ticket_user'])->getPermission(); - $result['no_visible_elements'] = 'FALSE'; - $result['username'] = $_SESSION['user']; - $result['target_id'] = $_POST['target_id']; - if(isset($_GET['id'])){ - if(Ticket_User::isMod(unserialize($_SESSION['ticket_user'])) && ($_POST['target_id'] != $_SESSION['id'])){ - $result['isMod'] = "TRUE"; - } - } - helpers :: loadtemplate( 'settings', $result); - exit; - } - - }else{ - //ERROR: permission denied! - $_SESSION['error_code'] = "403"; - header("Location: index.php?page=error"); - exit; - } - - }else{ - //ERROR: The form was not filled in correclty - header("Location: index.php?page=settings"); - exit; - } - }else{ - //ERROR: user is not logged in - header("Location: index.php"); - exit; - } - - }catch (PDOException $e) { - //go to error page or something, because can't access website db - print_r($e); - exit; - } - -} - diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/change_password.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/change_password.php deleted file mode 100644 index 420d78103..000000000 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/change_password.php +++ /dev/null @@ -1,88 +0,0 @@ -getUsername(); - //isAdmin is true when it's the admin, but the target_id != own id - $adminChangesOther = true; - $_POST["CurrentPass"] = "dummypass"; - } - - $webUser = new WebUsers($_POST['target_id']); - $params = Array( 'user' => $target_username, 'CurrentPass' => $_POST["CurrentPass"], 'NewPass' => $_POST["NewPass"], 'ConfirmNewPass' => $_POST["ConfirmNewPass"], 'adminChangesOther' => $adminChangesOther); - $result = $webUser->check_change_password($params); - if ($result == "success"){ - //edit stuff into db - global $SITEBASE; - require_once($SITEBASE . '/inc/settings.php'); - $succresult = settings(); - $status = WebUsers::setPassword($target_username, $_POST["NewPass"]); - if($status == 'ok'){ - $succresult['SUCCESS_PASS'] = "OK"; - }else if($status == 'shardoffline'){ - $succresult['SUCCESS_PASS'] = "SHARDOFF"; - } - $succresult['permission'] = unserialize($_SESSION['ticket_user'])->getPermission(); - $succresult['no_visible_elements'] = 'FALSE'; - $succresult['username'] = $_SESSION['user']; - $succresult['target_id'] = $_POST['target_id']; - helpers :: loadtemplate( 'settings', $succresult); - exit; - - }else{ - - $result['prevCurrentPass'] = filter_var($_POST["CurrentPass"], FILTER_SANITIZE_STRING); - $result['prevNewPass'] = filter_var($_POST["NewPass"], FILTER_SANITIZE_STRING); - $result['prevConfirmNewPass'] = filter_var($_POST["ConfirmNewPass"], FILTER_SANITIZE_STRING); - $result['permission'] = unserialize($_SESSION['ticket_user'])->getPermission(); - $result['no_visible_elements'] = 'FALSE'; - $result['username'] = $_SESSION['user']; - $result['target_id'] = $_POST['target_id']; - - global $SITEBASE; - require_once($SITEBASE . '/inc/settings.php'); - $settings = settings(); - - $result = array_merge($result,$settings); - helpers :: loadtemplate( 'settings', $result); - exit; - } - - }else{ - //ERROR: permission denied! - $_SESSION['error_code'] = "403"; - header("Location: index.php?page=error"); - exit; - } - - }else{ - //ERROR: The form was not filled in correclty - header("Location: index.php?page=settings"); - exit; - } - }else{ - //ERROR: user is not logged in - header("Location: index.php"); - exit; - } - - }catch (PDOException $e) { - //go to error page or something, because can't access website db - print_r($e); - exit; - } - -} - diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/create_ticket.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/create_ticket.php deleted file mode 100644 index cf497dabf..000000000 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/create_ticket.php +++ /dev/null @@ -1,58 +0,0 @@ -getTUserId(); - }else{ - $author= Ticket_User::constr_ExternId($_POST['target_id'])->getTUserId(); - } - $ticket_id = Ticket::create_Ticket($title, $content, $category, $author, unserialize($_SESSION['ticket_user'])->getTUserId(),0, $_POST); - if (Helpers::check_if_game_client()) { - header("Location: ".$INGAME_WEBPATH."?page=show_ticket&id=".$ticket_id); - }else{ - header("Location: ".$WEBPATH."?page=show_ticket&id=".$ticket_id); - } - exit; - - }catch (PDOException $e) { - //ERROR: LIB DB is not online! - print_r($e); - exit; - header("Location: index.php"); - exit; - } - - }else{ - //ERROR: permission denied! - $_SESSION['error_code'] = "403"; - header("Location: index.php?page=error"); - exit; - } - - }else{ - //ERROR: The form was not filled in correclty - header("Location: index.php?page=create_ticket"); - exit; - } - }else{ - //ERROR: user is not logged in - header("Location: index.php"); - exit; - } - -} - diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/login.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/login.php deleted file mode 100644 index 54d6b0672..000000000 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/login.php +++ /dev/null @@ -1,41 +0,0 @@ -getLanguage(); - - //go back to the index page. - if (Helpers::check_if_game_client()) { - header( 'Location: '. $INGAME_WEBPATH ); - }else{ - header( 'Location: '. $WEBPATH ); - } - exit; - }else{ - //handle login failure - $result = Array(); - $result['login_error'] = 'TRUE'; - $result['no_visible_elements'] = 'TRUE'; - helpers :: loadtemplate( 'login', $result); - exit; - } - - - }catch (PDOException $e) { - //go to error page or something, because can't access website db - print_r($e); - exit; - } - -} \ No newline at end of file diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/modify_email_of_sgroup.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/modify_email_of_sgroup.php deleted file mode 100644 index 9befa9ae4..000000000 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/modify_email_of_sgroup.php +++ /dev/null @@ -1,59 +0,0 @@ -setGroupEmail($groupemail); - $group->setIMAP_MailServer(filter_var($_POST['IMAP_MailServer'],FILTER_SANITIZE_STRING)); - $group->setIMAP_Username(filter_var($_POST['IMAP_Username'],FILTER_SANITIZE_STRING)); - - //encrypt password! - global $cfg; - $crypter = new MyCrypt($cfg['crypt']); - $enc_password = $crypter->encrypt($password); - $group->setIMAP_Password($enc_password); - $group->update(); - $result['RESULT_OF_MODIFYING'] = "SUCCESS"; - if($password == ""){ - $result['RESULT_OF_MODIFYING'] = "NO_PASSWORD"; - } - }else{ - $result['RESULT_OF_MODIFYING'] = "EMAIL_NOT_VALID"; - } - - $result['permission'] = unserialize($_SESSION['ticket_user'])->getPermission(); - $result['no_visible_elements'] = 'FALSE'; - $result['username'] = $_SESSION['user']; - //global $SITEBASE; - //require_once($SITEBASE . 'inc/show_sgroup.php'); - //$result= array_merge($result, show_sgroup()); - //helpers :: loadtemplate( 'show_sgroup', $result); - if (Helpers::check_if_game_client()) { - header("Location: ".$INGAME_WEBPATH."?page=show_sgroup&id=".$sgroupid); - }else{ - header("Location: ".$WEBPATH."?page=show_sgroup&id=".$sgroupid); - } - exit; - - }else{ - //ERROR: No access! - $_SESSION['error_code'] = "403"; - header("Location: index.php?page=error"); - exit; - } - }else{ - //ERROR: not logged in! - header("Location: index.php"); - exit; - } - -} \ No newline at end of file diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/reply_on_ticket.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/reply_on_ticket.php deleted file mode 100644 index e7841906f..000000000 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/func/reply_on_ticket.php +++ /dev/null @@ -1,59 +0,0 @@ -load_With_TId($ticket_id); - - if(($target_ticket->getAuthor() == unserialize($_SESSION['ticket_user'])->getTUserId()) || Ticket_User::isMod(unserialize($_SESSION['ticket_user'])) ){ - - try{ - $author = unserialize($_SESSION['ticket_user'])->getTUserId(); - if(isset($_POST['Content'])){ - $content = $_POST['Content']; - }else{ - $content=""; - } - $hidden = 0; - if(isset($_POST['hidden']) && Ticket_User::isMod(unserialize($_SESSION['ticket_user']))){ - $hidden = 1; - } - Ticket::createReply($content, $author, $ticket_id, $hidden); - - if(isset($_POST['ChangeStatus']) && isset($_POST['ChangePriority']) && Ticket_User::isMod(unserialize($_SESSION['ticket_user']))){ - $newStatus = filter_var($_POST['ChangeStatus'], FILTER_SANITIZE_NUMBER_INT); - $newPriority = filter_var($_POST['ChangePriority'], FILTER_SANITIZE_NUMBER_INT); - Ticket::updateTicketStatusAndPriority($ticket_id,$newStatus, $newPriority, $author); - } - if (Helpers::check_if_game_client()) { - header("Location: ".$INGAME_WEBPATH."?page=show_ticket&id=".$ticket_id); - }else{ - header("Location: ".$WEBPATH."?page=show_ticket&id=".$ticket_id); - } - exit; - - }catch (PDOException $e) { - //ERROR: LIB DB is not online! - print_r($e); - //header("Location: index.php"); - exit; - } - - }else{ - //ERROR: No access! - $_SESSION['error_code'] = "403"; - header("Location: index.php?page=error"); - exit; - } - }else{ - //ERROR: not logged in! - header("Location: index.php"); - exit; - } - -} \ No newline at end of file diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/change_permission.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/change_permission.php deleted file mode 100644 index 35f561290..000000000 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/change_permission.php +++ /dev/null @@ -1,49 +0,0 @@ -getTUserId(), $value); - if (Helpers::check_if_game_client()) { - header("Location: ".$INGAME_WEBPATH."?page=show_user&id=".$user_id); - }else{ - header("Location: ".$WEBPATH."?page=show_user&id=".$user_id); - } - exit; - - - }else{ - //ERROR: GET PARAMS not given or trying to change admin - if (Helpers::check_if_game_client()) { - header("Location: ".$INGAME_WEBPATH."?page=show_user&id=".$user_id); - }else{ - header("Location: ".$WEBPATH."?page=show_user&id=".$user_id); - } - exit; - } - - }else{ - //ERROR: No access! - $_SESSION['error_code'] = "403"; - header("Location: index.php?page=error"); - exit; - - } - - }else{ - //ERROR: not logged in! - header("Location: index.php"); - exit; - } - - -} \ No newline at end of file diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/createticket.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/createticket.php deleted file mode 100644 index 3fdb54b84..000000000 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/createticket.php +++ /dev/null @@ -1,48 +0,0 @@ -getTUserId(); - $result['nrToDo'] = Ticket_Queue_Handler::getNrOfTicketsToDo(unserialize($_SESSION['ticket_user'])->getTUserId()); - $result['nrAssignedWaiting'] = Ticket_Queue_Handler::getNrOfTicketsAssignedWaiting(unserialize($_SESSION['ticket_user'])->getTUserId()); - $result['nrTotalTickets'] = Ticket_Queue_Handler::getNrOfTickets(); - $ticket = Ticket_Queue_Handler::getNewestTicket(); - $result['newestTicketId'] = $ticket->getTId(); - $result['newestTicketTitle'] = $ticket->getTitle(); - $result['newestTicketAuthor'] = Ticket_User::get_username_from_id($ticket->getAuthor()); - global $INGAME_WEBPATH; - $result['ingame_webpath'] = $INGAME_WEBPATH; - return $result; - - }else{ - //ERROR: No access! - $_SESSION['error_code'] = "403"; - header("Location: index.php?page=error"); - exit; - - } - - }else{ - //ERROR: not logged in! - header("Location: index.php"); - exit; - } - - -} \ No newline at end of file diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/login.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/login.php deleted file mode 100644 index fca774ddc..000000000 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/login.php +++ /dev/null @@ -1,21 +0,0 @@ -getTUserId(); - $queueArray = array(); - $queue_handler = new Ticket_Queue_handler(); - - //Pagination Base Links - if (Helpers::check_if_game_client()) { - $result['pagination_base_link'] = $INGAME_WEBPATH."?page=show_queue&get=".$result['queue_view'] ; - }else{ - $result['pagination_base_link'] = $WEBPATH."?page=show_queue&get=".$result['queue_view'] ; - } - - //form url to keep the getters constant - - if (Helpers::check_if_game_client()) { - $result['getURL'] = $INGAME_WEBPATH."?page=show_queue&get=" . $result['queue_view']; - }else{ - $result['getURL'] = $WEBPATH."?page=show_queue&get=" . $result['queue_view']; - } - - if(isset($_GET['pagenum'])){ - $result['getURL'] = $result['getURL'] . "&pagenum=".$_GET['pagenum']; - } - - if(isset($_GET['get']) && ($_GET['get'] == "create") && isset($_GET['userid']) && isset($_GET['groupid']) && isset($_GET['what']) && isset($_GET['how']) && isset($_GET['who'])){ - $userid = filter_var($_GET['userid'], FILTER_SANITIZE_NUMBER_INT); - $groupid = filter_var($_GET['groupid'], FILTER_SANITIZE_NUMBER_INT); - $what = filter_var($_GET['what'], FILTER_SANITIZE_STRING); - $how = filter_var($_GET['how'], FILTER_SANITIZE_STRING); - $who = filter_var($_GET['who'], FILTER_SANITIZE_STRING); - $queue_handler->CreateQueue($userid, $groupid, $what, $how, $who); - - if (Helpers::check_if_game_client()) { - $result['pagination_base_link'] = $INGAME_WEBPATH."?page=show_queue&get=create&userid=".$userid."&groupid=".$groupid."&what=".$what."&how=".$how."&who=".$who; - }else{ - $result['pagination_base_link'] = $WEBPATH."?page=show_queue&get=create&userid=".$userid."&groupid=".$groupid."&what=".$what."&how=".$how."&who=".$who; - } - - - $result['prev_created_userid'] = $userid; - $result['prev_created_groupid'] = $groupid; - $result['prev_created_what'] = $what; - $result['prev_created_how'] = $how; - $result['prev_created_who'] = $who; - - $result['getURL'] = $result['getURL'] . "&userid=".$userid."&groupid=".$groupid."&what=".$what."&how=".$how."&who=".$who; - - } - - //if an action is set - if(isset($_POST['action'])){ - switch($_POST['action']){ - case "assignTicket": - $ticket_id = filter_var($_POST['ticket_id'], FILTER_SANITIZE_NUMBER_INT); - $result['ACTION_RESULT'] = Ticket::assignTicket($user_id, $ticket_id); - break; - - case "unAssignTicket": - $ticket_id = filter_var($_POST['ticket_id'], FILTER_SANITIZE_NUMBER_INT); - $result['ACTION_RESULT'] = Ticket::unAssignTicket($user_id, $ticket_id); - break; - - case "create_queue": - $userid = filter_var($_POST['userid'], FILTER_SANITIZE_NUMBER_INT); - $groupid = filter_var($_POST['groupid'], FILTER_SANITIZE_NUMBER_INT); - $what = filter_var($_POST['what'], FILTER_SANITIZE_STRING); - $how = filter_var($_POST['how'], FILTER_SANITIZE_STRING); - $who = filter_var($_POST['who'], FILTER_SANITIZE_STRING); - $queue_handler->CreateQueue($userid, $groupid, $what, $how, $who); - if (Helpers::check_if_game_client()) { - $result['pagination_base_link'] = $INGAME_WEBPATH."?page=show_queue&get=create&userid=".$userid."&groupid=".$groupid."&what=".$what."&how=".$how."&who=".$who; - }else{ - $result['pagination_base_link'] = $WEBPATH."?page=show_queue&get=create&userid=".$userid."&groupid=".$groupid."&what=".$what."&how=".$how."&who=".$who; - } - $result['prev_created_userid'] = $userid; - $result['prev_created_groupid'] = $groupid; - $result['prev_created_what'] = $what; - $result['prev_created_how'] = $how; - $result['prev_created_who'] = $who; - $result['getURL'] = $result['getURL'] . "&userid=".$userid."&groupid=".$groupid."&what=".$what."&how=".$how."&who=".$who; - - break; - - } - } - - $queueArray = $queue_handler->getTickets($result['queue_view'], $user_id); - - //pagination - $result['links'] = $queue_handler->getPagination()->getLinks(5); - $result['lastPage'] = $queue_handler->getPagination()->getLast(); - $result['currentPage'] = $queue_handler->getPagination()->getCurrent(); - - - //if queue_view is a valid parameter value - if ($queueArray != "ERROR"){ - $result['tickets'] = Gui_Elements::make_table($queueArray, Array("getTId","getTitle","getTimestamp","getAuthor()->getExternId","getTicket_Category()->getName","getStatus","getStatusText","getAssigned","getForwardedGroupName","getForwardedGroupId"), Array("tId","title","timestamp","authorExtern","category","status","statusText","assigned","forwardedGroupName","forwardedGroupId")); - $i = 0; - foreach( $result['tickets'] as $ticket){ - $web_author = new WebUsers($ticket['authorExtern']); - $result['tickets'][$i]['author'] = $web_author->getUsername(); - $web_assigned = new WebUsers($ticket['assigned']); - $result['tickets'][$i]['assignedText'] = $web_assigned->getUsername(); - $result['tickets'][$i]['timestamp_elapsed'] = Gui_Elements::time_elapsed_string($ticket['timestamp']); - $i++; - } - $result['user_id'] = unserialize($_SESSION['ticket_user'])->getTUserId(); - - //Queue creator field info - $result['grouplist'] = Gui_Elements::make_table(Support_Group::getGroups(), Array("getSGroupId","getName"), Array("sGroupId","name")); - $result['teamlist'] = Gui_Elements::make_table(Ticket_User::getModsAndAdmins(), Array("getTUserId","getExternId"), Array("tUserId","externId")); - $i = 0; - foreach( $result['teamlist'] as $member){ - $web_teammember = new Webusers($member['externId']); - $result['teamlist'][$i]['name'] = $web_teammember->getUsername(); - $i++; - } - global $INGAME_WEBPATH; - $result['ingame_webpath'] = $INGAME_WEBPATH; - return $result; - - }else{ - - //ERROR: Doesn't exist! - $_SESSION['error_code'] = "404"; - header("Location: ams?page=error"); - exit; - } - - }else{ - //ERROR: No access! - $_SESSION['error_code'] = "403"; - header("Location: index.php?page=error"); - exit; - } - }else{ - //ERROR: not logged in! - header("Location: index.php"); - exit; - } - -} \ No newline at end of file diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/show_reply.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/show_reply.php deleted file mode 100644 index 96cec0a6a..000000000 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/show_reply.php +++ /dev/null @@ -1,48 +0,0 @@ -load_With_TReplyId($result['reply_id']); - - - $ticket = new Ticket(); - $ticket->load_With_TId($reply->getTicket()); - - if(( $ticket->getAuthor() == unserialize($_SESSION['ticket_user'])->getTUserId() && ! $reply->getHidden()) || Ticket_User::isMod(unserialize($_SESSION['ticket_user']) )){ - $content = new Ticket_Content(); - $content->load_With_TContentId($reply->getContent()); - - $author = new Ticket_User(); - $author->load_With_TUserId($reply->getAuthor()); - - $result['hidden'] = $reply->getHidden(); - $result['ticket_id'] = $reply->getTicket(); - $result['reply_timestamp'] = $reply->getTimestamp(); - $result['author_permission'] = $author->getPermission(); - $result['reply_content'] = $content->getContent(); - $result['author'] = $author->getExternId(); - $webUser = new WebUsers($author->getExternId()); - $result['authorName'] = $webUser->getUsername(); - if(Ticket_User::isMod(unserialize($_SESSION['ticket_user']))){ - $result['isMod'] = "TRUE"; - } - global $INGAME_WEBPATH; - $result['ingame_webpath'] = $INGAME_WEBPATH; - return $result; - - }else{ - //ERROR: No access! - $_SESSION['error_code'] = "403"; - header("Location: index.php?page=error"); - exit; - } - }else{ - //ERROR: not logged in! - header("Location: index.php"); - exit; - } -} \ No newline at end of file diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/show_sgroup.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/show_sgroup.php deleted file mode 100644 index edf1ba0a9..000000000 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/show_sgroup.php +++ /dev/null @@ -1,66 +0,0 @@ -getName(); - $result['groupemail'] = $group->getGroupEmail(); - $result['imap_mailserver'] = $group->getIMAP_MailServer(); - $result['imap_username'] = $group->getIMAP_Username(); - $result['userlist'] = Gui_Elements::make_table(Support_Group::getAllUsersOfSupportGroup($result['target_id']), Array("getTUserId","getPermission","getExternId"), Array("tUserId","permission","externId")); - $i = 0; - foreach( $result['userlist'] as $user){ - $webuser = new Webusers($user['externId']); - $result['userlist'][$i]['name'] = $webuser->getUsername(); - $i++; - } - global $INGAME_WEBPATH; - $result['ingame_webpath'] = $INGAME_WEBPATH; - return $result; - - - }else{ - - //ERROR: No page specified! - $_SESSION['error_code'] = "404"; - header("Location: ams?page=error"); - exit; - } - - }else{ - //ERROR: No access! - $_SESSION['error_code'] = "403"; - header("Location: index.php?page=error"); - exit; - } - }else{ - //ERROR: not logged in! - header("Location: index.php"); - exit; - } - -} \ No newline at end of file diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/show_ticket.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/show_ticket.php deleted file mode 100644 index 4ecddb0b7..000000000 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/show_ticket.php +++ /dev/null @@ -1,86 +0,0 @@ -getTUserId(); - $result['ticket_id'] = filter_var($_GET['id'], FILTER_SANITIZE_NUMBER_INT); - $target_ticket = new Ticket(); - $target_ticket->load_With_TId($result['ticket_id']); - - if(Ticket_User::isMod(unserialize($_SESSION['ticket_user'] ))){ - if(isset($_POST['action'])){ - switch($_POST['action']){ - case "forward": - $ticket_id = filter_var($_POST['ticket_id'], FILTER_SANITIZE_NUMBER_INT); - $group_id = filter_var($_POST['group'], FILTER_SANITIZE_NUMBER_INT); - $result['ACTION_RESULT'] = Ticket::forwardTicket($result['user_id'], $ticket_id, $group_id); - break; - case "assignTicket": - $ticket_id = filter_var($_POST['ticket_id'], FILTER_SANITIZE_NUMBER_INT); - $result['ACTION_RESULT'] = Ticket::assignTicket($result['user_id'] , $ticket_id); - break; - case "unAssignTicket": - $ticket_id = filter_var($_POST['ticket_id'], FILTER_SANITIZE_NUMBER_INT); - $result['ACTION_RESULT'] = Ticket::unAssignTicket($result['user_id'], $ticket_id); - break; - - } - } - } - - if(($target_ticket->getAuthor() == unserialize($_SESSION['ticket_user'])->getTUserId()) || Ticket_User::isMod(unserialize($_SESSION['ticket_user']) )){ - - $show_as_admin = false; - if(Ticket_User::isMod(unserialize($_SESSION['ticket_user']))){ - $show_as_admin = true; - } - - $entire_ticket = Ticket::getEntireTicket( $result['ticket_id'],$show_as_admin); - Ticket_Log::createLogEntry($result['ticket_id'],unserialize($_SESSION['ticket_user'])->getTUserId(), 3); - $result['ticket_tId'] = $entire_ticket['ticket_obj']->getTId(); - $result['ticket_forwardedGroupName'] = $entire_ticket['ticket_obj']->getForwardedGroupName(); - $result['ticket_forwardedGroupId'] = $entire_ticket['ticket_obj']->getForwardedGroupId(); - $result['ticket_title'] = $entire_ticket['ticket_obj']->getTitle(); - $result['ticket_timestamp'] = $entire_ticket['ticket_obj']->getTimestamp(); - $result['ticket_status'] = $entire_ticket['ticket_obj']->getStatus(); - $result['ticket_author'] = $entire_ticket['ticket_obj']->getAuthor(); - $result['ticket_prioritytext'] = $entire_ticket['ticket_obj']->getPriorityText(); - $result['ticket_priorities'] = Ticket::getPriorityArray(); - $result['ticket_priority'] = $entire_ticket['ticket_obj']->getPriority(); - $result['ticket_statustext'] = $entire_ticket['ticket_obj']->getStatusText(); - $result['ticket_lastupdate'] = Gui_Elements::time_elapsed_string(Ticket::getLatestReply($result['ticket_id'])->getTimestamp()); - $result['ticket_category'] = $entire_ticket['ticket_obj']->getCategoryName(); - $webUser = new WebUsers(Assigned::getUserAssignedToTicket($result['ticket_tId'])); - $result['ticket_assignedToText'] = $webUser->getUsername(); - $result['ticket_assignedTo'] = Assigned::getUserAssignedToTicket($result['ticket_tId']); - $result['ticket_replies'] = Gui_Elements::make_table($entire_ticket['reply_array'], Array("getTReplyId","getContent()->getContent","getTimestamp","getAuthor()->getExternId","getAuthor()->getPermission","getHidden"), Array("tReplyId","replyContent","timestamp","authorExtern","permission","hidden")); - $i = 0; - foreach( $result['ticket_replies'] as $reply){ - $webReplyUser = new WebUsers($reply['authorExtern']); - $result['ticket_replies'][$i]['author'] = $webReplyUser->getUsername(); - $i++; - } - if(Ticket_User::isMod(unserialize($_SESSION['ticket_user']))){ - $result['isMod'] = "TRUE"; - $result['statusList'] = Ticket::getStatusArray(); - $result['sGroups'] = Gui_Elements::make_table_with_key_is_id(Support_Group::getAllSupportGroups(), Array("getName"), "getSGroupId" ); - } - $result['hasInfo'] = $target_ticket->hasInfo(); - global $INGAME_WEBPATH; - $result['ingame_webpath'] = $INGAME_WEBPATH; - return $result; - - }else{ - //ERROR: No access! - $_SESSION['error_code'] = "403"; - header("Location: index.php?page=error"); - exit; - } - }else{ - //ERROR: not logged in! - header("Location: index.php"); - exit; - } -} \ No newline at end of file diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/show_ticket_info.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/show_ticket_info.php deleted file mode 100644 index 4e43c7aad..000000000 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/show_ticket_info.php +++ /dev/null @@ -1,55 +0,0 @@ -load_With_TId($result['ticket_id']); - - if( $target_ticket->hasInfo() && (($target_ticket->getAuthor() == unserialize($_SESSION['ticket_user'])->getTUserId()) || Ticket_User::isMod(unserialize($_SESSION['ticket_user']) ))){ - $result['ticket_title'] = $target_ticket->getTitle(); - $result['ticket_author'] = $target_ticket->getAuthor(); - - $ticket_info = new Ticket_Info(); - $ticket_info->load_With_Ticket($result['ticket_id']); - $result['shard_id'] = $ticket_info->getShardId(); - $result['user_position'] = $ticket_info->getUser_Position(); - $result['view_position'] = $ticket_info->getView_Position(); - $result['client_version'] = $ticket_info->getClient_Version(); - $result['patch_version'] = $ticket_info->getPatch_Version(); - $result['server_tick'] = $ticket_info->getServer_Tick(); - $result['connect_state'] = $ticket_info->getConnect_State(); - $result['local_address'] = $ticket_info->getLocal_Address(); - $result['memory'] = $ticket_info->getMemory(); - $result['os'] = $ticket_info->getOS(); - $result['processor'] = $ticket_info->getProcessor(); - $result['cpu_id'] = $ticket_info->getCPUId(); - $result['cpu_mask'] = $ticket_info->getCPU_Mask(); - $result['ht'] = $ticket_info->getHT(); - $result['nel3d'] = $ticket_info->getNel3D(); - $result['user_id'] = $ticket_info->getUser_Id(); - global $IMAGELOC_WEBPATH; - $result['IMAGELOC_WEBPATH'] = $IMAGELOC_WEBPATH; - - if(Ticket_User::isMod(unserialize($_SESSION['ticket_user']))){ - $result['isMod'] = "TRUE"; - } - global $INGAME_WEBPATH; - $result['ingame_webpath'] = $INGAME_WEBPATH; - return $result; - - }else{ - //ERROR: No access! - $_SESSION['error_code'] = "403"; - header("Location: index.php?page=error"); - exit; - } - }else{ - //ERROR: not logged in! - header("Location: index.php"); - exit; - } -} \ No newline at end of file diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/show_ticket_log.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/show_ticket_log.php deleted file mode 100644 index 6d1ea9701..000000000 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/show_ticket_log.php +++ /dev/null @@ -1,67 +0,0 @@ -load_With_TId($result['ticket_id']); - $result['ticket_title'] = $target_ticket->getTitle(); - $ticket_logs = Ticket_Log::getLogsOfTicket( $result['ticket_id']); - $log_action_array = Ticket_Log::getActionTextArray(); - $result['ticket_logs'] = Gui_Elements::make_table($ticket_logs, Array("getTLogId","getTimestamp","getAuthor()->getExternId","getAction","getArgument()"), Array("tLogId","timestamp","authorExtern","action","argument")); - $i = 0; - foreach( $result['ticket_logs'] as $log){ - $webUser = new WebUsers($log['authorExtern']); - $author = $webUser->getUsername(); - $result['ticket_logs'][$i]['author'] = $author; - $query_backpart = ""; - if($log['action'] == 2){ - $webUser2 = new WebUsers($log['argument']); - $query_backpart = $webUser2->getUsername(); - }else if($log['action'] == 4){ - if (Helpers::check_if_game_client()) { - $query_backpart = "ID#" . $log['argument'] . ""; - }else{ - $query_backpart = "ID#" . $log['argument'] . ""; - } - }else if($log['action'] == 5){ - $statusArray = Ticket::getStatusArray(); - $query_backpart = $statusArray[$log['argument'] ]; - }else if($log['action'] == 6){ - $priorityArray = Ticket::getPriorityArray(); - $query_backpart = $priorityArray[$log['argument'] ]; - }else if($log['action'] == 8){ - if (Helpers::check_if_game_client()) { - $query_backpart = "" . Support_Group::getGroup($log['argument'])->getName() . ""; - }else{ - $query_backpart = "" . Support_Group::getGroup($log['argument'])->getName() . ""; - } - } - $result['ticket_logs'][$i]['query'] = $author . " " . $log_action_array[$log['action']] . " " . $query_backpart; - $result['ticket_logs'][$i]['timestamp_elapsed'] = Gui_Elements::time_elapsed_string($log['timestamp']); - $i++; - } - if(Ticket_User::isMod(unserialize($_SESSION['ticket_user']))){ - $result['isMod'] = "TRUE"; - } - global $INGAME_WEBPATH; - $result['ingame_webpath'] = $INGAME_WEBPATH; - return $result; - - }else{ - //ERROR: No access! - $_SESSION['error_code'] = "403"; - header("Location: index.php?page=error"); - exit; - } - }else{ - //ERROR: not logged in! - header("Location: index.php"); - exit; - } -} \ No newline at end of file diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/userlist.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/userlist.php deleted file mode 100644 index 2c408f683..000000000 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/inc/userlist.php +++ /dev/null @@ -1,30 +0,0 @@ -getElements() , Array("getUId","getUsername","getEmail"), Array("id","username","email")); - $pageResult['links'] = $pagination->getLinks(5); - $pageResult['lastPage'] = $pagination->getLast(); - $pageResult['currentPage'] = $pagination->getCurrent(); - - $i = 0; - foreach( $pageResult['userlist'] as $user ){ - $pageResult['userlist'][$i]['permission'] = Ticket_User::constr_ExternId($pageResult['userlist'][$i]['id'])->getPermission(); - $i++; - } - - if (Ticket_User::isAdmin(unserialize($_SESSION['ticket_user']))){ - $pageResult['isAdmin'] = "TRUE"; - } - global $INGAME_WEBPATH; - $pageResult['ingame_webpath'] = $INGAME_WEBPATH; - return $pageResult; - }else{ - //ERROR: No access! - $_SESSION['error_code'] = "403"; - header("Location: index.php?page=error"); - exit; - } -} \ No newline at end of file diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/userlist.tpl b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/userlist.tpl index bf39cc6ae..b6eab4abb 100644 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/userlist.tpl +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/userlist.tpl @@ -25,7 +25,7 @@ Show User {if isset($isAdmin) and $isAdmin eq 'TRUE' and $element.id neq 1} diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/todo.txt b/code/ryzom/tools/server/ryzom_ams/todo.txt similarity index 69% rename from code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/todo.txt rename to code/ryzom/tools/server/ryzom_ams/todo.txt index 20bb8235b..9c8b36197 100644 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/todo.txt +++ b/code/ryzom/tools/server/ryzom_ams/todo.txt @@ -2,4 +2,6 @@ -Make Permission www dependend, so it can be implemented in drupal with hook_permission(); -in helpers make_folders mkdir($value); should be drupal_mkdir(); -write backwards compatible script for existing nel db! --fix the callback in add_user_to_sgroup.php and show_sgroup.php in the func dir \ No newline at end of file +-fix the callback in add_user_to_sgroup.php and show_sgroup.php in the func dir +-add createPermissions function to www side. +-add db install stuff to drupal install file + create a matching admin link! diff --git a/code/ryzom/tools/server/ryzom_ams/www/config.php b/code/ryzom/tools/server/ryzom_ams/www/config.php index 25679ac64..1867875ba 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/config.php +++ b/code/ryzom/tools/server/ryzom_ams/www/config.php @@ -72,8 +72,14 @@ $CREATE_RING = true ; $AMS_LIB = dirname( dirname( __FILE__ ) ) . '/ams_lib'; $AMS_TRANS = $AMS_LIB . '/translations'; $AMS_CACHEDIR = $AMS_LIB . '/cache'; +//Here your inc and func resides $SITEBASE = dirname( __FILE__ ) . '/html/' ; -$WEBPATH ='http://localhost:40917' ; + +$BASE_WEBPATH = 'http://localhost:40917/www/html'; +$IMAGELOC_WEBPATH = 'http://localhost:40917'; +$WEBPATH = $BASE_WEBPATH . '/index.php'; +$INGAME_WEBPATH = $BASE_WEBPATH . '/index.php'; +$CONFIG_PATH = dirname( __FILE__ ); //defines the default language $DEFAULT_LANGUAGE = 'en'; @@ -87,4 +93,5 @@ $TIME_FORMAT = "m-d-Y H:i:s"; //defines which ingame layout template should be used $INGAME_LAYOUT = "basic"; +$FORCE_INGAME = false; diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/autoload/webusers.php b/code/ryzom/tools/server/ryzom_ams/www/html/autoload/webusers.php index 2311916fb..4e9ae845a 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/autoload/webusers.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/autoload/webusers.php @@ -59,10 +59,10 @@ class WebUsers extends Users{ * @return string Info: Returns true or false if a login match is found in the web db */ public function checkLoginMatch($username,$password){ + $dbw = new DBLayer("web"); $statement = $dbw->execute("SELECT * FROM ams_user WHERE Login=:user", array('user' => $username)); $row = $statement->fetch(); - $salt = substr($row['Password'],0,2); $hashed_input_pass = crypt($password, $salt); if($hashed_input_pass == $row['Password']){ diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/blank.php b/code/ryzom/tools/server/ryzom_ams/www/html/blank.php deleted file mode 100644 index e6d4b3268..000000000 --- a/code/ryzom/tools/server/ryzom_ams/www/html/blank.php +++ /dev/null @@ -1,33 +0,0 @@ - - - -
    - -
    - -
    -
    -
    -

    Blank

    -
    - - - -
    -
    -
    - -
    -
    - -
    - - - diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/calendar.php b/code/ryzom/tools/server/ryzom_ams/www/html/calendar.php deleted file mode 100644 index 83f714dfe..000000000 --- a/code/ryzom/tools/server/ryzom_ams/www/html/calendar.php +++ /dev/null @@ -1,45 +0,0 @@ - - -
    - -
    - -
    -
    -
    -

    Calendar

    -
    - - - -
    -
    -
    -
    -

    Draggable Events

    -
    Default
    -
    Completed
    -
    Warning
    -
    Important
    -
    Info
    -
    Other
    -

    - -

    -
    - -
    - -
    -
    -
    -
    - - diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/chart.php b/code/ryzom/tools/server/ryzom_ams/www/html/chart.php deleted file mode 100644 index 10cd049ed..000000000 --- a/code/ryzom/tools/server/ryzom_ams/www/html/chart.php +++ /dev/null @@ -1,120 +0,0 @@ - - - -
    - -
    - -
    - -
    -
    -

    Chart with points

    -
    - - - -
    -
    -
    -
    -

    Mouse position at (0, 0).

    -
    -
    - -
    -
    -

    Flot

    -
    - - - -
    -
    -
    -
    -
    -
    - -
    -
    -

    Stack Example

    -
    - - - -
    -
    -
    -
    - -

    - - -

    - -

    - - - -

    -
    -
    - -
    - -
    -
    -
    -

    Pie

    -
    - - - -
    -
    -
    -
    -
    -
    - -
    -
    -

    Realtime

    -
    - - - -
    -
    -
    -
    -

    You can update a chart periodically to get a real-time effect by using a timer to insert the new data in the plot and redraw it.

    -

    Time between updates: milliseconds

    -
    -
    - -
    -
    -

    Donut

    -
    - - - -
    -
    -
    -
    -
    -
    -
    -
    - - diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/file-manager.php b/code/ryzom/tools/server/ryzom_ams/www/html/file-manager.php deleted file mode 100644 index 251b42847..000000000 --- a/code/ryzom/tools/server/ryzom_ams/www/html/file-manager.php +++ /dev/null @@ -1,36 +0,0 @@ - - -
    - -
    - -
    -
    -
    -

    File Manager

    -
    - - - -
    -
    -
    -
    - - As its a demo, you currently have read-only permission, in your server you may do everything like, upload or delete. It will work on a server only. -
    -
    -
    -
    - -
    - - - diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/footer.php b/code/ryzom/tools/server/ryzom_ams/www/html/footer.php deleted file mode 100644 index e7053a9c6..000000000 --- a/code/ryzom/tools/server/ryzom_ams/www/html/footer.php +++ /dev/null @@ -1,121 +0,0 @@ - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/form.php b/code/ryzom/tools/server/ryzom_ams/www/html/form.php deleted file mode 100644 index fce48a142..000000000 --- a/code/ryzom/tools/server/ryzom_ams/www/html/form.php +++ /dev/null @@ -1,333 +0,0 @@ - - - -
    - -
    - -
    -
    -
    -

    Form Elements

    -
    - - - -
    -
    -
    -
    -
    - Datepicker, Autocomplete, WYSIWYG -
    - -
    - -

    Start typing to activate auto complete!

    -
    -
    -
    - -
    - -
    -
    - -
    - -
    - -
    -
    -
    - -
    - -
    -
    -
    - - -
    -
    - - -
    -
    - -
    - - -
    -
    -
    -

    Form Elements

    -
    - - - -
    -
    -
    -
    -
    -
    - -
    - -
    -
    -
    - -
    - Some value here -
    -
    -
    - -
    - -
    -
    -
    - -
    - -
    -
    -
    - -
    - - Something may have gone wrong -
    -
    -
    - -
    - - Please correct the error -
    -
    -
    - -
    - - Woohoo! -
    -
    -
    - -
    - -
    -
    -
    - -
    - -
    -
    -
    - -
    - -
    -
    -
    - -
    - -
    -
    -
    - - -
    -
    - - -
    -
    - -
    - -
    -
    -
    -

    Form Elements

    -
    - - - -
    -
    -
    -
    -
    -
    - -
    -
    - @ -
    -

    Here's some help text

    -
    -
    -
    - -
    -
    - .00 -
    - Here's more help text -
    -
    -
    - -
    -
    - $.00 -
    -
    -
    -
    - -
    -
    - -
    -
    -
    -
    - -
    -
    - -
    -
    -
    -
    - -
    - - - -
    -
    -
    - -
    - -
    -
    -
    - -
    - -
    - -
    -
    -
    - - -
    -
    - -
    -
    - -
    - - diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/func/add_sgroup.php b/code/ryzom/tools/server/ryzom_ams/www/html/func/add_sgroup.php index 4a5ff8452..960ec2c6b 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/func/add_sgroup.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/func/add_sgroup.php @@ -1,10 +1,11 @@ getPermission(); + $result['permission'] = unserialize($_SESSION['ticket_user'])->getPermission(); $result['no_visible_elements'] = 'FALSE'; $result['username'] = $_SESSION['user']; - global $SITEBASE; - require_once($SITEBASE . 'inc/sgroup_list.php'); - $result= array_merge($result, sgroup_list()); - helpers :: loadtemplate( 'sgroup_list', $result); + //global $SITEBASE; + //require($SITEBASE . '/inc/sgroup_list.php'); + //$result= array_merge($result, sgroup_list()); + //return helpers :: loadtemplate( 'sgroup_list', $result, true); + if (Helpers::check_if_game_client()) { + header("Location: ".$INGAME_WEBPATH."?page=sgroup_list"); + }else{ + header("Location: ".$WEBPATH."?page=sgroup_list"); + } exit; }else{ @@ -36,4 +42,4 @@ function add_sgroup(){ exit; } -} \ No newline at end of file +} diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/func/add_user.php b/code/ryzom/tools/server/ryzom_ams/www/html/func/add_user.php index a7f17b0aa..2cb583061 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/func/add_user.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/func/add_user.php @@ -1,7 +1,7 @@ $_POST["Username"], 'Password' => $_POST["Password"], 'ConfirmPass' => $_POST["ConfirmPass"], 'Email' => $_POST["Email"]); $webUser = new WebUsers(); $result = $webUser->check_Register($params); @@ -20,6 +20,7 @@ function add_user(){ $status = write_user( $edit ); $pageElements['status'] = $status; $pageElements['no_visible_elements'] = 'TRUE'; + $pageElements['ingame_webpath'] = $INGAME_WEBPATH; helpers :: loadtemplate( 'register_feedback', $pageElements); exit; }else{ @@ -29,6 +30,7 @@ function add_user(){ $result['prevConfirmPass'] = $_POST["ConfirmPass"]; $result['prevEmail'] = $_POST["Email"]; $result['no_visible_elements'] = 'TRUE'; + $pageElements['ingame_webpath'] = $INGAME_WEBPATH; helpers :: loadtemplate( 'register', $result); exit; } diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/func/add_user_to_sgroup.php b/code/ryzom/tools/server/ryzom_ams/www/html/func/add_user_to_sgroup.php index 3eb79b928..e4d46e610 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/func/add_user_to_sgroup.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/func/add_user_to_sgroup.php @@ -1,10 +1,11 @@ getPermission(); + $result['permission'] = unserialize($_SESSION['ticket_user'])->getPermission(); $result['no_visible_elements'] = 'FALSE'; $result['username'] = $_SESSION['user']; - global $SITEBASE; - require_once($SITEBASE . 'inc/show_sgroup.php'); - $result= array_merge($result, show_sgroup()); - helpers :: loadtemplate( 'show_sgroup', $result); + //global $SITEBASE; + //require_once($SITEBASE . 'inc/show_sgroup.php'); + //$result= array_merge($result, show_sgroup()); + //helpers :: loadtemplate( 'show_sgroup', $result); + if (Helpers::check_if_game_client()) { + header("Location: ".$INGAME_WEBPATH."?page=show_sgroup&id=".$id); + }else{ + header("Location: ".$WEBPATH."?page=show_sgroup&id=".$id); + } exit; }else{ diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/func/change_info.php b/code/ryzom/tools/server/ryzom_ams/www/html/func/change_info.php index 311935e9c..1fb0f10c0 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/func/change_info.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/func/change_info.php @@ -9,7 +9,7 @@ function change_info(){ if(isset($_POST['target_id'])){ - if( ($_POST['target_id'] == $_SESSION['id']) || Ticket_User::isMod($_SESSION['ticket_user'] ) ){ + if( ($_POST['target_id'] == $_SESSION['id']) || Ticket_User::isMod(unserialize($_SESSION['ticket_user']) ) ){ if($_POST['target_id'] == $_SESSION['id']){ $target_username = $_SESSION['user']; }else{ @@ -77,15 +77,17 @@ function change_info(){ } global $SITEBASE; - require_once($SITEBASE . 'inc/settings.php'); + require_once($SITEBASE . '/inc/settings.php'); $result = settings(); if($updated){ $result['info_updated'] = "OK"; } - $result['permission'] = $_SESSION['ticket_user']->getPermission(); + $result['permission'] = unserialize($_SESSION['ticket_user'])->getPermission(); $result['username'] = $_SESSION['user']; $result['no_visible_elements'] = 'FALSE'; $result['target_id'] = $_POST['target_id']; + global $INGAME_WEBPATH; + $result['ingame_webpath'] = $INGAME_WEBPATH; helpers :: loadtemplate( 'settings', $result); exit; @@ -112,4 +114,4 @@ function change_info(){ print_r($e); exit; } -} \ No newline at end of file +} diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/func/change_mail.php b/code/ryzom/tools/server/ryzom_ams/www/html/func/change_mail.php index f807e46b5..f04648343 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/func/change_mail.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/func/change_mail.php @@ -9,7 +9,7 @@ function change_mail(){ if(isset($_POST['target_id'])){ - if( ($_POST['target_id'] == $_SESSION['id']) || Ticket_User::isMod($_SESSION['ticket_user']) ){ + if( ($_POST['target_id'] == $_SESSION['id']) || Ticket_User::isMod(unserialize($_SESSION['ticket_user'])) ){ if($_POST['target_id'] == $_SESSION['id']){ $target_username = $_SESSION['user']; }else{ @@ -21,7 +21,7 @@ function change_mail(){ $reply = $webUser->checkEmail($_POST['NewEmail']); global $SITEBASE; - require_once($SITEBASE . 'inc/settings.php'); + require_once($SITEBASE . '/inc/settings.php'); $result = settings(); if ( $reply != "success" ){ @@ -38,12 +38,12 @@ function change_mail(){ }else if($status == 'shardoffline'){ $result['SUCCESS_MAIL'] = "SHARDOFF"; } - $result['permission'] = $_SESSION['ticket_user']->getPermission(); + $result['permission'] = unserialize($_SESSION['ticket_user'])->getPermission(); $result['no_visible_elements'] = 'FALSE'; $result['username'] = $_SESSION['user']; $result['target_id'] = $_POST['target_id']; if(isset($_GET['id'])){ - if(Ticket_User::isMod($_SESSION['ticket_user']) && ($_POST['target_id'] != $_SESSION['id'])){ + if(Ticket_User::isMod(unserialize($_SESSION['ticket_user'])) && ($_POST['target_id'] != $_SESSION['id'])){ $result['isMod'] = "TRUE"; } } @@ -52,12 +52,12 @@ function change_mail(){ }else{ $result['EMAIL'] = $reply; - $result['permission'] = $_SESSION['ticket_user']->getPermission(); + $result['permission'] = unserialize($_SESSION['ticket_user'])->getPermission(); $result['no_visible_elements'] = 'FALSE'; $result['username'] = $_SESSION['user']; $result['target_id'] = $_POST['target_id']; if(isset($_GET['id'])){ - if(Ticket_User::isMod($_SESSION['ticket_user']) && ($_POST['target_id'] != $_SESSION['id'])){ + if(Ticket_User::isMod(unserialize($_SESSION['ticket_user'])) && ($_POST['target_id'] != $_SESSION['id'])){ $result['isMod'] = "TRUE"; } } @@ -91,4 +91,3 @@ function change_mail(){ } - diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/func/change_password.php b/code/ryzom/tools/server/ryzom_ams/www/html/func/change_password.php index ab4a71892..420d78103 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/func/change_password.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/func/change_password.php @@ -9,7 +9,7 @@ function change_password(){ if(isset($_POST['target_id'])){ $adminChangesOther = false; //if target_id is the same as session id or is admin - if( ($_POST['target_id'] == $_SESSION['id']) || Ticket_User::isMod($_SESSION['ticket_user']) ){ + if( ($_POST['target_id'] == $_SESSION['id']) || Ticket_User::isMod(unserialize($_SESSION['ticket_user'])) ){ if($_POST['target_id'] == $_SESSION['id']){ $target_username = $_SESSION['user']; }else{ @@ -26,16 +26,15 @@ function change_password(){ if ($result == "success"){ //edit stuff into db global $SITEBASE; - require_once($SITEBASE . 'inc/settings.php'); + require_once($SITEBASE . '/inc/settings.php'); $succresult = settings(); - $hashpass = crypt($_POST["NewPass"], WebUsers::generateSALT()); - $status = WebUsers::setPassword($target_username, $hashpass); + $status = WebUsers::setPassword($target_username, $_POST["NewPass"]); if($status == 'ok'){ $succresult['SUCCESS_PASS'] = "OK"; }else if($status == 'shardoffline'){ $succresult['SUCCESS_PASS'] = "SHARDOFF"; } - $succresult['permission'] = $_SESSION['ticket_user']->getPermission(); + $succresult['permission'] = unserialize($_SESSION['ticket_user'])->getPermission(); $succresult['no_visible_elements'] = 'FALSE'; $succresult['username'] = $_SESSION['user']; $succresult['target_id'] = $_POST['target_id']; @@ -47,13 +46,13 @@ function change_password(){ $result['prevCurrentPass'] = filter_var($_POST["CurrentPass"], FILTER_SANITIZE_STRING); $result['prevNewPass'] = filter_var($_POST["NewPass"], FILTER_SANITIZE_STRING); $result['prevConfirmNewPass'] = filter_var($_POST["ConfirmNewPass"], FILTER_SANITIZE_STRING); - $result['permission'] = $_SESSION['ticket_user']->getPermission(); + $result['permission'] = unserialize($_SESSION['ticket_user'])->getPermission(); $result['no_visible_elements'] = 'FALSE'; $result['username'] = $_SESSION['user']; $result['target_id'] = $_POST['target_id']; global $SITEBASE; - require_once($SITEBASE . 'inc/settings.php'); + require_once($SITEBASE . '/inc/settings.php'); $settings = settings(); $result = array_merge($result,$settings); @@ -87,4 +86,3 @@ function change_password(){ } - diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/func/change_receivemail.php b/code/ryzom/tools/server/ryzom_ams/www/html/func/change_receivemail.php index 85cbec965..5bfba0b8d 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/func/change_receivemail.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/func/change_receivemail.php @@ -4,18 +4,24 @@ function change_receivemail(){ try{ //if logged in + global $INGAME_WEBPATH; + global $WEBPATH; if(WebUsers::isLoggedIn()){ if(isset($_POST['target_id'])){ - if( ( ($_POST['target_id'] == $_SESSION['id']) || Ticket_User::isMod($_SESSION['ticket_user'])) && isset($_POST['ReceiveMail']) ){ + if( ( ($_POST['target_id'] == $_SESSION['id']) || Ticket_User::isMod(unserialize($_SESSION['ticket_user']))) && isset($_POST['ReceiveMail']) ){ $user_id = filter_var($_POST['target_id'], FILTER_SANITIZE_NUMBER_INT); $receiveMail = filter_var($_POST['ReceiveMail'], FILTER_SANITIZE_NUMBER_INT); if($receiveMail == 0 || $receiveMail == 1){ WebUsers::setReceiveMail($user_id, $receiveMail); } - header("Location: index.php?page=settings&id=".$user_id); + if (Helpers::check_if_game_client()) { + header("Location: ".$INGAME_WEBPATH."?page=settings&id=".$user_id); + }else{ + header("Location: ".$WEBPATH."?page=settings&id=".$user_id); + } exit; }else{ diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/func/create_ticket.php b/code/ryzom/tools/server/ryzom_ams/www/html/func/create_ticket.php index 5fce2d9de..cf497dabf 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/func/create_ticket.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/func/create_ticket.php @@ -2,24 +2,30 @@ function create_ticket(){ //if logged in + global $INGAME_WEBPATH; + global $WEBPATH; if(WebUsers::isLoggedIn() && isset($_SESSION['ticket_user'])){ if(isset($_POST['target_id'])){ //if target_id is the same as session id or is admin - if( ($_POST['target_id'] == $_SESSION['id']) || Ticket_User::isMod($_SESSION['ticket_user']) ){ + if( ($_POST['target_id'] == $_SESSION['id']) || Ticket_User::isMod(unserialize($_SESSION['ticket_user'])) ){ $category = filter_var($_POST['Category'], FILTER_SANITIZE_NUMBER_INT); $title = filter_var($_POST['Title'], FILTER_SANITIZE_STRING); $content = filter_var($_POST['Content'], FILTER_SANITIZE_STRING); try{ if($_POST['target_id'] == $_SESSION['id']){ - $author = $_SESSION['ticket_user']->getTUserId(); + $author = unserialize($_SESSION['ticket_user'])->getTUserId(); }else{ $author= Ticket_User::constr_ExternId($_POST['target_id'])->getTUserId(); } - $ticket_id = Ticket::create_Ticket($title, $content, $category, $author, $_SESSION['ticket_user']->getTUserId(),0, $_POST); - header("Location: index.php?page=show_ticket&id=".$ticket_id); + $ticket_id = Ticket::create_Ticket($title, $content, $category, $author, unserialize($_SESSION['ticket_user'])->getTUserId(),0, $_POST); + if (Helpers::check_if_game_client()) { + header("Location: ".$INGAME_WEBPATH."?page=show_ticket&id=".$ticket_id); + }else{ + header("Location: ".$WEBPATH."?page=show_ticket&id=".$ticket_id); + } exit; }catch (PDOException $e) { diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/func/login.php b/code/ryzom/tools/server/ryzom_ams/www/html/func/login.php index da3b29478..6841b7ffd 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/func/login.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/func/login.php @@ -1,21 +1,27 @@ getLanguage(); - + //go back to the index page. - header( 'Location: index.php' ); + if (Helpers::check_if_game_client()) { + header( 'Location: '. $INGAME_WEBPATH ); + }else{ + header( 'Location: '. $WEBPATH ); + } exit; }else{ //handle login failure diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/func/modify_email_of_sgroup.php b/code/ryzom/tools/server/ryzom_ams/www/html/func/modify_email_of_sgroup.php index bf842feff..9befa9ae4 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/func/modify_email_of_sgroup.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/func/modify_email_of_sgroup.php @@ -1,10 +1,11 @@ getPermission(); + $result['permission'] = unserialize($_SESSION['ticket_user'])->getPermission(); $result['no_visible_elements'] = 'FALSE'; $result['username'] = $_SESSION['user']; - global $SITEBASE; - require_once($SITEBASE . 'inc/show_sgroup.php'); - $result= array_merge($result, show_sgroup()); - helpers :: loadtemplate( 'show_sgroup', $result); + //global $SITEBASE; + //require_once($SITEBASE . 'inc/show_sgroup.php'); + //$result= array_merge($result, show_sgroup()); + //helpers :: loadtemplate( 'show_sgroup', $result); + if (Helpers::check_if_game_client()) { + header("Location: ".$INGAME_WEBPATH."?page=show_sgroup&id=".$sgroupid); + }else{ + header("Location: ".$WEBPATH."?page=show_sgroup&id=".$sgroupid); + } exit; }else{ diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/func/reply_on_ticket.php b/code/ryzom/tools/server/ryzom_ams/www/html/func/reply_on_ticket.php index 76f8ee71e..e7841906f 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/func/reply_on_ticket.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/func/reply_on_ticket.php @@ -1,32 +1,40 @@ load_With_TId($ticket_id); - if(($target_ticket->getAuthor() == $_SESSION['ticket_user']->getTUserId()) || Ticket_User::isMod($_SESSION['ticket_user']) ){ + if(($target_ticket->getAuthor() == unserialize($_SESSION['ticket_user'])->getTUserId()) || Ticket_User::isMod(unserialize($_SESSION['ticket_user'])) ){ try{ - $author = $_SESSION['ticket_user']->getTUserId(); - - $content = $_POST['Content']; + $author = unserialize($_SESSION['ticket_user'])->getTUserId(); + if(isset($_POST['Content'])){ + $content = $_POST['Content']; + }else{ + $content=""; + } $hidden = 0; - if(isset($_POST['hidden']) && Ticket_User::isMod($_SESSION['ticket_user'])){ + if(isset($_POST['hidden']) && Ticket_User::isMod(unserialize($_SESSION['ticket_user']))){ $hidden = 1; } Ticket::createReply($content, $author, $ticket_id, $hidden); - if(isset($_POST['ChangeStatus']) && isset($_POST['ChangePriority']) && Ticket_User::isMod($_SESSION['ticket_user'])){ + if(isset($_POST['ChangeStatus']) && isset($_POST['ChangePriority']) && Ticket_User::isMod(unserialize($_SESSION['ticket_user']))){ $newStatus = filter_var($_POST['ChangeStatus'], FILTER_SANITIZE_NUMBER_INT); $newPriority = filter_var($_POST['ChangePriority'], FILTER_SANITIZE_NUMBER_INT); Ticket::updateTicketStatusAndPriority($ticket_id,$newStatus, $newPriority, $author); } - header("Location: index.php?page=show_ticket&id=".$ticket_id); + if (Helpers::check_if_game_client()) { + header("Location: ".$INGAME_WEBPATH."?page=show_ticket&id=".$ticket_id); + }else{ + header("Location: ".$WEBPATH."?page=show_ticket&id=".$ticket_id); + } exit; }catch (PDOException $e) { diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/gallery.php b/code/ryzom/tools/server/ryzom_ams/www/html/gallery.php deleted file mode 100644 index a822e00ab..000000000 --- a/code/ryzom/tools/server/ryzom_ams/www/html/gallery.php +++ /dev/null @@ -1,41 +0,0 @@ - - -
    - -
    - -
    -
    -
    -

    Gallery

    -
    - - - -
    -
    -
    -

    - -

    -
    - -
    -
    - -
    - - diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/grid.php b/code/ryzom/tools/server/ryzom_ams/www/html/grid.php deleted file mode 100644 index cd278aa3d..000000000 --- a/code/ryzom/tools/server/ryzom_ams/www/html/grid.php +++ /dev/null @@ -1,240 +0,0 @@ - - - -
    - -
    - -
    -
    -
    -

    Grid 12

    -
    - - - -
    -
    -
    -
    -
    span 4
    -
    span 4
    -
    span 4
    -
    -
    -
    -
    - -
    -
    -
    -

    Grid 3

    -
    - -
    -
    -
    -
    -
    span 4
    -
    span 4
    -
    span 4
    -
    -
    -
    -
    -
    -

    Grid 3

    -
    - -
    -
    -
    -
    -
    span 4
    -
    span 4
    -
    span 4
    -
    -
    -
    -
    -
    -

    Grid 3

    -
    - -
    -
    -
    -
    -
    span 4
    -
    span 4
    -
    span 4
    -
    -
    -
    -
    -
    -

    Plain

    -
    -
    -
    -
    span 4
    -
    span 4
    -
    span 4
    -
    -
    -
    -
    - -
    -
    -
    -

    Grid 6

    -
    - - - -
    -
    -
    -
    -
    span 4
    -
    span 4
    -
    span 4
    -
    -
    -
    - -
    -
    -

    Grid 6

    -
    - - - -
    -
    -
    -
    -
    span 4
    -
    span 4
    -
    span 4
    -
    -
    -
    -
    -
    -
    -
    -

    Grid 4

    -
    - - - -
    -
    -
    -
    -
    span 4
    -
    span 4
    -
    span 4
    -
    -
    -
    - -
    -
    -

    Grid 4

    -
    - - - -
    -
    -
    -
    -
    span 4
    -
    span 4
    -
    span 4
    -
    -
    -
    - -
    -
    -

    Grid 4

    -
    - - - -
    -
    -
    -
    -
    span 4
    -
    span 4
    -
    span 4
    -
    -
    -
    -
    - -
    -
    -
    -

    Box less area

    -

    The flat boxes can be created using grids. But you can also use grids inside grids, which makes the layout 100% flexible!

    -
    -
    -
    - -
    -
    1
    -
    1
    -
    1
    -
    1
    -
    1
    -
    1
    -
    1
    -
    1
    -
    1
    -
    1
    -
    1
    -
    1
    -
    - -
    -
    4
    -
    4
    -
    4
    -
    - -
    -
    3
    -
    3
    -
    3
    -
    3
    -
    - -
    -
    4
    -
    8
    -
    - -
    -
    6
    -
    6
    -
    - -
    -
    12
    -
    - - - diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/header.php b/code/ryzom/tools/server/ryzom_ams/www/html/header.php deleted file mode 100644 index c3af26217..000000000 --- a/code/ryzom/tools/server/ryzom_ams/www/html/header.php +++ /dev/null @@ -1,158 +0,0 @@ - - - - - - Free HTML5 Bootstrap Admin Template - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    - - - - - - - - -
    - - diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/icon.php b/code/ryzom/tools/server/ryzom_ams/www/html/icon.php deleted file mode 100644 index 071b4252d..000000000 --- a/code/ryzom/tools/server/ryzom_ams/www/html/icon.php +++ /dev/null @@ -1,355 +0,0 @@ - - - -
    - -
    - -
    -
    -
    -

    Icons

    -
    - - - -
    -
    -
    -
    - -
    -
    -
      -
    • icon-glass
    • -
    • icon-music
    • -
    • icon-search
    • -
    • icon-envelope
    • -
    • icon-heart
    • -
    • icon-star
    • -
    • icon-star-empty
    • -
    • icon-user
    • -
    • icon-film
    • -
    • icon-th-large
    • -
    • icon-th
    • -
    • icon-th-list
    • -
    • icon-ok
    • -
    • icon-remove
    • -
    • icon-zoom-in
    • -
    • icon-zoom-out
    • -
    • icon-off
    • -
    • icon-signal
    • -
    • icon-cog
    • -
    • icon-trash
    • -
    • icon-home
    • -
    • icon-file
    • -
    • icon-time
    • -
    • icon-road
    • -
    • icon-download-alt
    • -
    • icon-download
    • -
    • icon-upload
    • -
    • icon-inbox
    • -
    • icon-play-circle
    • -
    • icon-repeat
    • -
    • icon-refresh
    • -
    • icon-list-alt
    • -
    • icon-lock
    • -
    • icon-flag
    • -
    • icon-headphones
    • -
    -
    -
    -
      -
    • icon-volume-off
    • -
    • icon-volume-down
    • -
    • icon-volume-up
    • -
    • icon-qrcode
    • -
    • icon-barcode
    • -
    • icon-tag
    • -
    • icon-tags
    • -
    • icon-book
    • -
    • icon-bookmark
    • -
    • icon-print
    • -
    • icon-camera
    • -
    • icon-font
    • -
    • icon-bold
    • -
    • icon-italic
    • -
    • icon-text-height
    • -
    • icon-text-width
    • -
    • icon-align-left
    • -
    • icon-align-center
    • -
    • icon-align-right
    • -
    • icon-align-justify
    • -
    • icon-list
    • -
    • icon-indent-left
    • -
    • icon-indent-right
    • -
    • icon-facetime-video
    • -
    • icon-picture
    • -
    • icon-pencil
    • -
    • icon-map-marker
    • -
    • icon-adjust
    • -
    • icon-tint
    • -
    • icon-edit
    • -
    • icon-share
    • -
    • icon-check
    • -
    • icon-move
    • -
    • icon-step-backward
    • -
    • icon-fast-backward
    • -
    -
    -
    -
      -
    • icon-backward
    • -
    • icon-play
    • -
    • icon-pause
    • -
    • icon-stop
    • -
    • icon-forward
    • -
    • icon-fast-forward
    • -
    • icon-step-forward
    • -
    • icon-eject
    • -
    • icon-chevron-left
    • -
    • icon-chevron-right
    • -
    • icon-plus-sign
    • -
    • icon-minus-sign
    • -
    • icon-remove-sign
    • -
    • icon-ok-sign
    • -
    • icon-question-sign
    • -
    • icon-info-sign
    • -
    • icon-screenshot
    • -
    • icon-remove-circle
    • -
    • icon-ok-circle
    • -
    • icon-ban-circle
    • -
    • icon-arrow-left
    • -
    • icon-arrow-right
    • -
    • icon-arrow-up
    • -
    • icon-arrow-down
    • -
    • icon-share-alt
    • -
    • icon-resize-full
    • -
    • icon-resize-small
    • -
    • icon-plus
    • -
    • icon-minus
    • -
    • icon-asterisk
    • -
    • icon-exclamation-sign
    • -
    • icon-gift
    • -
    • icon-leaf
    • -
    • icon-fire
    • -
    • icon-eye-open
    • -
    -
    -
    -
      -
    • icon-eye-close
    • -
    • icon-warning-sign
    • -
    • icon-plane
    • -
    • icon-calendar
    • -
    • icon-random
    • -
    • icon-comment
    • -
    • icon-magnet
    • -
    • icon-chevron-up
    • -
    • icon-chevron-down
    • -
    • icon-retweet
    • -
    • icon-shopping-cart
    • -
    • icon-folder-close
    • -
    • icon-folder-open
    • -
    • icon-resize-vertical
    • -
    • icon-resize-horizontal
    • -
    • icon-hdd
    • -
    • icon-bullhorn
    • -
    • icon-bell
    • -
    • icon-certificate
    • -
    • icon-thumbs-up
    • -
    • icon-thumbs-down
    • -
    • icon-hand-right
    • -
    • icon-hand-left
    • -
    • icon-hand-up
    • -
    • icon-hand-down
    • -
    • icon-circle-arrow-right
    • -
    • icon-circle-arrow-left
    • -
    • icon-circle-arrow-up
    • -
    • icon-circle-arrow-down
    • -
    • icon-globe
    • -
    • icon-wrench
    • -
    • icon-tasks
    • -
    • icon-filter
    • -
    • icon-briefcase
    • -
    • icon-fullscreen
    • -
    -
    -
    - -
    - -
    -
    -

    Built as a sprite

    -

    Instead of making every icon an extra request, we've compiled them into a sprite—a bunch of images in one file that uses CSS to position the images with background-position. This is the same method we use on Twitter.com and it has worked well for us.

    -

    All icons classes are prefixed with .icon- for proper namespacing and scoping, much like our other components. This will help avoid conflicts with other tools.

    -
    -
    -

    How to use

    -

    Bootstrap uses an <i> tag for all icons, but they have no case class—only a shared prefix. To use, place the following code just about anywhere:

    -
    <i class="icon-search"></i>
    -

    There are also styles available for inverted (white) icons, made ready with one extra class:

    -
    <i class="icon-search icon-white"></i>
    -
    In dark themes (Cyborg and Slate) normal icons become white and icon-white become black.
    -

    There are 140 classes to choose from for your icons. Just add an <i> tag with the right classes and you're set. You can find the full list in sprites.less or right here in this document.

    -

    - Heads up! - When using beside strings of text, as in buttons or nav links, be sure to leave a space after the <i> tag for proper spacing. -

    -
    -
    -

    Use cases

    -

    Icons are great, but where would one use them? Here are a few ideas:

    -
      -
    • As visuals for your sidebar navigation
    • -
    • For a purely icon-driven navigation
    • -
    • For buttons to help convey the meaning of an action
    • -
    • With links to share context on a user's destination
    • -
    -

    Essentially, anywhere you can put an <i> tag, you can put an icon.

    -
    -
    - -

    Examples

    -

    Use them in buttons, button groups for a toolbar, navigation, or prepended form inputs.

    -
    -
    -
    -
    - - - - -
    - -
    -

    - Refresh - Checkout - Delete -

    -

    - Comment - Settings - More Info -

    -
    - -
    -
    -
    - -
    -
    - -
    -
    -
    - -
    -
    -
    - -
    - -
    -
    -

    Icon sets

    -

    Default icon set has gray style suitable for white and light backgrounds, but a number of functional classes can be applied for different icon color styles. - These classes include a 5-color (blue, green, red, yellow and gray) .icon-color class, a black .icon-black class, a white .icon-white class, and a dark-gray .icon-darkgray class. -

    - Note: place your cursor on the chosen icon image to see its class. -
    -
    -
    -

    Default Icon Sets

    -

    Default icon classes .icon or .icon32 are best suitable for white and light backgrounds.

    -

    16x16 pixels icon set

    -

    Set .icon class for 16x16 pixels icon followed by a class corresponding to the chosen image.

    -
    <span class="icon icon-home"/>
    -
    -

    32x32 pixels icon set

    Set .icon32 class for 32x32 pixels icon followed by a class corresponding to the chosen image.

    <span class="icon32 icon-user"/>

    Special class for the whole section

    To use gray icon set for the whole section set .icons-gray class on the parent element.

    -
    <div class="icons-gray">
    -	<span class="icon icon-add"/>
    -	<span class="icon icon-remove"/>
    -</div>
    -											
    -

    Dark-gray Icon Sets

    Dark-gray icon sets are used for default :hover effect. - The .icon-darkgray class should be used for the icons on light and middle gray backgrounds.

    16x16 pixels icon set

    Set .icon class for 16x16 pixels icon, then .icon-darkgray class for color and a class corresponding to the chosen image.

    <span class="icon icon-darkgray icon-add"/>

    32x32 pixels icon set

    Set .icon32 class for 32x32 pixels icon, then .icon-darkgray class for color and a class corresponding to the chosen image.

    <span class="icon32 icon-darkgray icon-remove"/>

    Special class for the whole section

    To use dark-gray icon set for the whole section set .icons-darkgray class on the parent element.

    <div class="icons-darkgray">
    -	<span class="icon icon-add"/>
    -	<span class="icon icon-remove"/>
    -</div>

    Color Icon Sets

    Color icon sets are used for default .active icon effect. - The .icon-color class should be used for the icons on white and light backgrounds.

    16x16 pixels icon set

    Set .icon class for 16x16 pixels icon, then .icon-color class for color and a class corresponding to the chosen image.

    <span class="icon icon-color icon-triangle-n"/>

    32x32 pixels icon set

    Set .icon32 class for 32x32 pixels icon, then .icon-color class for color and a class corresponding to the chosen image.

    <span class="icon32 icon-color icon-triangle-s"/>

    Special class for the whole section

    To use color icon set for the whole section set .icons-color - class on the parent element.

    <div class="icons-color">
    -	<span class="icon icon-triangle-n"/>
    -	<span class="icon icon-triangle-s"/>
    -</div>

    Black Icon Sets

    The .icon-black class could be used for the icons on light and middle gray backgrounds.

    16x16 pixels icon set

    Set .icon class for 16x16 pixels icon, then .icon-black class for color and a class corresponding to the chosen image.

    <span class="icon icon-black icon-folder-open"/>

    32x32 pixels icon set

    Set .icon32 class for 32x32 pixels icon, then .icon-black class for color and a class corresponding to the chosen image.

    <span class="icon32 icon-black icon-folder-collapsed"/>

    Special class for the whole section

    To use black icon set for the whole section set .icons-black - class on the parent element.

    <div class="icons-black">
    -	<span class="icon icon-folder-open"/>
    -	<span class="icon icon-folder-collapsed"/>
    -</div>
    -

    White Icon Sets

    The .icon-white class should be used for the icons on vivid and dark backgrounds.

    16x16 pixels icon set

    Set .icon class for 16x16 pixels icon, then .icon-white class for color and a class corresponding to the chosen image.

    <span class="icon icon-white icon-bullet-on"/>

    32x32 pixels icon set

    Set .icon32 class for 32x32 pixels icon, then .icon-white class for color and a class corresponding to the chosen image.

    <span class="icon32 icon-white icon-bullet-off"/>

    Special class for the whole section

    To use white icon set for the whole section set .icons-white class on the parent element.

    <div class="icons-white">
    -	<span class="icon icon-bullet-on"/>
    -	<span class="icon icon-bullet-off"/>
    -</div>
    -

    Blue Icon Sets

    The .icon-blue class could be used for the icons on light or dark backgrounds.

    16x16 pixels icon set

    Set .icon class for 16x16 pixels icon, then .icon-blue class for color and a class corresponding to the chosen image.

    <span class="icon icon-blue icon-carat-1-n"/>

    32x32 pixels icon set

    Set .icon32 class for 32x32 pixels icon, then .icon-blue class for color and a class corresponding to the chosen image.

    <span class="icon32 icon-blue icon-carat-1-s"/>

    Special class for the whole section

    To use blue icon set for the whole section set .icons-blue - class on the parent element.

    <div class="icons-blue">
    -	<span class="icon icon-carat-1-n"/>
    -	<span class="icon icon-carat-1-s"/>
    -</div>
    -

    Green Icon Sets

    The .icon-green class could be used for the icons on light or dark backgrounds.

    16x16 pixels icon set

    Set .icon class for 16x16 pixels icon, then .icon-green class for color and a class corresponding to the chosen image.

    <span class="icon icon-green icon-arrow-e"/>

    32x32 pixels icon set

    Set .icon32 class for 32x32 pixels icon, then .icon-green class for color and a class corresponding to the chosen image.

    <span class="icon32 icon-green icon-arrow-w"/>

    Special class for the whole section

    To use green icon set for the whole section set .icons-green - class on the parent element.

    <div class="icons-green">
    -	<span class="icon icon-arrow-e"/>
    -	<span class="icon icon-arrow-w"/>
    -</div>
    -

    Red Icon Sets

    The .icon-red class could be used for the icons on light or dark backgrounds.

    16x16 pixels icon set

    Set .icon class for 16x16 pixels icon, then .icon-red class for color and a class corresponding to the chosen image.

    <span class="icon icon-red icon-arrowthick-ne"/>

    32x32 pixels icon set

    Set .icon32 class for 32x32 pixels icon, then .icon-red class for color and a class corresponding to the chosen image.

    <span class="icon32 icon-red icon-arrowthick-sw"/>

    Special class for the whole section

    To use red icon set for the whole section set .icons-red - class on the parent element.

    <div class="icons-red">
    -	<span class="icon icon-arrowthick-ne"/>
    -	<span class="icon icon-arrowthick-sw"/>
    -</div>
    -

    Orange Icon Sets

    The .icon-orange class could be used for the icons on light or dark backgrounds.

    16x16 pixels icon set

    Set .icon class for 16x16 pixels icon, then .icon-orange class for color and a class corresponding to the chosen image.

    <span class="icon icon-orange icon-undo"/>

    32x32 pixels icon set

    Set .icon32 class for 32x32 pixels icon, then .icon-orange class for color and a class corresponding to the chosen image.

    <span class="icon32 icon-orange icon-redo"/>

    Special class for the whole section

    To use orange icon set for the whole section set .icons-orange - class on the parent element.

    <div class="icons-orange">
    -	<span class="icon icon-undo"/>
    -	<span class="icon icon-redo"/>
    -</div>
    -
    - - -
    -
    -
    - -
    - - diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/inc/change_permission.php b/code/ryzom/tools/server/ryzom_ams/www/html/inc/change_permission.php index 084aa3923..35f561290 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/inc/change_permission.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/inc/change_permission.php @@ -1,24 +1,33 @@ getTUserId(), $value); - header("Location: index.php?page=show_user&id=".$user_id); + if (Helpers::check_if_game_client()) { + header("Location: ".$INGAME_WEBPATH."?page=show_user&id=".$user_id); + }else{ + header("Location: ".$WEBPATH."?page=show_user&id=".$user_id); + } exit; }else{ //ERROR: GET PARAMS not given or trying to change admin - header("Location: index.php?page=show_user&id=".$user_id); + if (Helpers::check_if_game_client()) { + header("Location: ".$INGAME_WEBPATH."?page=show_user&id=".$user_id); + }else{ + header("Location: ".$WEBPATH."?page=show_user&id=".$user_id); + } exit; } diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/inc/createticket.php b/code/ryzom/tools/server/ryzom_ams/www/html/inc/createticket.php index 40b6610ec..3fdb54b84 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/inc/createticket.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/inc/createticket.php @@ -7,7 +7,7 @@ function createticket(){ //in case user_id-GET param set it's value as target_id, if no user_id-param is given, use the session id. if(isset($_GET['user_id'])){ - if(($_GET['user_id'] != $_SESSION['id']) && ( ! ticket_user::isMod($_SESSION['ticket_user'])) ){ + if(($_GET['user_id'] != $_SESSION['id']) && ( ! ticket_user::isMod(unserialize($_SESSION['ticket_user']))) ){ //ERROR: No access! $_SESSION['error_code'] = "403"; @@ -35,6 +35,8 @@ function createticket(){ //create array of category id & names $catArray = Ticket_Category::getAllCategories(); $result['category'] = Gui_Elements::make_table_with_key_is_id($catArray, Array("getName"), "getTCategoryId" ); + global $INGAME_WEBPATH; + $result['ingame_webpath'] = $INGAME_WEBPATH; return $result; }else{ diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/inc/dashboard.php b/code/ryzom/tools/server/ryzom_ams/www/html/inc/dashboard.php index 980da3cb1..74475dcd1 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/inc/dashboard.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/inc/dashboard.php @@ -1,19 +1,22 @@ getTUserId(); - $result['nrToDo'] = Ticket_Queue_Handler::getNrOfTicketsToDo($_SESSION['ticket_user']->getTUserId()); - $result['nrAssignedWaiting'] = Ticket_Queue_Handler::getNrOfTicketsAssignedWaiting($_SESSION['ticket_user']->getTUserId()); + if(ticket_user::isMod(unserialize($_SESSION['ticket_user']))){ + $result['user_id'] = unserialize($_SESSION['ticket_user'])->getTUserId(); + $result['nrToDo'] = Ticket_Queue_Handler::getNrOfTicketsToDo(unserialize($_SESSION['ticket_user'])->getTUserId()); + $result['nrAssignedWaiting'] = Ticket_Queue_Handler::getNrOfTicketsAssignedWaiting(unserialize($_SESSION['ticket_user'])->getTUserId()); $result['nrTotalTickets'] = Ticket_Queue_Handler::getNrOfTickets(); $ticket = Ticket_Queue_Handler::getNewestTicket(); $result['newestTicketId'] = $ticket->getTId(); $result['newestTicketTitle'] = $ticket->getTitle(); $result['newestTicketAuthor'] = Ticket_User::get_username_from_id($ticket->getAuthor()); + global $INGAME_WEBPATH; + $result['ingame_webpath'] = $INGAME_WEBPATH; return $result; }else{ diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/inc/login.php b/code/ryzom/tools/server/ryzom_ams/www/html/inc/login.php index 745d48288..4d04f3cc7 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/inc/login.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/inc/login.php @@ -1,6 +1,8 @@ getInfo(); - if(Ticket_User::isMod($_SESSION['ticket_user']) && ($_GET['id']!= $_SESSION['id'])){ + if(Ticket_User::isMod(unserialize($_SESSION['ticket_user'])) && ($_GET['id']!= $_SESSION['id'])){ $result['changesOther'] = "TRUE"; } $result['target_id'] = $_GET['id']; $result['current_mail'] = $webUser->getEmail(); + $result['target_username'] = $webUser->getUsername(); } }else{ $webUser = new Webusers($_SESSION['id']); $result = $webUser->getInfo(); $result['target_id'] = $_SESSION['id']; - $result['current_mail'] = $webUser->getEmail(); + $result['current_mail'] = $webUser->getEmail(); + $result['target_username'] = $webUser->getUsername(); } //Sanitize Data $result['current_mail'] = filter_var($result['current_mail'], FILTER_SANITIZE_EMAIL); - //$result['Login'] = filter_var($result['Login'], FILTER_SANITIZE_STRING); + $result['target_username'] = filter_var($result['target_username'], FILTER_SANITIZE_STRING); $result['FirstName'] = filter_var($result['FirstName'], FILTER_SANITIZE_STRING); $result['LastName'] = filter_var($result['LastName'], FILTER_SANITIZE_STRING); $result['Country'] = filter_var($result['Country'], FILTER_SANITIZE_STRING); $result['Gender'] = filter_var($result['Gender'], FILTER_SANITIZE_NUMBER_INT); $result['ReceiveMail'] = filter_var($result['ReceiveMail'], FILTER_SANITIZE_NUMBER_INT); $result['country_array'] = getCountryArray(); + global $INGAME_WEBPATH; + $result['ingame_webpath'] = $INGAME_WEBPATH; return $result; }else{ //ERROR: not logged in! diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/inc/sgroup_list.php b/code/ryzom/tools/server/ryzom_ams/www/html/inc/sgroup_list.php index f9c2ac150..0b446fc2c 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/inc/sgroup_list.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/inc/sgroup_list.php @@ -1,20 +1,28 @@ getTUserId(); + $user_id = unserialize($_SESSION['ticket_user'])->getTUserId(); $queueArray = array(); $queue_handler = new Ticket_Queue_handler(); //Pagination Base Links - $result['pagination_base_link'] = "index.php?page=show_queue&get=".$result['queue_view'] ; + if (Helpers::check_if_game_client()) { + $result['pagination_base_link'] = $INGAME_WEBPATH."?page=show_queue&get=".$result['queue_view'] ; + }else{ + $result['pagination_base_link'] = $WEBPATH."?page=show_queue&get=".$result['queue_view'] ; + } //form url to keep the getters constant - $result['getURL'] = "index.php?page=show_queue&get=" . $result['queue_view']; + + if (Helpers::check_if_game_client()) { + $result['getURL'] = $INGAME_WEBPATH."?page=show_queue&get=" . $result['queue_view']; + }else{ + $result['getURL'] = $WEBPATH."?page=show_queue&get=" . $result['queue_view']; + } + if(isset($_GET['pagenum'])){ $result['getURL'] = $result['getURL'] . "&pagenum=".$_GET['pagenum']; } @@ -29,7 +40,14 @@ function show_queue(){ $how = filter_var($_GET['how'], FILTER_SANITIZE_STRING); $who = filter_var($_GET['who'], FILTER_SANITIZE_STRING); $queue_handler->CreateQueue($userid, $groupid, $what, $how, $who); - $result['pagination_base_link'] = "index.php?page=show_queue&get=create&userid=".$userid."&groupid=".$groupid."&what=".$what."&how=".$how."&who=".$who; + + if (Helpers::check_if_game_client()) { + $result['pagination_base_link'] = $INGAME_WEBPATH."?page=show_queue&get=create&userid=".$userid."&groupid=".$groupid."&what=".$what."&how=".$how."&who=".$who; + }else{ + $result['pagination_base_link'] = $WEBPATH."?page=show_queue&get=create&userid=".$userid."&groupid=".$groupid."&what=".$what."&how=".$how."&who=".$who; + } + + $result['prev_created_userid'] = $userid; $result['prev_created_groupid'] = $groupid; $result['prev_created_what'] = $what; @@ -60,7 +78,11 @@ function show_queue(){ $how = filter_var($_POST['how'], FILTER_SANITIZE_STRING); $who = filter_var($_POST['who'], FILTER_SANITIZE_STRING); $queue_handler->CreateQueue($userid, $groupid, $what, $how, $who); - $result['pagination_base_link'] = "index.php?page=show_queue&get=create&userid=".$userid."&groupid=".$groupid."&what=".$what."&how=".$how."&who=".$who; + if (Helpers::check_if_game_client()) { + $result['pagination_base_link'] = $INGAME_WEBPATH."?page=show_queue&get=create&userid=".$userid."&groupid=".$groupid."&what=".$what."&how=".$how."&who=".$who; + }else{ + $result['pagination_base_link'] = $WEBPATH."?page=show_queue&get=create&userid=".$userid."&groupid=".$groupid."&what=".$what."&how=".$how."&who=".$who; + } $result['prev_created_userid'] = $userid; $result['prev_created_groupid'] = $groupid; $result['prev_created_what'] = $what; @@ -93,7 +115,7 @@ function show_queue(){ $result['tickets'][$i]['timestamp_elapsed'] = Gui_Elements::time_elapsed_string($ticket['timestamp']); $i++; } - $result['user_id'] = $_SESSION['ticket_user']->getTUserId(); + $result['user_id'] = unserialize($_SESSION['ticket_user'])->getTUserId(); //Queue creator field info $result['grouplist'] = Gui_Elements::make_table(Support_Group::getGroups(), Array("getSGroupId","getName"), Array("sGroupId","name")); @@ -104,13 +126,15 @@ function show_queue(){ $result['teamlist'][$i]['name'] = $web_teammember->getUsername(); $i++; } + global $INGAME_WEBPATH; + $result['ingame_webpath'] = $INGAME_WEBPATH; return $result; }else{ //ERROR: Doesn't exist! $_SESSION['error_code'] = "404"; - header("Location: index.php?page=error"); + header("Location: ams?page=error"); exit; } diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/inc/show_reply.php b/code/ryzom/tools/server/ryzom_ams/www/html/inc/show_reply.php index 1640c5938..96cec0a6a 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/inc/show_reply.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/inc/show_reply.php @@ -12,7 +12,7 @@ function show_reply(){ $ticket = new Ticket(); $ticket->load_With_TId($reply->getTicket()); - if(( $ticket->getAuthor() == $_SESSION['ticket_user']->getTUserId() && ! $reply->getHidden()) || Ticket_User::isMod($_SESSION['ticket_user'] )){ + if(( $ticket->getAuthor() == unserialize($_SESSION['ticket_user'])->getTUserId() && ! $reply->getHidden()) || Ticket_User::isMod(unserialize($_SESSION['ticket_user']) )){ $content = new Ticket_Content(); $content->load_With_TContentId($reply->getContent()); @@ -27,9 +27,11 @@ function show_reply(){ $result['author'] = $author->getExternId(); $webUser = new WebUsers($author->getExternId()); $result['authorName'] = $webUser->getUsername(); - if(Ticket_User::isMod($_SESSION['ticket_user'])){ + if(Ticket_User::isMod(unserialize($_SESSION['ticket_user']))){ $result['isMod'] = "TRUE"; } + global $INGAME_WEBPATH; + $result['ingame_webpath'] = $INGAME_WEBPATH; return $result; }else{ diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/inc/show_sgroup.php b/code/ryzom/tools/server/ryzom_ams/www/html/inc/show_sgroup.php index 1c9ab406f..edf1ba0a9 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/inc/show_sgroup.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/inc/show_sgroup.php @@ -1,22 +1,28 @@ getUsername(); $i++; } + global $INGAME_WEBPATH; + $result['ingame_webpath'] = $INGAME_WEBPATH; return $result; @@ -39,7 +47,7 @@ function show_sgroup(){ //ERROR: No page specified! $_SESSION['error_code'] = "404"; - header("Location: index.php?page=error"); + header("Location: ams?page=error"); exit; } diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/inc/show_ticket.php b/code/ryzom/tools/server/ryzom_ams/www/html/inc/show_ticket.php index d69fbc319..4ecddb0b7 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/inc/show_ticket.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/inc/show_ticket.php @@ -4,12 +4,12 @@ function show_ticket(){ //if logged in if(WebUsers::isLoggedIn() && isset($_GET['id'])){ - $result['user_id'] = $_SESSION['ticket_user']->getTUserId(); + $result['user_id'] = unserialize($_SESSION['ticket_user'])->getTUserId(); $result['ticket_id'] = filter_var($_GET['id'], FILTER_SANITIZE_NUMBER_INT); $target_ticket = new Ticket(); $target_ticket->load_With_TId($result['ticket_id']); - if(Ticket_User::isMod($_SESSION['ticket_user'] )){ + if(Ticket_User::isMod(unserialize($_SESSION['ticket_user'] ))){ if(isset($_POST['action'])){ switch($_POST['action']){ case "forward": @@ -30,15 +30,15 @@ function show_ticket(){ } } - if(($target_ticket->getAuthor() == $_SESSION['ticket_user']->getTUserId()) || Ticket_User::isMod($_SESSION['ticket_user'] )){ + if(($target_ticket->getAuthor() == unserialize($_SESSION['ticket_user'])->getTUserId()) || Ticket_User::isMod(unserialize($_SESSION['ticket_user']) )){ $show_as_admin = false; - if(Ticket_User::isMod($_SESSION['ticket_user'])){ + if(Ticket_User::isMod(unserialize($_SESSION['ticket_user']))){ $show_as_admin = true; } $entire_ticket = Ticket::getEntireTicket( $result['ticket_id'],$show_as_admin); - Ticket_Log::createLogEntry($result['ticket_id'],$_SESSION['ticket_user']->getTUserId(), 3); + Ticket_Log::createLogEntry($result['ticket_id'],unserialize($_SESSION['ticket_user'])->getTUserId(), 3); $result['ticket_tId'] = $entire_ticket['ticket_obj']->getTId(); $result['ticket_forwardedGroupName'] = $entire_ticket['ticket_obj']->getForwardedGroupName(); $result['ticket_forwardedGroupId'] = $entire_ticket['ticket_obj']->getForwardedGroupId(); @@ -62,12 +62,14 @@ function show_ticket(){ $result['ticket_replies'][$i]['author'] = $webReplyUser->getUsername(); $i++; } - if(Ticket_User::isMod($_SESSION['ticket_user'])){ + if(Ticket_User::isMod(unserialize($_SESSION['ticket_user']))){ $result['isMod'] = "TRUE"; $result['statusList'] = Ticket::getStatusArray(); $result['sGroups'] = Gui_Elements::make_table_with_key_is_id(Support_Group::getAllSupportGroups(), Array("getName"), "getSGroupId" ); } $result['hasInfo'] = $target_ticket->hasInfo(); + global $INGAME_WEBPATH; + $result['ingame_webpath'] = $INGAME_WEBPATH; return $result; }else{ diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/inc/show_ticket_info.php b/code/ryzom/tools/server/ryzom_ams/www/html/inc/show_ticket_info.php index 4f1e98d61..4e43c7aad 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/inc/show_ticket_info.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/inc/show_ticket_info.php @@ -9,7 +9,7 @@ function show_ticket_info(){ $target_ticket = new Ticket(); $target_ticket->load_With_TId($result['ticket_id']); - if( $target_ticket->hasInfo() && (($target_ticket->getAuthor() == $_SESSION['ticket_user']->getTUserId()) || Ticket_User::isMod($_SESSION['ticket_user'] ))){ + if( $target_ticket->hasInfo() && (($target_ticket->getAuthor() == unserialize($_SESSION['ticket_user'])->getTUserId()) || Ticket_User::isMod(unserialize($_SESSION['ticket_user']) ))){ $result['ticket_title'] = $target_ticket->getTitle(); $result['ticket_author'] = $target_ticket->getAuthor(); @@ -31,12 +31,14 @@ function show_ticket_info(){ $result['ht'] = $ticket_info->getHT(); $result['nel3d'] = $ticket_info->getNel3D(); $result['user_id'] = $ticket_info->getUser_Id(); - global $WEBPATH; - $result['WEBPATH'] = $WEBPATH; + global $IMAGELOC_WEBPATH; + $result['IMAGELOC_WEBPATH'] = $IMAGELOC_WEBPATH; - if(Ticket_User::isMod($_SESSION['ticket_user'])){ + if(Ticket_User::isMod(unserialize($_SESSION['ticket_user']))){ $result['isMod'] = "TRUE"; } + global $INGAME_WEBPATH; + $result['ingame_webpath'] = $INGAME_WEBPATH; return $result; }else{ diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/inc/show_ticket_log.php b/code/ryzom/tools/server/ryzom_ams/www/html/inc/show_ticket_log.php index 5d4cbb76b..6d1ea9701 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/inc/show_ticket_log.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/inc/show_ticket_log.php @@ -1,11 +1,12 @@ load_With_TId($result['ticket_id']); @@ -23,7 +24,11 @@ function show_ticket_log(){ $webUser2 = new WebUsers($log['argument']); $query_backpart = $webUser2->getUsername(); }else if($log['action'] == 4){ - $query_backpart = "ID#" . $log['argument'] . ""; + if (Helpers::check_if_game_client()) { + $query_backpart = "ID#" . $log['argument'] . ""; + }else{ + $query_backpart = "ID#" . $log['argument'] . ""; + } }else if($log['action'] == 5){ $statusArray = Ticket::getStatusArray(); $query_backpart = $statusArray[$log['argument'] ]; @@ -31,15 +36,21 @@ function show_ticket_log(){ $priorityArray = Ticket::getPriorityArray(); $query_backpart = $priorityArray[$log['argument'] ]; }else if($log['action'] == 8){ - $query_backpart = "" . Support_Group::getGroup($log['argument'])->getName() . ""; + if (Helpers::check_if_game_client()) { + $query_backpart = "" . Support_Group::getGroup($log['argument'])->getName() . ""; + }else{ + $query_backpart = "" . Support_Group::getGroup($log['argument'])->getName() . ""; + } } $result['ticket_logs'][$i]['query'] = $author . " " . $log_action_array[$log['action']] . " " . $query_backpart; $result['ticket_logs'][$i]['timestamp_elapsed'] = Gui_Elements::time_elapsed_string($log['timestamp']); $i++; } - if(Ticket_User::isMod($_SESSION['ticket_user'])){ + if(Ticket_User::isMod(unserialize($_SESSION['ticket_user']))){ $result['isMod'] = "TRUE"; } + global $INGAME_WEBPATH; + $result['ingame_webpath'] = $INGAME_WEBPATH; return $result; }else{ diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/inc/show_user.php b/code/ryzom/tools/server/ryzom_ams/www/html/inc/show_user.php index a8774ba83..aae4d2caa 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/inc/show_user.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/inc/show_user.php @@ -3,8 +3,7 @@ function show_user(){ //if logged in if(WebUsers::isLoggedIn()){ - - if( !isset($_GET['id']) || Ticket_User::isMod($_SESSION['ticket_user']) || $_GET['id'] == $_SESSION['id'] ){ + if( !isset($_GET['id']) || Ticket_User::isMod(unserialize($_SESSION['ticket_user'])) || $_GET['id'] == $_SESSION['id'] ){ if(isset($_GET['id'])){ $result['target_id'] = filter_var($_GET['id'], FILTER_SANITIZE_NUMBER_INT); @@ -22,12 +21,14 @@ function show_user(){ $ticket_user = Ticket_User::constr_ExternId($result['target_id']); $result['userPermission'] = $ticket_user->getPermission(); - if(Ticket_User::isAdmin($_SESSION['ticket_user'])){ + if(Ticket_User::isAdmin(unserialize($_SESSION['ticket_user']))){ $result['isAdmin'] = "TRUE"; } $ticketlist = Ticket::getTicketsOf($ticket_user->getTUserId()); $result['ticketlist'] = Gui_Elements::make_table($ticketlist, Array("getTId","getTimestamp","getTitle","getStatus","getStatusText","getStatusText","getCategoryName"), Array("tId","timestamp","title","status","statustext","statusText","category")); + global $INGAME_WEBPATH; + $result['ingame_webpath'] = $INGAME_WEBPATH; return $result; }else{ @@ -41,4 +42,4 @@ function show_user(){ header("Location: index.php"); exit; } -} \ No newline at end of file +} diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/inc/userlist.php b/code/ryzom/tools/server/ryzom_ams/www/html/inc/userlist.php index 9e4c8a079..76074abcd 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/inc/userlist.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/inc/userlist.php @@ -1,7 +1,7 @@ getElements() , Array("getUId","getUsername","getEmail"), Array("id","username","email")); @@ -15,9 +15,13 @@ function userlist(){ $i++; } - if (Ticket_User::isAdmin($_SESSION['ticket_user'])){ + if (Ticket_User::isAdmin(unserialize($_SESSION['ticket_user']))){ $pageResult['isAdmin'] = "TRUE"; } + global $INGAME_WEBPATH; + $pageResult['ingame_webpath'] = $INGAME_WEBPATH; + global $BASE_WEBPATH; + $pageResult['base_webpath'] = $BASE_WEBPATH; return $pageResult; }else{ //ERROR: No access! @@ -25,4 +29,4 @@ function userlist(){ header("Location: index.php?page=error"); exit; } -} \ No newline at end of file +} diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/index.php b/code/ryzom/tools/server/ryzom_ams/www/html/index.php index b52ccce3b..a57d4aa45 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/index.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/index.php @@ -8,7 +8,7 @@ session_start(); //Decide what page to load if ( ! isset( $_GET["page"]) ){ if(isset($_SESSION['user'])){ - if(Ticket_User::isMod($_SESSION['ticket_user'])){ + if(Ticket_User::isMod(unserialize($_SESSION['ticket_user']))){ $page = 'dashboard'; }else{ $page = 'show_user'; @@ -44,7 +44,7 @@ if(isset($_SESSION['user'])){ //Set permission if(isset($_SESSION['ticket_user'])){ - $return['permission'] = $_SESSION['ticket_user']->getPermission(); + $return['permission'] = unserialize($_SESSION['ticket_user'])->getPermission(); }else{ //default permission $return['permission'] = 0; diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/index_charisma.php b/code/ryzom/tools/server/ryzom_ams/www/html/index_charisma.php deleted file mode 100644 index b4b943104..000000000 --- a/code/ryzom/tools/server/ryzom_ams/www/html/index_charisma.php +++ /dev/null @@ -1,329 +0,0 @@ - - - -
    - -
    - - -
    -
    -
    -

    Introduction

    -
    - - - -
    -
    -
    -

    Charisma free, premium quality, responsive, multiple skin admin template.

    -

    Its a live demo of the template. I have created Charisma to ease the repeat work I have to do on my projects. Now I re-use Charisma as a base for my admin panel work and I am sharing it with you :)

    -

    All pages in the menu are functional, take a look at all, please share this with your followers.

    - -

    - Back to article - Download Page -

    -
    -
    -
    -
    - -
    -
    -
    -

    Tabs

    -
    - - - -
    -
    -
    - - -
    -
    -

    Charisma a fully featued template

    -

    Its a fully featured, responsive template for your admin panel. Its optimized for tablet and mobile phones. Scan the QR code below to view it in your mobile device.

    QR Code -
    -
    -

    Custom small text

    -

    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur bibendum ornare dolor.

    -

    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur bibendum ornare dolor, quis ullamcorper ligula sodales at. Nulla tellus elit, varius non commodo eget, mattis vel eros. In sed ornare nulla. Donec consectetur, velit a pharetra ultricies, diam lorem lacinia risus, ac commodo orci erat eu massa. Sed sit amet nulla ipsum. Donec felis mauris, vulputate sed tempor at, aliquam a ligula. Pellentesque non pulvinar nisi.

    -
    -
    -

    Messages small text

    -

    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur bibendum ornare dolor, quis ullamcorper ligula sodales at. Nulla tellus elit, varius non commodo eget, mattis vel eros. In sed ornare nulla. Donec consectetur, velit a pharetra ultricies, diam lorem lacinia risus, ac commodo orci erat eu massa. Sed sit amet nulla ipsum. Donec felis mauris, vulputate sed tempor at, aliquam a ligula. Pellentesque non pulvinar nisi.

    -

    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur bibendum ornare dolor.

    -
    -
    -
    -
    - -
    -
    -

    Member Activity

    -
    - - -
    -
    -
    -
    - -
    -
    -
    - -
    -
    -

    Realtime Traffic

    -
    - - -
    -
    -
    -
    -

    You can update a chart periodically to get a real-time effect by using a timer to insert the new data in the plot and redraw it.

    -

    Time between updates: milliseconds

    -
    -
    -
    - -
    -
    -
    -

    Buttons

    -
    - - - -
    -
    -
    -

    - - - -

    -

    - - - -

    -

    - - - -

    -

    - - - - -

    -

    - - - - -

    -

    - - - -

    -
    -
    - -
    -
    -

    Buttons

    -
    - - - -
    -
    -
    -

    - - -

    -

    - - -

    -

    - - -

    -

    - -

    - - -
    -
    - - -
    - - - - - diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/table.php b/code/ryzom/tools/server/ryzom_ams/www/html/table.php deleted file mode 100644 index 0a023296c..000000000 --- a/code/ryzom/tools/server/ryzom_ams/www/html/table.php +++ /dev/null @@ -1,1844 +0,0 @@ - - - -
    - -
    - -
    -
    -
    -

    Members

    -
    - - - -
    -
    -
    -
    - - -
    -
    - - - -
    - - - {if isset($ACTION_RESULT)} - -
    - - - -
    -

    Tickets

    -

    -

    - - - - - - - - - - - - - - - - -
    Show - - tickets - - to - - : - - or - - - - -
    -
    -

    -

    - - - - - - - - - - - - {foreach from=$tickets item=ticket} - - - - - - - - - - - {/foreach} -
    IDTitleAssignedTimestampCategoryStatusSupportGroupActions
    {$ticket.tId}{$ticket.title}{if $ticket.assignedText neq ""} {$ticket.assignedText}{else} {$not_assigned} {/if}{$ticket.timestamp}{$ticket.category}{if $ticket.status eq 0}{else if $ticket.status eq 1}{else if $ticket.status eq 2}{/if}{$ticket.statusText} - - {if $ticket.forwardedGroupName eq "0"} - {$public_sgroup} - {else} - {$ticket.forwardedGroupName} - {/if} - - - {if $ticket.assigned eq 0} -
    - - - -
    - {else if $ticket.assigned eq $user_id} -
    - - - -
    - {/if} -
    -

    -
    - - - - {foreach from=$links item=link} - - {/foreach} - - -
    «{$link}»
    -
    -
    - - - - - - -
    - {if isset($ACTION_RESULT) and $ACTION_RESULT eq "SUCCESS_ASSIGNED"} -

    - {$success_assigned} -

    - {else if isset($ACTION_RESULT) and $ACTION_RESULT eq "SUCCESS_UNASSIGNED"} -

    - {$success_unassigned} -

    - {else if isset($ACTION_RESULT) and $ACTION_RESULT eq "TICKET_NOT_EXISTING"} -

    - {$ticket_not_existing} -

    - {else if isset($ACTION_RESULT) and $ACTION_RESULT eq "ALREADY_ASSIGNED"} -

    - {$ticket_already_assigned} -

    - {else if isset($ACTION_RESULT) and $ACTION_RESULT eq "NOT_ASSIGNED"} -

    - {$ticket_not_assigned} -

    - {/if} -
    - - {/if} -
    -
    -
    - -
    - - - - - - - -
    - - - - -
    - - - - - -
    Show TicketShow Ticket Log
    -
    -
    -
    - - - - - - - - - -

    Reply ID#{$reply_id} of Ticket #{$ticket_id}

    -
    - - - - - - - - - - -
    - - -
    -
    - - -
    - - -
    - - -
    -

    Reply:

    -

    - - - - -
    -

    - {$reply_timestamp} - {if $author_permission eq '1'} - {if isset($isMod) and $isMod eq "TRUE"} {$authorName}{else} {$authorName} {/if} - {else if $reply.permission gt '1'} - {if isset($isMod) and $isMod eq "TRUE"} {$authorName}{else} {$authorName} {/if} - {/if}

    -

    {$reply_content}

    -
    -

    -
    -
    -
    -
    - -
    - - - - - - - - - -

    Support Group: {$groupsname}

    -
    - - - - - - - - - - -
    - - -
    -
    - - - - -
    - - -
    - - -
    -

    Add user to the list

    - {if isset($isAdmin) && $isAdmin eq 'TRUE'} -
    - - - - - -
    Username:
    - - - -

    - -

    - - {if isset($RESULT_OF_ADDING) and $RESULT_OF_ADDING eq "SUCCESS"} -

    - {$add_to_group_success} -

    - {else if isset($RESULT_OF_ADDING) and $RESULT_OF_ADDING eq "ALREADY_ADDED"} -

    - {$user_already_added} -

    - {else if isset($RESULT_OF_ADDING) and $RESULT_OF_ADDING eq "GROUP_NOT_EXISTING"} -

    - {$group_not_existing} -

    - {else if isset($RESULT_OF_ADDING) and $RESULT_OF_ADDING eq "USER_NOT_EXISTING"} -

    - {$user_not_existing} -

    - {else if isset($RESULT_OF_ADDING) and $RESULT_OF_ADDING eq "NOT_MOD_OR_ADMIN"} -

    - {$not_mod_or_admin} -

    - {/if} -
    - {/if} -
    -
    -
    - - -
    - - -
    -

    All members

    - - - - - {if isset($isAdmin) && $isAdmin eq 'TRUE'}{/if} - - - {foreach from=$userlist item=user} - - - - {if isset($isAdmin) && $isAdmin eq 'TRUE'}{/if} - - {/foreach} -
    IDNameAction
    {$user.tUserId}{$user.name}Delete
    -
    -
    -
    - - -
    - - -
    -

    Mail settings

    -
    - - - - - - - - - - - - - - - - - - - - - - -
    Group Email:
    IMAP Mail Server:
    IMAP Username:
    IMAP Password:
    - - - - -

    - -

    - - {if isset($RESULT_OF_MODIFYING) and $RESULT_OF_MODIFYING eq "SUCCESS"} -

    - {$modify_mail_of_group_success} -

    - {else if isset($RESULT_OF_MODIFYING) and $RESULT_OF_MODIFYING eq "EMAIL_NOT_VALID"} -

    - {$email_not_valid} -

    - {else if isset($RESULT_OF_MODIFYING) and $RESULT_OF_MODIFYING eq "NO_PASSWORD"} -

    - {$no_password_given} -

    - {/if} - -
    -
    -
    -
    -
    - -
    - - - - - - - -
    - - - - -
    - - - {if isset($isMod) and $isMod eq "TRUE"}{/if} - - {if $hasInfo}{/if} - -
    Show Ticket LogSend Other TicketShow Additional Info
    -
    -
    -
    - - - - - - - - - -

    [{$t_title}-#{$ticket_tId}] {$ticket_title}

    -
    - - - - - - - - - - -
    - - -
    -
    - - -
    - - -
    - - -
    - - - - - {if isset($isMod) and $isMod eq "TRUE"} - -
    - - -
    - - - - - - - - - - - - - - - - -
    Submitted: {$ticket_timestamp}Last Updated: {$ticket_lastupdate}Status: {if $ticket_status neq 3}Open{/if} {if $ticket_status eq 3} {$ticket_statustext}{else}{$ticket_statustext} {/if}
    Category: {$ticket_category}Priority {$ticket_prioritytext}Support Group: - - {if $ticket_forwardedGroupName eq "0"} - {$public_sgroup} - {else} - {$ticket_forwardedGroupName} - {/if} - -
    Assigned To: {if $ticket_assignedTo neq ""} {$ticket_assignedToText}{else} {$not_assigned} {/if}
    -
    -
    - - {foreach from=$ticket_replies item=reply} - - - - {/foreach} - - {if $ticket_status eq 3} - - - - {/if} - - - - -
    - - -
    -

    - {$reply.timestamp} - {if $reply.permission eq '1'} - {if isset($isMod) and $isMod eq "TRUE"} {$reply.author}{else} {$reply.author} {/if} - {else if $reply.permission gt '1'} - {if isset($isMod) and $isMod eq "TRUE"} {$reply.author}{else} {$reply.author} {/if} - {/if} -

    -

    {$reply.replyContent}

    -
    -
    - - -
    -

    [Ticket is closed]

    -
    -
    - -
    - - - - - - {if $ticket_status neq 3} - - - - {if isset($isMod) and $isMod eq "TRUE"} - - - - {/if} - {/if} - - - - - - -
    -

    {$t_reply}:

    -
    Hide reply for user.
    - {if isset($isMod) and $isMod eq "TRUE"} - - - - - -
    - Change status to - - - Change priority to - -
    - {/if} -
    - - - -
    -
    -
    -
    - - -
    - - - - - -
    -

    - Ticket Assigning: - {if $ticket_assignedTo eq 0} -

    - - - -
    - {else if $ticket_assignedTo eq $user_id} -
    - - - -
    - {/if} -

    - {if isset($ACTION_RESULT) and $ACTION_RESULT eq "SUCCESS_ASSIGNED"} -

    - {$success_assigned} -

    - {else if isset($ACTION_RESULT) and $ACTION_RESULT eq "SUCCESS_UNASSIGNED"} -

    - {$success_unassigned} -

    - {else if isset($ACTION_RESULT) and $ACTION_RESULT eq "TICKET_NOT_EXISTING"} -

    - {$ticket_not_existing} -

    - {else if isset($ACTION_RESULT) and $ACTION_RESULT eq "ALREADY_ASSIGNED"} -

    - {$ticket_already_assigned} -

    - {else if isset($ACTION_RESULT) and $ACTION_RESULT eq "NOT_ASSIGNED"} -

    - {$ticket_not_assigned} -

    - {/if} - - -
    -

    - Forward to Group: -

    - - - - - -
    -

    - {if isset($ACTION_RESULT) and $ACTION_RESULT eq "INVALID_SGROUP"} -

    - {$invalid_sgroup} -

    - {else if isset($ACTION_RESULT) and $ACTION_RESULT eq "TICKET_NOT_EXISTING"} -

    - {$ticket_not_existing} -

    - {else if isset($ACTION_RESULT) and $ACTION_RESULT eq "SUCCESS_FORWARDED"} -

    - {$success_forwarded} -

    - {/if} -
    -
    -
    - {/if} - -
    -
    -
    -
    -
    - -
    - - - - - - - -
    - - - - -
    - - - {if isset($isMod) and $isMod eq "TRUE"}{/if} - - - -
    Show Ticket LogSend Other TicketShow Ticket
    -
    -
    -
    - - - - - - - - - -

    Additional Info For Ticket [#{$ticket_id}]

    -
    - - - - - - - - - - -
    - - -
    -
    - - -
    - - -
    - - -
    - - - - -
    - - -
    -

    Ingame related

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Shard ID: {$shard_id}
    User_Id: {$user_id}
    User Position: {$user_position}
    View Position: {$view_position}
    Client_Version: {$client_version}
    Patch_Version: {$patch_version}
    Server_Tick: {$server_tick}
    -
    -
    - - -
    -

    Hardware & Software related

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Memory: {$memory}
    Processor: {$processor}
    Cpu_Id: {$cpu_id}
    Cpu_Mask: {$cpu_mask}
    HT: {$ht}
    OS: {$os}
    NeL3D: {$nel3d}
    -
    -
    - - -
    -

    Network related

    - - - - - - - - - - -
    Connect_State: {$connect_state}
    Local_Address: {$local_address}
    -
    -
    -
    -
    -
    -
    - -
    - - - - - - - -
    - - - - -
    - - - - -
    Show Ticket
    -
    -
    -
    - - - - - - - - - -

    Log of Ticket #{$ticket_id}

    -
    - - - - - - - - - - -
    - - -
    -
    - - -
    - - -
    - - -
    -

    Title: {$ticket_title}

    -

    - - - - - - - - {foreach from=$ticket_logs item=log} - - - - - - {/foreach} - -
    IDTimestampQuery
    {$log.tLogId}{$log.timestamp}{$log.query}
    -

    -
    -
    -
    -
    - -
    - - - - - - - -
    - - - - -
    - - - - - {if isset($isAdmin) and $isAdmin eq 'TRUE' and $target_id neq 1} - {if $userPermission eq 1} - - - {else if $userPermission eq 2 } - - - {else if $userPermission eq 3 } - - - {/if} - {/if} - -
    Edit UserSend TicketMake ModeratorMake AdminDemote to UserMake AdminDemote to UserDemote to Moderator
    -
    -
    -
    - - - - - - - - - -

    Profile of {$target_name}

    -
    - - - - - - - - - - -
    - - -
    -
    - - - -
    - - -
    - - -
    - -

    Info

    - - - - - - - - - - - - {if $firstName neq ""} - - - - - {/if} - {if $lastName neq ""} - - - - - {/if} - {if $country neq ""} - - - - - {/if} - {if $gender neq 0} - - - {if $gender eq 1} - - {else if $gender eq 2} - - {/if} - - {/if} - -
    Email:{$mail}
    Role: - {if $userPermission eq 1}User{/if} - {if $userPermission eq 2}Moderator{/if} - {if $userPermission eq 3}Admin{/if} -
    Firstname:{$firstName}
    LastName:{$lastName}
    Country:{$country}
    Gender:♂♀
    -
    -
    -
    - - -
    - - -
    -

    Tickets

    - - - - - - - - - - - {foreach from=$ticketlist item=ticket} - - - - - - - - - {/foreach} - -
    IDTitleTimestampCategoryStatus
    {$ticket.tId}{$ticket.title}{$ticket.timestamp}{$ticket.category}{if $ticket.status eq 0} {/if} {$ticket.statusText}
    -
    -
    -
    -
    - -
    - - - - - - - - - -

    Members

    -
    - - - - - - - - - - -
    - - -
    -
    - - -
    - - -
    - - - -
    -

    All Acounts

    - - - - - - - - - - {foreach from=$userlist item=element} - - - - - {if $element.permission eq 1}{/if} - {if $element.permission eq 2}{/if} - {if $element.permission eq 3}{/if} - - - - {/foreach} -
    IdUsernameEmailPermissionAction
    {$element.id}{$element.username}{$element.email}UserModeratorAdmin - Show User - Edit User - {if isset($isAdmin) and $isAdmin eq 'TRUE' and $element.id neq 1} - {if $element.permission eq 1} - Make Moderator - Make Admin - {else if $element.permission eq 2 } - Demote to User - Make Admin - {else if $element.permission eq 3 } - Demote to User - Demote to Moderator - {/if} - {/if} -
    -
    - - - - {foreach from=$links item=link} - - {/foreach} - - -
    «{$link}»
    -
    -
    -
    -
    - -
    - Edit User + Edit User
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    UsernameDate registeredRoleStatusActions
    David R2012/01/01Member - Active - - - - View - - - - Edit - - - - Delete - -
    Chris Jack2012/01/01Member - Active - - - - View - - - - Edit - - - - Delete - -
    Jack Chris2012/01/01Member - Active - - - - View - - - - Edit - - - - Delete - -
    Muhammad Usman2012/01/01Member - Active - - - - View - - - - Edit - - - - Delete - -
    Sheikh Heera2012/02/01Staff - Banned - - - - View - - - - Edit - - - - Delete - -
    Helen Garner2012/02/01Staff - Banned - - - - View - - - - Edit - - - - Delete - -
    Saruar Ahmed2012/03/01Member - Pending - - - - View - - - - Edit - - - - Delete - -
    Ahemd Saruar2012/03/01Member - Pending - - - - View - - - - Edit - - - - Delete - -
    David R2012/01/01Member - Active - - - - View - - - - Edit - - - - Delete - -
    Chris Jack2012/01/01Member - Active - - - - View - - - - Edit - - - - Delete - -
    Jack Chris2012/01/01Member - Active - - - - View - - - - Edit - - - - Delete - -
    Muhammad Usman2012/01/01Member - Active - - - - View - - - - Edit - - - - Delete - -
    Sheikh Heera2012/02/01Staff - Banned - - - - View - - - - Edit - - - - Delete - -
    Helen Garner2012/02/01Staff - Banned - - - - View - - - - Edit - - - - Delete - -
    Saruar Ahmed2012/03/01Member - Pending - - - - View - - - - Edit - - - - Delete - -
    Ahemd Saruar2012/03/01Member - Pending - - - - View - - - - Edit - - - - Delete - -
    David R2012/01/01Member - Active - - - - View - - - - Edit - - - - Delete - -
    Chris Jack2012/01/01Member - Active - - - - View - - - - Edit - - - - Delete - -
    Jack Chris2012/01/01Member - Active - - - - View - - - - Edit - - - - Delete - -
    Muhammad Usman2012/01/01Member - Active - - - - View - - - - Edit - - - - Delete - -
    Sheikh Heera2012/02/01Staff - Banned - - - - View - - - - Edit - - - - Delete - -
    Helen Garner2012/02/01Staff - Banned - - - - View - - - - Edit - - - - Delete - -
    Saruar Ahmed2012/03/01Member - Pending - - - - View - - - - Edit - - - - Delete - -
    Ahemd Saruar2012/03/01Member - Pending - - - - View - - - - Edit - - - - Delete - -
    David R2012/01/01Member - Active - - - - View - - - - Edit - - - - Delete - -
    Chris Jack2012/01/01Member - Active - - - - View - - - - Edit - - - - Delete - -
    Jack Chris2012/01/01Member - Active - - - - View - - - - Edit - - - - Delete - -
    Muhammad Usman2012/01/01Member - Active - - - - View - - - - Edit - - - - Delete - -
    Sheikh Heera2012/02/01Staff - Banned - - - - View - - - - Edit - - - - Delete - -
    Helen Garner2012/02/01Staff - Banned - - - - View - - - - Edit - - - - Delete - -
    Saruar Ahmed2012/03/01Member - Pending - - - - View - - - - Edit - - - - Delete - -
    Ahemd Saruar2012/03/01Member - Pending - - - - View - - - - Edit - - - - Delete - -
    David R2012/01/01Member - Active - - - - View - - - - Edit - - - - Delete - -
    Chris Jack2012/01/01Member - Active - - - - View - - - - Edit - - - - Delete - -
    Jack Chris2012/01/01Member - Active - - - - View - - - - Edit - - - - Delete - -
    Muhammad Usman2012/01/01Member - Active - - - - View - - - - Edit - - - - Delete - -
    Sheikh Heera2012/02/01Staff - Banned - - - - View - - - - Edit - - - - Delete - -
    Helen Garner2012/02/01Staff - Banned - - - - View - - - - Edit - - - - Delete - -
    Saruar Ahmed2012/03/01Member - Pending - - - - View - - - - Edit - - - - Delete - -
    Ahemd Saruar2012/03/01Member - Pending - - - - View - - - - Edit - - - - Delete - -
    Habib Rizwan2012/01/21Staff - Active - - - - View - - - - Edit - - - - Delete - -
    Rizwan Habib2012/01/21Staff - Active - - - - View - - - - Edit - - - - Delete - -
    Amrin Sana2012/08/23Staff - Banned - - - - View - - - - Edit - - - - Delete - -
    Sana Amrin2012/08/23Staff - Banned - - - - View - - - - Edit - - - - Delete - -
    Ifrah Jannat2012/06/01Admin - Inactive - - - - View - - - - Edit - - - - Delete - -
    Jannat Ifrah2012/06/01Admin - Inactive - - - - View - - - - Edit - - - - Delete - -
    Robert2012/03/01Member - Pending - - - - View - - - - Edit - - - - Delete - -
    Dave Robert2012/03/01Member - Pending - - - - View - - - - Edit - - - - Delete - -
    Brown Robert2012/03/01Member - Pending - - - - View - - - - Edit - - - - Delete - -
    Usman Muhammad2012/01/01Member - Active - - - - View - - - - Edit - - - - Delete - -
    Abdullah2012/02/01Staff - Banned - - - - View - - - - Edit - - - - Delete - -
    Dow John2012/02/01Admin - Inactive - - - - View - - - - Edit - - - - Delete - -
    John R2012/02/01Admin - Inactive - - - - View - - - - Edit - - - - Delete - -
    Paul Wilson2012/03/01Member - Pending - - - - View - - - - Edit - - - - Delete - -
    Wilson Paul2012/03/01Member - Pending - - - - View - - - - Edit - - - - Delete - -
    Heera Sheikh2012/01/21Staff - Active - - - - View - - - - Edit - - - - Delete - -
    Sheikh Heera2012/01/21Staff - Active - - - - View - - - - Edit - - - - Delete - -
    Christopher2012/08/23Staff - Banned - - - - View - - - - Edit - - - - Delete - -
    Andro Christopher2012/08/23Staff - Banned - - - - View - - - - Edit - - - - Delete - -
    Jhon Doe2012/06/01Admin - Inactive - - - - View - - - - Edit - - - - Delete - -
    Lorem Ipsum2012/03/01Member - Pending - - - - View - - - - Edit - - - - Delete - -
    Abraham2012/03/01Member - Pending - - - - View - - - - Edit - - - - Delete - -
    Brown Blue2012/03/01Member - Pending - - - - View - - - - Edit - - - - Delete - -
    Worth Name2012/03/01Member - Pending - - - - View - - - - Edit - - - - Delete - -
    - - - - - -
    -
    -
    -

    Simple Table

    -
    - - - -
    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    UsernameDate registeredRoleStatus
    Muhammad Usman2012/01/01Member - Active -
    White Horse2012/02/01Staff - Banned -
    Sheikh Heera2012/02/01Admin - Inactive -
    Saruar2012/03/01Member - Pending -
    Sana Amrin2012/01/21Staff - Active -
    - -
    -
    - -
    -
    -

    Striped Table

    -
    - - - -
    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    UsernameDate registeredRoleStatus
    Muhammad Usman2012/01/01Member - Active -
    White Horse2012/02/01Staff - Banned -
    Sheikh Heera2012/02/01Admin - Inactive -
    Saruar2012/03/01Member - Pending -
    Sana Amrin2012/01/21Staff - Active -
    - -
    -
    -
    - -
    -
    -
    -

    Bordered Table

    -
    - - - -
    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    UsernameDate registeredRoleStatus
    Muhammad Usman2012/01/01Member - Active -
    White Horse2012/02/01Staff - Banned -
    Sheikh Heera2012/02/01Admin - Inactive -
    Saruar2012/03/01Member - Pending -
    Sana Amrin2012/01/21Staff - Active -
    - -
    -
    - -
    -
    -

    Condensed Table

    -
    - - - -
    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    UsernameDate registeredRoleStatus
    Muhammad Usman2012/01/01Member - Active -
    White Horse2012/02/01Staff - Banned -
    Sheikh Heera2012/02/01Admin - Inactive -
    Saruar2012/03/01Member - Pending -
    Sana Amrin2012/01/21Staff - Active -
    - -
    -
    - -
    - -
    -
    -
    -

    Combined All

    -
    - - - -
    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    UsernameDate registeredRoleStatus
    Muhammad Usman2012/01/01Member - Active -
    White Horse2012/02/01Staff - Banned -
    Sheikh Heera2012/02/01Admin - Inactive -
    Saruar2012/03/01Member - Pending -
    Sana Amrin2012/01/21Staff - Active -
    - -
    -
    -
    - - diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/templates/show_ticket_info.tpl b/code/ryzom/tools/server/ryzom_ams/www/html/templates/show_ticket_info.tpl index 23eaebdab..d8a7420d5 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/templates/show_ticket_info.tpl +++ b/code/ryzom/tools/server/ryzom_ams/www/html/templates/show_ticket_info.tpl @@ -17,61 +17,61 @@
    Ingame related
    Shard ID: {$shard_id} Shard ID: {$shard_id}
    User_Id: {$user_id} User_Id: {$user_id}
    User Position: {$user_position} User Position: {$user_position}
    View Position: {$view_position} View Position: {$view_position}
    Client_Version: {$client_version} Client_Version: {$client_version}
    Patch_Version: {$patch_version} Patch_Version: {$patch_version}
    Server_Tick: {$server_tick} Server_Tick: {$server_tick}
    Hardware & Software related
    Memory: {$memory} Memory: {$memory}
    Processor: {$processor} Processor: {$processor}
    Cpu_Id: {$cpu_id} Cpu_Id: {$cpu_id}
    Cpu_Mask: {$cpu_mask} Cpu_Mask: {$cpu_mask}
    HT: {$ht} HT: {$ht}
    OS: {$os} OS: {$os}
    NeL3D: {$nel3d} NeL3D: {$nel3d}
    Network related
    Connect_State: {$connect_state} Connect_State: {$connect_state}
    Local_Address: {$local_address} Local_Address: {$local_address}
    diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/tour.php b/code/ryzom/tools/server/ryzom_ams/www/html/tour.php deleted file mode 100644 index d4e24047f..000000000 --- a/code/ryzom/tools/server/ryzom_ams/www/html/tour.php +++ /dev/null @@ -1,32 +0,0 @@ - - - -
    - -
    - -
    -
    -
    -

    Tour

    -
    - - - -
    -
    -
    - Click Here to restart the tour
    - You can create Custom Tour, like this.
    For more help on implementing tour go here -
    -
    -
    - - diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/typography.php b/code/ryzom/tools/server/ryzom_ams/www/html/typography.php deleted file mode 100644 index b49b9430d..000000000 --- a/code/ryzom/tools/server/ryzom_ams/www/html/typography.php +++ /dev/null @@ -1,176 +0,0 @@ - - -
    - -
    - -
    -
    -
    -

    Typography

    -
    - - - -
    -
    -
    - -
    -
    -

    Sample text and paragraphs

    -

    - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur bibendum ornare dolor, quis ullamcorper ligula sodales at. Nulla tellus elit, varius non commodo eget, mattis vel eros. In sed ornare nulla. Donec consectetur, velit a pharetra ultricies, diam lorem lacinia risus, ac commodo orci erat eu massa. Sed sit amet nulla ipsum. Donec felis mauris, vulputate sed tempor at, aliquam a ligula. Pellentesque non pulvinar nisi. -

    -

    - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur bibendum ornare dolor, quis ullamcorper ligula sodales at. Nulla tellus elit, varius non commodo eget, mattis vel eros. In sed ornare nulla. Donec consectetur, velit a pharetra ultricies, diam lorem lacinia risus, ac commodo orci erat eu massa. Sed sit amet nulla ipsum. Donec felis mauris, vulputate sed tempor at, aliquam a ligula. Pellentesque non pulvinar nisi. -

    -
    -
    -

    Example body text

    -

    Nullam quis risus eget urna mollis ornare vel eu leo. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nullam id dolor id nibh ultricies vehicula ut id elit.

    -

    Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Donec sed odio dui.

    -
    -
    -
    -

    h1. Heading 1

    -

    h2. Heading 2

    -

    h3. Heading 3

    -

    h4. Heading 4

    -
    h5. Heading 5
    -
    h6. Heading 6
    -
    -
    -
    - -
    -
    -

    Example blockquotes

    -
    -
    -

    Default blockquotes are styled as such:

    -
    -

    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer posuere erat a ante venenatis.

    - Someone famous in Body of work -
    -
    -
    -

    You can always float your blockquote to the right:

    -
    -

    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer posuere erat a ante venenatis.

    - Someone famous in Body of work -
    -
    -
    -
    -
    -
    -
    -

    More Sample Text

    -

    - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur bibendum ornare dolor, quis ullamcorper ligula sodales at. Nulla tellus elit, varius non commodo eget, mattis vel eros. In sed ornare nulla. Donec consectetur, velit a pharetra ultricies, diam lorem lacinia risus, ac commodo orci erat eu massa. Sed sit amet nulla ipsum. Donec felis mauris, vulputate sed tempor at, aliquam a ligula. Pellentesque non pulvinar nisi. -

    -
    -
    -

    And Paragraphs

    -

    - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur bibendum ornare dolor, quis ullamcorper ligula sodales at. Nulla tellus elit, varius non commodo eget, mattis vel eros. In sed ornare nulla. Donec consectetur, velit a pharetra ultricies, diam lorem lacinia risus, ac commodo orci erat eu massa. Sed sit amet nulla ipsum. Donec felis mauris, vulputate sed tempor at, aliquam a ligula. Pellentesque non pulvinar nisi. -

    -
    -
    -
    -
    -

    Example use of Tooltips

    -

    Hover over the links below to see tooltips:

    -
    -

    Tight pants next level keffiyeh you probably haven't heard of them. Photo booth beard raw denim letterpress vegan messenger bag stumptown. Farm-to-table seitan, mcsweeney's fixie sustainable quinoa 8-bit american appadata-rel have a terry richardson vinyl chambray. Beard stumptown, cardigans banh mi lomo thundercats. Tofu biodiesel williamsburg marfa, four loko mcsweeney's cleanse vegan chambray. A really ironic artisan whatever keytar, scenester farm-to-table banksy Austin twitter handle freegan cred raw denim single-origin coffee viral. -

    -
    -
    -
    -
    -
    - -
    -
    -

    Unordered List

    -
    - -
    -
    -
    -
      -
    • Lorem ipsum dolor sit amet
    • -
    • Consectetur adipiscing elit
    • -
    • Integer molestie lorem at massa
    • -
    • Facilisis in pretium nisl aliquet
    • -
    • Nulla volutpat aliquam velit -
        -
      • Phasellus iaculis neque
      • -
      • Purus sodales ultricies
      • -
      • Vestibulum laoreet porttitor sem
      • -
      • Ac tristique libero volutpat at
      • -
      -
    • -
    • Faucibus porta lacus fringilla vel
    • -
    • Aenean sit amet erat nunc
    • -
    • Eget porttitor lorem
    • -
    -
    -
    - -
    -
    -

    Ordered List

    -
    - -
    -
    -
    -
      -
    1. Lorem ipsum dolor sit amet
    2. -
    3. Consectetur adipiscing elit
    4. -
    5. Integer molestie lorem at massa
    6. -
    7. Facilisis in pretium nisl aliquet
    8. -
    9. Nulla volutpat aliquam velit
    10. -
    11. Faucibus porta lacus fringilla vel
    12. -
    13. Aenean sit amet erat nunc
    14. -
    15. Eget porttitor lorem
    16. -
    -
    -
    - -
    -
    -

    Description List

    -
    - -
    -
    -
    -
    -
    Description lists
    -
    A description list is perfect for defining terms.
    -
    Euismod
    -
    Vestibulum id ligula porta felis euismod semper eget lacinia odio sem nec elit.
    -
    Donec id elit non mi porta gravida at eget metus.
    -
    Malesuada porta
    -
    Etiam porta sem malesuada magna mollis euismod.
    -
    -
    -
    - - - -
    - - diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/ui.php b/code/ryzom/tools/server/ryzom_ams/www/html/ui.php deleted file mode 100644 index c0bda6be2..000000000 --- a/code/ryzom/tools/server/ryzom_ams/www/html/ui.php +++ /dev/null @@ -1,287 +0,0 @@ - - - -
    - -
    - -
    -
    -
    -

    Extended Elements

    -
    - - - -
    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    Multiple File Upload

    - -

    Star Rating

    -
    -
    <div class="raty"></div>

    Toggle Switch

    - - <input data-no-uniform="true" type="checkbox" class="iphone-toggle">

    Auto Growing Textarea

    - - <textarea class="autogrow"></textarea>

    Popover

    - Hover for popover - <a href="#" class="btn btn-danger" data-rel="popover" data-content="And here's some amazing content. It's very engaging. right?" title="A Title">hover for popover</a>

    Slider

    -
    -
    <div class="slider"></div>

    Dialog

    - Click for dialog -

    Tooltip

    - Hover for tooltip - <a href="#" title="Tooltip, you can change the position." data-rel="tooltip" class="btn btn-warning">Hover for tooltip</a>
    -
    -
    - -
    - -
    -
    -
    -

    Progress Bars

    -
    - - - -
    -
    -
    -

    Basic

    -
    -
    -
    -

    Striped

    -
    -
    -
    -

    Animated

    -
    -
    -
    -

    Additional Colors

    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    - -
    -
    -

    Labels and Annotations

    -
    - - - -
    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    LabelsMarkup
    - Default - - <span class="label">Default</span> -
    - Success - - <span class="label label-success">Success</span> -
    - Warning - - <span class="label label-warning">Warning</span> -
    - Important - - <span class="label label-important">Important</span> -
    - Info - - <span class="label label-info">Info</span> -
    - Inverse - - <span class="label label-inverse">Inverse</span> -
    -
    -
    - -
    -
    -
    -
    -

    Alerts

    -
    - - - -
    -
    -
    -
    - - Oh snap! Change a few things up and try submitting again. -
    -
    - - Well done! You successfully read this important alert message. -
    -
    - - Heads up! This alert needs your attention, but it's not super important. -
    -
    - -

    Warning!

    -

    Best check yo self, you're not looking too good. Nulla vitae elit libero, a pharetra augue. Praesent commodo cursus magna, vel scelerisque nisl consectetur et.

    -
    -
    -
    - -
    -
    -

    Notifications

    -
    - - - -
    -
    -
    -
    - Click buttons below to see Pop Notifications. -
    -

    - - - - -

    -

    - - -

    - -

    - - -

    - -

    - -

    -
    -
    - -
    -
    -

    Ajax Loaders

    -
    - - - -
    -
    -
    -
      - -
    • - -
    - From / More http://ajaxload.info/ -
    -
    -
    - - - From ea25e5375d7d648e684cda50922c21d79270f13b Mon Sep 17 00:00:00 2001 From: kaetemi Date: Mon, 9 Sep 2013 12:18:20 +0200 Subject: [PATCH 194/313] Expand parameter interface --- code/nel/include/nel/3d/driver.h | 8 +- code/nel/include/nel/3d/gpu_program_params.h | 36 +- code/nel/src/3d/gpu_program_params.cpp | 334 ++++++++++++++++++- 3 files changed, 359 insertions(+), 19 deletions(-) diff --git a/code/nel/include/nel/3d/driver.h b/code/nel/include/nel/3d/driver.h index 0fe4c6511..ca6bc0601 100644 --- a/code/nel/include/nel/3d/driver.h +++ b/code/nel/include/nel/3d/driver.h @@ -1132,7 +1132,7 @@ public: /** Return true if the driver supports the specified pixel program profile. */ - virtual bool supportPixelProgram(CPixelProgram::TProfile profile = CPixelProgram::nelvp) const = 0; + virtual bool supportPixelProgram(CPixelProgram::TProfile profile) const = 0; /** Compile the given pixel program, return if successful. Error information is returned as a string. */ @@ -1162,10 +1162,16 @@ public: virtual void setUniform2i(TProgram program, uint index, sint32 i0, sint32 i1) = 0; virtual void setUniform3i(TProgram program, uint index, sint32 i0, sint32 i1, sint32 i2) = 0; virtual void setUniform4i(TProgram program, uint index, sint32 i0, sint32 i1, sint32 i2, sint32 i3) = 0; + virtual void setUniform1ui(TProgram program, uint index, uint32 ui0) = 0; + virtual void setUniform2ui(TProgram program, uint index, uint32 ui0, uint32 ui1) = 0; + virtual void setUniform3ui(TProgram program, uint index, uint32 ui0, uint32 ui1, uint32 ui2) = 0; + virtual void setUniform4ui(TProgram program, uint index, uint32 ui0, uint32 ui1, uint32 ui2, uint32 ui3) = 0; virtual void setUniform3f(TProgram program, uint index, const NLMISC::CVector& v) = 0; virtual void setUniform4f(TProgram program, uint index, const NLMISC::CVector& v, float f3) = 0; virtual void setUniform4x4f(TProgram program, uint index, const NLMISC::CMatrix& m) = 0; virtual void setUniform4fv(TProgram program, uint index, size_t num, const float *src) = 0; + virtual void setUniform4iv(TProgram program, uint index, size_t num, const sint32 *src) = 0; + virtual void setUniform4uiv(TProgram program, uint index, size_t num, const uint32 *src) = 0; // Set builtin parameters virtual void setUniformMatrix(TProgram program, uint index, TMatrix matrix, TTransform transform) = 0; virtual void setUniformFog(TProgram program, uint index) = 0; diff --git a/code/nel/include/nel/3d/gpu_program_params.h b/code/nel/include/nel/3d/gpu_program_params.h index 270c6cff5..ce6b8b2f0 100644 --- a/code/nel/include/nel/3d/gpu_program_params.h +++ b/code/nel/include/nel/3d/gpu_program_params.h @@ -60,7 +60,7 @@ class CGPUProgramParams { public: enum TType { Float, Int, UInt }; - struct CMeta { uint Index, Size, Count; TType Type; size_t Next, Prev; }; // size is element size, count is nb of elements + struct CMeta { uint Index, Size, Count; TType Type; std::string Name; size_t Next, Prev; }; // size is element size, count is nb of elements private: union CVec { float F[4]; sint32 I[4]; uint32 UI[4]; }; @@ -69,6 +69,8 @@ public: CGPUProgramParams(); virtual ~CGPUProgramParams(); + /// \name User functions + // @{ // Copy from another params storage void copy(CGPUProgramParams *params); @@ -81,10 +83,16 @@ public: void set2i(uint index, sint32 i0, sint32 i1); void set3i(uint index, sint32 i0, sint32 i1, sint32 i2); void set4i(uint index, sint32 i0, sint32 i1, sint32 i2, sint32 i3); + void set1ui(uint index, uint32 ui0); + void set2ui(uint index, uint32 ui0, uint32 ui1); + void set3ui(uint index, uint32 ui0, uint32 ui1, uint32 ui2); + void set4ui(uint index, uint32 ui0, uint32 ui1, uint32 ui2, uint32 ui3); void set3f(uint index, const NLMISC::CVector& v); void set4f(uint index, const NLMISC::CVector& v, float f3); void set4x4f(uint index, const NLMISC::CMatrix& m); void set4fv(uint index, size_t num, const float *src); + void set4iv(uint index, size_t num, const sint32 *src); + void set4uiv(uint index, size_t num, const uint32 *src); void unset(uint index); // Set by name, it is recommended to use index when repeatedly setting an element @@ -96,25 +104,42 @@ public: void set2i(const std::string &name, sint32 i0, sint32 i1); void set3i(const std::string &name, sint32 i0, sint32 i1, sint32 i2); void set4i(const std::string &name, sint32 i0, sint32 i1, sint32 i2, sint32 i3); + void set1ui(const std::string &name, uint32 ui0); + void set2ui(const std::string &name, uint32 ui0, uint32 ui1); + void set3ui(const std::string &name, uint32 ui0, uint32 ui1, uint32 ui2); + void set4ui(const std::string &name, uint32 ui0, uint32 ui1, uint32 ui2, uint32 ui3); void set3f(const std::string &name, const NLMISC::CVector& v); void set4f(const std::string &name, const NLMISC::CVector& v, float f3); void set4x4f(const std::string &name, const NLMISC::CMatrix& m); void set4fv(const std::string &name, size_t num, const float *src); + void set4iv(const std::string &name, size_t num, const sint32 *src); + void set4uiv(const std::string &name, size_t num, const uint32 *src); void unset(const std::string &name); + // @} - /// Maps the given name to the given index, on duplicate entry the data set by name will be prefered as it can be assumed to have been set after the data set by index + // Maps the given name to the given index. + // on duplicate entry the data set by name will be prefered, as it can be + // assumed to have been set after the data set by index, and the mapping + // will usually happen while iterating and finding an element with name + // but no known index. + // Unknown index will be set to ~0, unknown name will have an empty string. void map(uint index, const std::string &name); - // Internal - /// Allocate specified number of components if necessary + /// \name Internal + // @{ + /// Allocate specified number of components if necessary (internal use only) size_t allocOffset(uint index, uint size, uint count, TType type); size_t allocOffset(const std::string &name, uint size, uint count, TType type); + size_t allocOffset(uint size, uint count, TType type); /// Return offset for specified index size_t getOffset(uint index) const; size_t getOffset(const std::string &name) const; /// Remove by offset void freeOffset(size_t offset); + // @} + /// \name Driver and dev tools + // @{ // Iteration (returns the offsets for access using getFooByOffset) inline size_t getBegin() const { return m_Meta.size() ? m_First : s_End; } inline size_t getNext(size_t offset) const { return m_Meta[offset].Next; } @@ -129,7 +154,8 @@ public: inline uint32 *getPtrUIByOffset(size_t offset) { return m_Vec[offset].UI; } inline TType getTypeByOffset(size_t offset) const { return m_Meta[offset].Type; } inline uint getIndexByOffset(size_t offset) const { return m_Meta[offset].Index; } - const std::string *getNameByOffset(size_t offset) const; // non-optimized for dev tools only, may return NULL if name unknown + const std::string &getNameByOffset(size_t offset) const { return m_Meta[offset].Name; }; + // @} // Utility static inline uint getNbRegistersByComponents(uint nbComponents) { return (nbComponents + 3) >> 2; } // vector register per 4 components diff --git a/code/nel/src/3d/gpu_program_params.cpp b/code/nel/src/3d/gpu_program_params.cpp index bfcc4d32c..87ba01381 100644 --- a/code/nel/src/3d/gpu_program_params.cpp +++ b/code/nel/src/3d/gpu_program_params.cpp @@ -83,36 +83,66 @@ void CGPUProgramParams::set4f(uint index, float f0, float f1, float f2, float f3 f[3] = f3; } -void CGPUProgramParams::set1i(uint index, int i0) +void CGPUProgramParams::set1i(uint index, sint32 i0) { - int *i = getPtrIByOffset(allocOffset(index, 1, 1, Int)); + sint32 *i = getPtrIByOffset(allocOffset(index, 1, 1, Int)); i[0] = i0; } -void CGPUProgramParams::set2i(uint index, int i0, int i1) +void CGPUProgramParams::set2i(uint index, sint32 i0, sint32 i1) { - int *i = getPtrIByOffset(allocOffset(index, 2, 1, Int)); + sint32 *i = getPtrIByOffset(allocOffset(index, 2, 1, Int)); i[0] = i0; i[1] = i1; } -void CGPUProgramParams::set3i(uint index, int i0, int i1, int i2) +void CGPUProgramParams::set3i(uint index, sint32 i0, sint32 i1, sint32 i2) { - int *i = getPtrIByOffset(allocOffset(index, 3, 1, Int)); + sint32 *i = getPtrIByOffset(allocOffset(index, 3, 1, Int)); i[0] = i0; i[1] = i1; i[2] = i2; } -void CGPUProgramParams::set4i(uint index, int i0, int i1, int i2, int i3) +void CGPUProgramParams::set4i(uint index, sint32 i0, sint32 i1, sint32 i2, sint32 i3) { - int *i = getPtrIByOffset(allocOffset(index, 4, 1, Int)); + sint32 *i = getPtrIByOffset(allocOffset(index, 4, 1, Int)); i[0] = i0; i[1] = i1; i[2] = i2; i[3] = i3; } +void CGPUProgramParams::set1ui(uint index, uint32 ui0) +{ + uint32 *ui = getPtrUIByOffset(allocOffset(index, 1, 1, UInt)); + ui[0] = ui0; +} + +void CGPUProgramParams::set2ui(uint index, uint32 ui0, uint32 ui1) +{ + uint32 *ui = getPtrUIByOffset(allocOffset(index, 2, 1, UInt)); + ui[0] = ui0; + ui[1] = ui1; +} + +void CGPUProgramParams::set3ui(uint index, uint32 ui0, uint32 ui1, uint32 ui2) +{ + uint32 *ui = getPtrUIByOffset(allocOffset(index, 3, 1, UInt)); + ui[0] = ui0; + ui[1] = ui1; + ui[2] = ui2; +} + +void CGPUProgramParams::set4ui(uint index, uint32 ui0, uint32 ui1, uint32 ui2, uint32 ui3) +{ + uint32 *ui = getPtrUIByOffset(allocOffset(index, 4, 1, UInt)); + ui[0] = ui0; + ui[1] = ui1; + ui[2] = ui2; + ui[3] = ui3; +} + void CGPUProgramParams::set3f(uint index, const NLMISC::CVector& v) { float *f = getPtrFByOffset(allocOffset(index, 3, 1, Float)); @@ -121,6 +151,15 @@ void CGPUProgramParams::set3f(uint index, const NLMISC::CVector& v) f[2] = v.z; } +void CGPUProgramParams::set4f(uint index, const NLMISC::CVector& v, float f3) +{ + float *f = getPtrFByOffset(allocOffset(index, 4, 1, Float)); + f[0] = v.x; + f[1] = v.y; + f[2] = v.z; + f[3] = f3; +} + void CGPUProgramParams::set4x4f(uint index, const NLMISC::CMatrix& m) { // TODO: Verify this! @@ -138,6 +177,210 @@ void CGPUProgramParams::set4fv(uint index, size_t num, const float *src) f[c] = src[c]; } +void CGPUProgramParams::set4iv(uint index, size_t num, const sint32 *src) +{ + sint32 *i = getPtrIByOffset(allocOffset(index, 4, num, Int)); + size_t nb = 4 * num; + for (uint c = 0; c < nb; ++c) + i[c] = src[c]; +} + +void CGPUProgramParams::set4uiv(uint index, size_t num, const uint32 *src) +{ + uint32 *ui = getPtrUIByOffset(allocOffset(index, 4, num, UInt)); + size_t nb = 4 * num; + for (uint c = 0; c < nb; ++c) + ui[c] = src[c]; +} + +void CGPUProgramParams::unset(uint index) +{ + size_t offset = getOffset(index); + if (offset != getEnd()) + { + freeOffset(offset); + } +} + +void CGPUProgramParams::set1f(const std::string &name, float f0) +{ + float *f = getPtrFByOffset(allocOffset(name, 1, 1, Float)); + f[0] = f0; +} + +void CGPUProgramParams::set2f(const std::string &name, float f0, float f1) +{ + float *f = getPtrFByOffset(allocOffset(name, 2, 1, Float)); + f[0] = f0; + f[1] = f1; +} + +void CGPUProgramParams::set3f(const std::string &name, float f0, float f1, float f2) +{ + float *f = getPtrFByOffset(allocOffset(name, 3, 1, Float)); + f[0] = f0; + f[1] = f1; + f[2] = f2; +} + +void CGPUProgramParams::set4f(const std::string &name, float f0, float f1, float f2, float f3) +{ + float *f = getPtrFByOffset(allocOffset(name, 4, 1, Float)); + f[0] = f0; + f[1] = f1; + f[2] = f2; + f[3] = f3; +} + +void CGPUProgramParams::set1i(const std::string &name, sint32 i0) +{ + sint32 *i = getPtrIByOffset(allocOffset(name, 1, 1, Int)); + i[0] = i0; +} + +void CGPUProgramParams::set2i(const std::string &name, sint32 i0, sint32 i1) +{ + sint32 *i = getPtrIByOffset(allocOffset(name, 2, 1, Int)); + i[0] = i0; + i[1] = i1; +} + +void CGPUProgramParams::set3i(const std::string &name, sint32 i0, sint32 i1, sint32 i2) +{ + sint32 *i = getPtrIByOffset(allocOffset(name, 3, 1, Int)); + i[0] = i0; + i[1] = i1; + i[2] = i2; +} + +void CGPUProgramParams::set4i(const std::string &name, sint32 i0, sint32 i1, sint32 i2, sint32 i3) +{ + sint32 *i = getPtrIByOffset(allocOffset(name, 4, 1, Int)); + i[0] = i0; + i[1] = i1; + i[2] = i2; + i[3] = i3; +} + +void CGPUProgramParams::set1ui(const std::string &name, uint32 ui0) +{ + uint32 *ui = getPtrUIByOffset(allocOffset(name, 1, 1, UInt)); + ui[0] = ui0; +} + +void CGPUProgramParams::set2ui(const std::string &name, uint32 ui0, uint32 ui1) +{ + uint32 *ui = getPtrUIByOffset(allocOffset(name, 2, 1, UInt)); + ui[0] = ui0; + ui[1] = ui1; +} + +void CGPUProgramParams::set3ui(const std::string &name, uint32 ui0, uint32 ui1, uint32 ui2) +{ + uint32 *ui = getPtrUIByOffset(allocOffset(name, 3, 1, UInt)); + ui[0] = ui0; + ui[1] = ui1; + ui[2] = ui2; +} + +void CGPUProgramParams::set4ui(const std::string &name, uint32 ui0, uint32 ui1, uint32 ui2, uint32 ui3) +{ + uint32 *ui = getPtrUIByOffset(allocOffset(name, 4, 1, UInt)); + ui[0] = ui0; + ui[1] = ui1; + ui[2] = ui2; + ui[3] = ui3; +} + +void CGPUProgramParams::set3f(const std::string &name, const NLMISC::CVector& v) +{ + float *f = getPtrFByOffset(allocOffset(name, 3, 1, Float)); + f[0] = v.x; + f[1] = v.y; + f[2] = v.z; +} + +void CGPUProgramParams::set4f(const std::string &name, const NLMISC::CVector& v, float f3) +{ + float *f = getPtrFByOffset(allocOffset(name, 4, 1, Float)); + f[0] = v.x; + f[1] = v.y; + f[2] = v.z; + f[3] = f3; +} + +void CGPUProgramParams::set4x4f(const std::string &name, const NLMISC::CMatrix& m) +{ + // TODO: Verify this! + float *f = getPtrFByOffset(allocOffset(name, 4, 4, Float)); + NLMISC::CMatrix mt = m; + mt.transpose(); + mt.get(f); +} + +void CGPUProgramParams::set4fv(const std::string &name, size_t num, const float *src) +{ + float *f = getPtrFByOffset(allocOffset(name, 4, num, Float)); + size_t nb = 4 * num; + for (uint c = 0; c < nb; ++c) + f[c] = src[c]; +} + +void CGPUProgramParams::set4iv(const std::string &name, size_t num, const sint32 *src) +{ + sint32 *i = getPtrIByOffset(allocOffset(name, 4, num, Int)); + size_t nb = 4 * num; + for (uint c = 0; c < nb; ++c) + i[c] = src[c]; +} + +void CGPUProgramParams::set4uiv(const std::string &name, size_t num, const uint32 *src) +{ + uint32 *ui = getPtrUIByOffset(allocOffset(name, 4, num, UInt)); + size_t nb = 4 * num; + for (uint c = 0; c < nb; ++c) + ui[c] = src[c]; +} + +void CGPUProgramParams::unset(const std::string &name) +{ + size_t offset = getOffset(name); + if (offset != getEnd()) + { + freeOffset(offset); + } +} + +void CGPUProgramParams::map(uint index, const std::string &name) +{ + size_t offsetIndex = getOffset(index); + size_t offsetName = getOffset(name); + if (offsetName != getEnd()) + { + // Remove possible duplicate + if (offsetIndex != getEnd()) + { + freeOffset(offsetIndex); + } + + // Set index + m_Meta[offsetName].Index = index; + + // Map index to name + if (index >= m_Map.size()) + m_Map.resize(index + 1, s_End); + m_Map[index] = offsetName; + } + else if (offsetIndex != getEnd()) + { + // Set name + m_Meta[offsetIndex].Name = name; + + // Map name to index + m_MapName[name] = offsetIndex; + } +} + /// Allocate specified number of components if necessary size_t CGPUProgramParams::allocOffset(uint index, uint size, uint count, TType type) { @@ -163,18 +406,67 @@ size_t CGPUProgramParams::allocOffset(uint index, uint size, uint count, TType t } // Allocate space - offset = m_Meta.size(); - uint blocks = getNbRegistersByComponents(nbComponents); // per 4 components - m_Meta.resize(offset + blocks); - m_Vec.resize(offset + blocks); + offset = allocOffset(size, count, type); + + // Fill + m_Meta[offset].Index = index; // Store offset in map if (index >= m_Map.size()) m_Map.resize(index + 1, s_End); m_Map[index] = offset; + return offset; +} + +/// Allocate specified number of components if necessary +size_t CGPUProgramParams::allocOffset(const std::string &name, uint size, uint count, TType type) +{ + nlassert(count > 0); // this code will not properly handle 0 + nlassert(size > 0); // this code will not properly handle 0 + nlassert(!name.empty()); // sanity check + + uint nbComponents = size * count; + size_t offset = getOffset(name); + if (offset != s_End) + { + if (getCountByOffset(offset) >= nbComponents) + { + m_Meta[offset].Type = type; + m_Meta[offset].Size = size; + m_Meta[offset].Count = count; + return offset; + } + if (getCountByOffset(offset) < nbComponents) + { + freeOffset(offset); + } + } + + // Allocate space + offset = allocOffset(size, count, type); + + // Fill + m_Meta[offset].Name = name; + + // Store offset in map + m_MapName[name] = offset; + + return offset; +} + +/// Allocate specified number of components if necessary +size_t CGPUProgramParams::allocOffset(uint size, uint count, TType type) +{ + uint nbComponents = size * count; + + // Allocate space + size_t offset = m_Meta.size(); + uint blocks = getNbRegistersByComponents(nbComponents); // per 4 components + m_Meta.resize(offset + blocks); + m_Vec.resize(offset + blocks); + // Fill - m_Meta[offset].Index = index; m_Meta[offset].Size = size; m_Meta[offset].Count = count; m_Meta[offset].Type = type; @@ -207,6 +499,22 @@ size_t CGPUProgramParams::getOffset(uint index) const /// Remove by offset void CGPUProgramParams::freeOffset(size_t offset) { + uint index = getIndexByOffset(offset); + if (index != ~0) + { + if (m_Map.size() > index) + { + m_Map[index] = getEnd(); + } + } + const std::string &name = getNameByOffset(offset); + if (!name.empty()) + { + if (m_MapName.find(name) != m_MapName.end()) + { + m_MapName.erase(name); + } + } if (offset == m_Last) { nlassert(m_Meta[offset].Next == s_End); From 99a48d5511bd7e2936da6159b199c5387ca2b06b Mon Sep 17 00:00:00 2001 From: kaetemi Date: Mon, 9 Sep 2013 14:43:10 +0200 Subject: [PATCH 195/313] Implement new driver interface in OpenGL driver --- code/nel/include/nel/3d/driver.h | 26 +- code/nel/src/3d/driver/opengl/driver_opengl.h | 124 ++- .../opengl/driver_opengl_pixel_program.cpp | 217 +---- .../driver/opengl/driver_opengl_uniform.cpp | 298 ++++++ .../opengl/driver_opengl_vertex_program.cpp | 886 +++++++----------- 5 files changed, 793 insertions(+), 758 deletions(-) create mode 100644 code/nel/src/3d/driver/opengl/driver_opengl_uniform.cpp diff --git a/code/nel/include/nel/3d/driver.h b/code/nel/include/nel/3d/driver.h index ca6bc0601..8890a9cc7 100644 --- a/code/nel/include/nel/3d/driver.h +++ b/code/nel/include/nel/3d/driver.h @@ -161,9 +161,9 @@ public: enum TProgram { - VertexProgram, - PixelProgram, - GeometryProgram + VertexProgram = 0, + PixelProgram = 1, + GeometryProgram = 2 }; protected: @@ -1105,19 +1105,17 @@ public: */ virtual bool supportVertexProgram(CVertexProgram::TProfile profile = CVertexProgram::nelvp) const = 0; - /** Compile the given vertex program, return if successful. Error information is returned as a string. + /** Compile the given vertex program, return if successful. + * If a vertex program was set active before compilation, + * the state of the active vertex program is undefined behaviour afterwards. */ - virtual bool compileVertexProgram(CVertexProgram *program, std::string &error) const = 0; + virtual bool compileVertexProgram(CVertexProgram *program) = 0; /** Set the active vertex program. This will override vertex programs specified in CMaterial render calls. * Also used internally by setupMaterial(CMaterial) when getVertexProgram returns NULL. * The vertex program is activated immediately. */ virtual bool activeVertexProgram(CVertexProgram *program) = 0; - - /** Get the currently active vertex program. - */ - virtual CVertexProgram *getActiveVertexProgram() const = 0; // @} @@ -1134,19 +1132,17 @@ public: */ virtual bool supportPixelProgram(CPixelProgram::TProfile profile) const = 0; - /** Compile the given pixel program, return if successful. Error information is returned as a string. + /** Compile the given pixel program, return if successful. + * If a pixel program was set active before compilation, + * the state of the active pixel program is undefined behaviour afterwards. */ - virtual bool compilePixelProgram(CPixelProgram *program, std::string &error) const = 0; + virtual bool compilePixelProgram(CPixelProgram *program) = 0; /** Set the active pixel program. This will override pixel programs specified in CMaterial render calls. * Also used internally by setupMaterial(CMaterial) when getPixelProgram returns NULL. * The pixel program is activated immediately. */ virtual bool activePixelProgram(CPixelProgram *program) = 0; - - /** Get the currently active pixel program. - */ - virtual CPixelProgram *getActivePixelProgram() const = 0; // @} diff --git a/code/nel/src/3d/driver/opengl/driver_opengl.h b/code/nel/src/3d/driver/opengl/driver_opengl.h index 89b71c931..f70a0627f 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl.h +++ b/code/nel/src/3d/driver/opengl/driver_opengl.h @@ -1299,45 +1299,115 @@ private: // @} - /// \name Vertex program interface + + + + + /// \name Vertex Program // @{ - bool supportVertexProgram () const; - bool supportPixelProgram () const; - bool supportPixelProgram (CPixelProgram::TProfile profile) const; - bool isVertexProgramEmulated () const; - bool activeVertexProgram (CVertexProgram *program); - bool activePixelProgram (CPixelProgram *program); - void setConstant (uint index, float, float, float, float); - void setConstant (uint index, double, double, double, double); - void setConstant (uint indexStart, const NLMISC::CVector& value); - void setConstant (uint indexStart, const NLMISC::CVectorD& value); - void setConstant (uint index, uint num, const float *src); - void setConstant (uint index, uint num, const double *src); - void setConstantMatrix (uint index, IDriver::TMatrix matrix, IDriver::TTransform transform); - void setConstantFog (uint index); - void enableVertexProgramDoubleSidedColor(bool doubleSided); - bool supportVertexProgramDoubleSidedColor() const; - - // Pixel program - virtual void setPixelProgramConstant (uint index, float, float, float, float); - virtual void setPixelProgramConstant (uint index, double, double, double, double); - virtual void setPixelProgramConstant (uint index, const NLMISC::CVector& value); - virtual void setPixelProgramConstant (uint index, const NLMISC::CVectorD& value); - virtual void setPixelProgramConstant (uint index, uint num, const float *src); - virtual void setPixelProgramConstant (uint index, uint num, const double *src); - virtual void setPixelProgramConstantMatrix (uint index, IDriver::TMatrix matrix, IDriver::TTransform transform); + // Order of preference + // - activeVertexProgram + // - CMaterial pass[n] VP (uses activeVertexProgram, but does not override if one already set by code) + // - default generic VP that mimics fixed pipeline / no VP with fixed pipeline + + /** + * Does the driver supports vertex program, but emulated by CPU ? + */ + virtual bool isVertexProgramEmulated() const; + + /** Return true if the driver supports the specified vertex program profile. + */ + virtual bool supportVertexProgram(CVertexProgram::TProfile profile = CVertexProgram::nelvp) const; + + /** Compile the given vertex program, return if successful. + * If a vertex program was set active before compilation, + * the state of the active vertex program is undefined behaviour afterwards. + */ + virtual bool compileVertexProgram(CVertexProgram *program); + + /** Set the active vertex program. This will override vertex programs specified in CMaterial render calls. + * Also used internally by setupMaterial(CMaterial) when getVertexProgram returns NULL. + * The vertex program is activated immediately. + */ + virtual bool activeVertexProgram(CVertexProgram *program); + // @} + + + + /// \name Pixel Program + // @{ + + // Order of preference + // - activePixelProgram + // - CMaterial pass[n] PP (uses activePixelProgram, but does not override if one already set by code) + // - PP generated from CMaterial (uses activePixelProgram, but does not override if one already set by code) + + /** Return true if the driver supports the specified pixel program profile. + */ + virtual bool supportPixelProgram(CPixelProgram::TProfile profile = CPixelProgram::arbfp1) const; + + /** Compile the given pixel program, return if successful. + * If a pixel program was set active before compilation, + * the state of the active pixel program is undefined behaviour afterwards. + */ + virtual bool compilePixelProgram(CPixelProgram *program); + + /** Set the active pixel program. This will override pixel programs specified in CMaterial render calls. + * Also used internally by setupMaterial(CMaterial) when getPixelProgram returns NULL. + * The pixel program is activated immediately. + */ + virtual bool activePixelProgram(CPixelProgram *program); + // @} + + + + /// \name Program parameters + // @{ + // Set parameters + virtual void setUniform1f(TProgram program, uint index, float f0); + virtual void setUniform2f(TProgram program, uint index, float f0, float f1); + virtual void setUniform3f(TProgram program, uint index, float f0, float f1, float f2); + virtual void setUniform4f(TProgram program, uint index, float f0, float f1, float f2, float f3); + virtual void setUniform1i(TProgram program, uint index, sint32 i0); + virtual void setUniform2i(TProgram program, uint index, sint32 i0, sint32 i1); + virtual void setUniform3i(TProgram program, uint index, sint32 i0, sint32 i1, sint32 i2); + virtual void setUniform4i(TProgram program, uint index, sint32 i0, sint32 i1, sint32 i2, sint32 i3); + virtual void setUniform1ui(TProgram program, uint index, uint32 ui0); + virtual void setUniform2ui(TProgram program, uint index, uint32 ui0, uint32 ui1); + virtual void setUniform3ui(TProgram program, uint index, uint32 ui0, uint32 ui1, uint32 ui2); + virtual void setUniform4ui(TProgram program, uint index, uint32 ui0, uint32 ui1, uint32 ui2, uint32 ui3); + virtual void setUniform3f(TProgram program, uint index, const NLMISC::CVector& v); + virtual void setUniform4f(TProgram program, uint index, const NLMISC::CVector& v, float f3); + virtual void setUniform4x4f(TProgram program, uint index, const NLMISC::CMatrix& m); + virtual void setUniform4fv(TProgram program, uint index, size_t num, const float *src); + virtual void setUniform4iv(TProgram program, uint index, size_t num, const sint32 *src); + virtual void setUniform4uiv(TProgram program, uint index, size_t num, const uint32 *src); + // Set builtin parameters + virtual void setUniformMatrix(TProgram program, uint index, TMatrix matrix, TTransform transform); + virtual void setUniformFog(TProgram program, uint index); + // @} + + + + + + virtual void enableVertexProgramDoubleSidedColor(bool doubleSided); + virtual bool supportVertexProgramDoubleSidedColor() const; virtual bool supportMADOperator() const ; - // @} /// \name Vertex program implementation // @{ bool activeNVVertexProgram (CVertexProgram *program); bool activeARBVertexProgram (CVertexProgram *program); bool activeEXTVertexShader (CVertexProgram *program); + + bool compileNVVertexProgram (CVertexProgram *program); + bool compileARBVertexProgram (CVertexProgram *program); + bool compileEXTVertexShader (CVertexProgram *program); //@} diff --git a/code/nel/src/3d/driver/opengl/driver_opengl_pixel_program.cpp b/code/nel/src/3d/driver/opengl/driver_opengl_pixel_program.cpp index b2867a95a..31e6ed482 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl_pixel_program.cpp +++ b/code/nel/src/3d/driver/opengl/driver_opengl_pixel_program.cpp @@ -50,6 +50,7 @@ namespace NLDRIVERGL { #endif // *************************************************************************** + CPixelProgamDrvInfosGL::CPixelProgamDrvInfosGL (CDriverGL *drv, ItGPUPrgDrvInfoPtrList it) : IGPUProgramDrvInfos (drv, it) { H_AUTO_OGL(CPixelProgamDrvInfosGL_CPixelProgamDrvInfosGL) @@ -63,11 +64,7 @@ CPixelProgamDrvInfosGL::CPixelProgamDrvInfosGL (CDriverGL *drv, ItGPUPrgDrvInfoP } // *************************************************************************** -bool CDriverGL::supportPixelProgram() const -{ - H_AUTO_OGL(CPixelProgamDrvInfosGL_supportPixelProgram) - return _Extensions.ARBFragmentProgram; -} + bool CDriverGL::supportPixelProgram(CPixelProgram::TProfile profile) const { H_AUTO_OGL(CPixelProgamDrvInfosGL_supportPixelProgram_profile) @@ -82,6 +79,7 @@ bool CDriverGL::supportPixelProgram(CPixelProgram::TProfile profile) const } // *************************************************************************** + bool CDriverGL::activePixelProgram(CPixelProgram *program) { H_AUTO_OGL(CDriverGL_activePixelProgram) @@ -95,59 +93,68 @@ bool CDriverGL::activePixelProgram(CPixelProgram *program) } // *************************************************************************** + +bool CDriverGL::compilePixelProgram(NL3D::CPixelProgram *program) +{ + // Program setuped ? + if (program->_DrvInfo == NULL) + { + glDisable(GL_FRAGMENT_PROGRAM_ARB); + _PixelProgramEnabled = false; + + // Insert into driver list. (so it is deleted when driver is deleted). + ItGPUPrgDrvInfoPtrList it = _GPUPrgDrvInfos.insert(_GPUPrgDrvInfos.end(), (NL3D::IGPUProgramDrvInfos*)NULL); + + // Create a driver info + CPixelProgamDrvInfosGL *drvInfo; + *it = drvInfo = new CPixelProgamDrvInfosGL(this, it); + // Set the pointer + program->_DrvInfo = drvInfo; + + if (!setupPixelProgram(program, drvInfo->ID)) + { + delete drvInfo; + program->_DrvInfo = NULL; + _GPUPrgDrvInfos.erase(it); + return false; + } + } + + return true; +} + +// *************************************************************************** + bool CDriverGL::activeARBPixelProgram(CPixelProgram *program) { H_AUTO_OGL(CDriverGL_activeARBPixelProgram) // Setup or unsetup ? if (program) - { - // Driver info - CPixelProgamDrvInfosGL *drvInfo; - + { // Program setuped ? - if (program->_DrvInfo==NULL) - { - // Insert into driver list. (so it is deleted when driver is deleted). - ItGPUPrgDrvInfoPtrList it = _GPUPrgDrvInfos.insert(_GPUPrgDrvInfos.end(), (NL3D::IGPUProgramDrvInfos*)NULL); + if (!CDriverGL::compilePixelProgram(program)) return false; - // Create a driver info - *it = drvInfo = new CPixelProgamDrvInfosGL(this, it); - // Set the pointer - program->_DrvInfo = drvInfo; - - if (!setupPixelProgram(program, drvInfo->ID)) - { - delete drvInfo; - program->_DrvInfo = NULL; - _GPUPrgDrvInfos.erase(it); - return false; - } - } - else - { - // Cast the driver info pointer - drvInfo=safe_cast((IGPUProgramDrvInfos*)program->_DrvInfo); - } - glEnable( GL_FRAGMENT_PROGRAM_ARB ); + // Cast the driver info pointer + CPixelProgamDrvInfosGL *drvInfo = safe_cast((IGPUProgramDrvInfos*)program->_DrvInfo); + + glEnable(GL_FRAGMENT_PROGRAM_ARB); _PixelProgramEnabled = true; - nglBindProgramARB( GL_FRAGMENT_PROGRAM_ARB, drvInfo->ID ); - - glDisable( GL_COLOR_SUM_ARB ); // no specular written + nglBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, drvInfo->ID); _LastSetuppedPP = program; } else { - glDisable( GL_FRAGMENT_PROGRAM_ARB ); - glDisable( GL_COLOR_SUM_ARB ); - _PixelProgramEnabled = false; + glDisable(GL_FRAGMENT_PROGRAM_ARB); + _PixelProgramEnabled = false; } return true; } // *************************************************************************** + bool CDriverGL::setupPixelProgram(CPixelProgram *program, GLuint id/*, bool &specularWritten*/) { H_AUTO_OGL(CDriverGL_setupARBPixelProgram) @@ -170,9 +177,9 @@ bool CDriverGL::setupPixelProgram(CPixelProgram *program, GLuint id/*, bool &spe } // Compile the program - nglBindProgramARB( GL_FRAGMENT_PROGRAM_ARB, id); + nglBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, id); glGetError(); - nglProgramStringARB( GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, source->SourceLen, source->SourcePtr); + nglProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, source->SourceLen, source->SourcePtr); GLenum err = glGetError(); if (err != GL_NO_ERROR) { @@ -222,136 +229,6 @@ bool CDriverGL::setupPixelProgram(CPixelProgram *program, GLuint id/*, bool &spe return true; } -// *************************************************************************** - -void CDriverGL::setPixelProgramConstant (uint index, float f0, float f1, float f2, float f3) -{ - H_AUTO_OGL(CDriverGL_setPixelProgramConstant) - - if (_Extensions.ARBFragmentProgram) - nglProgramEnvParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, index, f0, f1, f2, f3); -} - - -// *************************************************************************** - -void CDriverGL::setPixelProgramConstant (uint index, double d0, double d1, double d2, double d3) -{ - H_AUTO_OGL(CDriverGL_setPixelProgramConstant) - - if (_Extensions.ARBFragmentProgram) - nglProgramEnvParameter4dARB(GL_FRAGMENT_PROGRAM_ARB, index, d0, d1, d2, d3); -} - - -// *************************************************************************** - -void CDriverGL::setPixelProgramConstant (uint index, const NLMISC::CVector& value) -{ - H_AUTO_OGL(CDriverGL_setPixelProgramConstant) - - if (_Extensions.ARBFragmentProgram) - nglProgramEnvParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, index, value.x, value.y, value.z, 0); -} - - -// *************************************************************************** - -void CDriverGL::setPixelProgramConstant (uint index, const NLMISC::CVectorD& value) -{ - H_AUTO_OGL(CDriverGL_setPixelProgramConstant) - - if (_Extensions.ARBFragmentProgram) - nglProgramEnvParameter4dARB(GL_FRAGMENT_PROGRAM_ARB, index, value.x, value.y, value.z, 0); -} - - -// *************************************************************************** -void CDriverGL::setPixelProgramConstant (uint index, uint num, const float *src) -{ - H_AUTO_OGL(CDriverGL_setPixelProgramConstant) - - if (_Extensions.ARBFragmentProgram) - { - for(uint k = 0; k < num; ++k) - { - nglProgramEnvParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, index + k, src + 4 * k); - } - } -} - -// *************************************************************************** -void CDriverGL::setPixelProgramConstant (uint index, uint num, const double *src) -{ - H_AUTO_OGL(CDriverGL_setPixelProgramConstant) - - if (_Extensions.ARBFragmentProgram) - { - for(uint k = 0; k < num; ++k) - { - nglProgramEnvParameter4dvARB(GL_FRAGMENT_PROGRAM_ARB, index + k, src + 4 * k); - } - } -} - -// *************************************************************************** - -void CDriverGL::setPixelProgramConstantMatrix (uint index, IDriver::TMatrix matrix, IDriver::TTransform transform) -{ - H_AUTO_OGL(CDriverGL_setPixelProgramConstantMatrix) - - if (_Extensions.ARBFragmentProgram) - { - - // First, ensure that the render setup is correctly setuped. - refreshRenderSetup(); - CMatrix mat; - switch (matrix) - { - case IDriver::ModelView: - mat = _ModelViewMatrix; - break; - case IDriver::Projection: - { - refreshProjMatrixFromGL(); - mat = _GLProjMat; - } - break; - case IDriver::ModelViewProjection: - refreshProjMatrixFromGL(); - mat = _GLProjMat * _ModelViewMatrix; - break; - default: - break; - } - - switch(transform) - { - case IDriver::Identity: break; - case IDriver::Inverse: - mat.invert(); - break; - case IDriver::Transpose: - mat.transpose(); - break; - case IDriver::InverseTranspose: - mat.invert(); - mat.transpose(); - break; - default: - break; - } - mat.transpose(); - float matDatas[16]; - mat.get(matDatas); - - nglProgramEnvParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, index, matDatas); - nglProgramEnvParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, index + 1, matDatas + 4); - nglProgramEnvParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, index + 2, matDatas + 8); - nglProgramEnvParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, index + 3, matDatas + 12); - } -} - #ifdef NL_STATIC } // NLDRIVERGL/ES #endif diff --git a/code/nel/src/3d/driver/opengl/driver_opengl_uniform.cpp b/code/nel/src/3d/driver/opengl/driver_opengl_uniform.cpp new file mode 100644 index 000000000..6b429e706 --- /dev/null +++ b/code/nel/src/3d/driver/opengl/driver_opengl_uniform.cpp @@ -0,0 +1,298 @@ +// NeL - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#include "stdopengl.h" + +#include "driver_opengl.h" + +using namespace std; +using namespace NLMISC; + +namespace NL3D { + +#ifdef NL_STATIC +#ifdef USE_OPENGLES +namespace NLDRIVERGLES { +#else +namespace NLDRIVERGL { +#endif +#endif + +inline void CDriverGL::setUniform4f(TProgram program, uint index, float f0, float f1, float f2, float f3) +{ + H_AUTO_OGL(CDriverGL_setUniform4f); + +#ifndef USE_OPENGLES + switch (program) + { + case VertexProgram: + if (_Extensions.NVVertexProgram) + { + // Setup constant + nglProgramParameter4fNV(GL_VERTEX_PROGRAM_NV, index, f0, f1, f2, f3); + } + else if (_Extensions.ARBVertexProgram) + { + nglProgramEnvParameter4fARB(GL_VERTEX_PROGRAM_ARB, index, f0, f1, f2, f3); + } + else if (_Extensions.EXTVertexShader) + { + float datas[] = { f0, f1, f2, f3 }; + nglSetInvariantEXT(_EVSConstantHandle + index, GL_FLOAT, datas); + } + break; + case PixelProgram: + if (_Extensions.ARBFragmentProgram) + { + nglProgramEnvParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, index, f0, f1, f2, f3); + } + break; + } +#endif +} + +inline void CDriverGL::setUniform4fv(TProgram program, uint index, size_t num, const float *src) +{ + H_AUTO_OGL(CDriverGL_setUniform4fv); + +#ifndef USE_OPENGLES + switch (program) + { + case VertexProgram: + if (_Extensions.NVVertexProgram) + { + nglProgramParameters4fvNV(GL_VERTEX_PROGRAM_NV, index, num, src); + } + else if (_Extensions.ARBVertexProgram) // ARB pixel and geometry program will only exist when ARB vertex program exists + { + for (uint k = 0; k < num; ++k) + { + nglProgramEnvParameter4fvARB(GL_VERTEX_PROGRAM_ARB, index + k, src + 4 * k); + } + } + else if (_Extensions.EXTVertexShader) + { + for (uint k = 0; k < num; ++k) + { + nglSetInvariantEXT(_EVSConstantHandle + index + k, GL_FLOAT, (void *)(src + 4 * k)); + } + } + break; + case PixelProgram: + if (_Extensions.ARBFragmentProgram) // ARB pixel and geometry program will only exist when ARB vertex program exists + { + for (uint k = 0; k < num; ++k) + { + nglProgramEnvParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, index + k, src + 4 * k); + } + } + break; + } +#endif +} + +void CDriverGL::setUniform1f(TProgram program, uint index, float f0) +{ + CDriverGL::setUniform4f(program, index, f0, 0.f, 0.f, 0.f); +} + +void CDriverGL::setUniform2f(TProgram program, uint index, float f0, float f1) +{ + CDriverGL::setUniform4f(program, index, f0, f1, 0.f, 0.f); +} + +void CDriverGL::setUniform3f(TProgram program, uint index, float f0, float f1, float f2) +{ + CDriverGL::setUniform4f(program, index, f0, f1, f2, 0.0f); +} + +void CDriverGL::setUniform1i(TProgram program, uint index, sint32 i0) +{ + +} + +void CDriverGL::setUniform2i(TProgram program, uint index, sint32 i0, sint32 i1) +{ + +} + +void CDriverGL::setUniform3i(TProgram program, uint index, sint32 i0, sint32 i1, sint32 i2) +{ + +} + +void CDriverGL::setUniform4i(TProgram program, uint index, sint32 i0, sint32 i1, sint32 i2, sint32 i3) +{ + +} + +void CDriverGL::setUniform1ui(TProgram program, uint index, uint32 ui0) +{ + +} + +void CDriverGL::setUniform2ui(TProgram program, uint index, uint32 ui0, uint32 ui1) +{ + +} + +void CDriverGL::setUniform3ui(TProgram program, uint index, uint32 ui0, uint32 ui1, uint32 ui2) +{ + +} + +void CDriverGL::setUniform4ui(TProgram program, uint index, uint32 ui0, uint32 ui1, uint32 ui2, uint32 ui3) +{ + +} + +void CDriverGL::setUniform3f(TProgram program, uint index, const NLMISC::CVector& v) +{ + CDriverGL::setUniform4f(program, index, v.x, v.y, v.z, 0.f); +} + +void CDriverGL::setUniform4f(TProgram program, uint index, const NLMISC::CVector& v, float f3) +{ + CDriverGL::setUniform4f(program, index, v.x, v.y, v.z, f3); +} + +void CDriverGL::setUniform4x4f(TProgram program, uint index, const NLMISC::CMatrix& m) +{ + H_AUTO_OGL(CDriverGL_setUniform4x4f); + + // TODO: Verify this! + NLMISC::CMatrix mat = m; + mat.transpose(); + const float *md = mat.get(); + + CDriverGL::setUniform4fv(program, index, 4, md); +} + +void CDriverGL::setUniform4iv(TProgram program, uint index, size_t num, const sint32 *src) +{ + +} + +void CDriverGL::setUniform4uiv(TProgram program, uint index, size_t num, const uint32 *src) +{ + +} + +const uint CDriverGL::GLMatrix[IDriver::NumMatrix]= +{ + GL_MODELVIEW, + GL_PROJECTION, +#ifdef USE_OPENGLES + GL_MODELVIEW +#else + GL_MODELVIEW_PROJECTION_NV +#endif +}; + +const uint CDriverGL::GLTransform[IDriver::NumTransform]= +{ +#ifdef USE_OPENGLES + 0, + 0, + 0, + 0 +#else + GL_IDENTITY_NV, + GL_INVERSE_NV, + GL_TRANSPOSE_NV, + GL_INVERSE_TRANSPOSE_NV +#endif +}; + +void CDriverGL::setUniformMatrix(NL3D::IDriver::TProgram program, uint index, NL3D::IDriver::TMatrix matrix, NL3D::IDriver::TTransform transform) +{ + H_AUTO_OGL(CDriverGL_setUniformMatrix); + +#ifndef USE_OPENGLES + // Vertex program exist ? + if (program == VertexProgram && _Extensions.NVVertexProgram) + { + // First, ensure that the render setup is correclty setuped. + refreshRenderSetup(); + + // Track the matrix + nglTrackMatrixNV(GL_VERTEX_PROGRAM_NV, index, GLMatrix[matrix], GLTransform[transform]); + // Release Track => matrix data is copied. + nglTrackMatrixNV(GL_VERTEX_PROGRAM_NV, index, GL_NONE, GL_IDENTITY_NV); + } + else + { + // First, ensure that the render setup is correctly setuped. + refreshRenderSetup(); + + CMatrix mat; + switch (matrix) + { + case IDriver::ModelView: + mat = _ModelViewMatrix; + break; + case IDriver::Projection: + { + refreshProjMatrixFromGL(); + mat = _GLProjMat; + } + break; + case IDriver::ModelViewProjection: + refreshProjMatrixFromGL(); + mat = _GLProjMat * _ModelViewMatrix; + break; + default: + break; + } + + switch(transform) + { + case IDriver::Identity: break; + case IDriver::Inverse: + mat.invert(); + break; + case IDriver::Transpose: + mat.transpose(); + break; + case IDriver::InverseTranspose: + mat.invert(); + mat.transpose(); + break; + default: + break; + } + + mat.transpose(); + const float *md = mat.get(); + + CDriverGL::setUniform4fv(program, index, 4, md); + } +#endif +} + +void CDriverGL::setUniformFog(NL3D::IDriver::TProgram program, uint index) +{ + H_AUTO_OGL(CDriverGL_setUniformFog) + + const float *values = _ModelViewMatrix.get(); + CDriverGL::setUniform4f(program, index, -values[2], -values[6], -values[10], -values[14]); +} + +#ifdef NL_STATIC +} // NLDRIVERGL/ES +#endif + +} // NL3D diff --git a/code/nel/src/3d/driver/opengl/driver_opengl_vertex_program.cpp b/code/nel/src/3d/driver/opengl/driver_opengl_vertex_program.cpp index 34ae2365a..e0b07af2a 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl_vertex_program.cpp +++ b/code/nel/src/3d/driver/opengl/driver_opengl_vertex_program.cpp @@ -70,23 +70,136 @@ CVertexProgamDrvInfosGL::CVertexProgamDrvInfosGL(CDriverGL *drv, ItGPUPrgDrvInfo // *************************************************************************** -bool CDriverGL::supportVertexProgram () const +bool CDriverGL::supportVertexProgram(CVertexProgram::TProfile profile) const { H_AUTO_OGL(CVertexProgamDrvInfosGL_supportVertexProgram) - return _Extensions.NVVertexProgram || _Extensions.EXTVertexShader || _Extensions.ARBVertexProgram; + return (profile == CVertexProgram::nelvp) + && (_Extensions.NVVertexProgram || _Extensions.EXTVertexShader || _Extensions.ARBVertexProgram); } // *************************************************************************** -bool CDriverGL::isVertexProgramEmulated () const +bool CDriverGL::isVertexProgramEmulated() const { H_AUTO_OGL(CVertexProgamDrvInfosGL_isVertexProgramEmulated) return _Extensions.NVVertexProgramEmulated; } +bool CDriverGL::compileNVVertexProgram(CVertexProgram *program) +{ + H_AUTO_OGL(CDriverGL_compileNVVertexProgram); +#ifndef USE_OPENGLES + + // Driver info + CVertexProgamDrvInfosGL *drvInfo; + + nlassert(!program->_DrvInfo); + glDisable(GL_VERTEX_PROGRAM_NV); + _VertexProgramEnabled = false; + + // Find nelvp + CGPUProgramSource *source = NULL; + for (uint i = 0; i < program->getProgramSource()->Sources.size(); ++i) + { + if (program->getProgramSource()->Sources[i]->Profile == CVertexProgram::nelvp) + { + source = program->getProgramSource()->Sources[i]; + } + } + if (!source) + { + nlwarning("OpenGL driver only supports 'nelvp' profile, vertex program cannot be used"); + return false; + } + + /** Check with our parser if the program will works with other implemented extensions, too. (EXT_vertex_shader ..). + * There are some incompatibilities. + */ + CVPParser parser; + CVPParser::TProgram parsedProgram; + std::string errorOutput; + bool result = parser.parse(source->SourcePtr, parsedProgram, errorOutput); + if (!result) + { + nlwarning("Unable to parse a vertex program :"); + nlwarning(errorOutput.c_str()); + #ifdef NL_DEBUG + nlassert(0); + #endif + return false; + } + + // Insert into driver list. (so it is deleted when driver is deleted). + ItGPUPrgDrvInfoPtrList it = _GPUPrgDrvInfos.insert(_GPUPrgDrvInfos.end(), (NL3D::IGPUProgramDrvInfos*)NULL); + + // Create a driver info + *it = drvInfo = new CVertexProgamDrvInfosGL(this, it); + + // Set the pointer + program->_DrvInfo = drvInfo; + + // Compile the program + nglLoadProgramNV(GL_VERTEX_PROGRAM_NV, drvInfo->ID, (GLsizei)source->SourceLen, (const GLubyte*)source->SourcePtr); + + // Get loading error code + GLint errorOff; + glGetIntegerv(GL_PROGRAM_ERROR_POSITION_NV, &errorOff); + + // Compilation error ? + if (errorOff >= 0) + { + // String length + uint length = (uint)source->SourceLen; + const char* sString = source->SourcePtr; + + // Line count and char count + uint line=1; + uint charC=1; + + // Find the line + uint offset=0; + while ((offset < length) && (offset < (uint)errorOff)) + { + if (sString[offset]=='\n') + { + line++; + charC=1; + } + else + charC++; + + // Next character + offset++; + } + + // Show the error + nlwarning("3D: Vertex program syntax error line %d character %d\n", line, charC); + + // Setup not ok + delete drvInfo; + program->_DrvInfo = NULL; + _GPUPrgDrvInfos.erase(it); + return false; + } + + // Set parameters for assembly programs + drvInfo->ParamIndices = source->ParamIndices; + + // Build the feature info + program->buildInfo(source->DisplayName.c_str(), source->Features); + + // Setup ok + return true; + +#else + + return false; + +#endif +} // *************************************************************************** -bool CDriverGL::activeNVVertexProgram (CVertexProgram *program) +bool CDriverGL::activeNVVertexProgram(CVertexProgram *program) { H_AUTO_OGL(CVertexProgamDrvInfosGL_activeNVVertexProgram); @@ -94,120 +207,16 @@ bool CDriverGL::activeNVVertexProgram (CVertexProgram *program) // Setup or unsetup ? if (program) { - // Enable vertex program - glEnable (GL_VERTEX_PROGRAM_NV); - _VertexProgramEnabled= true; - - // Driver info - CVertexProgamDrvInfosGL *drvInfo; + CVertexProgamDrvInfosGL *drvInfo = safe_cast((IGPUProgramDrvInfos*)program->_DrvInfo); + nlassert(drvInfo); - // Program setuped ? - if (program->_DrvInfo==NULL) - { - // Find nelvp - CGPUProgramSource *source = NULL; - for (uint i = 0; i < program->getProgramSource()->Sources.size(); ++i) - { - if (program->getProgramSource()->Sources[i]->Profile == CVertexProgram::nelvp) - { - source = program->getProgramSource()->Sources[i]; - } - } - if (!source) - { - nlwarning("OpenGL driver only supports 'nelvp' profile, vertex program cannot be used"); - return false; - } - - /** Check with our parser if the program will works with other implemented extensions, too. (EXT_vertex_shader ..). - * There are some incompatibilities. - */ - CVPParser parser; - CVPParser::TProgram parsedProgram; - std::string errorOutput; - bool result = parser.parse(source->SourcePtr, parsedProgram, errorOutput); - if (!result) - { - nlwarning("Unable to parse a vertex program :"); - nlwarning(errorOutput.c_str()); - #ifdef NL_DEBUG - nlassert(0); - #endif - return false; - } - - // Insert into driver list. (so it is deleted when driver is deleted). - ItGPUPrgDrvInfoPtrList it= _GPUPrgDrvInfos.insert(_GPUPrgDrvInfos.end(), (NL3D::IGPUProgramDrvInfos*)NULL); - - // Create a driver info - *it = drvInfo = new CVertexProgamDrvInfosGL (this, it); - - // Set the pointer - program->_DrvInfo=drvInfo; - - // Compile the program - nglLoadProgramNV (GL_VERTEX_PROGRAM_NV, drvInfo->ID, (GLsizei)source->SourceLen, (const GLubyte*)source->SourcePtr); - - // Get loading error code - GLint errorOff; - glGetIntegerv (GL_PROGRAM_ERROR_POSITION_NV, &errorOff); - - // Compilation error ? - if (errorOff>=0) - { - // String length - uint length = (uint)source->SourceLen; - const char* sString = source->SourcePtr; - - // Line count and char count - uint line=1; - uint charC=1; - - // Find the line - uint offset=0; - while ((offsetParamIndices = source->ParamIndices; - - // Build the feature info - program->buildInfo(source->DisplayName.c_str(), source->Features); - - // Setup ok - return true; - } - else - { - // Cast the driver info pointer - drvInfo=safe_cast((IGPUProgramDrvInfos*)program->_DrvInfo); - } + // Enable vertex program + glEnable(GL_VERTEX_PROGRAM_NV); + _VertexProgramEnabled = true; // Setup this program - nglBindProgramNV (GL_VERTEX_PROGRAM_NV, drvInfo->ID); + nglBindProgramNV(GL_VERTEX_PROGRAM_NV, drvInfo->ID); _LastSetuppedVP = program; // Ok @@ -216,8 +225,8 @@ bool CDriverGL::activeNVVertexProgram (CVertexProgram *program) else // Unsetup { // Disable vertex program - glDisable (GL_VERTEX_PROGRAM_NV); - _VertexProgramEnabled= false; + glDisable(GL_VERTEX_PROGRAM_NV); + _VertexProgramEnabled = false; // Ok return true; } @@ -1507,207 +1516,273 @@ bool CDriverGL::setupARBVertexProgram (const CVPParser::TProgram &inParsedProgra #endif } +// *************************************************************************** +bool CDriverGL::compileARBVertexProgram(NL3D::CVertexProgram *program) +{ + H_AUTO_OGL(CDriverGL_compileARBVertexProgram); + +#ifndef USE_OPENGLES + + nlassert(!program->_DrvInfo); + glDisable(GL_VERTEX_PROGRAM_ARB); + _VertexProgramEnabled = false; + + // Find nelvp + CGPUProgramSource *source = NULL; + for (uint i = 0; i < program->getProgramSource()->Sources.size(); ++i) + { + if (program->getProgramSource()->Sources[i]->Profile == CVertexProgram::nelvp) + { + source = program->getProgramSource()->Sources[i]; + } + } + if (!source) + { + nlwarning("OpenGL driver only supports 'nelvp' profile, vertex program cannot be used"); + return false; + } + + // try to parse the program + CVPParser parser; + CVPParser::TProgram parsedProgram; + std::string errorOutput; + bool result = parser.parse(source->SourcePtr, parsedProgram, errorOutput); + if (!result) + { + nlwarning("Unable to parse a vertex program."); + #ifdef NL_DEBUG + nlerror(errorOutput.c_str()); + #endif + return false; + } + // Insert into driver list. (so it is deleted when driver is deleted). + ItGPUPrgDrvInfoPtrList it = _GPUPrgDrvInfos.insert(_GPUPrgDrvInfos.end(), (NL3D::IGPUProgramDrvInfos*)NULL); + + // Create a driver info + CVertexProgamDrvInfosGL *drvInfo; + *it = drvInfo = new CVertexProgamDrvInfosGL(this, it); + // Set the pointer + program->_DrvInfo=drvInfo; + + if (!setupARBVertexProgram(parsedProgram, drvInfo->ID, drvInfo->SpecularWritten)) + { + delete drvInfo; + program->_DrvInfo = NULL; + _GPUPrgDrvInfos.erase(it); + return false; + } + + // Set parameters for assembly programs + drvInfo->ParamIndices = source->ParamIndices; + + // Build the feature info + program->buildInfo(source->DisplayName.c_str(), source->Features); + + return true; + +#else + + return false; + +#endif +} // *************************************************************************** -bool CDriverGL::activeARBVertexProgram (CVertexProgram *program) + +bool CDriverGL::activeARBVertexProgram(CVertexProgram *program) { H_AUTO_OGL(CDriverGL_activeARBVertexProgram); #ifndef USE_OPENGLES + // Setup or unsetup ? if (program) { // Driver info - CVertexProgamDrvInfosGL *drvInfo; + CVertexProgamDrvInfosGL *drvInfo = safe_cast((IGPUProgramDrvInfos*)program->_DrvInfo); + nlassert(drvInfo); - // Program setuped ? - if (program->_DrvInfo==NULL) - { - // Find nelvp - CGPUProgramSource *source = NULL; - for (uint i = 0; i < program->getProgramSource()->Sources.size(); ++i) - { - if (program->getProgramSource()->Sources[i]->Profile == CVertexProgram::nelvp) - { - source = program->getProgramSource()->Sources[i]; - } - } - if (!source) - { - nlwarning("OpenGL driver only supports 'nelvp' profile, vertex program cannot be used"); - return false; - } - - // try to parse the program - CVPParser parser; - CVPParser::TProgram parsedProgram; - std::string errorOutput; - bool result = parser.parse(source->SourcePtr, parsedProgram, errorOutput); - if (!result) - { - nlwarning("Unable to parse a vertex program."); - #ifdef NL_DEBUG - nlerror(errorOutput.c_str()); - #endif - return false; - } - // Insert into driver list. (so it is deleted when driver is deleted). - ItGPUPrgDrvInfoPtrList it= _GPUPrgDrvInfos.insert(_GPUPrgDrvInfos.end(), (NL3D::IGPUProgramDrvInfos*)NULL); - - // Create a driver info - *it = drvInfo = new CVertexProgamDrvInfosGL (this, it); - // Set the pointer - program->_DrvInfo=drvInfo; - - if (!setupARBVertexProgram(parsedProgram, drvInfo->ID, drvInfo->SpecularWritten)) - { - delete drvInfo; - program->_DrvInfo = NULL; - _GPUPrgDrvInfos.erase(it); - return false; - } - - // Set parameters for assembly programs - drvInfo->ParamIndices = source->ParamIndices; - - // Build the feature info - program->buildInfo(source->DisplayName.c_str(), source->Features); - } - else - { - // Cast the driver info pointer - drvInfo=safe_cast((IGPUProgramDrvInfos*)program->_DrvInfo); - } glEnable( GL_VERTEX_PROGRAM_ARB ); _VertexProgramEnabled = true; - nglBindProgramARB( GL_VERTEX_PROGRAM_ARB, drvInfo->ID ); + nglBindProgramARB(GL_VERTEX_PROGRAM_ARB, drvInfo->ID); if (drvInfo->SpecularWritten) { - glEnable( GL_COLOR_SUM_ARB ); + glEnable(GL_COLOR_SUM_ARB); } else { - glDisable( GL_COLOR_SUM_ARB ); // no specular written + glDisable(GL_COLOR_SUM_ARB); // no specular written } _LastSetuppedVP = program; } else { - glDisable( GL_VERTEX_PROGRAM_ARB ); - glDisable( GL_COLOR_SUM_ARB ); + glDisable(GL_VERTEX_PROGRAM_ARB); + glDisable(GL_COLOR_SUM_ARB); _VertexProgramEnabled = false; } return true; + #else + return false; + #endif } // *************************************************************************** -bool CDriverGL::activeEXTVertexShader (CVertexProgram *program) + +bool CDriverGL::compileEXTVertexShader(CVertexProgram *program) { H_AUTO_OGL(CDriverGL_activeEXTVertexShader); #ifndef USE_OPENGLES - // Setup or unsetup ? - if (program) + + nlassert(program->_DrvInfo); + glDisable(GL_VERTEX_SHADER_EXT); + _VertexProgramEnabled = false; + + // Find nelvp + CGPUProgramSource *source = NULL; + for (uint i = 0; i < program->getProgramSource()->Sources.size(); ++i) { - // Driver info - CVertexProgamDrvInfosGL *drvInfo; - - // Program setuped ? - if (program->_DrvInfo==NULL) + if (program->getProgramSource()->Sources[i]->Profile == CVertexProgram::nelvp) { - // Find nelvp - CGPUProgramSource *source = NULL; - for (uint i = 0; i < program->getProgramSource()->Sources.size(); ++i) - { - if (program->getProgramSource()->Sources[i]->Profile == CVertexProgram::nelvp) - { - source = program->getProgramSource()->Sources[i]; - } - } - if (!source) - { - nlwarning("OpenGL driver only supports 'nelvp' profile, vertex program cannot be used"); - return false; - } - - // try to parse the program - CVPParser parser; - CVPParser::TProgram parsedProgram; - std::string errorOutput; - bool result = parser.parse(source->SourcePtr, parsedProgram, errorOutput); - if (!result) - { - nlwarning("Unable to parse a vertex program."); - #ifdef NL_DEBUG - nlerror(errorOutput.c_str()); - #endif - return false; - } - - /* - FILE *f = fopen(getLogDirectory() + "test.txt", "wb"); - if (f) - { - std::string vpText; - CVPParser::dump(parsedProgram, vpText); - fwrite(vpText.c_str(), vpText.size(), 1, f); - fclose(f); - } - */ - - // Insert into driver list. (so it is deleted when driver is deleted). - ItGPUPrgDrvInfoPtrList it= _GPUPrgDrvInfos.insert(_GPUPrgDrvInfos.end(), (NL3D::IGPUProgramDrvInfos*)NULL); - - // Create a driver info - *it = drvInfo = new CVertexProgamDrvInfosGL (this, it); - // Set the pointer - program->_DrvInfo=drvInfo; - - if (!setupEXTVertexShader(parsedProgram, drvInfo->ID, drvInfo->Variants, drvInfo->UsedVertexComponents)) - { - delete drvInfo; - program->_DrvInfo = NULL; - _GPUPrgDrvInfos.erase(it); - return false; - } - - // Set parameters for assembly programs - drvInfo->ParamIndices = source->ParamIndices; - - // Build the feature info - program->buildInfo(source->DisplayName.c_str(), source->Features); + source = program->getProgramSource()->Sources[i]; } - else - { - // Cast the driver info pointer - drvInfo=safe_cast((IGPUProgramDrvInfos*)program->_DrvInfo); - } - - glEnable( GL_VERTEX_SHADER_EXT); - _VertexProgramEnabled = true; - nglBindVertexShaderEXT( drvInfo->ID ); - _LastSetuppedVP = program; } - else + if (!source) { - glDisable( GL_VERTEX_SHADER_EXT ); - _VertexProgramEnabled = false; + nlwarning("OpenGL driver only supports 'nelvp' profile, vertex program cannot be used"); + return false; } + + // try to parse the program + CVPParser parser; + CVPParser::TProgram parsedProgram; + std::string errorOutput; + bool result = parser.parse(source->SourcePtr, parsedProgram, errorOutput); + if (!result) + { + nlwarning("Unable to parse a vertex program."); + #ifdef NL_DEBUG + nlerror(errorOutput.c_str()); + #endif + return false; + } + + /* + FILE *f = fopen(getLogDirectory() + "test.txt", "wb"); + if (f) + { + std::string vpText; + CVPParser::dump(parsedProgram, vpText); + fwrite(vpText.c_str(), vpText.size(), 1, f); + fclose(f); + } + */ + + // Insert into driver list. (so it is deleted when driver is deleted). + ItGPUPrgDrvInfoPtrList it= _GPUPrgDrvInfos.insert(_GPUPrgDrvInfos.end(), (NL3D::IGPUProgramDrvInfos*)NULL); + + // Create a driver info + CVertexProgamDrvInfosGL *drvInfo; + *it = drvInfo = new CVertexProgamDrvInfosGL (this, it); + // Set the pointer + program->_DrvInfo=drvInfo; + + if (!setupEXTVertexShader(parsedProgram, drvInfo->ID, drvInfo->Variants, drvInfo->UsedVertexComponents)) + { + delete drvInfo; + program->_DrvInfo = NULL; + _GPUPrgDrvInfos.erase(it); + return false; + } + + // Set parameters for assembly programs + drvInfo->ParamIndices = source->ParamIndices; + + // Build the feature info + program->buildInfo(source->DisplayName.c_str(), source->Features); + return true; + #else + return false; + #endif } // *************************************************************************** -bool CDriverGL::activeVertexProgram (CVertexProgram *program) + +bool CDriverGL::activeEXTVertexShader(CVertexProgram *program) +{ + H_AUTO_OGL(CDriverGL_activeEXTVertexShader); + +#ifndef USE_OPENGLES + + // Setup or unsetup ? + if (program) + { + // Driver info + CVertexProgamDrvInfosGL *drvInfo = safe_cast((IGPUProgramDrvInfos*)program->_DrvInfo); + nlassert(drvInfo); + + glEnable(GL_VERTEX_SHADER_EXT); + _VertexProgramEnabled = true; + nglBindVertexShaderEXT(drvInfo->ID); + _LastSetuppedVP = program; + } + else + { + glDisable(GL_VERTEX_SHADER_EXT); + _VertexProgramEnabled = false; + } + return true; + +#else + + return false; + +#endif +} + +bool CDriverGL::compileVertexProgram(NL3D::CVertexProgram *program) +{ + if (program->_DrvInfo == NULL) + { + // Extension + if (_Extensions.NVVertexProgram) + { + return compileNVVertexProgram(program); + } + else if (_Extensions.ARBVertexProgram) + { + return compileARBVertexProgram(program); + } + else if (_Extensions.EXTVertexShader) + { + return compileEXTVertexShader(program); + } + + // Can't do anything + return false; + } + return true; +} + +// *************************************************************************** + +bool CDriverGL::activeVertexProgram(CVertexProgram *program) { H_AUTO_OGL(CDriverGL_activeVertexProgram) - // Extension here ? + + // Compile if necessary + if (program && !CDriverGL::compileVertexProgram(program)) return false; + + // Extension if (_Extensions.NVVertexProgram) { return activeNVVertexProgram(program); @@ -1725,287 +1800,6 @@ bool CDriverGL::activeVertexProgram (CVertexProgram *program) return false; } - -// *************************************************************************** - -void CDriverGL::setConstant (uint index, float f0, float f1, float f2, float f3) -{ - H_AUTO_OGL(CDriverGL_setConstant); - -#ifndef USE_OPENGLES - // Vertex program exist ? - if (_Extensions.NVVertexProgram) - { - // Setup constant - nglProgramParameter4fNV (GL_VERTEX_PROGRAM_NV, index, f0, f1, f2, f3); - } - else if (_Extensions.ARBVertexProgram) - { - nglProgramEnvParameter4fARB(GL_VERTEX_PROGRAM_ARB, index, f0, f1, f2, f3); - } - else if (_Extensions.EXTVertexShader) - { - float datas[] = { f0, f1, f2, f3 }; - nglSetInvariantEXT(_EVSConstantHandle + index, GL_FLOAT, datas); - } -#endif -} - - -// *************************************************************************** - -void CDriverGL::setConstant (uint index, double d0, double d1, double d2, double d3) -{ - H_AUTO_OGL(CDriverGL_setConstant); - -#ifndef USE_OPENGLES - // Vertex program exist ? - if (_Extensions.NVVertexProgram) - { - // Setup constant - nglProgramParameter4dNV (GL_VERTEX_PROGRAM_NV, index, d0, d1, d2, d3); - } - else if (_Extensions.ARBVertexProgram) - { - nglProgramEnvParameter4dARB(GL_VERTEX_PROGRAM_ARB, index, d0, d1, d2, d3); - } - else if (_Extensions.EXTVertexShader) - { - double datas[] = { d0, d1, d2, d3 }; - nglSetInvariantEXT(_EVSConstantHandle + index, GL_DOUBLE, datas); - } -#endif -} - - -// *************************************************************************** - -void CDriverGL::setConstant (uint index, const NLMISC::CVector& value) -{ - H_AUTO_OGL(CDriverGL_setConstant); - -#ifndef USE_OPENGLES - // Vertex program exist ? - if (_Extensions.NVVertexProgram) - { - // Setup constant - nglProgramParameter4fNV (GL_VERTEX_PROGRAM_NV, index, value.x, value.y, value.z, 0); - } - else if (_Extensions.ARBVertexProgram) - { - nglProgramEnvParameter4fARB(GL_VERTEX_PROGRAM_ARB, index, value.x, value.y, value.z, 0); - } - else if (_Extensions.EXTVertexShader) - { - float datas[] = { value.x, value.y, value.z, 0 }; - nglSetInvariantEXT(_EVSConstantHandle + index, GL_FLOAT, datas); - } -#endif -} - - -// *************************************************************************** - -void CDriverGL::setConstant (uint index, const NLMISC::CVectorD& value) -{ - H_AUTO_OGL(CDriverGL_setConstant); - -#ifndef USE_OPENGLES - // Vertex program exist ? - if (_Extensions.NVVertexProgram) - { - // Setup constant - nglProgramParameter4dNV (GL_VERTEX_PROGRAM_NV, index, value.x, value.y, value.z, 0); - } - else if (_Extensions.ARBVertexProgram) - { - nglProgramEnvParameter4dARB(GL_VERTEX_PROGRAM_ARB, index, value.x, value.y, value.z, 0); - } - else if (_Extensions.EXTVertexShader) - { - double datas[] = { value.x, value.y, value.z, 0 }; - nglSetInvariantEXT(_EVSConstantHandle + index, GL_DOUBLE, datas); - } -#endif -} - - -// *************************************************************************** -void CDriverGL::setConstant (uint index, uint num, const float *src) -{ - H_AUTO_OGL(CDriverGL_setConstant); - -#ifndef USE_OPENGLES - // Vertex program exist ? - if (_Extensions.NVVertexProgram) - { - nglProgramParameters4fvNV(GL_VERTEX_PROGRAM_NV, index, num, src); - } - else if (_Extensions.ARBVertexProgram) - { - for(uint k = 0; k < num; ++k) - { - nglProgramEnvParameter4fvARB(GL_VERTEX_PROGRAM_ARB, index + k, src + 4 * k); - } - } - else if (_Extensions.EXTVertexShader) - { - for(uint k = 0; k < num; ++k) - { - nglSetInvariantEXT(_EVSConstantHandle + index + k, GL_FLOAT, (void *) (src + 4 * k)); - } - } -#endif -} - -// *************************************************************************** -void CDriverGL::setConstant (uint index, uint num, const double *src) -{ - H_AUTO_OGL(CDriverGL_setConstant); - -#ifndef USE_OPENGLES - // Vertex program exist ? - if (_Extensions.NVVertexProgram) - { - nglProgramParameters4dvNV(GL_VERTEX_PROGRAM_NV, index, num, src); - } - else if (_Extensions.ARBVertexProgram) - { - for(uint k = 0; k < num; ++k) - { - nglProgramEnvParameter4dvARB(GL_VERTEX_PROGRAM_ARB, index + k, src + 4 * k); - } - } - else if (_Extensions.EXTVertexShader) - { - for(uint k = 0; k < num; ++k) - { - nglSetInvariantEXT(_EVSConstantHandle + index + k, GL_DOUBLE, (void *) (src + 4 * k)); - } - } -#endif -} - -// *************************************************************************** - -const uint CDriverGL::GLMatrix[IDriver::NumMatrix]= -{ - GL_MODELVIEW, - GL_PROJECTION, -#ifdef USE_OPENGLES - GL_MODELVIEW -#else - GL_MODELVIEW_PROJECTION_NV -#endif -}; - - -// *************************************************************************** - -const uint CDriverGL::GLTransform[IDriver::NumTransform]= -{ -#ifdef USE_OPENGLES - 0, - 0, - 0, - 0 -#else - GL_IDENTITY_NV, - GL_INVERSE_NV, - GL_TRANSPOSE_NV, - GL_INVERSE_TRANSPOSE_NV -#endif -}; - - -// *************************************************************************** - -void CDriverGL::setConstantMatrix (uint index, IDriver::TMatrix matrix, IDriver::TTransform transform) -{ - H_AUTO_OGL(CDriverGL_setConstantMatrix); - -#ifndef USE_OPENGLES - // Vertex program exist ? - if (_Extensions.NVVertexProgram) - { - // First, ensure that the render setup is correclty setuped. - refreshRenderSetup(); - - // Track the matrix - nglTrackMatrixNV (GL_VERTEX_PROGRAM_NV, index, GLMatrix[matrix], GLTransform[transform]); - // Release Track => matrix data is copied. - nglTrackMatrixNV (GL_VERTEX_PROGRAM_NV, index, GL_NONE, GL_IDENTITY_NV); - } - else - { - // First, ensure that the render setup is correctly setuped. - refreshRenderSetup(); - CMatrix mat; - switch (matrix) - { - case IDriver::ModelView: - mat = _ModelViewMatrix; - break; - case IDriver::Projection: - { - refreshProjMatrixFromGL(); - mat = _GLProjMat; - } - break; - case IDriver::ModelViewProjection: - refreshProjMatrixFromGL(); - mat = _GLProjMat * _ModelViewMatrix; - break; - default: - break; - } - - switch(transform) - { - case IDriver::Identity: break; - case IDriver::Inverse: - mat.invert(); - break; - case IDriver::Transpose: - mat.transpose(); - break; - case IDriver::InverseTranspose: - mat.invert(); - mat.transpose(); - break; - default: - break; - } - mat.transpose(); - float matDatas[16]; - mat.get(matDatas); - if (_Extensions.ARBVertexProgram) - { - nglProgramEnvParameter4fvARB(GL_VERTEX_PROGRAM_ARB, index, matDatas); - nglProgramEnvParameter4fvARB(GL_VERTEX_PROGRAM_ARB, index + 1, matDatas + 4); - nglProgramEnvParameter4fvARB(GL_VERTEX_PROGRAM_ARB, index + 2, matDatas + 8); - nglProgramEnvParameter4fvARB(GL_VERTEX_PROGRAM_ARB, index + 3, matDatas + 12); - } - else - { - nglSetInvariantEXT(_EVSConstantHandle + index, GL_FLOAT, matDatas); - nglSetInvariantEXT(_EVSConstantHandle + index + 1, GL_FLOAT, matDatas + 4); - nglSetInvariantEXT(_EVSConstantHandle + index + 2, GL_FLOAT, matDatas + 8); - nglSetInvariantEXT(_EVSConstantHandle + index + 3, GL_FLOAT, matDatas + 12); - } - } -#endif -} - -// *************************************************************************** - -void CDriverGL::setConstantFog (uint index) -{ - H_AUTO_OGL(CDriverGL_setConstantFog) - const float *values = _ModelViewMatrix.get(); - setConstant (index, -values[2], -values[6], -values[10], -values[14]); -} - // *************************************************************************** void CDriverGL::enableVertexProgramDoubleSidedColor(bool doubleSided) From fcb0ee720eecc8400ecd785749643cb67ac2e61f Mon Sep 17 00:00:00 2001 From: kaetemi Date: Mon, 9 Sep 2013 16:36:29 +0200 Subject: [PATCH 196/313] Implement new driver interface in Direct3D driver --- .../3d/driver/direct3d/driver_direct3d.cpp | 2 +- .../src/3d/driver/direct3d/driver_direct3d.h | 118 ++++-- .../driver_direct3d_pixel_program.cpp | 280 +++---------- .../direct3d/driver_direct3d_uniform.cpp | 218 ++++++++++ .../driver_direct3d_vertex_program.cpp | 381 +++++------------- 5 files changed, 482 insertions(+), 517 deletions(-) create mode 100644 code/nel/src/3d/driver/direct3d/driver_direct3d_uniform.cpp diff --git a/code/nel/src/3d/driver/direct3d/driver_direct3d.cpp b/code/nel/src/3d/driver/direct3d/driver_direct3d.cpp index e7c1ba693..180f577f3 100644 --- a/code/nel/src/3d/driver/direct3d/driver_direct3d.cpp +++ b/code/nel/src/3d/driver/direct3d/driver_direct3d.cpp @@ -3634,7 +3634,7 @@ void CDriverD3D::CVertexProgramPtrState::apply(CDriverD3D *driver) void CDriverD3D::CPixelShaderPtrState::apply(CDriverD3D *driver) { H_AUTO_D3D(CDriverD3D_CPixelShaderPtrState); - if (!driver->supportPixelProgram()) return; + if (!driver->_PixelProgram) return; driver->_DeviceInterface->SetPixelShader(PixelShader); } diff --git a/code/nel/src/3d/driver/direct3d/driver_direct3d.h b/code/nel/src/3d/driver/direct3d/driver_direct3d.h index 3494812cc..79dcc917e 100644 --- a/code/nel/src/3d/driver/direct3d/driver_direct3d.h +++ b/code/nel/src/3d/driver/direct3d/driver_direct3d.h @@ -1092,33 +1092,103 @@ public: virtual void setupMaterialPass(uint pass); virtual void endMaterialMultiPass(); - // Vertex program - virtual bool supportVertexProgram () const; - virtual bool supportPixelProgram () const; - virtual bool supportPixelProgram (CPixelProgram::TProfile profile) const; - virtual bool isVertexProgramEmulated () const; - virtual bool activeVertexProgram (CVertexProgram *program); - virtual bool activePixelProgram (CPixelProgram *program); - virtual void setConstant (uint index, float, float, float, float); - virtual void setConstant (uint index, double, double, double, double); - virtual void setConstant (uint index, const NLMISC::CVector& value); - virtual void setConstant (uint index, const NLMISC::CVectorD& value); - virtual void setConstant (uint index, uint num, const float *src); - virtual void setConstant (uint index, uint num, const double *src); - virtual void setConstantMatrix (uint index, IDriver::TMatrix matrix, IDriver::TTransform transform); - virtual void setConstantFog (uint index); + + + + + + /// \name Vertex Program + // @{ + + // Order of preference + // - activeVertexProgram + // - CMaterial pass[n] VP (uses activeVertexProgram, but does not override if one already set by code) + // - default generic VP that mimics fixed pipeline / no VP with fixed pipeline + + /** + * Does the driver supports vertex program, but emulated by CPU ? + */ + virtual bool isVertexProgramEmulated() const; + + /** Return true if the driver supports the specified vertex program profile. + */ + virtual bool supportVertexProgram(CVertexProgram::TProfile profile = CVertexProgram::nelvp) const; + + /** Compile the given vertex program, return if successful. + * If a vertex program was set active before compilation, + * the state of the active vertex program is undefined behaviour afterwards. + */ + virtual bool compileVertexProgram(CVertexProgram *program); + + /** Set the active vertex program. This will override vertex programs specified in CMaterial render calls. + * Also used internally by setupMaterial(CMaterial) when getVertexProgram returns NULL. + * The vertex program is activated immediately. + */ + virtual bool activeVertexProgram(CVertexProgram *program); + // @} + + + + /// \name Pixel Program + // @{ + + // Order of preference + // - activePixelProgram + // - CMaterial pass[n] PP (uses activePixelProgram, but does not override if one already set by code) + // - PP generated from CMaterial (uses activePixelProgram, but does not override if one already set by code) + + /** Return true if the driver supports the specified pixel program profile. + */ + virtual bool supportPixelProgram(CPixelProgram::TProfile profile) const; + + /** Compile the given pixel program, return if successful. + * If a pixel program was set active before compilation, + * the state of the active pixel program is undefined behaviour afterwards. + */ + virtual bool compilePixelProgram(CPixelProgram *program); + + /** Set the active pixel program. This will override pixel programs specified in CMaterial render calls. + * Also used internally by setupMaterial(CMaterial) when getPixelProgram returns NULL. + * The pixel program is activated immediately. + */ + virtual bool activePixelProgram(CPixelProgram *program); + // @} + + + + /// \name Program parameters + // @{ + // Set parameters + virtual void setUniform1f(TProgram program, uint index, float f0); + virtual void setUniform2f(TProgram program, uint index, float f0, float f1); + virtual void setUniform3f(TProgram program, uint index, float f0, float f1, float f2); + virtual void setUniform4f(TProgram program, uint index, float f0, float f1, float f2, float f3); + virtual void setUniform1i(TProgram program, uint index, sint32 i0); + virtual void setUniform2i(TProgram program, uint index, sint32 i0, sint32 i1); + virtual void setUniform3i(TProgram program, uint index, sint32 i0, sint32 i1, sint32 i2); + virtual void setUniform4i(TProgram program, uint index, sint32 i0, sint32 i1, sint32 i2, sint32 i3); + virtual void setUniform1ui(TProgram program, uint index, uint32 ui0); + virtual void setUniform2ui(TProgram program, uint index, uint32 ui0, uint32 ui1); + virtual void setUniform3ui(TProgram program, uint index, uint32 ui0, uint32 ui1, uint32 ui2); + virtual void setUniform4ui(TProgram program, uint index, uint32 ui0, uint32 ui1, uint32 ui2, uint32 ui3); + virtual void setUniform3f(TProgram program, uint index, const NLMISC::CVector& v); + virtual void setUniform4f(TProgram program, uint index, const NLMISC::CVector& v, float f3); + virtual void setUniform4x4f(TProgram program, uint index, const NLMISC::CMatrix& m); + virtual void setUniform4fv(TProgram program, uint index, size_t num, const float *src); + virtual void setUniform4iv(TProgram program, uint index, size_t num, const sint32 *src); + virtual void setUniform4uiv(TProgram program, uint index, size_t num, const uint32 *src); + // Set builtin parameters + virtual void setUniformMatrix(TProgram program, uint index, TMatrix matrix, TTransform transform); + virtual void setUniformFog(TProgram program, uint index); + // @} + + + + + virtual void enableVertexProgramDoubleSidedColor(bool doubleSided); virtual bool supportVertexProgramDoubleSidedColor() const; - // Pixel program - virtual void setPixelProgramConstant (uint index, float, float, float, float); - virtual void setPixelProgramConstant (uint index, double, double, double, double); - virtual void setPixelProgramConstant (uint index, const NLMISC::CVector& value); - virtual void setPixelProgramConstant (uint index, const NLMISC::CVectorD& value); - virtual void setPixelProgramConstant (uint index, uint num, const float *src); - virtual void setPixelProgramConstant (uint index, uint num, const double *src); - virtual void setPixelProgramConstantMatrix (uint index, IDriver::TMatrix matrix, IDriver::TTransform transform); - // Occlusion query virtual bool supportOcclusionQuery() const; virtual IOcclusionQuery *createOcclusionQuery(); diff --git a/code/nel/src/3d/driver/direct3d/driver_direct3d_pixel_program.cpp b/code/nel/src/3d/driver/direct3d/driver_direct3d_pixel_program.cpp index db8763091..612155c38 100644 --- a/code/nel/src/3d/driver/direct3d/driver_direct3d_pixel_program.cpp +++ b/code/nel/src/3d/driver/direct3d/driver_direct3d_pixel_program.cpp @@ -54,12 +54,6 @@ CPixelProgramDrvInfosD3D::~CPixelProgramDrvInfosD3D() // *************************************************************************** -bool CDriverD3D::supportPixelProgram () const -{ - H_AUTO_D3D(CDriverD3D_supportPixelProgram) - return _PixelProgram; -} - bool CDriverD3D::supportPixelProgram (CPixelProgram::TProfile profile) const { H_AUTO_D3D(CDriverD3D_supportPixelProgram_profile) @@ -69,81 +63,77 @@ bool CDriverD3D::supportPixelProgram (CPixelProgram::TProfile profile) const // *************************************************************************** +bool CDriverD3D::compilePixelProgram(CPixelProgram *program) +{ + // Program setuped ? + if (program->_DrvInfo==NULL) + { + // Find a supported pixel program profile + CGPUProgramSource *source = NULL; + for (uint i = 0; i < program->getProgramSource()->Sources.size(); ++i) + { + if (supportPixelProgram(program->getProgramSource()->Sources[i]->Profile)) + { + source = program->getProgramSource()->Sources[i]; + } + } + if (!source) + { + nlwarning("No supported source profile for pixel program"); + return false; + } + + _GPUPrgDrvInfos.push_front (NULL); + ItGPUPrgDrvInfoPtrList itPix = _GPUPrgDrvInfos.begin(); + CPixelProgramDrvInfosD3D *drvInfo; + *itPix = drvInfo = new CPixelProgramDrvInfosD3D(this, itPix); + + // Create a driver info structure + program->_DrvInfo = *itPix; + + LPD3DXBUFFER pShader; + LPD3DXBUFFER pErrorMsgs; + if (D3DXAssembleShader(source->SourcePtr, source->SourceLen, NULL, NULL, 0, &pShader, &pErrorMsgs) == D3D_OK) + { + if (_DeviceInterface->CreatePixelShader((DWORD*)pShader->GetBufferPointer(), &(getPixelProgramD3D(*program)->Shader)) != D3D_OK) + return false; + } + else + { + nlwarning ("Can't assemble pixel program:"); + nlwarning ((const char*)pErrorMsgs->GetBufferPointer()); + return false; + } + + // Set parameters for assembly programs + drvInfo->ParamIndices = source->ParamIndices; + + // Build the feature info + program->buildInfo(source->DisplayName.c_str(), source->Features); + } + + return true; +} + +// *************************************************************************** + bool CDriverD3D::activePixelProgram(CPixelProgram *program) { H_AUTO_D3D(CDriverD3D_activePixelProgram ) if (_DisableHardwarePixelProgram) return false; - // Setup or unsetup ? - if (program) - { - // Program setuped ? - if (program->_DrvInfo==NULL) - { - // Find a supported pixel program profile - CGPUProgramSource *source = NULL; - for (uint i = 0; i < program->getProgramSource()->Sources.size(); ++i) - { - if (supportPixelProgram(program->getProgramSource()->Sources[i]->Profile)) - { - source = program->getProgramSource()->Sources[i]; - } - } - if (!source) - { - nlwarning("No supported source profile for pixel program"); - return false; - } - - _GPUPrgDrvInfos.push_front (NULL); - ItGPUPrgDrvInfoPtrList itPix = _GPUPrgDrvInfos.begin(); - CPixelProgramDrvInfosD3D *drvInfo; - *itPix = drvInfo = new CPixelProgramDrvInfosD3D(this, itPix); - - // Create a driver info structure - program->_DrvInfo = *itPix; - - LPD3DXBUFFER pShader; - LPD3DXBUFFER pErrorMsgs; - if (D3DXAssembleShader(source->SourcePtr, source->SourceLen, NULL, NULL, 0, &pShader, &pErrorMsgs) == D3D_OK) - { - if (_DeviceInterface->CreatePixelShader((DWORD*)pShader->GetBufferPointer(), &(getPixelProgramD3D(*program)->Shader)) != D3D_OK) - return false; - } - else - { - nlwarning ("Can't assemble pixel program:"); - nlwarning ((const char*)pErrorMsgs->GetBufferPointer()); - return false; - } - - // Set parameters for assembly programs - drvInfo->ParamIndices = source->ParamIndices; - - // Build the feature info - program->buildInfo(source->DisplayName.c_str(), source->Features); - } - } - // Set the pixel program if (program) { - CPixelProgramDrvInfosD3D *info = static_cast((IGPUProgramDrvInfos*)program->_DrvInfo); - setPixelShader (info->Shader); + if (!CDriverD3D::compilePixelProgram(program)) return false; - float z = 0; - float o = 1; - setRenderState (D3DRS_FOGSTART, *((DWORD*) (&o))); - setRenderState (D3DRS_FOGEND, *((DWORD*) (&z))); + CPixelProgramDrvInfosD3D *info = static_cast((IGPUProgramDrvInfos*)program->_DrvInfo); + setPixelShader(info->Shader); } else { - setPixelShader (NULL); - - // Set the old fog range - setRenderState (D3DRS_FOGSTART, *((DWORD*) (&_FogStart))); - setRenderState (D3DRS_FOGEND, *((DWORD*) (&_FogEnd))); + setPixelShader(NULL); } return true; @@ -151,160 +141,6 @@ bool CDriverD3D::activePixelProgram(CPixelProgram *program) // *************************************************************************** -void CDriverD3D::setPixelProgramConstant (uint index, float f0, float f1, float f2, float f3) -{ - H_AUTO_D3D(CDriverD3D_setPixelProgramConstant) - if (!_PixelProgram) - { - #ifdef NL_DEBUG - nlwarning("No pixel programs available!!"); - #endif - return; - } - const float tabl[4] = {f0, f1, f2, f3}; - setPixelShaderConstant (index, tabl); -} - -// *************************************************************************** - -void CDriverD3D::setPixelProgramConstant (uint index, double d0, double d1, double d2, double d3) -{ - H_AUTO_D3D(CDriverD3D_setPixelProgramConstant ) - if (!_PixelProgram) - { - #ifdef NL_DEBUG - nlwarning("No pixel programs available!!"); - #endif - return; - } - const float tabl[4] = {(float)d0, (float)d1, (float)d2, (float)d3}; - setPixelShaderConstant (index, tabl); -} - -// *************************************************************************** - -void CDriverD3D::setPixelProgramConstant (uint index, const NLMISC::CVector& value) -{ - H_AUTO_D3D(CDriverD3D_setPixelProgramConstant ) - if (!_PixelProgram) - { - #ifdef NL_DEBUG - nlwarning("No pixel programs available!!"); - #endif - return; - } - const float tabl[4] = {value.x, value.y, value.z, 0}; - setPixelShaderConstant (index, tabl); -} - -// *************************************************************************** - -void CDriverD3D::setPixelProgramConstant (uint index, const NLMISC::CVectorD& value) -{ - H_AUTO_D3D(CDriverD3D_setPixelProgramConstant ) - if (!_PixelProgram) - { - #ifdef NL_DEBUG - nlwarning("No pixel programs available!!"); - #endif - return; - } - const float tabl[4] = {(float)value.x, (float)value.y, (float)value.z, 0}; - setPixelShaderConstant (index, tabl); -} - -// *************************************************************************** - -void CDriverD3D::setPixelProgramConstant (uint index, uint num, const float *src) -{ - H_AUTO_D3D(CDriverD3D_setPixelProgramConstant ) - if (!_PixelProgram) - { - #ifdef NL_DEBUG - nlwarning("No pixel programs available!!"); - #endif - return; - } - uint i; - for (i=0; i_11, matPtr->_21, matPtr->_31, matPtr->_41); - setPixelProgramConstant (index+1, matPtr->_12, matPtr->_22, matPtr->_32, matPtr->_42); - setPixelProgramConstant (index+2, matPtr->_13, matPtr->_23, matPtr->_33, matPtr->_43); - setPixelProgramConstant (index+3, matPtr->_14, matPtr->_24, matPtr->_34, matPtr->_44); -} - -// *************************************************************************** - void CDriverD3D::disableHardwarePixelProgram() { H_AUTO_D3D(CDriverD3D_disableHardwarePixelProgram) diff --git a/code/nel/src/3d/driver/direct3d/driver_direct3d_uniform.cpp b/code/nel/src/3d/driver/direct3d/driver_direct3d_uniform.cpp new file mode 100644 index 000000000..27e76bfd4 --- /dev/null +++ b/code/nel/src/3d/driver/direct3d/driver_direct3d_uniform.cpp @@ -0,0 +1,218 @@ +// NeL - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#include "stddirect3d.h" + +#include "driver_direct3d.h" + +using namespace std; +using namespace NLMISC; + +namespace NL3D +{ + +void CDriverD3D::setUniform4f(TProgram program, uint index, float f0, float f1, float f2, float f3) +{ + H_AUTO_D3D(CDriverD3D_setUniform4f); + + const float tabl[4] = { f0, f1, f2, f3 }; + switch (program) + { + case VertexProgram: + if (_VertexProgram) + { + setVertexProgramConstant(index, tabl); + } + break; + case PixelProgram: + if (_PixelProgram) + { + setPixelShaderConstant(index, tabl); + } + break; + } +} + +void CDriverD3D::setUniform4fv(TProgram program, uint index, size_t num, const float *src) +{ + H_AUTO_D3D(CDriverD3D_setUniform4fv); + + switch (program) + { + case VertexProgram: + if (_VertexProgram) + { + for (uint i = 0; i < num; ++i) + { + setVertexProgramConstant(index + i, src + (i * 4)); + } + } + break; + case PixelProgram: + if (_PixelProgram) + { + for (uint i = 0; i < num; ++i) + { + setPixelShaderConstant(index + i, src + (i * 4)); + } + } + break; + } +} + +void CDriverD3D::setUniform1f(TProgram program, uint index, float f0) +{ + CDriverD3D::setUniform4f(program, index, f0, 0.f, 0.f, 0.f); +} + +void CDriverD3D::setUniform2f(TProgram program, uint index, float f0, float f1) +{ + CDriverD3D::setUniform4f(program, index, f0, f1, 0.f, 0.f); +} + +void CDriverD3D::setUniform3f(TProgram program, uint index, float f0, float f1, float f2) +{ + CDriverD3D::setUniform4f(program, index, f0, f1, f2, 0.0f); +} + +void CDriverD3D::setUniform1i(TProgram program, uint index, sint32 i0) +{ + +} + +void CDriverD3D::setUniform2i(TProgram program, uint index, sint32 i0, sint32 i1) +{ + +} + +void CDriverD3D::setUniform3i(TProgram program, uint index, sint32 i0, sint32 i1, sint32 i2) +{ + +} + +void CDriverD3D::setUniform4i(TProgram program, uint index, sint32 i0, sint32 i1, sint32 i2, sint32 i3) +{ + +} + +void CDriverD3D::setUniform1ui(TProgram program, uint index, uint32 ui0) +{ + +} + +void CDriverD3D::setUniform2ui(TProgram program, uint index, uint32 ui0, uint32 ui1) +{ + +} + +void CDriverD3D::setUniform3ui(TProgram program, uint index, uint32 ui0, uint32 ui1, uint32 ui2) +{ + +} + +void CDriverD3D::setUniform4ui(TProgram program, uint index, uint32 ui0, uint32 ui1, uint32 ui2, uint32 ui3) +{ + +} + +void CDriverD3D::setUniform3f(TProgram program, uint index, const NLMISC::CVector& v) +{ + CDriverD3D::setUniform4f(program, index, v.x, v.y, v.z, 0.f); +} + +void CDriverD3D::setUniform4f(TProgram program, uint index, const NLMISC::CVector& v, float f3) +{ + CDriverD3D::setUniform4f(program, index, v.x, v.y, v.z, f3); +} + +void CDriverD3D::setUniform4x4f(TProgram program, uint index, const NLMISC::CMatrix& m) +{ + H_AUTO_D3D(CDriverD3D_setUniform4x4f); + + // TODO: Verify this! + NLMISC::CMatrix mat = m; + mat.transpose(); + const float *md = mat.get(); + + CDriverD3D::setUniform4fv(program, index, 4, md); +} + +void CDriverD3D::setUniform4iv(TProgram program, uint index, size_t num, const sint32 *src) +{ + +} + +void CDriverD3D::setUniform4uiv(TProgram program, uint index, size_t num, const uint32 *src) +{ + +} + +void CDriverD3D::setUniformMatrix(NL3D::IDriver::TProgram program, uint index, NL3D::IDriver::TMatrix matrix, NL3D::IDriver::TTransform transform) +{ + H_AUTO_D3D(CDriverD3D_setUniformMatrix); + + D3DXMATRIX mat; + D3DXMATRIX *matPtr = NULL; + switch (matrix) + { + case IDriver::ModelView: + matPtr = &_D3DModelView; + break; + case IDriver::Projection: + matPtr = &(_MatrixCache[remapMatrixIndex(D3DTS_PROJECTION)].Matrix); + break; + case IDriver::ModelViewProjection: + matPtr = &_D3DModelViewProjection; + break; + } + if (transform != IDriver::Identity) + { + switch (transform) + { + case IDriver::Inverse: + D3DXMatrixInverse(&mat, NULL, matPtr); + break; + case IDriver::Transpose: + D3DXMatrixTranspose(&mat, matPtr); + break; + case IDriver::InverseTranspose: + D3DXMatrixInverse(&mat, NULL, matPtr); + D3DXMatrixTranspose(&mat, &mat); + break; + } + matPtr = &mat; + } + + D3DXMatrixTranspose(&mat, matPtr); + + CDriverD3D::setUniform4fv(program, index, 4, &mat.m[0][0]); +} + +void CDriverD3D::setUniformFog(NL3D::IDriver::TProgram program, uint index) +{ + H_AUTO_D3D(CDriverD3D_setUniformFog) + + /* "oFog" must always be between [1, 0] what ever you set in D3DRS_FOGSTART and D3DRS_FOGEND (1 for no fog, 0 for full fog). + The Geforce4 TI 4200 (drivers 53.03 and 45.23) doesn't accept other values for "oFog". */ + const float delta = _FogEnd - _FogStart; + CDriverD3D::setUniform4f(program, index, + -_D3DModelView._13 / delta, + -_D3DModelView._23 / delta, + -_D3DModelView._33 / delta, + 1 - (_D3DModelView._43 - _FogStart) / delta); +} + +} // NL3D diff --git a/code/nel/src/3d/driver/direct3d/driver_direct3d_vertex_program.cpp b/code/nel/src/3d/driver/direct3d/driver_direct3d_vertex_program.cpp index c7b8905a1..46e0f1c14 100644 --- a/code/nel/src/3d/driver/direct3d/driver_direct3d_vertex_program.cpp +++ b/code/nel/src/3d/driver/direct3d/driver_direct3d_vertex_program.cpp @@ -43,10 +43,10 @@ CVertexProgamDrvInfosD3D::~CVertexProgamDrvInfosD3D() // *************************************************************************** -bool CDriverD3D::supportVertexProgram () const +bool CDriverD3D::supportVertexProgram (CVertexProgram::TProfile profile) const { H_AUTO_D3D(CDriverD3D_supportVertexProgram ) - return _VertexProgram; + return (profile == CVertexProgram::nelvp) && _VertexProgram; } // *************************************************************************** @@ -262,123 +262,129 @@ void dump(const CVPParser::TProgram &prg, std::string &dest) // *************************************************************************** +bool CDriverD3D::compileVertexProgram(NL3D::CVertexProgram *program) +{ + // Program setuped ? + if (program->_DrvInfo == NULL) + { + // Find nelvp + CGPUProgramSource *source = NULL; + for (uint i = 0; i < program->getProgramSource()->Sources.size(); ++i) + { + if (program->getProgramSource()->Sources[i]->Profile == CVertexProgram::nelvp) + { + source = program->getProgramSource()->Sources[i]; + } + } + if (!source) + { + nlwarning("Direct3D driver only supports 'nelvp' profile, vertex program cannot be used"); + return false; + } + + _GPUPrgDrvInfos.push_front (NULL); + ItGPUPrgDrvInfoPtrList itTex = _GPUPrgDrvInfos.begin(); + CVertexProgamDrvInfosD3D *drvInfo; + *itTex = drvInfo = new CVertexProgamDrvInfosD3D(this, itTex); + + // Create a driver info structure + program->_DrvInfo = *itTex; + + /** Check with our parser if the program will works with other implemented extensions, too. (EXT_vertex_shader ..). + * There are some incompatibilities. + */ + CVPParser parser; + CVPParser::TProgram parsedProgram; + std::string errorOutput; + bool result = parser.parse(source->SourcePtr, parsedProgram, errorOutput); + if (!result) + { + nlwarning("Unable to parse a vertex program :"); + nlwarning(errorOutput.c_str()); + #ifdef NL_DEBUG_D3D + nlassert(0); + #endif // NL_DEBUG_D3D + return false; + } + + // tmp fix for Radeon 8500/9000/9200 + // Currently they hang when PaletteSkin / SkinWeight are present in the vertex declaration, but not used + // so disable them in the vertex declaration + // We don't use these component in vertex programs currently.. + #ifdef NL_DEBUG + for(uint k = 0; k < parsedProgram.size(); ++k) + { + for(uint l = 0; l < parsedProgram[k].getNumUsedSrc(); ++l) + { + const CVPOperand &op = parsedProgram[k].getSrc(l); + if (op.Type == CVPOperand::InputRegister) + { + nlassert(op.Value.InputRegisterValue != CVPOperand::IWeight); + nlassert(op.Value.InputRegisterValue != CVPOperand::IPaletteSkin); + } + } + } + #endif + + // Dump the vertex program + std::string dest; + dump(parsedProgram, dest); +#ifdef NL_DEBUG_D3D + nlinfo("Assemble Vertex Shader : "); + string::size_type lineBegin = 0; + string::size_type lineEnd; + while ((lineEnd = dest.find('\n', lineBegin)) != string::npos) + { + nlinfo(dest.substr (lineBegin, lineEnd-lineBegin).c_str()); + lineBegin = lineEnd+1; + } + nlinfo(dest.substr (lineBegin, lineEnd-lineBegin).c_str()); +#endif // NL_DEBUG_D3D + + LPD3DXBUFFER pShader; + LPD3DXBUFFER pErrorMsgs; + if (D3DXAssembleShader (dest.c_str(), (UINT)dest.size(), NULL, NULL, 0, &pShader, &pErrorMsgs) == D3D_OK) + { + if (_DeviceInterface->CreateVertexShader((DWORD*)pShader->GetBufferPointer(), &(getVertexProgramD3D(*program)->Shader)) != D3D_OK) + return false; + } + else + { + nlwarning ("Can't assemble vertex program:"); + nlwarning ((const char*)pErrorMsgs->GetBufferPointer()); + return false; + } + + // Set parameters for assembly programs + drvInfo->ParamIndices = source->ParamIndices; + + // Build the feature info + program->buildInfo(source->DisplayName.c_str(), source->Features); + } + + return true; +} + +// *************************************************************************** + bool CDriverD3D::activeVertexProgram (CVertexProgram *program) { H_AUTO_D3D(CDriverD3D_activeVertexProgram ) if (_DisableHardwareVertexProgram) return false; - // Setup or unsetup ? - if (program) - { - // Program setuped ? - if (program->_DrvInfo==NULL) - { - // Find nelvp - CGPUProgramSource *source = NULL; - for (uint i = 0; i < program->getProgramSource()->Sources.size(); ++i) - { - if (program->getProgramSource()->Sources[i]->Profile == CVertexProgram::nelvp) - { - source = program->getProgramSource()->Sources[i]; - } - } - if (!source) - { - nlwarning("Direct3D driver only supports 'nelvp' profile, vertex program cannot be used"); - return false; - } - - _GPUPrgDrvInfos.push_front (NULL); - ItGPUPrgDrvInfoPtrList itTex = _GPUPrgDrvInfos.begin(); - CVertexProgamDrvInfosD3D *drvInfo; - *itTex = drvInfo = new CVertexProgamDrvInfosD3D(this, itTex); - - // Create a driver info structure - program->_DrvInfo = *itTex; - - /** Check with our parser if the program will works with other implemented extensions, too. (EXT_vertex_shader ..). - * There are some incompatibilities. - */ - CVPParser parser; - CVPParser::TProgram parsedProgram; - std::string errorOutput; - bool result = parser.parse(source->SourcePtr, parsedProgram, errorOutput); - if (!result) - { - nlwarning("Unable to parse a vertex program :"); - nlwarning(errorOutput.c_str()); - #ifdef NL_DEBUG_D3D - nlassert(0); - #endif // NL_DEBUG_D3D - return false; - } - - // tmp fix for Radeon 8500/9000/9200 - // Currently they hang when PaletteSkin / SkinWeight are present in the vertex declaration, but not used - // so disable them in the vertex declaration - // We don't use these component in vertex programs currently.. - #ifdef NL_DEBUG - for(uint k = 0; k < parsedProgram.size(); ++k) - { - for(uint l = 0; l < parsedProgram[k].getNumUsedSrc(); ++l) - { - const CVPOperand &op = parsedProgram[k].getSrc(l); - if (op.Type == CVPOperand::InputRegister) - { - nlassert(op.Value.InputRegisterValue != CVPOperand::IWeight); - nlassert(op.Value.InputRegisterValue != CVPOperand::IPaletteSkin); - } - } - } - #endif - - // Dump the vertex program - std::string dest; - dump(parsedProgram, dest); -#ifdef NL_DEBUG_D3D - nlinfo("Assemble Vertex Shader : "); - string::size_type lineBegin = 0; - string::size_type lineEnd; - while ((lineEnd = dest.find('\n', lineBegin)) != string::npos) - { - nlinfo(dest.substr (lineBegin, lineEnd-lineBegin).c_str()); - lineBegin = lineEnd+1; - } - nlinfo(dest.substr (lineBegin, lineEnd-lineBegin).c_str()); -#endif // NL_DEBUG_D3D - - LPD3DXBUFFER pShader; - LPD3DXBUFFER pErrorMsgs; - if (D3DXAssembleShader (dest.c_str(), (UINT)dest.size(), NULL, NULL, 0, &pShader, &pErrorMsgs) == D3D_OK) - { - if (_DeviceInterface->CreateVertexShader((DWORD*)pShader->GetBufferPointer(), &(getVertexProgramD3D(*program)->Shader)) != D3D_OK) - return false; - } - else - { - nlwarning ("Can't assemble vertex program:"); - nlwarning ((const char*)pErrorMsgs->GetBufferPointer()); - return false; - } - - // Set parameters for assembly programs - drvInfo->ParamIndices = source->ParamIndices; - - // Build the feature info - program->buildInfo(source->DisplayName.c_str(), source->Features); - } - } - // Set the vertex program if (program) { - CVertexProgamDrvInfosD3D *info = static_cast((IGPUProgramDrvInfos*)program->_DrvInfo); + if (!CDriverD3D::compileVertexProgram(program)) return false; + + CVertexProgamDrvInfosD3D *info = NLMISC::safe_cast((IGPUProgramDrvInfos*)program->_DrvInfo); setVertexProgram (info->Shader, program); /* D3DRS_FOGSTART and D3DRS_FOGEND must be set with [1, 0] else the fog doesn't work properly on VertexShader and non-VertexShader objects (random fog flicking) with Geforce4 TI 4200 (drivers 53.03 and 45.23). The other cards seam to interpret the "oFog"'s values using D3DRS_FOGSTART, D3DRS_FOGEND. + Related to setUniformFog(). */ float z = 0; float o = 1; @@ -399,171 +405,6 @@ bool CDriverD3D::activeVertexProgram (CVertexProgram *program) // *************************************************************************** -void CDriverD3D::setConstant (uint index, float f0, float f1, float f2, float f3) -{ - H_AUTO_D3D(CDriverD3D_setConstant ) - if (!_VertexProgram) - { - #ifdef NL_DEBUG - nlwarning("No vertex programs available!!"); - #endif - return; - } - const float tabl[4] = {f0, f1, f2, f3}; - setVertexProgramConstant (index, tabl); -} - -// *************************************************************************** - -void CDriverD3D::setConstant (uint index, double d0, double d1, double d2, double d3) -{ - H_AUTO_D3D(CDriverD3D_setConstant ) - if (!_VertexProgram) - { - #ifdef NL_DEBUG - nlwarning("No vertex programs available!!"); - #endif - return; - } - const float tabl[4] = {(float)d0, (float)d1, (float)d2, (float)d3}; - setVertexProgramConstant (index, tabl); -} - -// *************************************************************************** - -void CDriverD3D::setConstant (uint index, const NLMISC::CVector& value) -{ - H_AUTO_D3D(CDriverD3D_setConstant ) - if (!_VertexProgram) - { - #ifdef NL_DEBUG - nlwarning("No vertex programs available!!"); - #endif - return; - } - const float tabl[4] = {value.x, value.y, value.z, 0}; - setVertexProgramConstant (index, tabl); -} - -// *************************************************************************** - -void CDriverD3D::setConstant (uint index, const NLMISC::CVectorD& value) -{ - H_AUTO_D3D(CDriverD3D_setConstant ) - if (!_VertexProgram) - { - #ifdef NL_DEBUG - nlwarning("No vertex programs available!!"); - #endif - return; - } - const float tabl[4] = {(float)value.x, (float)value.y, (float)value.z, 0}; - setVertexProgramConstant (index, tabl); -} - -// *************************************************************************** - -void CDriverD3D::setConstant (uint index, uint num, const float *src) -{ - H_AUTO_D3D(CDriverD3D_setConstant ) - if (!_VertexProgram) - { - #ifdef NL_DEBUG - nlwarning("No vertex programs available!!"); - #endif - return; - } - uint i; - for (i=0; i_11, matPtr->_21, matPtr->_31, matPtr->_41); - setConstant (index+1, matPtr->_12, matPtr->_22, matPtr->_32, matPtr->_42); - setConstant (index+2, matPtr->_13, matPtr->_23, matPtr->_33, matPtr->_43); - setConstant (index+3, matPtr->_14, matPtr->_24, matPtr->_34, matPtr->_44); -} - -// *************************************************************************** - -void CDriverD3D::setConstantFog (uint index) -{ - H_AUTO_D3D(CDriverD3D_setConstantFog ) - /* "oFog" must always be between [1, 0] what ever you set in D3DRS_FOGSTART and D3DRS_FOGEND (1 for no fog, 0 for full fog). - The Geforce4 TI 4200 (drivers 53.03 and 45.23) doesn't accept other values for "oFog". */ - const float delta = _FogEnd-_FogStart; - setConstant (index, - _D3DModelView._13/delta, -_D3DModelView._23/delta, -_D3DModelView._33/delta, 1-(_D3DModelView._43-_FogStart)/delta); -} - -// *************************************************************************** - void CDriverD3D::enableVertexProgramDoubleSidedColor(bool /* doubleSided */) { H_AUTO_D3D(CDriverD3D_enableVertexProgramDoubleSidedColor) From 057eab46809bda6f76876e3aede86ff702b3c3de Mon Sep 17 00:00:00 2001 From: kaetemi Date: Mon, 9 Sep 2013 18:33:38 +0200 Subject: [PATCH 197/313] Cleanup abstract gpu program interface --- code/nel/include/nel/3d/driver.h | 27 +++ code/nel/include/nel/3d/geometry_program.h | 36 +--- code/nel/include/nel/3d/gpu_program.h | 156 ++++++++++++++++-- code/nel/include/nel/3d/gpu_program_source.h | 93 ----------- code/nel/include/nel/3d/pixel_program.h | 43 +---- code/nel/include/nel/3d/vertex_program.h | 67 +------- code/nel/src/3d/CMakeLists.txt | 2 - .../src/3d/driver/direct3d/driver_direct3d.h | 35 +++- .../driver_direct3d_pixel_program.cpp | 16 +- .../driver_direct3d_vertex_program.cpp | 16 +- code/nel/src/3d/driver/opengl/driver_opengl.h | 31 +++- .../opengl/driver_opengl_pixel_program.cpp | 20 +-- .../3d/driver/opengl/driver_opengl_vertex.cpp | 4 +- .../opengl/driver_opengl_vertex_program.cpp | 56 +++---- code/nel/src/3d/geometry_program.cpp | 14 +- code/nel/src/3d/gpu_program.cpp | 143 +++++++++++++++- code/nel/src/3d/gpu_program_source.cpp | 47 ------ code/nel/src/3d/pixel_program.cpp | 34 +--- code/nel/src/3d/stereo_debugger.cpp | 9 +- code/nel/src/3d/stereo_ovr.cpp | 20 ++- code/nel/src/3d/vertex_program.cpp | 113 +------------ 21 files changed, 454 insertions(+), 528 deletions(-) delete mode 100644 code/nel/include/nel/3d/gpu_program_source.h delete mode 100644 code/nel/src/3d/gpu_program_source.cpp diff --git a/code/nel/include/nel/3d/driver.h b/code/nel/include/nel/3d/driver.h index 8890a9cc7..1f7739c01 100644 --- a/code/nel/include/nel/3d/driver.h +++ b/code/nel/include/nel/3d/driver.h @@ -1147,6 +1147,33 @@ public: + /// \name Geometry Program + // @{ + + // Order of preference + // - activeGeometryProgram + // - CMaterial pass[n] PP (uses activeGeometryProgram, but does not override if one already set by code) + // - none + + /** Return true if the driver supports the specified pixel program profile. + */ + virtual bool supportGeometryProgram(CGeometryProgram::TProfile profile) const = 0; + + /** Compile the given pixel program, return if successful. + * If a pixel program was set active before compilation, + * the state of the active pixel program is undefined behaviour afterwards. + */ + virtual bool compileGeometryProgram(CGeometryProgram *program) = 0; + + /** Set the active pixel program. This will override pixel programs specified in CMaterial render calls. + * Also used internally by setupMaterial(CMaterial) when getGeometryProgram returns NULL. + * The pixel program is activated immediately. + */ + virtual bool activeGeometryProgram(CGeometryProgram *program) = 0; + // @} + + + /// \name Program parameters // @{ // Set parameters diff --git a/code/nel/include/nel/3d/geometry_program.h b/code/nel/include/nel/3d/geometry_program.h index ac9451b27..adba5f01d 100644 --- a/code/nel/include/nel/3d/geometry_program.h +++ b/code/nel/include/nel/3d/geometry_program.h @@ -27,52 +27,18 @@ #include #include #include -#include #include namespace NL3D { -/** - * \brief CGeometryProgramInfo - * \date 2013-09-07 15:00GMT - * \author Jan Boon (Kaetemi) - * Read-only information structure. - */ -struct CGeometryProgramInfo -{ -public: - std::string DisplayName; - - /*enum TFeatures - { - - };*/ - - // Bitfield containing features used by this geometry program - uint Features; - - // Indices of parameters used by features - // ... -}; - class CGeometryProgram : public IGPUProgram { public: /// Constructor - CGeometryProgram(CGPUProgramSourceCont *programSource); + CGeometryProgram(); /// Destructor virtual ~CGeometryProgram (); - - /// Build feature information - void buildInfo(const char *displayName, uint features); - /// Get feature information - inline const CGeometryProgramInfo *getInfo() const { return _Info; } - -private: - - /// Feature information - CGeometryProgramInfo *_Info; }; } // NL3D diff --git a/code/nel/include/nel/3d/gpu_program.h b/code/nel/include/nel/3d/gpu_program.h index ba0afb9e4..d1b234dee 100644 --- a/code/nel/include/nel/3d/gpu_program.h +++ b/code/nel/include/nel/3d/gpu_program.h @@ -57,11 +57,103 @@ public: // The virtual dtor is important. virtual ~IGPUProgramDrvInfos(void); - virtual uint getParamIdx(char *name) const = 0; + virtual uint getUniformIndex(char *name) const = 0; }; -class CGPUProgramSource; -class CGPUProgramSourceCont; +#define NL_GPU_PROGRAM_LIGHTS 8 + +/// Features exposed by a program. Used to set builtin parameters on user provided shaders +struct CGPUProgramFeatures +{ + CGPUProgramFeatures() : DriverFlags(0), MaterialFlags(0) /*, NumLights(0) */ { } + + // Driver builtin parameters + enum TDriverFlags + { + // Matrices + ModelView = 0x00000001, + ModelViewInverse = 0x00000002, + ModelViewTranspose = 0x00000004, + ModelViewInverseTranspose = 0x00000008, + + Projection = 0x00000010, + ProjectionInverse = 0x00000020, + ProjectionTranspose = 0x00000040, + ProjectionInverseTranspose = 0x00000080, + + ModelViewProjection = 0x00000100, + ModelViewProjectionInverse = 0x00000200, + ModelViewProjectionTranspose = 0x00000400, + ModelViewProjectionInverseTranspose = 0x00000800, + + // + // Rough example, modify as necessary. + // + + // Lighting (todo) + /// Driver ambient, must be ignored when material ambient is flagged + //DriverAmbient = 0x00001000, + /// Lights, does not set diffuses if material lights is flagged + //DriverLights = 0x00002000, + // etcetera + + // Fog (todo) + // Fog = ..., + }; + uint32 DriverFlags; + // uint NumLights; // number of lights supported by the program (not used yet, modify as necessary) + + enum TMaterialFlags + { + /// Use the CMaterial texture stages as the textures for a Pixel Program + TextureStages = 0x00000001, // <- don't remove this one, it's already used, if you want to split them up into the different stages, then it's ok to change it + + // + // Rough example, modify as necessary. + // + + // Lighting (todo) + /// Material ambient premultiplied with driver ambient + //MaterialAmbient = 0x00000002, + /// Premultiply lights diffuse with material diffuse, requires driver lights to be flagged + //MaterialLights = 0x00000004, + // etcetera + + // Add all necessary feature sets used with builtin materials here + }; + // Material builtin parameters + uint32 MaterialFlags; +}; + +/// Stucture used to cache the indices of builtin parameters +struct CGPUProgramIndices +{ + uint ModelView; + uint ModelViewInverse; + uint ModelViewTranspose; + uint ModelViewInverseTranspose; + + uint Projection; + uint ProjectionInverse; + uint ProjectionTranspose; + uint ProjectionInverseTranspose; + + uint ModelViewProjection; + uint ModelViewProjectionInverse; + uint ModelViewProjectionTranspose; + uint ModelViewProjectionInverseTranspose; + + // + // Rough example, modify as necessary. + // + //uint Ambient; + + //uint LightType[NL_GPU_PROGRAM_LIGHTS]; + //uint LightAmbient[NL_GPU_PROGRAM_LIGHTS]; + //uint LightDiffuse[NL_GPU_PROGRAM_LIGHTS]; + //uint LightPosition[NL_GPU_PROGRAM_LIGHTS]; + //uint LightDirection[NL_GPU_PROGRAM_LIGHTS]; +}; /** * \brief IGPUProgram @@ -74,6 +166,8 @@ class IGPUProgram : public NLMISC::CRefCount public: enum TProfile { + none = 0, + // types // Vertex Shader = 0x01 // Pixel Shader = 0x02 @@ -123,24 +217,66 @@ public: glsl330g = 0x65030330, // GLSL geometry program version 330 }; + struct CSource : public NLMISC::CRefCount + { + public: + std::string DisplayName; + + /// Minimal required profile for this GPU program + IGPUProgram::TProfile Profile; + + const char *SourcePtr; + size_t SourceLen; + /// Copy the source code string + inline void setSource(const char *source) { SourceCopy = source; SourcePtr = &SourceCopy[0]; SourceLen = SourceCopy.size(); } + /// Set pointer to source code string without copying the string + inline void setSourcePtr(const char *sourcePtr, size_t sourceLen) { SourceCopy.clear(); SourcePtr = sourcePtr; SourceLen = sourceLen; } + inline void setSourcePtr(const char *sourcePtr) { SourceCopy.clear(); SourcePtr = sourcePtr; SourceLen = strlen(sourcePtr); } + + /// CVertexProgramInfo/CPixelProgramInfo/... NeL features + CGPUProgramFeatures Features; + + /// Map with known parameter indices, used for assembly programs + std::map ParamIndices; + + private: + std::string SourceCopy; + }; + public: IGPUProgram(); - IGPUProgram(CGPUProgramSourceCont *programSource); virtual ~IGPUProgram(); - /// Get the idx of a parameter (ogl: uniform, d3d: constant, etcetera) by name. Invalid name returns ~0 - inline uint getParamIdx(char *name) const { return _DrvInfo->getParamIdx(name); }; + // Manage the sources, not allowed after compilation. + // Add multiple sources using different profiles, the driver will use the first one it supports. + inline size_t getSourceNb() const { return m_Sources.size(); }; + inline CSource *getSource(size_t i) const { return m_Sources[i]; }; + inline size_t addSource(CSource *source) { nlassert(!m_Source); m_Sources.push_back(source); return (m_Sources.size() - 1); } + inline void removeSource(size_t i) { nlassert(!m_Source); m_Sources.erase(m_Sources.begin() + i); } - /// Get the program - inline const CGPUProgramSourceCont *getProgramSource() const { return _ProgramSource; }; + // Get the idx of a parameter (ogl: uniform, d3d: constant, etcetera) by name. Invalid name returns ~0 + inline uint getUniformIndex(char *name) const { return m_DrvInfo->getUniformIndex(name); }; + + // Get feature information of the current program + inline CSource *source() const { return m_Source; }; + inline const CGPUProgramFeatures &features() const { return m_Source->Features; }; + inline const CGPUProgramIndices &indices() const { return m_Indices; }; + inline TProfile profile() const { return m_Source->Profile; } + + // Build feature info, called automatically by the driver after compile succeeds + void buildInfo(CSource *source); protected: /// The progam source - NLMISC::CSmartPtr _ProgramSource; + std::vector > m_Sources; + + /// The source used for compilation + NLMISC::CSmartPtr m_Source; + CGPUProgramIndices m_Indices; public: /// The driver information. For the driver implementation only. - NLMISC::CRefPtr _DrvInfo; + NLMISC::CRefPtr m_DrvInfo; }; /* class IGPUProgram */ diff --git a/code/nel/include/nel/3d/gpu_program_source.h b/code/nel/include/nel/3d/gpu_program_source.h deleted file mode 100644 index c51ac67ce..000000000 --- a/code/nel/include/nel/3d/gpu_program_source.h +++ /dev/null @@ -1,93 +0,0 @@ -/** - * \file gpu_program_source.h - * \brief CGPUProgramSource - * \date 2013-09-07 14:54GMT - * \author Jan Boon (Kaetemi) - * CGPUProgramSource - */ - -/* - * Copyright (C) 2013 by authors - * - * This file is part of NL3D. - * NL3D is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * NL3D 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 Affero General - * Public License for more details. - * - * You should have received a copy of the GNU Affero General Public - * License along with NL3D. If not, see - * . - */ - -#ifndef NL3D_GPU_PROGRAM_SOURCE_H -#define NL3D_GPU_PROGRAM_SOURCE_H -#include - -// STL includes - -// NeL includes -#include - -// Project includes -#include - -namespace NL3D { - -/** - * \brief CGPUProgramSource - * \date 2013-09-07 14:54GMT - * \author Jan Boon (Kaetemi) - * A single GPU program with a specific profile. - */ -struct CGPUProgramSource : public NLMISC::CRefCount -{ -public: - std::string DisplayName; - - /// Minimal required profile for this GPU program - IGPUProgram::TProfile Profile; - - const char *SourcePtr; - size_t SourceLen; - /// Copy the source code string - inline void setSource(const char *source) { SourceCopy = source; SourcePtr = &SourceCopy[0]; SourceLen = SourceCopy.size(); } - /// Set pointer to source code string without copying the string - inline void setSourcePtr(const char *sourcePtr, size_t sourceLen) { SourceCopy.clear(); SourcePtr = sourcePtr; SourceLen = sourceLen; } - inline void setSourcePtr(const char *sourcePtr) { SourceCopy.clear(); SourcePtr = sourcePtr; SourceLen = strlen(sourcePtr); } - - /// CVertexProgramInfo/CPixelProgramInfo/... NeL features - uint Features; - - /// Map with known parameter indices, used for assembly programs - std::map ParamIndices; - -private: - std::string SourceCopy; - -}; /* class CGPUProgramSource */ - -/** - * \brief CGPUProgramSourceCont - * \date 2013-09-07 14:54GMT - * \author Jan Boon (Kaetemi) - * Container for the source code of a single GPU program, allowing - * variations in different language profiles. - */ -struct CGPUProgramSourceCont : public NLMISC::CRefCount -{ -public: - std::vector > Sources; - -}; /* class CGPUProgramSourceCont */ - -} /* namespace NL3D */ - -#endif /* #ifndef NL3D_GPU_PROGRAM_SOURCE_H */ - -/* end of file */ diff --git a/code/nel/include/nel/3d/pixel_program.h b/code/nel/include/nel/3d/pixel_program.h index e006844aa..1bfdb84d0 100644 --- a/code/nel/include/nel/3d/pixel_program.h +++ b/code/nel/include/nel/3d/pixel_program.h @@ -27,59 +27,18 @@ #include #include #include -#include #include namespace NL3D { -/** - * \brief CPixelProgramInfo - * \date 2013-09-07 15:00GMT - * \author Jan Boon (Kaetemi) - * Read-only information structure. - */ -struct CPixelProgramInfo -{ -public: - std::string DisplayName; - - enum TFeatures - { - /// Use texture stages from CMaterial as texture parameters - MaterialTextures = 0x0001, - /// Set driver fog parameters on this program - Fog = 0x0002, - /// Adds an enabled/disabled parameter to the fog, for user shaders - DynamicFog = 0x0004, - }; - - // Bitfield containing features used by this pixel program - uint Features; - - // Indices of parameters used by features - uint FogEnabledIdx; // (Fog && DynamicFog) nlFogEnabled, fog enabled - uint FogStartEndIdx; // (Fog) nlFogStartEnd, start and end of fog - uint FogColorIdx; // (Fog) nlFogColor, fog color -}; - class CPixelProgram : public IGPUProgram { public: /// Constructor - CPixelProgram(CGPUProgramSourceCont *programSource); + CPixelProgram(); /// Destructor virtual ~CPixelProgram (); - - /// Build feature information - void buildInfo(const char *displayName, uint features); - /// Get feature information - inline const CPixelProgramInfo *getInfo() const { return _Info; } - -private: - - /// Feature information - CPixelProgramInfo *_Info; }; } // NL3D diff --git a/code/nel/include/nel/3d/vertex_program.h b/code/nel/include/nel/3d/vertex_program.h index b8843e182..2e20db584 100644 --- a/code/nel/include/nel/3d/vertex_program.h +++ b/code/nel/include/nel/3d/vertex_program.h @@ -20,84 +20,21 @@ #include "nel/misc/types_nl.h" #include "nel/misc/smart_ptr.h" #include "nel/3d/gpu_program.h" -#include "nel/3d/gpu_program_source.h" #include namespace NL3D { -/** - * \brief CVertexProgramInfo - * \date 2013-09-07 15:00GMT - * \author Jan Boon (Kaetemi) - * Read-only information structure. - */ -struct CVertexProgramInfo -{ -public: - std::string DisplayName; - - enum TFeatures - { - // World - // transform - - // Lights - Ambient = 0x0001, - Sun = 0x0002, - PointLight0 = 0x0004, - PointLight1 = 0x0008, - PointLight2 = 0x0010, - - // Lights, additional parameters for user shaders - /// Adds an enabled/disabled parameter to all of the lights - DynamicLights = 0x0020, - }; - - /// Bitfield containing features used by this vertex program. - uint Features; - - /// Indices of parameters used by features. - - /// Lights, NeL supports: - /// - Ambient light - uint AmbientIdx; // (Ambient) - /// - One directional light - uint SunDirectionIdx; // (Sun) - uint SunDiffuseIdx; // (Sun) - /// - Zero to three point lights - uint PointLight0PositionIdx; // (PointLight0) - uint PointLight0DiffuseIdx; // (PointLight0) - uint PointLight1PositionIdx; // (PointLight1) - uint PointLight1DiffuseIdx; // (PointLight1) - uint PointLight2PositionIdx; // (PointLight2) - uint PointLight2DiffuseIdx; // (PointLight2) - - /// DynamicLights - uint SunEnabledIdx; // (DynamicLights && Sun) - uint PointLight0EnabledIdx; // (DynamicLights && PointLight0) - uint PointLight1EnabledIdx; // (DynamicLights && PointLight1) - uint PointLight2EnabledIdx; // (DynamicLights && PointLight2) -}; - class CVertexProgram : public IGPUProgram { public: /// Constructor - CVertexProgram(CGPUProgramSourceCont *programSource); + CVertexProgram(); CVertexProgram(const char *nelvp); + /// Destructor virtual ~CVertexProgram (); - /// Build feature information - void buildInfo(const char *displayName, uint features); - /// Get feature information - inline const CVertexProgramInfo *getInfo() const { return _Info; } - -private: - - /// Feature information - CVertexProgramInfo *_Info; }; } // NL3D diff --git a/code/nel/src/3d/CMakeLists.txt b/code/nel/src/3d/CMakeLists.txt index 5f5896ff9..a6ecf1ce8 100644 --- a/code/nel/src/3d/CMakeLists.txt +++ b/code/nel/src/3d/CMakeLists.txt @@ -169,8 +169,6 @@ SOURCE_GROUP(Driver FILES ../../include/nel/3d/geometry_program.h gpu_program.cpp ../../include/nel/3d/gpu_program.h - gpu_program_source.cpp - ../../include/nel/3d/gpu_program_source.h gpu_program_params.cpp ../../include/nel/3d/gpu_program_params.h) diff --git a/code/nel/src/3d/driver/direct3d/driver_direct3d.h b/code/nel/src/3d/driver/direct3d/driver_direct3d.h index 79dcc917e..a17ae32c3 100644 --- a/code/nel/src/3d/driver/direct3d/driver_direct3d.h +++ b/code/nel/src/3d/driver/direct3d/driver_direct3d.h @@ -309,7 +309,7 @@ public: CVertexProgamDrvInfosD3D(IDriver *drv, ItGPUPrgDrvInfoPtrList it); ~CVertexProgamDrvInfosD3D(); - virtual uint getParamIdx(char *name) const + virtual uint getUniformIndex(char *name) const { std::map::const_iterator it = ParamIndices.find(name); if (it != ParamIndices.end()) return it->second; @@ -331,7 +331,7 @@ public: CPixelProgramDrvInfosD3D(IDriver *drv, ItGPUPrgDrvInfoPtrList it); ~CPixelProgramDrvInfosD3D(); - virtual uint getParamIdx(char *name) const + virtual uint getUniformIndex(char *name) const { std::map::const_iterator it = ParamIndices.find(name); if (it != ParamIndices.end()) return it->second; @@ -1156,6 +1156,33 @@ public: + /// \name Geometry Program + // @{ + + // Order of preference + // - activeGeometryProgram + // - CMaterial pass[n] PP (uses activeGeometryProgram, but does not override if one already set by code) + // - none + + /** Return true if the driver supports the specified pixel program profile. + */ + virtual bool supportGeometryProgram(CGeometryProgram::TProfile profile) const { return false; } + + /** Compile the given pixel program, return if successful. + * If a pixel program was set active before compilation, + * the state of the active pixel program is undefined behaviour afterwards. + */ + virtual bool compileGeometryProgram(CGeometryProgram *program) { return false; } + + /** Set the active pixel program. This will override pixel programs specified in CMaterial render calls. + * Also used internally by setupMaterial(CMaterial) when getGeometryProgram returns NULL. + * The pixel program is activated immediately. + */ + virtual bool activeGeometryProgram(CGeometryProgram *program) { return false; } + // @} + + + /// \name Program parameters // @{ // Set parameters @@ -2078,7 +2105,7 @@ public: { H_AUTO_D3D(CDriverD3D_getPixelProgramD3D); CPixelProgramDrvInfosD3D* d3dPixelProgram; - d3dPixelProgram = (CPixelProgramDrvInfosD3D*)(IGPUProgramDrvInfos*)(pixelProgram._DrvInfo); + d3dPixelProgram = (CPixelProgramDrvInfosD3D*)(IGPUProgramDrvInfos*)(pixelProgram.m_DrvInfo); return d3dPixelProgram; } @@ -2087,7 +2114,7 @@ public: { H_AUTO_D3D(CDriverD3D_getVertexProgramD3D); CVertexProgamDrvInfosD3D* d3dVertexProgram; - d3dVertexProgram = (CVertexProgamDrvInfosD3D*)(IGPUProgramDrvInfos*)(vertexProgram._DrvInfo); + d3dVertexProgram = (CVertexProgamDrvInfosD3D*)(IGPUProgramDrvInfos*)(vertexProgram.m_DrvInfo); return d3dVertexProgram; } diff --git a/code/nel/src/3d/driver/direct3d/driver_direct3d_pixel_program.cpp b/code/nel/src/3d/driver/direct3d/driver_direct3d_pixel_program.cpp index 612155c38..0d97925b3 100644 --- a/code/nel/src/3d/driver/direct3d/driver_direct3d_pixel_program.cpp +++ b/code/nel/src/3d/driver/direct3d/driver_direct3d_pixel_program.cpp @@ -66,15 +66,15 @@ bool CDriverD3D::supportPixelProgram (CPixelProgram::TProfile profile) const bool CDriverD3D::compilePixelProgram(CPixelProgram *program) { // Program setuped ? - if (program->_DrvInfo==NULL) + if (program->m_DrvInfo==NULL) { // Find a supported pixel program profile - CGPUProgramSource *source = NULL; - for (uint i = 0; i < program->getProgramSource()->Sources.size(); ++i) + IGPUProgram::CSource *source = NULL; + for (uint i = 0; i < program->getSourceNb(); ++i) { - if (supportPixelProgram(program->getProgramSource()->Sources[i]->Profile)) + if (supportPixelProgram(program->getSource(i)->Profile)) { - source = program->getProgramSource()->Sources[i]; + source = program->getSource(i); } } if (!source) @@ -89,7 +89,7 @@ bool CDriverD3D::compilePixelProgram(CPixelProgram *program) *itPix = drvInfo = new CPixelProgramDrvInfosD3D(this, itPix); // Create a driver info structure - program->_DrvInfo = *itPix; + program->m_DrvInfo = *itPix; LPD3DXBUFFER pShader; LPD3DXBUFFER pErrorMsgs; @@ -109,7 +109,7 @@ bool CDriverD3D::compilePixelProgram(CPixelProgram *program) drvInfo->ParamIndices = source->ParamIndices; // Build the feature info - program->buildInfo(source->DisplayName.c_str(), source->Features); + program->buildInfo(source); } return true; @@ -128,7 +128,7 @@ bool CDriverD3D::activePixelProgram(CPixelProgram *program) { if (!CDriverD3D::compilePixelProgram(program)) return false; - CPixelProgramDrvInfosD3D *info = static_cast((IGPUProgramDrvInfos*)program->_DrvInfo); + CPixelProgramDrvInfosD3D *info = static_cast((IGPUProgramDrvInfos*)program->m_DrvInfo); setPixelShader(info->Shader); } else diff --git a/code/nel/src/3d/driver/direct3d/driver_direct3d_vertex_program.cpp b/code/nel/src/3d/driver/direct3d/driver_direct3d_vertex_program.cpp index 46e0f1c14..a409f20cf 100644 --- a/code/nel/src/3d/driver/direct3d/driver_direct3d_vertex_program.cpp +++ b/code/nel/src/3d/driver/direct3d/driver_direct3d_vertex_program.cpp @@ -265,15 +265,15 @@ void dump(const CVPParser::TProgram &prg, std::string &dest) bool CDriverD3D::compileVertexProgram(NL3D::CVertexProgram *program) { // Program setuped ? - if (program->_DrvInfo == NULL) + if (program->m_DrvInfo == NULL) { // Find nelvp - CGPUProgramSource *source = NULL; - for (uint i = 0; i < program->getProgramSource()->Sources.size(); ++i) + IGPUProgram::CSource *source = NULL; + for (uint i = 0; i < program->getSourceNb(); ++i) { - if (program->getProgramSource()->Sources[i]->Profile == CVertexProgram::nelvp) + if (program->getSource(i)->Profile == CVertexProgram::nelvp) { - source = program->getProgramSource()->Sources[i]; + source = program->getSource(i); } } if (!source) @@ -288,7 +288,7 @@ bool CDriverD3D::compileVertexProgram(NL3D::CVertexProgram *program) *itTex = drvInfo = new CVertexProgamDrvInfosD3D(this, itTex); // Create a driver info structure - program->_DrvInfo = *itTex; + program->m_DrvInfo = *itTex; /** Check with our parser if the program will works with other implemented extensions, too. (EXT_vertex_shader ..). * There are some incompatibilities. @@ -359,7 +359,7 @@ bool CDriverD3D::compileVertexProgram(NL3D::CVertexProgram *program) drvInfo->ParamIndices = source->ParamIndices; // Build the feature info - program->buildInfo(source->DisplayName.c_str(), source->Features); + program->buildInfo(source); } return true; @@ -378,7 +378,7 @@ bool CDriverD3D::activeVertexProgram (CVertexProgram *program) { if (!CDriverD3D::compileVertexProgram(program)) return false; - CVertexProgamDrvInfosD3D *info = NLMISC::safe_cast((IGPUProgramDrvInfos*)program->_DrvInfo); + CVertexProgamDrvInfosD3D *info = NLMISC::safe_cast((IGPUProgramDrvInfos*)program->m_DrvInfo); setVertexProgram (info->Shader, program); /* D3DRS_FOGSTART and D3DRS_FOGEND must be set with [1, 0] else the fog doesn't work properly on VertexShader and non-VertexShader objects diff --git a/code/nel/src/3d/driver/opengl/driver_opengl.h b/code/nel/src/3d/driver/opengl/driver_opengl.h index f70a0627f..e76563fb3 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl.h +++ b/code/nel/src/3d/driver/opengl/driver_opengl.h @@ -1362,6 +1362,33 @@ private: + /// \name Geometry Program + // @{ + + // Order of preference + // - activeGeometryProgram + // - CMaterial pass[n] PP (uses activeGeometryProgram, but does not override if one already set by code) + // - none + + /** Return true if the driver supports the specified pixel program profile. + */ + virtual bool supportGeometryProgram(CGeometryProgram::TProfile profile) const { return false; } + + /** Compile the given pixel program, return if successful. + * If a pixel program was set active before compilation, + * the state of the active pixel program is undefined behaviour afterwards. + */ + virtual bool compileGeometryProgram(CGeometryProgram *program) { return false; } + + /** Set the active pixel program. This will override pixel programs specified in CMaterial render calls. + * Also used internally by setupMaterial(CMaterial) when getGeometryProgram returns NULL. + * The pixel program is activated immediately. + */ + virtual bool activeGeometryProgram(CGeometryProgram *program) { return false; } + // @} + + + /// \name Program parameters // @{ // Set parameters @@ -1618,7 +1645,7 @@ public: // The gl id is auto created here. CVertexProgamDrvInfosGL (CDriverGL *drv, ItGPUPrgDrvInfoPtrList it); - virtual uint getParamIdx(char *name) const + virtual uint getUniformIndex(char *name) const { std::map::const_iterator it = ParamIndices.find(name); if (it != ParamIndices.end()) return it->second; @@ -1638,7 +1665,7 @@ public: // The gl id is auto created here. CPixelProgamDrvInfosGL (CDriverGL *drv, ItGPUPrgDrvInfoPtrList it); - virtual uint getParamIdx(char *name) const + virtual uint getUniformIndex(char *name) const { std::map::const_iterator it = ParamIndices.find(name); if (it != ParamIndices.end()) return it->second; diff --git a/code/nel/src/3d/driver/opengl/driver_opengl_pixel_program.cpp b/code/nel/src/3d/driver/opengl/driver_opengl_pixel_program.cpp index 31e6ed482..f787530e7 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl_pixel_program.cpp +++ b/code/nel/src/3d/driver/opengl/driver_opengl_pixel_program.cpp @@ -97,7 +97,7 @@ bool CDriverGL::activePixelProgram(CPixelProgram *program) bool CDriverGL::compilePixelProgram(NL3D::CPixelProgram *program) { // Program setuped ? - if (program->_DrvInfo == NULL) + if (program->m_DrvInfo == NULL) { glDisable(GL_FRAGMENT_PROGRAM_ARB); _PixelProgramEnabled = false; @@ -109,12 +109,12 @@ bool CDriverGL::compilePixelProgram(NL3D::CPixelProgram *program) CPixelProgamDrvInfosGL *drvInfo; *it = drvInfo = new CPixelProgamDrvInfosGL(this, it); // Set the pointer - program->_DrvInfo = drvInfo; + program->m_DrvInfo = drvInfo; if (!setupPixelProgram(program, drvInfo->ID)) { delete drvInfo; - program->_DrvInfo = NULL; + program->m_DrvInfo = NULL; _GPUPrgDrvInfos.erase(it); return false; } @@ -136,7 +136,7 @@ bool CDriverGL::activeARBPixelProgram(CPixelProgram *program) if (!CDriverGL::compilePixelProgram(program)) return false; // Cast the driver info pointer - CPixelProgamDrvInfosGL *drvInfo = safe_cast((IGPUProgramDrvInfos*)program->_DrvInfo); + CPixelProgamDrvInfosGL *drvInfo = safe_cast((IGPUProgramDrvInfos*)program->m_DrvInfo); glEnable(GL_FRAGMENT_PROGRAM_ARB); _PixelProgramEnabled = true; @@ -159,15 +159,15 @@ bool CDriverGL::setupPixelProgram(CPixelProgram *program, GLuint id/*, bool &spe { H_AUTO_OGL(CDriverGL_setupARBPixelProgram) - CPixelProgamDrvInfosGL *drvInfo = static_cast((IGPUProgramDrvInfos *)program->_DrvInfo); + CPixelProgamDrvInfosGL *drvInfo = static_cast((IGPUProgramDrvInfos *)program->m_DrvInfo); // Find a supported pixel program profile - CGPUProgramSource *source = NULL; - for (uint i = 0; i < program->getProgramSource()->Sources.size(); ++i) + IGPUProgram::CSource *source = NULL; + for (uint i = 0; i < program->getSourceNb(); ++i) { - if (supportPixelProgram(program->getProgramSource()->Sources[i]->Profile)) + if (supportPixelProgram(program->getSource(i)->Profile)) { - source = program->getProgramSource()->Sources[i]; + source = program->getSource(i); } } if (!source) @@ -224,7 +224,7 @@ bool CDriverGL::setupPixelProgram(CPixelProgram *program, GLuint id/*, bool &spe drvInfo->ParamIndices = source->ParamIndices; // Build the feature info - program->buildInfo(source->DisplayName.c_str(), source->Features); + program->buildInfo(source); return true; } diff --git a/code/nel/src/3d/driver/opengl/driver_opengl_vertex.cpp b/code/nel/src/3d/driver/opengl/driver_opengl_vertex.cpp index 521a13baf..dd1351955 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl_vertex.cpp +++ b/code/nel/src/3d/driver/opengl/driver_opengl_vertex.cpp @@ -1151,7 +1151,7 @@ void CDriverGL::toggleGlArraysForEXTVertexShader() CVertexProgram *vp = _LastSetuppedVP; if (vp) { - CVertexProgamDrvInfosGL *drvInfo = NLMISC::safe_cast((IGPUProgramDrvInfos *) vp->_DrvInfo); + CVertexProgamDrvInfosGL *drvInfo = NLMISC::safe_cast((IGPUProgramDrvInfos *) vp->m_DrvInfo); if (drvInfo) { // Disable all VertexAttribs. @@ -1396,7 +1396,7 @@ void CDriverGL::setupGlArraysForEXTVertexShader(CVertexBufferInfo &vb) CVertexProgram *vp = _LastSetuppedVP; if (!vp) return; - CVertexProgamDrvInfosGL *drvInfo = NLMISC::safe_cast((IGPUProgramDrvInfos *) vp->_DrvInfo); + CVertexProgamDrvInfosGL *drvInfo = NLMISC::safe_cast((IGPUProgramDrvInfos *) vp->m_DrvInfo); if (!drvInfo) return; uint32 flags= vb.VertexFormat; diff --git a/code/nel/src/3d/driver/opengl/driver_opengl_vertex_program.cpp b/code/nel/src/3d/driver/opengl/driver_opengl_vertex_program.cpp index e0b07af2a..a5e00d3f1 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl_vertex_program.cpp +++ b/code/nel/src/3d/driver/opengl/driver_opengl_vertex_program.cpp @@ -93,17 +93,17 @@ bool CDriverGL::compileNVVertexProgram(CVertexProgram *program) // Driver info CVertexProgamDrvInfosGL *drvInfo; - nlassert(!program->_DrvInfo); + nlassert(!program->m_DrvInfo); glDisable(GL_VERTEX_PROGRAM_NV); _VertexProgramEnabled = false; // Find nelvp - CGPUProgramSource *source = NULL; - for (uint i = 0; i < program->getProgramSource()->Sources.size(); ++i) + IGPUProgram::CSource *source = NULL; + for (uint i = 0; i < program->getSourceNb(); ++i) { - if (program->getProgramSource()->Sources[i]->Profile == CVertexProgram::nelvp) + if (program->getSource(i)->Profile == CVertexProgram::nelvp) { - source = program->getProgramSource()->Sources[i]; + source = program->getSource(i); } } if (!source) @@ -136,7 +136,7 @@ bool CDriverGL::compileNVVertexProgram(CVertexProgram *program) *it = drvInfo = new CVertexProgamDrvInfosGL(this, it); // Set the pointer - program->_DrvInfo = drvInfo; + program->m_DrvInfo = drvInfo; // Compile the program nglLoadProgramNV(GL_VERTEX_PROGRAM_NV, drvInfo->ID, (GLsizei)source->SourceLen, (const GLubyte*)source->SourcePtr); @@ -177,7 +177,7 @@ bool CDriverGL::compileNVVertexProgram(CVertexProgram *program) // Setup not ok delete drvInfo; - program->_DrvInfo = NULL; + program->m_DrvInfo = NULL; _GPUPrgDrvInfos.erase(it); return false; } @@ -186,7 +186,7 @@ bool CDriverGL::compileNVVertexProgram(CVertexProgram *program) drvInfo->ParamIndices = source->ParamIndices; // Build the feature info - program->buildInfo(source->DisplayName.c_str(), source->Features); + program->buildInfo(source); // Setup ok return true; @@ -208,7 +208,7 @@ bool CDriverGL::activeNVVertexProgram(CVertexProgram *program) if (program) { // Driver info - CVertexProgamDrvInfosGL *drvInfo = safe_cast((IGPUProgramDrvInfos*)program->_DrvInfo); + CVertexProgamDrvInfosGL *drvInfo = safe_cast((IGPUProgramDrvInfos*)program->m_DrvInfo); nlassert(drvInfo); // Enable vertex program @@ -1524,17 +1524,17 @@ bool CDriverGL::compileARBVertexProgram(NL3D::CVertexProgram *program) #ifndef USE_OPENGLES - nlassert(!program->_DrvInfo); + nlassert(!program->m_DrvInfo); glDisable(GL_VERTEX_PROGRAM_ARB); _VertexProgramEnabled = false; // Find nelvp - CGPUProgramSource *source = NULL; - for (uint i = 0; i < program->getProgramSource()->Sources.size(); ++i) + IGPUProgram::CSource *source = NULL; + for (uint i = 0; i < program->getSourceNb(); ++i) { - if (program->getProgramSource()->Sources[i]->Profile == CVertexProgram::nelvp) + if (program->getSource(i)->Profile == CVertexProgram::nelvp) { - source = program->getProgramSource()->Sources[i]; + source = program->getSource(i); } } if (!source) @@ -1563,12 +1563,12 @@ bool CDriverGL::compileARBVertexProgram(NL3D::CVertexProgram *program) CVertexProgamDrvInfosGL *drvInfo; *it = drvInfo = new CVertexProgamDrvInfosGL(this, it); // Set the pointer - program->_DrvInfo=drvInfo; + program->m_DrvInfo = drvInfo; if (!setupARBVertexProgram(parsedProgram, drvInfo->ID, drvInfo->SpecularWritten)) { delete drvInfo; - program->_DrvInfo = NULL; + program->m_DrvInfo = NULL; _GPUPrgDrvInfos.erase(it); return false; } @@ -1577,7 +1577,7 @@ bool CDriverGL::compileARBVertexProgram(NL3D::CVertexProgram *program) drvInfo->ParamIndices = source->ParamIndices; // Build the feature info - program->buildInfo(source->DisplayName.c_str(), source->Features); + program->buildInfo(source); return true; @@ -1600,7 +1600,7 @@ bool CDriverGL::activeARBVertexProgram(CVertexProgram *program) if (program) { // Driver info - CVertexProgamDrvInfosGL *drvInfo = safe_cast((IGPUProgramDrvInfos*)program->_DrvInfo); + CVertexProgamDrvInfosGL *drvInfo = safe_cast((IGPUProgramDrvInfos*)program->m_DrvInfo); nlassert(drvInfo); glEnable( GL_VERTEX_PROGRAM_ARB ); @@ -1639,17 +1639,17 @@ bool CDriverGL::compileEXTVertexShader(CVertexProgram *program) #ifndef USE_OPENGLES - nlassert(program->_DrvInfo); + nlassert(program->m_DrvInfo); glDisable(GL_VERTEX_SHADER_EXT); _VertexProgramEnabled = false; // Find nelvp - CGPUProgramSource *source = NULL; - for (uint i = 0; i < program->getProgramSource()->Sources.size(); ++i) + IGPUProgram::CSource *source = NULL; + for (uint i = 0; i < program->getSourceNb(); ++i) { - if (program->getProgramSource()->Sources[i]->Profile == CVertexProgram::nelvp) + if (program->getSource(i)->Profile == CVertexProgram::nelvp) { - source = program->getProgramSource()->Sources[i]; + source = program->getSource(i); } } if (!source) @@ -1690,12 +1690,12 @@ bool CDriverGL::compileEXTVertexShader(CVertexProgram *program) CVertexProgamDrvInfosGL *drvInfo; *it = drvInfo = new CVertexProgamDrvInfosGL (this, it); // Set the pointer - program->_DrvInfo=drvInfo; + program->m_DrvInfo=drvInfo; if (!setupEXTVertexShader(parsedProgram, drvInfo->ID, drvInfo->Variants, drvInfo->UsedVertexComponents)) { delete drvInfo; - program->_DrvInfo = NULL; + program->m_DrvInfo = NULL; _GPUPrgDrvInfos.erase(it); return false; } @@ -1704,7 +1704,7 @@ bool CDriverGL::compileEXTVertexShader(CVertexProgram *program) drvInfo->ParamIndices = source->ParamIndices; // Build the feature info - program->buildInfo(source->DisplayName.c_str(), source->Features); + program->buildInfo(source); return true; @@ -1727,7 +1727,7 @@ bool CDriverGL::activeEXTVertexShader(CVertexProgram *program) if (program) { // Driver info - CVertexProgamDrvInfosGL *drvInfo = safe_cast((IGPUProgramDrvInfos*)program->_DrvInfo); + CVertexProgamDrvInfosGL *drvInfo = safe_cast((IGPUProgramDrvInfos*)program->m_DrvInfo); nlassert(drvInfo); glEnable(GL_VERTEX_SHADER_EXT); @@ -1751,7 +1751,7 @@ bool CDriverGL::activeEXTVertexShader(CVertexProgram *program) bool CDriverGL::compileVertexProgram(NL3D::CVertexProgram *program) { - if (program->_DrvInfo == NULL) + if (program->m_DrvInfo == NULL) { // Extension if (_Extensions.NVVertexProgram) diff --git a/code/nel/src/3d/geometry_program.cpp b/code/nel/src/3d/geometry_program.cpp index ae73d669f..26fb15ae9 100644 --- a/code/nel/src/3d/geometry_program.cpp +++ b/code/nel/src/3d/geometry_program.cpp @@ -32,7 +32,7 @@ namespace NL3D // *************************************************************************** -CGeometryProgram::CGeometryProgram(CGPUProgramSourceCont *programSource) : _Info(NULL), IGPUProgram(programSource) +CGeometryProgram::CGeometryProgram() { } @@ -41,19 +41,9 @@ CGeometryProgram::CGeometryProgram(CGPUProgramSourceCont *programSource) : _Info CGeometryProgram::~CGeometryProgram () { - delete _Info; - _Info = NULL; + } // *************************************************************************** -void CGeometryProgram::buildInfo(const char *displayName, uint features) -{ - nlassert(_Info == NULL); - _Info = new CGeometryProgramInfo(); - CGeometryProgramInfo *info = _Info; - info->DisplayName = displayName; - info->Features = features; -} - } // NL3D diff --git a/code/nel/src/3d/gpu_program.cpp b/code/nel/src/3d/gpu_program.cpp index fc75e44e4..55f07e32b 100644 --- a/code/nel/src/3d/gpu_program.cpp +++ b/code/nel/src/3d/gpu_program.cpp @@ -66,17 +66,144 @@ IGPUProgram::IGPUProgram() // *************************************************************************** -IGPUProgram::IGPUProgram(CGPUProgramSourceCont *programSource) : _ProgramSource(programSource) -{ - -} - -// *************************************************************************** - IGPUProgram::~IGPUProgram() { // Must kill the drv mirror of this program. - _DrvInfo.kill(); + m_DrvInfo.kill(); +} + +void IGPUProgram::buildInfo(CSource *source) +{ + nlassert(!m_Source); + + m_Source = source; + + // Fill index cache + CGPUProgramFeatures &features = m_Source->Features; + TProfile profile = m_Source->Profile; // for special cases + + if (features.DriverFlags & CGPUProgramFeatures::ModelView) + { + m_Indices.ModelView = getUniformIndex("nlModelView"); + if (m_Indices.ModelView == ~0) + { + nlwarning("Missing 'nlModelView' in gpu program '%s', ModelView disabled", source->DisplayName.c_str()); + features.DriverFlags &= ~CGPUProgramFeatures::ModelView; + } + } + if (features.DriverFlags & CGPUProgramFeatures::ModelViewInverse) + { + m_Indices.ModelViewInverse = getUniformIndex("nlModelViewInverse"); + if (m_Indices.ModelViewInverse == ~0) + { + nlwarning("Missing 'nlModelViewInverse' in gpu program '%s', ModelViewInverse disabled", source->DisplayName.c_str()); + features.DriverFlags &= ~CGPUProgramFeatures::ModelViewInverse; + } + } + if (features.DriverFlags & CGPUProgramFeatures::ModelViewTranspose) + { + m_Indices.ModelViewTranspose = getUniformIndex("nlModelViewTranspose"); + if (m_Indices.ModelViewTranspose == ~0) + { + nlwarning("Missing 'nlModelViewTranspose' in gpu program '%s', ModelViewTranspose disabled", source->DisplayName.c_str()); + features.DriverFlags &= ~CGPUProgramFeatures::ModelViewTranspose; + } + } + if (features.DriverFlags & CGPUProgramFeatures::ModelViewInverseTranspose) + { + m_Indices.ModelViewInverseTranspose = getUniformIndex("nlModelViewInverseTranspose"); + if (m_Indices.ModelViewInverseTranspose == ~0) + { + nlwarning("Missing 'nlModelViewInverseTranspose' in gpu program '%s', ModelViewInverseTranspose disabled", source->DisplayName.c_str()); + features.DriverFlags &= ~CGPUProgramFeatures::ModelViewInverseTranspose; + } + } + if (features.DriverFlags & CGPUProgramFeatures::Projection) + { + m_Indices.Projection = getUniformIndex("nlProjection"); + if (m_Indices.Projection == ~0) + { + nlwarning("Missing 'nlProjection' in gpu program '%s', Projection disabled", source->DisplayName.c_str()); + features.DriverFlags &= ~CGPUProgramFeatures::Projection; + } + } + if (features.DriverFlags & CGPUProgramFeatures::ProjectionInverse) + { + m_Indices.ProjectionInverse = getUniformIndex("nlProjectionInverse"); + if (m_Indices.ProjectionInverse == ~0) + { + nlwarning("Missing 'nlProjectionInverse' in gpu program '%s', ProjectionInverse disabled", source->DisplayName.c_str()); + features.DriverFlags &= ~CGPUProgramFeatures::ProjectionInverse; + } + } + if (features.DriverFlags & CGPUProgramFeatures::ProjectionTranspose) + { + m_Indices.ProjectionTranspose = getUniformIndex("nlProjectionTranspose"); + if (m_Indices.ProjectionTranspose == ~0) + { + nlwarning("Missing 'nlProjectionTranspose' in gpu program '%s', ProjectionTranspose disabled", source->DisplayName.c_str()); + features.DriverFlags &= ~CGPUProgramFeatures::ProjectionTranspose; + } + } + if (features.DriverFlags & CGPUProgramFeatures::ProjectionInverseTranspose) + { + m_Indices.ProjectionInverseTranspose = getUniformIndex("nlProjectionInverseTranspose"); + if (m_Indices.ProjectionInverseTranspose == ~0) + { + nlwarning("Missing 'nlProjectionInverseTranspose' in gpu program '%s', ProjectionInverseTranspose disabled", source->DisplayName.c_str()); + features.DriverFlags &= ~CGPUProgramFeatures::ProjectionInverseTranspose; + } + } + if (features.DriverFlags & CGPUProgramFeatures::ModelViewProjection) + { + m_Indices.ModelViewProjection = getUniformIndex("nlModelViewProjection"); + if (m_Indices.ModelViewProjection == ~0) + { + nlwarning("Missing 'nlModelViewProjection' in gpu program '%s', ModelViewProjection disabled", source->DisplayName.c_str()); + features.DriverFlags &= ~CGPUProgramFeatures::ModelViewProjection; + } + } + if (features.DriverFlags & CGPUProgramFeatures::ModelViewProjectionInverse) + { + m_Indices.ModelViewProjectionInverse = getUniformIndex("nlModelViewProjectionInverse"); + if (m_Indices.ModelViewProjectionInverse == ~0) + { + nlwarning("Missing 'nlModelViewProjectionInverse' in gpu program '%s', ModelViewProjectionInverse disabled", source->DisplayName.c_str()); + features.DriverFlags &= ~CGPUProgramFeatures::ModelViewProjectionInverse; + } + } + if (features.DriverFlags & CGPUProgramFeatures::ModelViewProjectionTranspose) + { + m_Indices.ModelViewProjectionTranspose = getUniformIndex("nlModelViewProjectionTranspose"); + if (m_Indices.ModelViewProjectionTranspose == ~0) + { + nlwarning("Missing 'nlModelViewProjectionTranspose' in gpu program '%s', ModelViewProjectionTranspose disabled", source->DisplayName.c_str()); + features.DriverFlags &= ~CGPUProgramFeatures::ModelViewProjectionTranspose; + } + } + if (features.DriverFlags & CGPUProgramFeatures::ModelViewProjectionInverseTranspose) + { + m_Indices.ModelViewProjectionInverseTranspose = getUniformIndex("nlModelViewProjectionInverseTranspose"); + if (m_Indices.ModelViewProjectionInverseTranspose == ~0) + { + nlwarning("Missing 'nlModelViewProjectionInverseTranspose' in gpu program '%s', ModelViewProjectionInverseTranspose disabled", source->DisplayName.c_str()); + features.DriverFlags &= ~CGPUProgramFeatures::ModelViewProjectionInverseTranspose; + } + } + + // + // Rough example, modify as necessary. + // + /*if (features.DriverFlags & CGPUProgramFeatures::DriverAmbient || features.MaterialFlags & CGPUProgramFeatures::MaterialAmbient) + { + m_Indices.Ambient = getUniformIndex("nlAmbient"); + if (m_Indices.Ambient == ~0) + { + nlwarning("Missing 'nlAmbient' in gpu program '%s', Ambient disabled", source->DisplayName.c_str()); + features.DriverFlags &= ~CGPUProgramFeatures::DriverAmbient; + features.MaterialFlags &= ~CGPUProgramFeatures::MaterialAmbient; + } + }*/ } } /* namespace NL3D */ diff --git a/code/nel/src/3d/gpu_program_source.cpp b/code/nel/src/3d/gpu_program_source.cpp deleted file mode 100644 index 21d1cc18b..000000000 --- a/code/nel/src/3d/gpu_program_source.cpp +++ /dev/null @@ -1,47 +0,0 @@ -/** - * \file gpu_program_source.cpp - * \brief CGPUProgramSource - * \date 2013-09-07 14:54GMT - * \author Jan Boon (Kaetemi) - * CGPUProgramSource - */ - -/* - * Copyright (C) 2013 by authors - * - * This file is part of NL3D. - * NL3D is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * NL3D 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 Affero General - * Public License for more details. - * - * You should have received a copy of the GNU Affero General Public - * License along with NL3D. If not, see - * . - */ - -#include -#include - -// STL includes - -// NeL includes -// #include - -// Project includes - -using namespace std; -// using namespace NLMISC; - -namespace NL3D { - -void gpu_program_source_cpp_dummy() { } - -} /* namespace NL3D */ - -/* end of file */ diff --git a/code/nel/src/3d/pixel_program.cpp b/code/nel/src/3d/pixel_program.cpp index 8fbe8c0cf..adb2163e5 100644 --- a/code/nel/src/3d/pixel_program.cpp +++ b/code/nel/src/3d/pixel_program.cpp @@ -32,7 +32,7 @@ namespace NL3D // *************************************************************************** -CPixelProgram::CPixelProgram(CGPUProgramSourceCont *programSource) : _Info(NULL), IGPUProgram(programSource) +CPixelProgram::CPixelProgram() { } @@ -41,39 +41,9 @@ CPixelProgram::CPixelProgram(CGPUProgramSourceCont *programSource) : _Info(NULL) CPixelProgram::~CPixelProgram () { - delete _Info; - _Info = NULL; + } // *************************************************************************** -void CPixelProgram::buildInfo(const char *displayName, uint features) -{ - nlassert(_Info == NULL); - _Info = new CPixelProgramInfo(); - CPixelProgramInfo *info = _Info; - info->DisplayName = displayName; - info->Features = features; - if (features & CPixelProgramInfo::Fog) - { - if (features & CPixelProgramInfo::DynamicFog) - { - info->FogEnabledIdx = getParamIdx("nlFogEnabled"); - if (info->FogEnabledIdx == ~0) - { - nlwarning("Missing 'nlFogEnabled' in gpu program '%s', DynamicFog disabled", displayName); - info->Features &= ~CPixelProgramInfo::DynamicFog; - } - } - info->FogStartEndIdx = getParamIdx("nlFogStartEnd"); - info->FogColorIdx = getParamIdx("nlFogColor"); - if (info->FogStartEndIdx == ~0 - || info->FogStartEndIdx == ~0) - { - nlwarning("Missing 'nlFogStartEnd/nlFogColor' in gpu program '%s', Fog disabled", displayName); - info->Features &= ~CPixelProgramInfo::Fog; - } - } -} - } // NL3D diff --git a/code/nel/src/3d/stereo_debugger.cpp b/code/nel/src/3d/stereo_debugger.cpp index 509ba4769..588e36fcb 100644 --- a/code/nel/src/3d/stereo_debugger.cpp +++ b/code/nel/src/3d/stereo_debugger.cpp @@ -116,10 +116,8 @@ void CStereoDebugger::setDriver(NL3D::UDriver *driver) NL3D::IDriver *drvInternal = (static_cast(driver))->getDriver(); - CGPUProgramSource *source = new CGPUProgramSource(); - CGPUProgramSourceCont *sourceCont = new CGPUProgramSourceCont(); - sourceCont->Sources.push_back(source); - source->Features = CPixelProgramInfo::MaterialTextures; + IGPUProgram::CSource *source = new IGPUProgram::CSource(); + source->Features.MaterialFlags = CGPUProgramFeatures::TextureStages; /*if (drvInternal->supportPixelProgram(CPixelProgram::fp40) && drvInternal->supportBloomEffect() && drvInternal->supportNonPowerOfTwoTextures()) { @@ -131,7 +129,8 @@ void CStereoDebugger::setDriver(NL3D::UDriver *driver) nldebug("VR: arbfp1"); source->Profile = IGPUProgram::arbfp1; source->setSourcePtr(a_arbfp1); - m_PixelProgram = new CPixelProgram(sourceCont); + m_PixelProgram = new CPixelProgram(); + m_PixelProgram->addSource(source); } /*else if (drvInternal->supportPixelProgram(CPixelProgram::ps_2_0)) { diff --git a/code/nel/src/3d/stereo_ovr.cpp b/code/nel/src/3d/stereo_ovr.cpp index b89783cb9..ac2350f3c 100644 --- a/code/nel/src/3d/stereo_ovr.cpp +++ b/code/nel/src/3d/stereo_ovr.cpp @@ -239,31 +239,37 @@ void CStereoOVR::setDriver(NL3D::UDriver *driver) NL3D::IDriver *drvInternal = (static_cast(driver))->getDriver(); - CGPUProgramSource *source = new CGPUProgramSource(); - CGPUProgramSourceCont *sourceCont = new CGPUProgramSourceCont(); - sourceCont->Sources.push_back(source); - source->Features = CPixelProgramInfo::MaterialTextures; + m_PixelProgram = new CPixelProgram(); + IGPUProgram::CSource *source = new IGPUProgram::CSource(); + source->Features.MaterialFlags = CGPUProgramFeatures::TextureStages; + source->Profile = IGPUProgram::none; if (drvInternal->supportPixelProgram(CPixelProgram::fp40) && drvInternal->supportBloomEffect() && drvInternal->supportNonPowerOfTwoTextures()) { nldebug("VR: fp40"); source->Profile = IGPUProgram::fp40; source->setSourcePtr(g_StereoOVR_fp40); - m_PixelProgram = new CPixelProgram(sourceCont); + m_PixelProgram->addSource(source); } else if (drvInternal->supportPixelProgram(CPixelProgram::arbfp1) && drvInternal->supportBloomEffect() && drvInternal->supportNonPowerOfTwoTextures()) { nldebug("VR: arbfp1"); source->Profile = IGPUProgram::arbfp1; source->setSourcePtr(g_StereoOVR_arbfp1); - m_PixelProgram = new CPixelProgram(sourceCont); + m_PixelProgram->addSource(source); } else if (drvInternal->supportPixelProgram(CPixelProgram::ps_2_0)) { nldebug("VR: ps_2_0"); source->Profile = IGPUProgram::ps_2_0; source->setSourcePtr(g_StereoOVR_ps_2_0); - m_PixelProgram = new CPixelProgram(sourceCont); + m_PixelProgram->addSource(source); + } + + if (!drvInternal->compilePixelProgram(m_PixelProgram)) + { + delete m_PixelProgram; + m_PixelProgram = NULL; } if (m_PixelProgram) diff --git a/code/nel/src/3d/vertex_program.cpp b/code/nel/src/3d/vertex_program.cpp index d0acbe775..b47e706ee 100644 --- a/code/nel/src/3d/vertex_program.cpp +++ b/code/nel/src/3d/vertex_program.cpp @@ -26,129 +26,26 @@ namespace NL3D // *************************************************************************** -CVertexProgram::CVertexProgram(CGPUProgramSourceCont *programSource) : _Info(NULL), IGPUProgram(programSource) +CVertexProgram::CVertexProgram() { } // *************************************************************************** -CVertexProgram::CVertexProgram(const char *nelvp) : _Info(NULL) +CVertexProgram::CVertexProgram(const char *nelvp) { - CGPUProgramSource *source = new CGPUProgramSource(); - _ProgramSource = new CGPUProgramSourceCont(); - _ProgramSource->Sources.push_back(source); + CSource *source = new CSource(); source->Profile = IGPUProgram::nelvp; source->setSource(nelvp); - source->Features = 0; + addSource(source); } // *************************************************************************** CVertexProgram::~CVertexProgram () { - delete _Info; - _Info = NULL; -} - -// *************************************************************************** - -void CVertexProgram::buildInfo(const char *displayName, uint features) -{ - nlassert(_Info == NULL); - _Info = new CVertexProgramInfo(); - CVertexProgramInfo *info = _Info; - info->DisplayName = displayName; - info->Features = features; - if (features & CVertexProgramInfo::Ambient) - { - info->AmbientIdx = getParamIdx("nlAmbient"); - if (info->AmbientIdx == ~0) - { - nlwarning("Missing 'nlAmbient' in gpu program '%s', Ambient disabled", displayName); - info->Features &= ~CVertexProgramInfo::Ambient; - } - } - if (features & CVertexProgramInfo::Sun) - { - if (features & CVertexProgramInfo::DynamicLights) - { - info->SunEnabledIdx = getParamIdx("nlSunEnabled"); - if (info->SunEnabledIdx == ~0) - { - nlwarning("Missing 'nlSunEnabled' in gpu program '%s', DynamicLights disabled", displayName); - info->Features &= ~CVertexProgramInfo::DynamicLights; - } - } - info->SunDirectionIdx = getParamIdx("nlSunDirection"); - info->SunDiffuseIdx = getParamIdx("nlSunDiffuse"); - if (info->SunDirectionIdx == ~0 - || info->SunDiffuseIdx == ~0) - { - nlwarning("Missing 'nlSunDirection/nlSunDiffuse' in gpu program '%s', Sun disabled", displayName); - info->Features &= ~CVertexProgramInfo::Sun; - } - } - if (features & CVertexProgramInfo::PointLight0) - { - if (features & CVertexProgramInfo::DynamicLights) - { - info->PointLight0EnabledIdx = getParamIdx("nlPointLight0Enabled"); - if (info->PointLight0EnabledIdx == ~0) - { - nlwarning("Missing 'nlPointLight0Enabled' in gpu program '%s', DynamicLights disabled", displayName); - info->Features &= ~CVertexProgramInfo::DynamicLights; - } - } - info->PointLight0PositionIdx = getParamIdx("nlPointLight0Position"); - info->PointLight0DiffuseIdx = getParamIdx("nlPointLight0Diffuse"); - if (info->PointLight0PositionIdx == ~0 - || info->PointLight0DiffuseIdx == ~0) - { - nlwarning("Missing 'nlPointLight0Position/nlPointLight0Diffuse' in gpu program '%s', PointLight0 disabled", displayName); - info->Features &= ~CVertexProgramInfo::PointLight0; - } - } - if (features & CVertexProgramInfo::PointLight1) - { - if (features & CVertexProgramInfo::DynamicLights) - { - info->PointLight1EnabledIdx = getParamIdx("nlPointLight1Enabled"); - if (info->PointLight1EnabledIdx == ~0) - { - nlwarning("Missing 'nlPointLight1Enabled' in gpu program '%s', DynamicLights disabled", displayName); - info->Features &= ~CVertexProgramInfo::DynamicLights; - } - } - info->PointLight1PositionIdx = getParamIdx("nlPointLight1Position"); - info->PointLight1DiffuseIdx = getParamIdx("nlPointLight1Diffuse"); - if (info->PointLight1PositionIdx == ~0 - || info->PointLight1DiffuseIdx == ~0) - { - nlwarning("Missing 'nlPointLight1Position/nlPointLight1Diffuse' in gpu program '%s', PointLight1 disabled", displayName); - info->Features &= ~CVertexProgramInfo::PointLight1; - } - } - if (features & CVertexProgramInfo::PointLight2) - { - if (features & CVertexProgramInfo::DynamicLights) - { - info->PointLight2EnabledIdx = getParamIdx("nlPointLight2Enabled"); - if (info->PointLight2EnabledIdx == ~0) - { - nlwarning("Missing 'nlPointLight2Enabled' in gpu program '%s', DynamicLights disabled", displayName); - info->Features &= ~CVertexProgramInfo::DynamicLights; - } - } - info->PointLight2PositionIdx = getParamIdx("nlPointLight2Position"); - info->PointLight2DiffuseIdx = getParamIdx("nlPointLight2Diffuse"); - if (info->PointLight2PositionIdx == ~0 - || info->PointLight2DiffuseIdx == ~0) - { - nlwarning("Missing 'nlPointLight2Position/nlPointLight2Diffuse' in gpu program '%s', PointLight2 disabled", displayName); - info->Features &= ~CVertexProgramInfo::PointLight2; - } - } + } } // NL3D From 0bcb0d6ffd43e5d4b35309500616a9995901550f Mon Sep 17 00:00:00 2001 From: kaetemi Date: Mon, 9 Sep 2013 20:49:59 +0200 Subject: [PATCH 198/313] Add glsl pixel program for stereo distortion --- code/nel/include/nel/3d/gpu_program.h | 3 + code/nel/include/nel/3d/stereo_ovr.h | 4 +- code/nel/src/3d/gpu_program.cpp | 7 ++ code/nel/src/3d/stereo_ovr.cpp | 161 ++++++++++++++++++++------ code/nel/src/3d/stereo_ovr_fp.cpp | 55 ++++++++- code/snowballs2/bin/pp_oculus_vr.cg | 4 +- 6 files changed, 189 insertions(+), 45 deletions(-) diff --git a/code/nel/include/nel/3d/gpu_program.h b/code/nel/include/nel/3d/gpu_program.h index d1b234dee..0d8d30a61 100644 --- a/code/nel/include/nel/3d/gpu_program.h +++ b/code/nel/include/nel/3d/gpu_program.h @@ -266,6 +266,9 @@ public: // Build feature info, called automatically by the driver after compile succeeds void buildInfo(CSource *source); + // Override this to build additional info in a subclass + virtual void buildInfo(); + protected: /// The progam source std::vector > m_Sources; diff --git a/code/nel/include/nel/3d/stereo_ovr.h b/code/nel/include/nel/3d/stereo_ovr.h index c2dccf930..ba6895bf0 100644 --- a/code/nel/include/nel/3d/stereo_ovr.h +++ b/code/nel/include/nel/3d/stereo_ovr.h @@ -66,7 +66,7 @@ class ITexture; class CTextureUser; class CStereoOVRDevicePtr; class CStereoOVRDeviceHandle; -class CPixelProgram; +class CPixelProgramOVR; #define NL_STEREO_MAX_USER_CAMERAS 8 @@ -161,7 +161,7 @@ private: NL3D::UMaterial m_BarrelMat; NLMISC::CQuadUV m_BarrelQuadLeft; NLMISC::CQuadUV m_BarrelQuadRight; - CPixelProgram *m_PixelProgram; + NLMISC::CRefPtr m_PixelProgram; NLMISC::CVector m_EyePosition; float m_Scale; diff --git a/code/nel/src/3d/gpu_program.cpp b/code/nel/src/3d/gpu_program.cpp index 55f07e32b..ee16f1104 100644 --- a/code/nel/src/3d/gpu_program.cpp +++ b/code/nel/src/3d/gpu_program.cpp @@ -204,6 +204,13 @@ void IGPUProgram::buildInfo(CSource *source) features.MaterialFlags &= ~CGPUProgramFeatures::MaterialAmbient; } }*/ + + buildInfo(); +} + +void IGPUProgram::buildInfo() +{ + } } /* namespace NL3D */ diff --git a/code/nel/src/3d/stereo_ovr.cpp b/code/nel/src/3d/stereo_ovr.cpp index ac2350f3c..cedfe3434 100644 --- a/code/nel/src/3d/stereo_ovr.cpp +++ b/code/nel/src/3d/stereo_ovr.cpp @@ -72,6 +72,7 @@ namespace NL3D { extern const char *g_StereoOVR_fp40; extern const char *g_StereoOVR_arbfp1; extern const char *g_StereoOVR_ps_2_0; +extern const char *g_StereoOVR_glsl330f; namespace { @@ -233,43 +234,106 @@ CStereoOVR::~CStereoOVR() --s_DeviceCounter; } +class CPixelProgramOVR : public CPixelProgram +{ +public: + struct COVRIndices + { + uint LensCenter; + uint ScreenCenter; + uint Scale; + uint ScaleIn; + uint HmdWarpParam; + }; + + CPixelProgramOVR() + { + { + CSource *source = new CSource(); + source->Profile = glsl330f; + source->Features.MaterialFlags = CGPUProgramFeatures::TextureStages; + source->setSourcePtr(g_StereoOVR_glsl330f); + addSource(source); + } + { + CSource *source = new CSource(); + source->Profile = fp40; + source->Features.MaterialFlags = CGPUProgramFeatures::TextureStages; + source->setSourcePtr(g_StereoOVR_fp40); + source->ParamIndices["cLensCenter"] = 0; + source->ParamIndices["cScreenCenter"] = 1; + source->ParamIndices["cScale"] = 2; + source->ParamIndices["cScaleIn"] = 3; + source->ParamIndices["cHmdWarpParam"] = 4; + addSource(source); + } + { + CSource *source = new CSource(); + source->Profile = arbfp1; + source->Features.MaterialFlags = CGPUProgramFeatures::TextureStages; + source->setSourcePtr(g_StereoOVR_arbfp1); + source->ParamIndices["cLensCenter"] = 0; + source->ParamIndices["cScreenCenter"] = 1; + source->ParamIndices["cScale"] = 2; + source->ParamIndices["cScaleIn"] = 3; + source->ParamIndices["cHmdWarpParam"] = 4; + addSource(source); + } + { + CSource *source = new CSource(); + source->Profile = ps_2_0; + source->Features.MaterialFlags = CGPUProgramFeatures::TextureStages; + source->setSourcePtr(g_StereoOVR_ps_2_0); + source->ParamIndices["cLensCenter"] = 0; + source->ParamIndices["cScreenCenter"] = 1; + source->ParamIndices["cScale"] = 2; + source->ParamIndices["cScaleIn"] = 3; + source->ParamIndices["cHmdWarpParam"] = 4; + addSource(source); + } + } + + virtual ~CPixelProgramOVR() + { + + } + + virtual void buildInfo() + { + CPixelProgram::buildInfo(); + + m_OVRIndices.LensCenter = getUniformIndex("cLensCenter"); + nlassert(m_OVRIndices.LensCenter != ~0); + m_OVRIndices.ScreenCenter = getUniformIndex("cScreenCenter"); + nlassert(m_OVRIndices.ScreenCenter != ~0); + m_OVRIndices.Scale = getUniformIndex("cScale"); + nlassert(m_OVRIndices.Scale != ~0); + m_OVRIndices.ScaleIn = getUniformIndex("cScaleIn"); + nlassert(m_OVRIndices.ScaleIn != ~0); + m_OVRIndices.HmdWarpParam = getUniformIndex("cHmdWarpParam"); + nlassert(m_OVRIndices.HmdWarpParam != ~0); + } + + inline const COVRIndices &ovrIndices() { return m_OVRIndices; } + +private: + COVRIndices m_OVRIndices; + +}; + void CStereoOVR::setDriver(NL3D::UDriver *driver) { nlassert(!m_PixelProgram); NL3D::IDriver *drvInternal = (static_cast(driver))->getDriver(); - m_PixelProgram = new CPixelProgram(); - - IGPUProgram::CSource *source = new IGPUProgram::CSource(); - source->Features.MaterialFlags = CGPUProgramFeatures::TextureStages; - source->Profile = IGPUProgram::none; - if (drvInternal->supportPixelProgram(CPixelProgram::fp40) && drvInternal->supportBloomEffect() && drvInternal->supportNonPowerOfTwoTextures()) + if (drvInternal->supportBloomEffect() && drvInternal->supportNonPowerOfTwoTextures()) { - nldebug("VR: fp40"); - source->Profile = IGPUProgram::fp40; - source->setSourcePtr(g_StereoOVR_fp40); - m_PixelProgram->addSource(source); - } - else if (drvInternal->supportPixelProgram(CPixelProgram::arbfp1) && drvInternal->supportBloomEffect() && drvInternal->supportNonPowerOfTwoTextures()) - { - nldebug("VR: arbfp1"); - source->Profile = IGPUProgram::arbfp1; - source->setSourcePtr(g_StereoOVR_arbfp1); - m_PixelProgram->addSource(source); - } - else if (drvInternal->supportPixelProgram(CPixelProgram::ps_2_0)) - { - nldebug("VR: ps_2_0"); - source->Profile = IGPUProgram::ps_2_0; - source->setSourcePtr(g_StereoOVR_ps_2_0); - m_PixelProgram->addSource(source); - } - - if (!drvInternal->compilePixelProgram(m_PixelProgram)) - { - delete m_PixelProgram; - m_PixelProgram = NULL; + m_PixelProgram = new CPixelProgramOVR(); + if (!drvInternal->compilePixelProgram(m_PixelProgram)) + { + m_PixelProgram.kill(); + } } if (m_PixelProgram) @@ -590,11 +654,27 @@ bool CStereoOVR::endRenderTarget() float scaleInX = (2 / w); float scaleInY = (2 / h); - drvInternal->setUniform2f(IDriver::PixelProgram, 0, lensCenterX, lensCenterY); - drvInternal->setUniform2f(IDriver::PixelProgram, 1, screenCenterX, screenCenterY); - drvInternal->setUniform2f(IDriver::PixelProgram, 2, scaleX, scaleY); - drvInternal->setUniform2f(IDriver::PixelProgram, 3, scaleInX, scaleInY); - drvInternal->setUniform4fv(IDriver::PixelProgram, 4, 1, m_DevicePtr->HMDInfo.DistortionK); + + drvInternal->setUniform2f(IDriver::PixelProgram, + m_PixelProgram->ovrIndices().LensCenter, + lensCenterX, lensCenterY); + + drvInternal->setUniform2f(IDriver::PixelProgram, + m_PixelProgram->ovrIndices().ScreenCenter, + screenCenterX, screenCenterY); + + drvInternal->setUniform2f(IDriver::PixelProgram, + m_PixelProgram->ovrIndices().Scale, + scaleX, scaleY); + + drvInternal->setUniform2f(IDriver::PixelProgram, + m_PixelProgram->ovrIndices().ScaleIn, + scaleInX, scaleInY); + + + drvInternal->setUniform4fv(IDriver::PixelProgram, + m_PixelProgram->ovrIndices().HmdWarpParam, + 1, m_DevicePtr->HMDInfo.DistortionK); m_Driver->drawQuad(m_BarrelQuadLeft, m_BarrelMat); @@ -602,8 +682,15 @@ bool CStereoOVR::endRenderTarget() lensCenterX = x + (w - lensViewportShift * 0.5f) * 0.5f; screenCenterX = x + w * 0.5f; - drvInternal->setUniform2f(IDriver::PixelProgram, 0, lensCenterX, lensCenterY); - drvInternal->setUniform2f(IDriver::PixelProgram, 1, screenCenterX, screenCenterY); + + drvInternal->setUniform2f(IDriver::PixelProgram, + m_PixelProgram->ovrIndices().LensCenter, + lensCenterX, lensCenterY); + + drvInternal->setUniform2f(IDriver::PixelProgram, + m_PixelProgram->ovrIndices().ScreenCenter, + screenCenterX, screenCenterY); + m_Driver->drawQuad(m_BarrelQuadRight, m_BarrelMat); diff --git a/code/nel/src/3d/stereo_ovr_fp.cpp b/code/nel/src/3d/stereo_ovr_fp.cpp index b81ee8421..940be0bfe 100644 --- a/code/nel/src/3d/stereo_ovr_fp.cpp +++ b/code/nel/src/3d/stereo_ovr_fp.cpp @@ -44,7 +44,7 @@ const char *g_StereoOVR_fp40 = //#var float2 cScale : : c[2] : 3 : 1 //#var float2 cScaleIn : : c[3] : 4 : 1 //#var float4 cHmdWarpParam : : c[4] : 5 : 1 - //#var sampler2D cTex0 : TEX0 : texunit 0 : 6 : 1 + //#var sampler2D nlTex0 : TEX0 : texunit 0 : 6 : 1 //#var float4 oCol : $vout.COLOR : COL : 7 : 1 //#const c[5] = 0.25 0.5 0 "PARAM c[6] = { program.env[0..4],\n" // program.local->program.env! @@ -81,6 +81,7 @@ const char *g_StereoOVR_fp40 = "ENDIF;\n" "END\n"; //# 24 instructions, 2 R-regs, 1 H-regs + const char *g_StereoOVR_arbfp1 = "!!ARBfp1.0\n" //# cgc version 3.1.0013, build date Apr 18 2012 @@ -102,7 +103,7 @@ const char *g_StereoOVR_arbfp1 = //#var float2 cScale : : c[2] : 3 : 1 //#var float2 cScaleIn : : c[3] : 4 : 1 //#var float4 cHmdWarpParam : : c[4] : 5 : 1 - //#var sampler2D cTex0 : TEX0 : texunit 0 : 6 : 1 + //#var sampler2D nlTex0 : TEX0 : texunit 0 : 6 : 1 //#var float4 oCol : $vout.COLOR : COL : 7 : 1 //#const c[5] = 0.25 0.5 0 1 "PARAM c[6] = { program.env[0..4],\n" @@ -139,6 +140,7 @@ const char *g_StereoOVR_arbfp1 = "CMP result.color, -R1.x, R0, c[5].z;\n" "END\n"; //# 28 instructions, 2 R-regs + const char *g_StereoOVR_ps_2_0 = "ps_2_0\n" // cgc version 3.1.0013, build date Apr 18 2012 @@ -160,7 +162,7 @@ const char *g_StereoOVR_ps_2_0 = //var float2 cScale : : c[2] : 3 : 1 //var float2 cScaleIn : : c[3] : 4 : 1 //var float4 cHmdWarpParam : : c[4] : 5 : 1 - //var sampler2D cTex0 : TEX0 : texunit 0 : 6 : 1 + //var sampler2D nlTex0 : TEX0 : texunit 0 : 6 : 1 //var float4 oCol : $vout.COLOR : COL : 7 : 1 //const c[5] = -0.25 -0.5 0.25 0.5 //const c[6] = 1 0 @@ -199,4 +201,49 @@ const char *g_StereoOVR_ps_2_0 = "texld r0, r3, s0\n" "cmp r0, -r1.x, r0, c6.y\n" "mov oC0, r0\n"; -} \ No newline at end of file + +const char *g_StereoOVR_glsl330f = + "#version 330\n" + "\n" + "bool _TMP2;\n" + "bvec2 _TMP1;\n" + "vec2 _TMP3;\n" + "uniform vec2 cLensCenter;\n" + "uniform vec2 cScreenCenter;\n" + "uniform vec2 cScale;\n" + "uniform vec2 cScaleIn;\n" + "uniform vec4 cHmdWarpParam;\n" + "uniform sampler2D nlTex0;\n" + "vec2 _TMP10;\n" + "vec2 _b0011;\n" + "vec2 _a0011;\n" + "in vec4 nlTexCoord0;\n" + "out vec4 nlCol;\n" + "\n" + "void main()\n" + "{\n" + " vec2 _theta;\n" + " float _rSq;\n" + " vec2 _theta1;\n" + " vec2 _tc;\n" + "\n" + " _theta = (nlTexCoord0.xy - cLensCenter)*cScaleIn;\n" + " _rSq = _theta.x*_theta.x + _theta.y*_theta.y;\n" + " _theta1 = _theta*(cHmdWarpParam.x + cHmdWarpParam.y*_rSq + cHmdWarpParam.z*_rSq*_rSq + cHmdWarpParam.w*_rSq*_rSq*_rSq);\n" + " _tc = cLensCenter + cScale*_theta1;\n" + " _a0011 = cScreenCenter - vec2( 0.25, 0.5);\n" + " _b0011 = cScreenCenter + vec2( 0.25, 0.5);\n" + " _TMP3 = min(_b0011, _tc);\n" + " _TMP10 = max(_a0011, _TMP3);\n" + " _TMP1 = bvec2(_TMP10.x == _tc.x, _TMP10.y == _tc.y);\n" + " _TMP2 = _TMP1.x && _TMP1.y;\n" + " if (!_TMP2) {\n" + " nlCol = vec4(0, 0, 0, 0);\n" + " } else {\n" + " nlCol = texture(nlTex0, _tc);\n" + " }\n" + "}\n"; + +} + +/* end of file */ diff --git a/code/snowballs2/bin/pp_oculus_vr.cg b/code/snowballs2/bin/pp_oculus_vr.cg index 1d84e0d54..c7de282ef 100644 --- a/code/snowballs2/bin/pp_oculus_vr.cg +++ b/code/snowballs2/bin/pp_oculus_vr.cg @@ -32,7 +32,7 @@ void pp_oculus_vr( uniform float2 cScale, uniform float2 cScaleIn, uniform float4 cHmdWarpParam, - uniform sampler2D cTex0 : TEX0, + uniform sampler2D nlTex0 : TEX0, // Output color out float4 oCol : COLOR) @@ -49,6 +49,6 @@ void pp_oculus_vr( } else { - oCol = tex2D(cTex0, tc); + oCol = tex2D(nlTex0, tc); } } From aa85673b3b248a5e7b812c38d28780ff1609ba72 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Mon, 9 Sep 2013 20:55:23 +0200 Subject: [PATCH 199/313] Flag some TODO's --- code/nel/src/3d/bloom_effect.cpp | 2 ++ code/nel/src/3d/landscapevb_allocator.cpp | 4 ++++ code/nel/src/3d/meshvp_wind_tree.cpp | 1 + code/nel/src/3d/vegetable_manager.cpp | 2 +- code/nel/src/3d/water_shape.cpp | 5 +++-- code/ryzom/client/src/decal.cpp | 2 ++ 6 files changed, 13 insertions(+), 3 deletions(-) diff --git a/code/nel/src/3d/bloom_effect.cpp b/code/nel/src/3d/bloom_effect.cpp index 3eb24518e..23d5c1e18 100644 --- a/code/nel/src/3d/bloom_effect.cpp +++ b/code/nel/src/3d/bloom_effect.cpp @@ -57,6 +57,8 @@ static const char *TextureOffset = static CVertexProgram TextureOffsetVertexProgram(TextureOffset); +// TODO_VP_GLSL + //----------------------------------------------------------------------------------------------------------- diff --git a/code/nel/src/3d/landscapevb_allocator.cpp b/code/nel/src/3d/landscapevb_allocator.cpp index f57d6ef8d..eccfac66e 100644 --- a/code/nel/src/3d/landscapevb_allocator.cpp +++ b/code/nel/src/3d/landscapevb_allocator.cpp @@ -563,6 +563,7 @@ void CLandscapeVBAllocator::setupVBFormatAndVertexProgram(bool withVertexProgr string vpgram= string(NL3D_LandscapeCommonStartProgram) + string(NL3D_LandscapeFar0EndProgram); _VertexProgram[0]= new CVertexProgram(vpgram.c_str()); + // TODO_VP_GLSL } else if(_Type==Far1) { @@ -580,6 +581,7 @@ void CLandscapeVBAllocator::setupVBFormatAndVertexProgram(bool withVertexProgr string vpgram= string(NL3D_LandscapeCommonStartProgram) + string(NL3D_LandscapeFar1EndProgram); _VertexProgram[0]= new CVertexProgram(vpgram.c_str()); + // TODO_VP_GLSL } else { @@ -597,11 +599,13 @@ void CLandscapeVBAllocator::setupVBFormatAndVertexProgram(bool withVertexProgr string vpgram= string(NL3D_LandscapeCommonStartProgram) + string(NL3D_LandscapeTileEndProgram); _VertexProgram[0]= new CVertexProgram(vpgram.c_str()); + // TODO_VP_GLSL // Init the Vertex Program for lightmap pass vpgram= string(NL3D_LandscapeCommonStartProgram) + string(NL3D_LandscapeTileLightMapEndProgram); _VertexProgram[1]= new CVertexProgram(vpgram.c_str()); + // TODO_VP_GLSL } } diff --git a/code/nel/src/3d/meshvp_wind_tree.cpp b/code/nel/src/3d/meshvp_wind_tree.cpp index 579557012..6d7901ec2 100644 --- a/code/nel/src/3d/meshvp_wind_tree.cpp +++ b/code/nel/src/3d/meshvp_wind_tree.cpp @@ -158,6 +158,7 @@ void CMeshVPWindTree::initInstance(CMeshBaseInstance *mbi) + CRenderTrav::getLightVPFragment(numPls, VPLightConstantStart, specular, normalize) + WindTreeVPCodeEnd; _VertexProgram[i]= std::auto_ptr(new CVertexProgram(vpCode.c_str())); + // TODO_VP_GLSL } } diff --git a/code/nel/src/3d/vegetable_manager.cpp b/code/nel/src/3d/vegetable_manager.cpp index fe1c63dc4..6d9e15752 100644 --- a/code/nel/src/3d/vegetable_manager.cpp +++ b/code/nel/src/3d/vegetable_manager.cpp @@ -602,7 +602,7 @@ void CVegetableManager::initVertexProgram(uint vpType, bool fogEnabled) // create VP. _VertexProgram[vpType][fogEnabled ? 1 : 0] = new CVertexProgram(vpgram.c_str()); - + // TODO_VP_GLSL } diff --git a/code/nel/src/3d/water_shape.cpp b/code/nel/src/3d/water_shape.cpp index 227b85254..bb43dc5f5 100644 --- a/code/nel/src/3d/water_shape.cpp +++ b/code/nel/src/3d/water_shape.cpp @@ -223,6 +223,7 @@ static CVertexProgram *BuildWaterVP(bool diffuseMap, bool bumpMap, bool use2Bump vp += "\nEND"; return new CVertexProgram(vp.c_str()); + // TODO_VP_GLSL } @@ -330,8 +331,8 @@ void CWaterShape::initVertexProgram() _VertexProgramNoBump = std::auto_ptr(BuildWaterVP(false, false, false)); _VertexProgramNoBumpDiffuse = std::auto_ptr(BuildWaterVP(true, false, false)); // no waves - _VertexProgramNoWave.reset(new CVertexProgram(WaterVPNoWave)); - _VertexProgramNoWaveDiffuse.reset(new CVertexProgram(WaterVPNoWaveDiffuse)); + _VertexProgramNoWave.reset(new CVertexProgram(WaterVPNoWave)); // TODO_VP_GLSL + _VertexProgramNoWaveDiffuse.reset(new CVertexProgram(WaterVPNoWaveDiffuse)); // TODO_VP_GLSL created = true; } } diff --git a/code/ryzom/client/src/decal.cpp b/code/ryzom/client/src/decal.cpp index 7aa4b3c3a..da0fdcb82 100644 --- a/code/ryzom/client/src/decal.cpp +++ b/code/ryzom/client/src/decal.cpp @@ -86,6 +86,8 @@ static const char *DecalAttenuationVertexProgramCode = static NL3D::CVertexProgram DecalAttenuationVertexProgram(DecalAttenuationVertexProgramCode); +// TODO_VP_GLSL + typedef CShadowPolyReceiver::CRGBAVertex CRGBAVertex; From 01577f9f958a77be6c9ff11ba5f714560b66a74f Mon Sep 17 00:00:00 2001 From: kaetemi Date: Mon, 9 Sep 2013 21:05:12 +0200 Subject: [PATCH 200/313] Implement param storage copy --- code/nel/src/3d/gpu_program_params.cpp | 37 ++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/code/nel/src/3d/gpu_program_params.cpp b/code/nel/src/3d/gpu_program_params.cpp index 87ba01381..55b14d41f 100644 --- a/code/nel/src/3d/gpu_program_params.cpp +++ b/code/nel/src/3d/gpu_program_params.cpp @@ -53,6 +53,43 @@ CGPUProgramParams::~CGPUProgramParams() } +void CGPUProgramParams::copy(CGPUProgramParams *params) +{ + size_t offset = params->getBegin(); + while (offset != params->getEnd()) + { + uint index = params->getIndexByOffset(offset); + const std::string &name = params->getNameByOffset(offset); + size_t local; + uint size = params->getSizeByOffset(offset); + uint count = params->getCountByOffset(offset); + uint nbComponents = size * count; + if (index) + { + local = allocOffset(index, size, count, params->getTypeByOffset(offset)); + if (!name.empty()) + { + map(index, name); + } + } + else + { + nlassert(!name.empty()); + local = allocOffset(name, size, count, params->getTypeByOffset(offset)); + } + + uint32 *src = params->getPtrUIByOffset(offset); + uint32 *dst = getPtrUIByOffset(local); + + for (uint c = 0; c < nbComponents; ++c) + { + dst[c] = src[c]; + } + + offset = params->getNext(offset); + } +} + void CGPUProgramParams::set1f(uint index, float f0) { float *f = getPtrFByOffset(allocOffset(index, 1, 1, Float)); From 1447e2f2d8a70770b8868cec4917629bb3aaeac6 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Mon, 9 Sep 2013 21:44:14 +0200 Subject: [PATCH 201/313] Replace temporary CMaterial code --- code/nel/include/nel/3d/driver.h | 3 + code/nel/include/nel/3d/material.h | 8 +- .../src/3d/driver/direct3d/driver_direct3d.h | 7 ++ .../driver_direct3d_pixel_program.cpp | 2 + .../direct3d/driver_direct3d_uniform.cpp | 19 ++++ .../driver_direct3d_vertex_program.cpp | 2 + code/nel/src/3d/driver/opengl/driver_opengl.h | 5 + .../driver/opengl/driver_opengl_material.cpp | 50 ++++++--- .../driver/opengl/driver_opengl_uniform.cpp | 101 ++++++++++++++++++ code/nel/src/3d/flare_model.cpp | 3 + code/nel/src/3d/scene.cpp | 2 + code/nel/src/3d/stereo_debugger.cpp | 2 +- code/nel/src/3d/stereo_ovr.cpp | 2 +- 13 files changed, 186 insertions(+), 20 deletions(-) diff --git a/code/nel/include/nel/3d/driver.h b/code/nel/include/nel/3d/driver.h index 1f7739c01..9d4a0e828 100644 --- a/code/nel/include/nel/3d/driver.h +++ b/code/nel/include/nel/3d/driver.h @@ -1198,6 +1198,9 @@ public: // Set builtin parameters virtual void setUniformMatrix(TProgram program, uint index, TMatrix matrix, TTransform transform) = 0; virtual void setUniformFog(TProgram program, uint index) = 0; + // Set feature parameters + virtual bool setUniformDriver(TProgram program) = 0; // set all driver-specific features params (based on program->features->DriverFlags) (called automatically when rendering with cmaterial and using a user program) + virtual void setUniformParams(TProgram program, const CGPUProgramParams ¶ms) = 0; // set all user-provided params from the storage // @} diff --git a/code/nel/include/nel/3d/material.h b/code/nel/include/nel/3d/material.h index 6d9589ca5..671f3339a 100644 --- a/code/nel/include/nel/3d/material.h +++ b/code/nel/include/nel/3d/material.h @@ -172,10 +172,6 @@ public: * - RGB still unchanged * Water : * - Water - * PostProcessing : - * - For internal use only when a pixel program is set manually through activePixelProgram. - * - Only textures are set by CMaterial (does not work with ps_3_0 for some reason), the rest must be set manually. - * - May be replaced in the future by some generic shader system. */ enum TShader { Normal=0, Bump, @@ -187,8 +183,8 @@ public: PerPixelLightingNoSpec, Cloud, Water, - PostProcessing, - shaderCount}; + shaderCount, + Program /* internally used when a pixel program is active */ }; /// \name Texture Env Modes. // @{ diff --git a/code/nel/src/3d/driver/direct3d/driver_direct3d.h b/code/nel/src/3d/driver/direct3d/driver_direct3d.h index a17ae32c3..d360fb915 100644 --- a/code/nel/src/3d/driver/direct3d/driver_direct3d.h +++ b/code/nel/src/3d/driver/direct3d/driver_direct3d.h @@ -1207,6 +1207,10 @@ public: // Set builtin parameters virtual void setUniformMatrix(TProgram program, uint index, TMatrix matrix, TTransform transform); virtual void setUniformFog(TProgram program, uint index); + // Set feature parameters + virtual bool setUniformDriver(TProgram program); // set all driver-specific features params (based on program->features->DriverFlags) + virtual bool setUniformMaterial(TProgram program, CMaterial &material); // set all material-specific feature params (based on program->features->MaterialFlags) + virtual void setUniformParams(TProgram program, const CGPUProgramParams ¶ms); // set all user-provided params from the storage // @} @@ -2537,6 +2541,9 @@ private: // The last vertex buffer needs vertex color bool _FogEnabled; + bool _VertexProgramUser; + bool _PixelProgramUser; + // *** Internal resources // Current render pass diff --git a/code/nel/src/3d/driver/direct3d/driver_direct3d_pixel_program.cpp b/code/nel/src/3d/driver/direct3d/driver_direct3d_pixel_program.cpp index 0d97925b3..ea636b348 100644 --- a/code/nel/src/3d/driver/direct3d/driver_direct3d_pixel_program.cpp +++ b/code/nel/src/3d/driver/direct3d/driver_direct3d_pixel_program.cpp @@ -129,11 +129,13 @@ bool CDriverD3D::activePixelProgram(CPixelProgram *program) if (!CDriverD3D::compilePixelProgram(program)) return false; CPixelProgramDrvInfosD3D *info = static_cast((IGPUProgramDrvInfos*)program->m_DrvInfo); + _PixelProgramUser = true; setPixelShader(info->Shader); } else { setPixelShader(NULL); + _PixelProgramUser = false; } return true; diff --git a/code/nel/src/3d/driver/direct3d/driver_direct3d_uniform.cpp b/code/nel/src/3d/driver/direct3d/driver_direct3d_uniform.cpp index 27e76bfd4..2ade8cfb4 100644 --- a/code/nel/src/3d/driver/direct3d/driver_direct3d_uniform.cpp +++ b/code/nel/src/3d/driver/direct3d/driver_direct3d_uniform.cpp @@ -215,4 +215,23 @@ void CDriverD3D::setUniformFog(NL3D::IDriver::TProgram program, uint index) 1 - (_D3DModelView._43 - _FogStart) / delta); } +bool CDriverD3D::setUniformDriver(TProgram program) +{ + // todo + + return true; +} + +bool CDriverD3D::setUniformMaterial(TProgram program, const CMaterial &material) +{ + // todo + + return true; +} + +void CDriverD3D::setUniformParams(TProgram program, const CGPUProgramParams ¶ms) +{ + // todo +} + } // NL3D diff --git a/code/nel/src/3d/driver/direct3d/driver_direct3d_vertex_program.cpp b/code/nel/src/3d/driver/direct3d/driver_direct3d_vertex_program.cpp index a409f20cf..74ddf9011 100644 --- a/code/nel/src/3d/driver/direct3d/driver_direct3d_vertex_program.cpp +++ b/code/nel/src/3d/driver/direct3d/driver_direct3d_vertex_program.cpp @@ -379,6 +379,7 @@ bool CDriverD3D::activeVertexProgram (CVertexProgram *program) if (!CDriverD3D::compileVertexProgram(program)) return false; CVertexProgamDrvInfosD3D *info = NLMISC::safe_cast((IGPUProgramDrvInfos*)program->m_DrvInfo); + _VertexProgramUser = true; setVertexProgram (info->Shader, program); /* D3DRS_FOGSTART and D3DRS_FOGEND must be set with [1, 0] else the fog doesn't work properly on VertexShader and non-VertexShader objects @@ -394,6 +395,7 @@ bool CDriverD3D::activeVertexProgram (CVertexProgram *program) else { setVertexProgram (NULL, NULL); + _VertexProgramUser = false; // Set the old fog range setRenderState (D3DRS_FOGSTART, *((DWORD*) (&_FogStart))); diff --git a/code/nel/src/3d/driver/opengl/driver_opengl.h b/code/nel/src/3d/driver/opengl/driver_opengl.h index e76563fb3..48e50ab8e 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl.h +++ b/code/nel/src/3d/driver/opengl/driver_opengl.h @@ -1413,6 +1413,11 @@ private: // Set builtin parameters virtual void setUniformMatrix(TProgram program, uint index, TMatrix matrix, TTransform transform); virtual void setUniformFog(TProgram program, uint index); + // Set feature parameters + virtual bool setUniformDriver(TProgram program); // set all driver-specific features params (based on program->features->DriverFlags) + virtual bool setUniformMaterial(TProgram program, CMaterial &material); // set all material-specific feature params (based on program->features->MaterialFlags) + bool setUniformMaterialInternal(TProgram program, CMaterial &material); // set all material-specific feature params (based on program->features->MaterialFlags) + virtual void setUniformParams(TProgram program, const CGPUProgramParams ¶ms); // set all user-provided params from the storage // @} diff --git a/code/nel/src/3d/driver/opengl/driver_opengl_material.cpp b/code/nel/src/3d/driver/opengl/driver_opengl_material.cpp index 6d9dbb247..67afe7868 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl_material.cpp +++ b/code/nel/src/3d/driver/opengl/driver_opengl_material.cpp @@ -283,14 +283,15 @@ void CDriverGL::setTextureShaders(const uint8 *addressingModes, const CSmartPtr< bool CDriverGL::setupMaterial(CMaterial& mat) { H_AUTO_OGL(CDriverGL_setupMaterial) - CShaderGL* pShader; - GLenum glenum = GL_ZERO; - uint32 touched = mat.getTouched(); - uint stage; // profile. _NbSetupMaterialCall++; + CMaterial::TShader matShader; + + CShaderGL* pShader; + GLenum glenum = GL_ZERO; + uint32 touched = mat.getTouched(); // 0. Retrieve/Create driver shader. //================================== @@ -359,9 +360,29 @@ bool CDriverGL::setupMaterial(CMaterial& mat) mat.clearTouched(0xFFFFFFFF); } - // Now we can get the supported shader from the cache. - CMaterial::TShader matShader = pShader->SupportedShader; + // 2b. User supplied pixel shader overrides material + //================================== + if (_VertexProgramEnabled) + { + if (!setUniformDriver(VertexProgram)) return false; + if (!setUniformMaterialInternal(VertexProgram, mat)) return false; + } + if (_PixelProgramEnabled) + { + matShader = CMaterial::Program; + if (!setUniformDriver(PixelProgram)) return false; + if (!setUniformMaterialInternal(PixelProgram, mat)) return false; + if (!_LastSetuppedPP) return false; + } + else + { + // Now we can get the supported shader from the cache. + matShader = pShader->SupportedShader; + } + + // 2b. Update more shader state + //================================== // if the shader has changed since last time if(matShader != _CurrentMaterialSupportedShader) { @@ -382,9 +403,11 @@ bool CDriverGL::setupMaterial(CMaterial& mat) // Must setup textures each frame. (need to test if touched). // Must separate texture setup and texture activation in 2 "for"... // because setupTexture() may disable all stage. - if (matShader != CMaterial::Water) + if (matShader != CMaterial::Water + && ((matShader != CMaterial::Program) || (_LastSetuppedPP->features().MaterialFlags & CGPUProgramFeatures::TextureStages)) + ) { - for(stage=0 ; stagefeatures().MaterialFlags & CGPUProgramFeatures::TextureStages)) ) { - for(stage=0 ; stagefeatures().MaterialFlags & CGPUProgramFeatures::TextureMatrices)) + ) { setupUserTextureMatrix(inlGetNumTextStages(), mat); } - else // deactivate texture matrix + else { disableUserTextureMatrix(); } diff --git a/code/nel/src/3d/driver/opengl/driver_opengl_uniform.cpp b/code/nel/src/3d/driver/opengl/driver_opengl_uniform.cpp index 6b429e706..3ba97efd3 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl_uniform.cpp +++ b/code/nel/src/3d/driver/opengl/driver_opengl_uniform.cpp @@ -291,6 +291,107 @@ void CDriverGL::setUniformFog(NL3D::IDriver::TProgram program, uint index) CDriverGL::setUniform4f(program, index, -values[2], -values[6], -values[10], -values[14]); } +bool CDriverGL::setUniformDriver(TProgram program) +{ + IGPUProgram *prog = NULL; + switch (program) + { + case VertexProgram: + prog = _LastSetuppedVP; + break; + case PixelProgram: + prog = _LastSetuppedPP; + break; + } + if (!prog) return false; + + const CGPUProgramFeatures &features = prog->features(); + + if (features.DriverFlags) + { + // todo + } + + return true; +} + +bool CDriverGL::setUniformMaterial(TProgram program, CMaterial &material) +{ + IGPUProgram *prog = NULL; + switch (program) + { + case VertexProgram: + prog = _LastSetuppedVP; + break; + case PixelProgram: + prog = _LastSetuppedPP; + break; + } + if (!prog) return false; + + const CGPUProgramFeatures &features = prog->features(); + + // These are also already set by setupMaterial, so setupMaterial uses setUniformMaterialInternal instead + if (features.MaterialFlags & (CGPUProgramFeatures::TextureStages | CGPUProgramFeatures::TextureMatrices)) + { + if (features.MaterialFlags & CGPUProgramFeatures::TextureStages) + { + for (uint stage = 0; stage < inlGetNumTextStages(); ++stage) + { + ITexture *text= material.getTexture(uint8(stage)); + + // Must setup textures each frame. (need to test if touched). + if (text != NULL && !setupTexture(*text)) + return false; + + // activate the texture, or disable texturing if NULL. + activateTexture(stage, text); + + // If texture not NULL, Change texture env function. + setTextureEnvFunction(stage, material); + } + + + } + if (features.MaterialFlags & CGPUProgramFeatures::TextureMatrices) + { + // Textures user matrix + setupUserTextureMatrix(inlGetNumTextStages(), material); + } + } + + return true; +} + +bool CDriverGL::setUniformMaterialInternal(TProgram program, CMaterial &material) +{ + IGPUProgram *prog = NULL; + switch (program) + { + case VertexProgram: + prog = _LastSetuppedVP; + break; + case PixelProgram: + prog = _LastSetuppedPP; + break; + } + if (!prog) return false; + + const CGPUProgramFeatures &features = prog->features(); + + if (features.MaterialFlags & ~(CGPUProgramFeatures::TextureStages | CGPUProgramFeatures::TextureMatrices)) + { + // todo + } + + return true; +} + +void CDriverGL::setUniformParams(TProgram program, const CGPUProgramParams ¶ms) +{ + // todo +} + #ifdef NL_STATIC } // NLDRIVERGL/ES #endif diff --git a/code/nel/src/3d/flare_model.cpp b/code/nel/src/3d/flare_model.cpp index ba5cc8098..6c422aac9 100644 --- a/code/nel/src/3d/flare_model.cpp +++ b/code/nel/src/3d/flare_model.cpp @@ -364,6 +364,7 @@ void CFlareModel::traverseRender() // setup driver drv->activeVertexProgram(NULL); drv->activePixelProgram(NULL); + drv->activeGeometryProgram(NULL); drv->setupModelMatrix(fs->getLookAtMode() ? CMatrix::Identity : getWorldMatrix()); // we don't change the fustrum to draw 2d shapes : it is costly, and we need to restore it after the drawing has been done // we setup Z to be (near + far) / 2, and setup x and y to get the screen coordinates we want @@ -567,6 +568,7 @@ void CFlareModel::updateOcclusionQueryBegin(IDriver *drv) nlassert(drv); drv->activeVertexProgram(NULL); drv->activePixelProgram(NULL); + drv->activeGeometryProgram(NULL); drv->setupModelMatrix(CMatrix::Identity); initStatics(); drv->setColorMask(false, false, false, false); // don't write any pixel during the test @@ -664,6 +666,7 @@ void CFlareModel::occlusionTest(CMesh &mesh, IDriver &drv) drv.setColorMask(false, false, false, false); // don't write any pixel during the test drv.activeVertexProgram(NULL); drv.activePixelProgram(NULL); + drv.activeGeometryProgram(NULL); setupOcclusionMeshMatrix(drv, *_Scene); drv.activeVertexBuffer(const_cast(mesh.getVertexBuffer())); // query drawn count diff --git a/code/nel/src/3d/scene.cpp b/code/nel/src/3d/scene.cpp index 76761bc71..fb2d476ac 100644 --- a/code/nel/src/3d/scene.cpp +++ b/code/nel/src/3d/scene.cpp @@ -382,6 +382,7 @@ void CScene::endPartRender() IDriver *drv = getDriver(); drv->activeVertexProgram(NULL); drv->activePixelProgram(NULL); + drv->activeGeometryProgram(NULL); // Ensure nothing animates on subsequent renders _EllapsedTime = 0.f; @@ -1577,6 +1578,7 @@ void CScene::renderOcclusionTestMeshs() RenderTrav.getDriver()->setupViewport(RenderTrav.getViewport()); RenderTrav.getDriver()->activeVertexProgram(NULL); RenderTrav.getDriver()->activePixelProgram(NULL); + RenderTrav.getDriver()->activeGeometryProgram(NULL); IDriver::TPolygonMode oldPolygonMode = RenderTrav.getDriver()->getPolygonMode(); CMaterial m; m.initUnlit(); diff --git a/code/nel/src/3d/stereo_debugger.cpp b/code/nel/src/3d/stereo_debugger.cpp index 588e36fcb..4dc9a39bb 100644 --- a/code/nel/src/3d/stereo_debugger.cpp +++ b/code/nel/src/3d/stereo_debugger.cpp @@ -150,7 +150,7 @@ void CStereoDebugger::setDriver(NL3D::UDriver *driver) m_Mat.setBlend (false); m_Mat.setAlphaTest (false); NL3D::CMaterial *mat = m_Mat.getObjectPtr(); - mat->setShader(NL3D::CMaterial::PostProcessing); + mat->setShader(NL3D::CMaterial::Normal); mat->setBlendFunc(CMaterial::one, CMaterial::zero); mat->setZWrite(false); mat->setZFunc(CMaterial::always); diff --git a/code/nel/src/3d/stereo_ovr.cpp b/code/nel/src/3d/stereo_ovr.cpp index cedfe3434..b3eb2f235 100644 --- a/code/nel/src/3d/stereo_ovr.cpp +++ b/code/nel/src/3d/stereo_ovr.cpp @@ -356,7 +356,7 @@ void CStereoOVR::setDriver(NL3D::UDriver *driver) m_BarrelMat.setBlend (false); m_BarrelMat.setAlphaTest (false); NL3D::CMaterial *barrelMat = m_BarrelMat.getObjectPtr(); - barrelMat->setShader(NL3D::CMaterial::PostProcessing); + barrelMat->setShader(NL3D::CMaterial::Normal); barrelMat->setBlendFunc(CMaterial::one, CMaterial::zero); barrelMat->setZWrite(false); barrelMat->setZFunc(CMaterial::always); From b6fde3c70612575198f77330f198f7e1db72da97 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Mon, 9 Sep 2013 23:01:43 +0200 Subject: [PATCH 202/313] Missed file in last commit --- code/nel/include/nel/3d/gpu_program.h | 1 + 1 file changed, 1 insertion(+) diff --git a/code/nel/include/nel/3d/gpu_program.h b/code/nel/include/nel/3d/gpu_program.h index 0d8d30a61..ebb06f4c4 100644 --- a/code/nel/include/nel/3d/gpu_program.h +++ b/code/nel/include/nel/3d/gpu_program.h @@ -107,6 +107,7 @@ struct CGPUProgramFeatures { /// Use the CMaterial texture stages as the textures for a Pixel Program TextureStages = 0x00000001, // <- don't remove this one, it's already used, if you want to split them up into the different stages, then it's ok to change it + TextureMatrices = 0x00000002, // // Rough example, modify as necessary. From f46ef4fd0737202f0e85f244f287cf21fc8adfff Mon Sep 17 00:00:00 2001 From: kaetemi Date: Mon, 9 Sep 2013 23:03:52 +0200 Subject: [PATCH 203/313] Fix incorrectly removed line --- code/nel/include/nel/3d/driver.h | 1 + 1 file changed, 1 insertion(+) diff --git a/code/nel/include/nel/3d/driver.h b/code/nel/include/nel/3d/driver.h index 9d4a0e828..36bbae255 100644 --- a/code/nel/include/nel/3d/driver.h +++ b/code/nel/include/nel/3d/driver.h @@ -1200,6 +1200,7 @@ public: virtual void setUniformFog(TProgram program, uint index) = 0; // Set feature parameters virtual bool setUniformDriver(TProgram program) = 0; // set all driver-specific features params (based on program->features->DriverFlags) (called automatically when rendering with cmaterial and using a user program) + virtual bool setUniformMaterial(TProgram program, CMaterial &material) = 0; // set all material-specific feature params (based on program->features->MaterialFlags) (called automatically when rendering with cmaterial and using a user program) virtual void setUniformParams(TProgram program, const CGPUProgramParams ¶ms) = 0; // set all user-provided params from the storage // @} From 1b2475281f00322716d8891f600ee59882899ca5 Mon Sep 17 00:00:00 2001 From: Quitta Date: Mon, 9 Sep 2013 23:30:45 +0200 Subject: [PATCH 204/313] Added CreatePermissions + handling it while syncing + changed image path to refer to the image locations dir + other fixes --- .../ryzom_ams/ams_lib/autoload/sync.php | 13 +- .../ryzom_ams/ams_lib/autoload/users.php | 24 ++++ .../ingame_templates/show_ticket_info.tpl | 32 ++--- .../drupal_module/ryzommanage/config.php | 6 +- .../ryzommanage/ryzommanage.module | 128 ++---------------- .../templates/show_ticket_info.tpl | 32 ++--- .../tools/server/ryzom_ams/www/config.php | 2 +- .../ryzom_ams/www/html/func/add_user.php | 1 + .../server/ryzom_ams/www/html/inc/logout.php | 4 +- .../server/ryzom_ams/www/html/sql/install.php | 25 +++- .../www/html/templates/show_ticket_info.tpl | 32 ++--- 11 files changed, 125 insertions(+), 174 deletions(-) diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/sync.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/sync.php index 12376d9b4..752ce7dd7 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/sync.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/sync.php @@ -23,6 +23,17 @@ class Sync{ $db = new DBLayer($record['db']); switch($record['type']) { case 'createPermissions': + $decode = json_decode($record['query']); + $values = array('username' => $decode[0]); + //make connection with and put into shard db & delete from the lib + $sth = $db->execute("SELECT UId FROM user WHERE Login= :username;", $values); + $result = $sth->fetchAll(); + foreach ($result as $UId) { + $ins_values = array('id' => $UId['UId']); + $db->execute("INSERT INTO permission (UId, ClientApplication, AccessPrivilege) VALUES (:id, 'r2', 'OPEN');", $ins_values); + $db->execute("INSERT INTO permission (UId, ClientApplication, AccessPrivilege) VALUES (:id , 'ryzom_open', 'OPEN');", $ins_values); + } + break; case 'change_pass': $decode = json_decode($record['query']); $values = array('user' => $decode[0], 'pass' => $decode[1]); @@ -47,7 +58,7 @@ class Sync{ print('Syncing completed'); } catch (PDOException $e) { - print('Something went wrong!'); + print('Something went wrong! The shard is probably still offline!'); print_r($e); } diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/users.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/users.php index e03a8fdff..3eb67b97e 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/users.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/users.php @@ -308,6 +308,30 @@ class Users{ } + public static function createPermissions($pvalues) { + + try { + $values = array('username' => $pvalues[0]); + $dbs = new DBLayer("shard"); + $sth = $dbs->execute("SELECT UId FROM user WHERE Login= :username;", $values); + $result = $sth->fetchAll(); + foreach ($result as $UId) { + $ins_values = array('id' => $UId['UId']); + $dbs->execute("INSERT INTO permission (UId, ClientApplication, AccessPrivilege) VALUES (:id, 'r2', 'OPEN');", $ins_values); + $dbs->execute("INSERT INTO permission (UId, ClientApplication, AccessPrivilege) VALUES (:id , 'ryzom_open', 'OPEN');", $ins_values); + } + } + catch (PDOException $e) { + //oh noooz, the shard is offline! Put it in query queue at ams_lib db! + $dbl = new DBLayer("lib"); + $dbl->execute("INSERT INTO ams_querycache (type, query, db) VALUES (:type, :query, :db)",array("type" => "createPermissions", + "query" => json_encode(array($pvalues[0])), "db" => "shard")); + + + } + return true; + } + protected function checkLoginMatch($user,$pass){ print('This is the base class!'); diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/show_ticket_info.tpl b/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/show_ticket_info.tpl index 4431331c4..f986bff5f 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/show_ticket_info.tpl +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/ingame_templates/show_ticket_info.tpl @@ -64,31 +64,31 @@ - + - + - + - + - + - + - +
    Shard ID: {$shard_id}
    User_Id: {$user_id}
    User Position: {$user_position}
    View Position: {$view_position}
    Client_Version: {$client_version}
    Patch_Version: {$patch_version}
    Server_Tick: {$server_tick}
    @@ -102,31 +102,31 @@ - + - + - + - + - + - + - +
    Memory: {$memory}
    Processor: {$processor}
    Cpu_Id: {$cpu_id}
    Cpu_Mask: {$cpu_mask}
    HT: {$ht}
    OS: {$os}
    NeL3D: {$nel3d}
    @@ -140,11 +140,11 @@ - + - +
    Connect_State: {$connect_state}
    Local_Address: {$local_address}
    diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/config.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/config.php index ef2459117..130cee9a5 100644 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/config.php +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/config.php @@ -15,8 +15,8 @@ $cfg['db']['web']['pass'] = variable_get('ryzommanage_webpassword', 'lol123') $cfg['db']['lib']['host'] = variable_get('ryzommanage_libserverurl', 'localhost'); $cfg['db']['lib']['port'] = variable_get('ryzommanage_libmysqlport', '3306'); $cfg['db']['lib']['name'] = variable_get('ryzommanage_libdbname', 'ryzom_ams_lib'); -$cfg['db']['lib']['user'] = variable_get('ryzommanage_libusername', 'shard'); -$cfg['db']['lib']['pass'] = variable_get('ryzommanage_libpassword', ''); +$cfg['db']['lib']['user'] = variable_get('ryzommanage_libusername', 'root'); +$cfg['db']['lib']['pass'] = variable_get('ryzommanage_libpassword', 'lol123'); $cfg['db']['shard']['host'] = variable_get('ryzommanage_shardserverurl', 'localhost'); $cfg['db']['shard']['port'] = variable_get('ryzommanage_shardmysqlport', '3306'); @@ -74,7 +74,7 @@ $AMS_TRANS = $AMS_LIB . '/translations'; $AMS_CACHEDIR = $AMS_LIB . '/cache'; $SITEBASE = dirname( __FILE__ ); $BASE_WEBPATH = 'http://localhost:40917/drupal'; -$IMAGELOC_WEBPATH = $BASE_WEBPATH. '/sites/all/modules/ryzommanage/' ; +$IMAGELOC_WEBPATH = $BASE_WEBPATH. '/sites/all/modules/ryzommanage/ams_lib/img' ; $WEBPATH = $BASE_WEBPATH .'/ams'; $INGAME_WEBPATH = $BASE_WEBPATH . '/ingame'; $CONFIG_PATH = dirname( __FILE__ ); diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module index e094aeceb..0bc14c908 100644 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ryzommanage.module @@ -29,11 +29,11 @@ global $BASE_WEBPATH; global $INGAME_LAYOUT; global $FORCE_INGAME; +require 'config.php'; + require 'ams_lib/libinclude.php'; spl_autoload_register('__autoload'); -require 'config.php'; - /* Drupal 7 ryzom core module Copyright (C) 2013 Matthew Lagoe (Botanic) & Paige Offerdahl (Tobi) @@ -354,12 +354,13 @@ function _ams_handler($ingame = false) */ function _collect_register($nids, $collection) { - syncdata(); + Sync::syncdata(); //if not using ryzom core client show registration page if (Helpers::check_if_game_client()) { return_client_httpdata(); } else { //redirect to registration page + global $BASE_WEBPATH; header("Location: ".$BASE_WEBPATH. "/user/register"); } } @@ -368,7 +369,7 @@ function _collect_register($nids, $collection) /** * - * Function _collect_register + * Function _collect_ingame_ams * * @takes * @return Nothing @@ -486,10 +487,10 @@ function createUser($values, $user_id) //Create the user on the shard + in case shard is offline put copy of query in query db //returns: ok, shardoffline or liboffline $result = WebUsers::createUser($params, $user_id); - createPermissions(array($login)); + Users::createPermissions(array($login)); } -function createPermissions($values) { +/*function createPermissions($values) { try { $hostname = variable_get('ryzommanage_serverurl', 'localhost'); @@ -536,7 +537,7 @@ function createPermissions($values) { } return true; -} +}*/ function ryzommanage_user_login(&$edit, $account){ $_SESSION['user'] = $account->name; @@ -683,118 +684,15 @@ function user_edit($values) { $username = $values[0]; $newpassword = $values[1]; - $salt = generateSALT(); + $salt = WebUsers::generateSALT(); $pass = crypt($newpassword, $salt); - - try { - $hostname = variable_get('ryzommanage_serverurl', 'localhost'); - $port = variable_get('ryzommanage_mysqlport', '3306'); - $dbname = variable_get('ryzommanage_dbname', 'nel'); - $ryusername = variable_get('ryzommanage_username', 'root'); - $password = variable_get('ryzommanage_password', ''); - $dbh = new PDO("mysql:host=$hostname;port=$port;dbname=$dbname", $ryusername, $password); - $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); - } - catch (PDOException $e) { - watchdog('ryzommanage', $e->getMessage(), NULL, WATCHDOG_ERROR); - $nid = db_insert('ryzommanage_querycache')->fields(array( - "SID" => NULL, - "type" => "user_edit", - "query" => json_encode(array( - $username, - $newpassword - )) - ))->execute(); - return true; - } - $sql = "UPDATE `nel`.`user` SET `Password` = ? WHERE `user`.`Login` = ?"; - - try { - $q = $dbh->prepare($sql); - } - catch (PDOException $e) { - watchdog('ryzommanage', $e->getMessage(), NULL, WATCHDOG_ERROR); - $nid = db_insert('ryzommanage_querycache')->fields(array( - "SID" => NULL, - "type" => "user_edit", - "query" => json_encode(array( - $username, - $newpassword - )) - ))->execute(); - return true; - } - - try { - $q->execute(array( $pass, $username)); - } - catch (PDOException $e) { - watchdog('ryzommanage', $e->getMessage(), NULL, WATCHDOG_ERROR); - $nid = db_insert('ryzommanage_querycache')->fields(array( - "SID" => NULL, - "type" => "user_edit", - "query" => json_encode(array( - $username, - $newpassword - )) - ))->execute(); - return true; - } - + $webUser = new WebUsers(); + $webUser->setAmsPassword($username,$pass); return true; } -/** - * - * Function syncdata - * - * @takes Nothing - * @return array $values - * - * Info: Runs functions to finish syncing data when shard is offline - * - */ -function syncdata () { - - try { - $hostname = variable_get('ryzommanage_serverurl', 'localhost'); - $port = variable_get('ryzommanage_mysqlport', '3306'); - $dbname = variable_get('ryzommanage_dbname', 'nel'); - $ryusername = variable_get('ryzommanage_username', 'root'); - $password = variable_get('ryzommanage_password', ''); - $dbh = new PDO("mysql:host=$hostname;port=$port;dbname=$dbname", $ryusername, $password); - $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); - } - catch (PDOException $e) { - watchdog('ryzommanage', $e->getMessage(), NULL, WATCHDOG_ERROR); - return true; - } - - $query = db_select('ryzommanage_querycache', 'q') - ->fields('q', array('SID', 'type', 'query')); - - $result = $query->execute(); - - foreach ($result as $record) { - watchdog('ryzommanage_usersync', $record->query, NULL, WATCHDOG_ERROR); - - switch($record->type) { - case 'createPermissions': - case 'user_edit': - case 'createUser': - watchdog('ryzommanage_usersync', $record->type, NULL, WATCHDOG_INFO); - $SID = $record->SID; - $num_deleted = db_delete('ryzommanage_querycache') - ->condition('SID', $SID) - ->execute(); - $func = $record->type; - $func(json_decode($record->query)); - } - - } - -} + /** * * Function ryzommanage_cron @@ -807,7 +705,7 @@ function syncdata () { */ function ryzommanage_cron() { - syncdata(); + Sync::syncdata(); } diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/show_ticket_info.tpl b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/show_ticket_info.tpl index 04384009e..b8100c158 100644 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/show_ticket_info.tpl +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/templates/show_ticket_info.tpl @@ -16,61 +16,61 @@
    Ingame related
    Shard ID: {$shard_id} Shard ID: {$shard_id}
    User_Id: {$user_id} User_Id: {$user_id}
    User Position: {$user_position} User Position: {$user_position}
    View Position: {$view_position} View Position: {$view_position}
    Client_Version: {$client_version} Client_Version: {$client_version}
    Patch_Version: {$patch_version} Patch_Version: {$patch_version}
    Server_Tick: {$server_tick} Server_Tick: {$server_tick}
    Hardware & Software related
    Memory: {$memory} Memory: {$memory}
    Processor: {$processor} Processor: {$processor}
    Cpu_Id: {$cpu_id} Cpu_Id: {$cpu_id}
    Cpu_Mask: {$cpu_mask} Cpu_Mask: {$cpu_mask}
    HT: {$ht} HT: {$ht}
    OS: {$os} OS: {$os}
    NeL3D: {$nel3d} NeL3D: {$nel3d}
    Network related
    Connect_State: {$connect_state} Connect_State: {$connect_state}
    Local_Address: {$local_address} Local_Address: {$local_address}
    diff --git a/code/ryzom/tools/server/ryzom_ams/www/config.php b/code/ryzom/tools/server/ryzom_ams/www/config.php index 1867875ba..45acc4d40 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/config.php +++ b/code/ryzom/tools/server/ryzom_ams/www/config.php @@ -76,7 +76,7 @@ $AMS_CACHEDIR = $AMS_LIB . '/cache'; $SITEBASE = dirname( __FILE__ ) . '/html/' ; $BASE_WEBPATH = 'http://localhost:40917/www/html'; -$IMAGELOC_WEBPATH = 'http://localhost:40917'; +$IMAGELOC_WEBPATH = 'http://localhost:40917/ams_lib/img'; $WEBPATH = $BASE_WEBPATH . '/index.php'; $INGAME_WEBPATH = $BASE_WEBPATH . '/index.php'; $CONFIG_PATH = dirname( __FILE__ ); diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/func/add_user.php b/code/ryzom/tools/server/ryzom_ams/www/html/func/add_user.php index 2cb583061..a53208c5a 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/func/add_user.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/func/add_user.php @@ -55,6 +55,7 @@ function write_user($newUser){ //Create the user on the shard + in case shard is offline put copy of query in query db //returns: ok, shardoffline or liboffline $result = WebUsers::createUser($params, $user_id); + Users::createPermissions(array($newUser["name"])); }catch (PDOException $e) { diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/inc/logout.php b/code/ryzom/tools/server/ryzom_ams/www/html/inc/logout.php index 83ed2080d..fb684f03e 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/inc/logout.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/inc/logout.php @@ -2,5 +2,5 @@ function logout(){ session_unset(); - session_destroy(); -} \ No newline at end of file + session_destroy(); +} diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/sql/install.php b/code/ryzom/tools/server/ryzom_ams/www/html/sql/install.php index b63f0d4d1..aa113f81d 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/sql/install.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/sql/install.php @@ -39,7 +39,7 @@ $sql = " CREATE DATABASE IF NOT EXISTS `" . $cfg['db']['lib']['name'] ."`; USE `" . $cfg['db']['lib']['name'] ."`; - DROP TABLE IF EXISTS ams_querycache; + DROP TABLE IF EXISTS `" . $cfg['db']['lib']['name'] ."`.`ams_querycache`; CREATE TABLE ams_querycache ( `SID` INT NOT NULL AUTO_INCREMENT PRIMARY KEY , @@ -48,7 +48,27 @@ `db` VARCHAR( 80 ) NOT NULL ); + -- ----------------------------------------------------------------------------------------------------------------------- + -- ----------------------------------------------------------------------------------------------------------------------- + DROP TABLE IF EXISTS `" . $cfg['db']['lib']['name'] ."`.`ticket_log` ; + DROP TABLE IF EXISTS `" . $cfg['db']['lib']['name'] ."`.`tagged` ; + DROP TABLE IF EXISTS `" . $cfg['db']['lib']['name'] ."`.`tag` ; + DROP TABLE IF EXISTS `" . $cfg['db']['lib']['name'] ."`.`in_support_group` ; + DROP TABLE IF EXISTS `" . $cfg['db']['lib']['name'] ."`.`in_group` ; + DROP TABLE IF EXISTS `" . $cfg['db']['lib']['name'] ."`.`ticket_group` ; + DROP TABLE IF EXISTS `" . $cfg['db']['lib']['name'] ."`.`ticket_info` ; + DROP TABLE IF EXISTS `" . $cfg['db']['lib']['name'] ."`.`email` ; + DROP TABLE IF EXISTS `" . $cfg['db']['lib']['name'] ."`.`forwarded` ; + DROP TABLE IF EXISTS `" . $cfg['db']['lib']['name'] ."`.`assigned` ; + DROP TABLE IF EXISTS `" . $cfg['db']['lib']['name'] ."`.`ticket_reply` ; + DROP TABLE IF EXISTS `" . $cfg['db']['lib']['name'] ."`.`ticket_content` ; + DROP TABLE IF EXISTS `" . $cfg['db']['lib']['name'] ."`.`ticket` ; + DROP TABLE IF EXISTS `" . $cfg['db']['lib']['name'] ."`.`support_group` ; + DROP TABLE IF EXISTS `" . $cfg['db']['lib']['name'] ."`.`ticket_category` ; + DROP TABLE IF EXISTS `" . $cfg['db']['lib']['name'] ."`.`ticket_user` ; + + -- ----------------------------------------------------- -- Table `" . $cfg['db']['lib']['name'] ."`.`ticket_category` -- ----------------------------------------------------- @@ -415,9 +435,6 @@ ON DELETE NO ACTION ON UPDATE NO ACTION) ENGINE = InnoDB; - - - "; $dbl->executeWithoutParams($sql); print "The Lib & Web database were correctly installed!
    "; diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/templates/show_ticket_info.tpl b/code/ryzom/tools/server/ryzom_ams/www/html/templates/show_ticket_info.tpl index d8a7420d5..ed09e1892 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/templates/show_ticket_info.tpl +++ b/code/ryzom/tools/server/ryzom_ams/www/html/templates/show_ticket_info.tpl @@ -17,61 +17,61 @@
    Ingame related
    Shard ID: {$shard_id} Shard ID: {$shard_id}
    User_Id: {$user_id} User_Id: {$user_id}
    User Position: {$user_position} User Position: {$user_position}
    View Position: {$view_position} View Position: {$view_position}
    Client_Version: {$client_version} Client_Version: {$client_version}
    Patch_Version: {$patch_version} Patch_Version: {$patch_version}
    Server_Tick: {$server_tick} Server_Tick: {$server_tick}
    Hardware & Software related
    Memory: {$memory} Memory: {$memory}
    Processor: {$processor} Processor: {$processor}
    Cpu_Id: {$cpu_id} Cpu_Id: {$cpu_id}
    Cpu_Mask: {$cpu_mask} Cpu_Mask: {$cpu_mask}
    HT: {$ht} HT: {$ht}
    OS: {$os} OS: {$os}
    NeL3D: {$nel3d} NeL3D: {$nel3d}
    Network related
    Connect_State: {$connect_state} Connect_State: {$connect_state}
    Local_Address: {$local_address} Local_Address: {$local_address}
    From 9b74660631c2c69c741c8c0d94f2ca4d50e6b74a Mon Sep 17 00:00:00 2001 From: Quitta Date: Mon, 9 Sep 2013 23:31:49 +0200 Subject: [PATCH 205/313] removed my testing pass out of the config file :D --- .../server/ryzom_ams/drupal_module/ryzommanage/config.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/config.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/config.php index 130cee9a5..7a2ea8ac6 100644 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/config.php +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/config.php @@ -9,14 +9,14 @@ $cfg['db']['web']['host'] = variable_get('ryzommanage_webserverurl', 'localhost'); $cfg['db']['web']['port'] = variable_get('ryzommanage_webmysqlport', '3306'); $cfg['db']['web']['name'] = variable_get('ryzommanage_webdbname', 'drupal'); -$cfg['db']['web']['user'] = variable_get('ryzommanage_webusername', 'root'); -$cfg['db']['web']['pass'] = variable_get('ryzommanage_webpassword', 'lol123'); +$cfg['db']['web']['user'] = variable_get('ryzommanage_webusername', 'shard'); +$cfg['db']['web']['pass'] = variable_get('ryzommanage_webpassword', ''); $cfg['db']['lib']['host'] = variable_get('ryzommanage_libserverurl', 'localhost'); $cfg['db']['lib']['port'] = variable_get('ryzommanage_libmysqlport', '3306'); $cfg['db']['lib']['name'] = variable_get('ryzommanage_libdbname', 'ryzom_ams_lib'); -$cfg['db']['lib']['user'] = variable_get('ryzommanage_libusername', 'root'); -$cfg['db']['lib']['pass'] = variable_get('ryzommanage_libpassword', 'lol123'); +$cfg['db']['lib']['user'] = variable_get('ryzommanage_libusername', 'shard'); +$cfg['db']['lib']['pass'] = variable_get('ryzommanage_libpassword', ''); $cfg['db']['shard']['host'] = variable_get('ryzommanage_shardserverurl', 'localhost'); $cfg['db']['shard']['port'] = variable_get('ryzommanage_shardmysqlport', '3306'); From 14026e6f08aa58bf1bcaa8d1b87298498c09d6ac Mon Sep 17 00:00:00 2001 From: kaetemi Date: Mon, 9 Sep 2013 23:39:18 +0200 Subject: [PATCH 206/313] Set params from storage --- .../driver/opengl/driver_opengl_uniform.cpp | 36 ++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/code/nel/src/3d/driver/opengl/driver_opengl_uniform.cpp b/code/nel/src/3d/driver/opengl/driver_opengl_uniform.cpp index 3ba97efd3..a8434b3bc 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl_uniform.cpp +++ b/code/nel/src/3d/driver/opengl/driver_opengl_uniform.cpp @@ -389,7 +389,41 @@ bool CDriverGL::setUniformMaterialInternal(TProgram program, CMaterial &material void CDriverGL::setUniformParams(TProgram program, const CGPUProgramParams ¶ms) { - // todo + IGPUProgram *prog = NULL; + switch (program) + { + case VertexProgram: + prog = _LastSetuppedVP; + break; + case PixelProgram: + prog = _LastSetuppedPP; + break; + } + if (!prog) return; + + size_t offset = params.getBegin(); + while (offset != params.getEnd()) + { + uint size = params.getSizeByOffset(offset); + uint count = params.getCountByOffset(offset); + + nlassert(size == 4 || count == 1); // only support float4 arrays + nlassert(params.getTypeByOffset(offset) == CGPUProgramParams::Float); // only support float + + uint index = params.getIndexByOffset(offset); + if (index == ~0) + { + const std::string &name = params.getNameByOffset(offset); + nlassert(!name.empty() /* missing both parameter name and index, code error */); + uint index = prog->getUniformIndex(name); + nlassert(index != ~0 /* invalid parameter name */); + params.map(index, name); + } + + setUniform4fv(program, index, count, params.getPtrFByOffset(offset)); + + offset = params.getNext(offset); + } } #ifdef NL_STATIC From 680f2608033fc2c07b10616c5b8bf03411d74fc5 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Tue, 10 Sep 2013 00:31:26 +0200 Subject: [PATCH 207/313] Use CSmartPtr for vertex program --- code/nel/include/nel/3d/gpu_program.h | 5 ++ .../include/nel/3d/meshvp_per_pixel_light.h | 2 +- code/nel/include/nel/3d/meshvp_wind_tree.h | 2 +- code/nel/include/nel/3d/water_shape.h | 16 +++--- .../driver/opengl/driver_opengl_uniform.cpp | 2 +- code/nel/src/3d/gpu_program.cpp | 57 +++++++++++-------- code/nel/src/3d/meshvp_per_pixel_light.cpp | 6 +- code/nel/src/3d/meshvp_wind_tree.cpp | 10 ++-- code/nel/src/3d/water_model.cpp | 6 +- code/nel/src/3d/water_shape.cpp | 32 +++++------ 10 files changed, 77 insertions(+), 61 deletions(-) diff --git a/code/nel/include/nel/3d/gpu_program.h b/code/nel/include/nel/3d/gpu_program.h index ebb06f4c4..6ff2ed940 100644 --- a/code/nel/include/nel/3d/gpu_program.h +++ b/code/nel/include/nel/3d/gpu_program.h @@ -86,6 +86,9 @@ struct CGPUProgramFeatures ModelViewProjectionTranspose = 0x00000400, ModelViewProjectionInverseTranspose = 0x00000800, + // Fog + Fog = 0x00001000, + // // Rough example, modify as necessary. // @@ -144,6 +147,8 @@ struct CGPUProgramIndices uint ModelViewProjectionTranspose; uint ModelViewProjectionInverseTranspose; + uint Fog; + // // Rough example, modify as necessary. // diff --git a/code/nel/include/nel/3d/meshvp_per_pixel_light.h b/code/nel/include/nel/3d/meshvp_per_pixel_light.h index d7be0f3f9..5df1bfc16 100644 --- a/code/nel/include/nel/3d/meshvp_per_pixel_light.h +++ b/code/nel/include/nel/3d/meshvp_per_pixel_light.h @@ -84,7 +84,7 @@ private: bool _IsPointLight; // enum { NumVp = 8}; - static std::auto_ptr _VertexProgram[NumVp]; + static NLMISC::CSmartPtr _VertexProgram[NumVp]; }; } // NL3D diff --git a/code/nel/include/nel/3d/meshvp_wind_tree.h b/code/nel/include/nel/3d/meshvp_wind_tree.h index 7553d7cca..4e5d2875a 100644 --- a/code/nel/include/nel/3d/meshvp_wind_tree.h +++ b/code/nel/include/nel/3d/meshvp_wind_tree.h @@ -112,7 +112,7 @@ private: /** The 16 versions: Specular or not (0 or 2), + normalize normal or not (0 or 1). * All multiplied by 4, because support from 0 to 3 pointLights activated. (0.., 4.., 8.., 12..) */ - static std::auto_ptr _VertexProgram[NumVp]; + static NLMISC::CSmartPtr _VertexProgram[NumVp]; // WindTree Time for this mesh param setup. Stored in mesh because same for all instances. float _CurrentTime[HrcDepth]; diff --git a/code/nel/include/nel/3d/water_shape.h b/code/nel/include/nel/3d/water_shape.h index f7a7e1b0b..ae0fda5c6 100644 --- a/code/nel/include/nel/3d/water_shape.h +++ b/code/nel/include/nel/3d/water_shape.h @@ -247,17 +247,17 @@ private: static bool _GridSizeTouched; // - static std::auto_ptr _VertexProgramBump1; - static std::auto_ptr _VertexProgramBump2; + static NLMISC::CSmartPtr _VertexProgramBump1; + static NLMISC::CSmartPtr _VertexProgramBump2; // - static std::auto_ptr _VertexProgramBump1Diffuse; - static std::auto_ptr _VertexProgramBump2Diffuse; + static NLMISC::CSmartPtr _VertexProgramBump1Diffuse; + static NLMISC::CSmartPtr _VertexProgramBump2Diffuse; // - static std::auto_ptr _VertexProgramNoBump; - static std::auto_ptr _VertexProgramNoBumpDiffuse; + static NLMISC::CSmartPtr _VertexProgramNoBump; + static NLMISC::CSmartPtr _VertexProgramNoBumpDiffuse; // - static std::auto_ptr _VertexProgramNoWave; - static std::auto_ptr _VertexProgramNoWaveDiffuse; + static NLMISC::CSmartPtr _VertexProgramNoWave; + static NLMISC::CSmartPtr _VertexProgramNoWaveDiffuse; }; diff --git a/code/nel/src/3d/driver/opengl/driver_opengl_uniform.cpp b/code/nel/src/3d/driver/opengl/driver_opengl_uniform.cpp index a8434b3bc..6fe7e1f1c 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl_uniform.cpp +++ b/code/nel/src/3d/driver/opengl/driver_opengl_uniform.cpp @@ -415,7 +415,7 @@ void CDriverGL::setUniformParams(TProgram program, const CGPUProgramParams ¶ { const std::string &name = params.getNameByOffset(offset); nlassert(!name.empty() /* missing both parameter name and index, code error */); - uint index = prog->getUniformIndex(name); + uint index = prog->getUniformIndex(name.c_str()); nlassert(index != ~0 /* invalid parameter name */); params.map(index, name); } diff --git a/code/nel/src/3d/gpu_program.cpp b/code/nel/src/3d/gpu_program.cpp index ee16f1104..0ffdb49d9 100644 --- a/code/nel/src/3d/gpu_program.cpp +++ b/code/nel/src/3d/gpu_program.cpp @@ -84,112 +84,121 @@ void IGPUProgram::buildInfo(CSource *source) if (features.DriverFlags & CGPUProgramFeatures::ModelView) { - m_Indices.ModelView = getUniformIndex("nlModelView"); + m_Indices.ModelView = getUniformIndex("modelView"); if (m_Indices.ModelView == ~0) { - nlwarning("Missing 'nlModelView' in gpu program '%s', ModelView disabled", source->DisplayName.c_str()); + nlwarning("Missing 'modelView' in gpu program '%s', ModelView disabled", source->DisplayName.c_str()); features.DriverFlags &= ~CGPUProgramFeatures::ModelView; } } if (features.DriverFlags & CGPUProgramFeatures::ModelViewInverse) { - m_Indices.ModelViewInverse = getUniformIndex("nlModelViewInverse"); + m_Indices.ModelViewInverse = getUniformIndex("modelViewInverse"); if (m_Indices.ModelViewInverse == ~0) { - nlwarning("Missing 'nlModelViewInverse' in gpu program '%s', ModelViewInverse disabled", source->DisplayName.c_str()); + nlwarning("Missing 'modelViewInverse' in gpu program '%s', ModelViewInverse disabled", source->DisplayName.c_str()); features.DriverFlags &= ~CGPUProgramFeatures::ModelViewInverse; } } if (features.DriverFlags & CGPUProgramFeatures::ModelViewTranspose) { - m_Indices.ModelViewTranspose = getUniformIndex("nlModelViewTranspose"); + m_Indices.ModelViewTranspose = getUniformIndex("modelViewTranspose"); if (m_Indices.ModelViewTranspose == ~0) { - nlwarning("Missing 'nlModelViewTranspose' in gpu program '%s', ModelViewTranspose disabled", source->DisplayName.c_str()); + nlwarning("Missing 'modelViewTranspose' in gpu program '%s', ModelViewTranspose disabled", source->DisplayName.c_str()); features.DriverFlags &= ~CGPUProgramFeatures::ModelViewTranspose; } } if (features.DriverFlags & CGPUProgramFeatures::ModelViewInverseTranspose) { - m_Indices.ModelViewInverseTranspose = getUniformIndex("nlModelViewInverseTranspose"); + m_Indices.ModelViewInverseTranspose = getUniformIndex("modelViewInverseTranspose"); if (m_Indices.ModelViewInverseTranspose == ~0) { - nlwarning("Missing 'nlModelViewInverseTranspose' in gpu program '%s', ModelViewInverseTranspose disabled", source->DisplayName.c_str()); + nlwarning("Missing 'modelViewInverseTranspose' in gpu program '%s', ModelViewInverseTranspose disabled", source->DisplayName.c_str()); features.DriverFlags &= ~CGPUProgramFeatures::ModelViewInverseTranspose; } } if (features.DriverFlags & CGPUProgramFeatures::Projection) { - m_Indices.Projection = getUniformIndex("nlProjection"); + m_Indices.Projection = getUniformIndex("projection"); if (m_Indices.Projection == ~0) { - nlwarning("Missing 'nlProjection' in gpu program '%s', Projection disabled", source->DisplayName.c_str()); + nlwarning("Missing 'projection' in gpu program '%s', Projection disabled", source->DisplayName.c_str()); features.DriverFlags &= ~CGPUProgramFeatures::Projection; } } if (features.DriverFlags & CGPUProgramFeatures::ProjectionInverse) { - m_Indices.ProjectionInverse = getUniformIndex("nlProjectionInverse"); + m_Indices.ProjectionInverse = getUniformIndex("projectionInverse"); if (m_Indices.ProjectionInverse == ~0) { - nlwarning("Missing 'nlProjectionInverse' in gpu program '%s', ProjectionInverse disabled", source->DisplayName.c_str()); + nlwarning("Missing 'projectionInverse' in gpu program '%s', ProjectionInverse disabled", source->DisplayName.c_str()); features.DriverFlags &= ~CGPUProgramFeatures::ProjectionInverse; } } if (features.DriverFlags & CGPUProgramFeatures::ProjectionTranspose) { - m_Indices.ProjectionTranspose = getUniformIndex("nlProjectionTranspose"); + m_Indices.ProjectionTranspose = getUniformIndex("projectionTranspose"); if (m_Indices.ProjectionTranspose == ~0) { - nlwarning("Missing 'nlProjectionTranspose' in gpu program '%s', ProjectionTranspose disabled", source->DisplayName.c_str()); + nlwarning("Missing 'projectionTranspose' in gpu program '%s', ProjectionTranspose disabled", source->DisplayName.c_str()); features.DriverFlags &= ~CGPUProgramFeatures::ProjectionTranspose; } } if (features.DriverFlags & CGPUProgramFeatures::ProjectionInverseTranspose) { - m_Indices.ProjectionInverseTranspose = getUniformIndex("nlProjectionInverseTranspose"); + m_Indices.ProjectionInverseTranspose = getUniformIndex("projectionInverseTranspose"); if (m_Indices.ProjectionInverseTranspose == ~0) { - nlwarning("Missing 'nlProjectionInverseTranspose' in gpu program '%s', ProjectionInverseTranspose disabled", source->DisplayName.c_str()); + nlwarning("Missing 'projectionInverseTranspose' in gpu program '%s', ProjectionInverseTranspose disabled", source->DisplayName.c_str()); features.DriverFlags &= ~CGPUProgramFeatures::ProjectionInverseTranspose; } } if (features.DriverFlags & CGPUProgramFeatures::ModelViewProjection) { - m_Indices.ModelViewProjection = getUniformIndex("nlModelViewProjection"); + m_Indices.ModelViewProjection = getUniformIndex("modelViewProjection"); if (m_Indices.ModelViewProjection == ~0) { - nlwarning("Missing 'nlModelViewProjection' in gpu program '%s', ModelViewProjection disabled", source->DisplayName.c_str()); + nlwarning("Missing 'modelViewProjection' in gpu program '%s', ModelViewProjection disabled", source->DisplayName.c_str()); features.DriverFlags &= ~CGPUProgramFeatures::ModelViewProjection; } } if (features.DriverFlags & CGPUProgramFeatures::ModelViewProjectionInverse) { - m_Indices.ModelViewProjectionInverse = getUniformIndex("nlModelViewProjectionInverse"); + m_Indices.ModelViewProjectionInverse = getUniformIndex("modelViewProjectionInverse"); if (m_Indices.ModelViewProjectionInverse == ~0) { - nlwarning("Missing 'nlModelViewProjectionInverse' in gpu program '%s', ModelViewProjectionInverse disabled", source->DisplayName.c_str()); + nlwarning("Missing 'modelViewProjectionInverse' in gpu program '%s', ModelViewProjectionInverse disabled", source->DisplayName.c_str()); features.DriverFlags &= ~CGPUProgramFeatures::ModelViewProjectionInverse; } } if (features.DriverFlags & CGPUProgramFeatures::ModelViewProjectionTranspose) { - m_Indices.ModelViewProjectionTranspose = getUniformIndex("nlModelViewProjectionTranspose"); + m_Indices.ModelViewProjectionTranspose = getUniformIndex("modelViewProjectionTranspose"); if (m_Indices.ModelViewProjectionTranspose == ~0) { - nlwarning("Missing 'nlModelViewProjectionTranspose' in gpu program '%s', ModelViewProjectionTranspose disabled", source->DisplayName.c_str()); + nlwarning("Missing 'modelViewProjectionTranspose' in gpu program '%s', ModelViewProjectionTranspose disabled", source->DisplayName.c_str()); features.DriverFlags &= ~CGPUProgramFeatures::ModelViewProjectionTranspose; } } if (features.DriverFlags & CGPUProgramFeatures::ModelViewProjectionInverseTranspose) { - m_Indices.ModelViewProjectionInverseTranspose = getUniformIndex("nlModelViewProjectionInverseTranspose"); + m_Indices.ModelViewProjectionInverseTranspose = getUniformIndex("modelViewProjectionInverseTranspose"); if (m_Indices.ModelViewProjectionInverseTranspose == ~0) { - nlwarning("Missing 'nlModelViewProjectionInverseTranspose' in gpu program '%s', ModelViewProjectionInverseTranspose disabled", source->DisplayName.c_str()); + nlwarning("Missing 'modelViewProjectionInverseTranspose' in gpu program '%s', ModelViewProjectionInverseTranspose disabled", source->DisplayName.c_str()); features.DriverFlags &= ~CGPUProgramFeatures::ModelViewProjectionInverseTranspose; } } + if (features.DriverFlags & CGPUProgramFeatures::Fog) + { + m_Indices.Fog = getUniformIndex("fog"); + if (m_Indices.Fog == ~0) + { + nlwarning("Missing 'fog' in gpu program '%s', Fog disabled", source->DisplayName.c_str()); + features.DriverFlags &= ~CGPUProgramFeatures::Fog; + } + } // // Rough example, modify as necessary. diff --git a/code/nel/src/3d/meshvp_per_pixel_light.cpp b/code/nel/src/3d/meshvp_per_pixel_light.cpp index dd06e951c..e4f43efc5 100644 --- a/code/nel/src/3d/meshvp_per_pixel_light.cpp +++ b/code/nel/src/3d/meshvp_per_pixel_light.cpp @@ -32,7 +32,7 @@ namespace NL3D { -std::auto_ptr CMeshVPPerPixelLight::_VertexProgram[NumVp]; +NLMISC::CSmartPtr CMeshVPPerPixelLight::_VertexProgram[NumVp]; // *************************************************************************** // Light VP fragment constants start at 24 @@ -414,7 +414,7 @@ void CMeshVPPerPixelLight::initInstance(CMeshBaseInstance *mbi) nlassert(0); } #endif - _VertexProgram[vp]= std::auto_ptr(new CVertexProgram(vpCode.c_str())); + _VertexProgram[vp] = new CVertexProgram(vpCode.c_str()); } } @@ -521,7 +521,7 @@ void CMeshVPPerPixelLight::enable(bool enabled, IDriver *drv) | (SpecularLighting ? 2 : 0) | (_IsPointLight ? 1 : 0); // - drv->activeVertexProgram(_VertexProgram[idVP].get()); + drv->activeVertexProgram(_VertexProgram[idVP]); } else { diff --git a/code/nel/src/3d/meshvp_wind_tree.cpp b/code/nel/src/3d/meshvp_wind_tree.cpp index 6d7901ec2..bf1bc0c4e 100644 --- a/code/nel/src/3d/meshvp_wind_tree.cpp +++ b/code/nel/src/3d/meshvp_wind_tree.cpp @@ -39,7 +39,7 @@ static const uint VPLightConstantStart= 24; // *************************************************************************** -std::auto_ptr CMeshVPWindTree::_VertexProgram[CMeshVPWindTree::NumVp]; +NLMISC::CSmartPtr CMeshVPWindTree::_VertexProgram[CMeshVPWindTree::NumVp]; static const char* WindTreeVPCodeWave= "!!VP1.0 \n\ @@ -157,7 +157,7 @@ void CMeshVPWindTree::initInstance(CMeshBaseInstance *mbi) vpCode= string(WindTreeVPCodeWave) + CRenderTrav::getLightVPFragment(numPls, VPLightConstantStart, specular, normalize) + WindTreeVPCodeEnd; - _VertexProgram[i]= std::auto_ptr(new CVertexProgram(vpCode.c_str())); + _VertexProgram[i] = new CVertexProgram(vpCode.c_str()); // TODO_VP_GLSL } } @@ -312,7 +312,7 @@ bool CMeshVPWindTree::begin(IDriver *driver, CScene *scene, CMeshBaseInstance *m idVP= numPls*4 + idVP; // activate VP. - driver->activeVertexProgram(_VertexProgram[idVP].get()); + driver->activeVertexProgram(_VertexProgram[idVP]); return true; @@ -383,7 +383,7 @@ void CMeshVPWindTree::beginMBRMesh(IDriver *driver, CScene *scene) _LastMBRIdVP= 0; // activate VP. - driver->activeVertexProgram(_VertexProgram[_LastMBRIdVP].get()); + driver->activeVertexProgram(_VertexProgram[_LastMBRIdVP]); } // *************************************************************************** @@ -407,7 +407,7 @@ void CMeshVPWindTree::beginMBRInstance(IDriver *driver, CScene *scene, CMeshBase if( idVP!=_LastMBRIdVP ) { _LastMBRIdVP= idVP; - driver->activeVertexProgram(_VertexProgram[_LastMBRIdVP].get()); + driver->activeVertexProgram(_VertexProgram[_LastMBRIdVP]); } } diff --git a/code/nel/src/3d/water_model.cpp b/code/nel/src/3d/water_model.cpp index e72d7bab9..7ad9274b0 100644 --- a/code/nel/src/3d/water_model.cpp +++ b/code/nel/src/3d/water_model.cpp @@ -908,6 +908,10 @@ void CWaterModel::setupMaterialNVertexShader(IDriver *drv, CWaterShape *shape, c //=========================// // setup Water material // //=========================// + shape->initVertexProgram(); + CVertexProgram *program = shape->_ColorMap ? CWaterShape::_VertexProgramNoWaveDiffuse : CWaterShape::_VertexProgramNoWave; + drv->activeVertexProgram(program); + // TODO_VP_MATERIAL CWaterModel::_WaterMat.setTexture(0, shape->_BumpMap[0]); CWaterModel::_WaterMat.setTexture(1, shape->_BumpMap[1]); CWaterModel::_WaterMat.setTexture(3, shape->_ColorMap); @@ -969,8 +973,6 @@ void CWaterModel::setupMaterialNVertexShader(IDriver *drv, CWaterShape *shape, c cst[10].set(0.5f, 0.5f, 0.f, 1.f); // used to scale reflected ray into the envmap /// set all our constants in one call drv->setConstant(cstOffset, sizeof(cst) / sizeof(cst[0]) - cstOffset, (float *) &cst[cstOffset]); - shape->initVertexProgram(); - drv->activeVertexProgram(shape->_ColorMap ? CWaterShape::_VertexProgramNoWaveDiffuse.get() : CWaterShape::_VertexProgramNoWave.get()); drv->setConstantFog(4); } diff --git a/code/nel/src/3d/water_shape.cpp b/code/nel/src/3d/water_shape.cpp index bb43dc5f5..2376cdfe6 100644 --- a/code/nel/src/3d/water_shape.cpp +++ b/code/nel/src/3d/water_shape.cpp @@ -188,15 +188,15 @@ uint32 CWaterShape::_XGridBorder = 4; uint32 CWaterShape::_YGridBorder = 4; uint32 CWaterShape::_MaxGridSize; bool CWaterShape::_GridSizeTouched = true; -std::auto_ptr CWaterShape::_VertexProgramBump1; -std::auto_ptr CWaterShape::_VertexProgramBump2; -std::auto_ptr CWaterShape::_VertexProgramBump1Diffuse; -std::auto_ptr CWaterShape::_VertexProgramBump2Diffuse; -std::auto_ptr CWaterShape::_VertexProgramNoBump; -std::auto_ptr CWaterShape::_VertexProgramNoBumpDiffuse; +NLMISC::CSmartPtr CWaterShape::_VertexProgramBump1; +NLMISC::CSmartPtr CWaterShape::_VertexProgramBump2; +NLMISC::CSmartPtr CWaterShape::_VertexProgramBump1Diffuse; +NLMISC::CSmartPtr CWaterShape::_VertexProgramBump2Diffuse; +NLMISC::CSmartPtr CWaterShape::_VertexProgramNoBump; +NLMISC::CSmartPtr CWaterShape::_VertexProgramNoBumpDiffuse; // water with no waves -std::auto_ptr CWaterShape::_VertexProgramNoWave; -std::auto_ptr CWaterShape::_VertexProgramNoWaveDiffuse; +NLMISC::CSmartPtr CWaterShape::_VertexProgramNoWave; +NLMISC::CSmartPtr CWaterShape::_VertexProgramNoWaveDiffuse; /** Build a vertex program for water depending on requirements @@ -322,17 +322,17 @@ void CWaterShape::initVertexProgram() if (!created) { // waves - _VertexProgramBump1 = std::auto_ptr(BuildWaterVP(false, true, false)); - _VertexProgramBump2 = std::auto_ptr(BuildWaterVP(false, true, true)); + _VertexProgramBump1 = BuildWaterVP(false, true, false); + _VertexProgramBump2 = BuildWaterVP(false, true, true); - _VertexProgramBump1Diffuse = std::auto_ptr(BuildWaterVP(true, true, false)); - _VertexProgramBump2Diffuse = std::auto_ptr(BuildWaterVP(true, true, true)); + _VertexProgramBump1Diffuse = BuildWaterVP(true, true, false); + _VertexProgramBump2Diffuse = BuildWaterVP(true, true, true); - _VertexProgramNoBump = std::auto_ptr(BuildWaterVP(false, false, false)); - _VertexProgramNoBumpDiffuse = std::auto_ptr(BuildWaterVP(true, false, false)); + _VertexProgramNoBump = BuildWaterVP(false, false, false); + _VertexProgramNoBumpDiffuse = BuildWaterVP(true, false, false); // no waves - _VertexProgramNoWave.reset(new CVertexProgram(WaterVPNoWave)); // TODO_VP_GLSL - _VertexProgramNoWaveDiffuse.reset(new CVertexProgram(WaterVPNoWaveDiffuse)); // TODO_VP_GLSL + _VertexProgramNoWave = new CVertexProgram(WaterVPNoWave); // TODO_VP_GLSL + _VertexProgramNoWaveDiffuse = new CVertexProgram(WaterVPNoWaveDiffuse); // TODO_VP_GLSL created = true; } } From c9a2f9dbfc4fcc28df4d922bf24579ca03a5bce3 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Tue, 10 Sep 2013 01:29:53 +0200 Subject: [PATCH 208/313] Some fixes --- code/nel/include/nel/3d/driver.h | 2 +- code/nel/include/nel/3d/gpu_program.h | 4 ++-- code/nel/src/3d/driver/direct3d/driver_direct3d.h | 6 +++--- .../src/3d/driver/direct3d/driver_direct3d_uniform.cpp | 2 +- code/nel/src/3d/driver/opengl/driver_opengl.h | 6 +++--- code/nel/src/3d/driver/opengl/driver_opengl_uniform.cpp | 2 +- code/nel/src/3d/gpu_program_params.cpp | 8 ++++++++ 7 files changed, 19 insertions(+), 11 deletions(-) diff --git a/code/nel/include/nel/3d/driver.h b/code/nel/include/nel/3d/driver.h index 36bbae255..9ad704f81 100644 --- a/code/nel/include/nel/3d/driver.h +++ b/code/nel/include/nel/3d/driver.h @@ -1201,7 +1201,7 @@ public: // Set feature parameters virtual bool setUniformDriver(TProgram program) = 0; // set all driver-specific features params (based on program->features->DriverFlags) (called automatically when rendering with cmaterial and using a user program) virtual bool setUniformMaterial(TProgram program, CMaterial &material) = 0; // set all material-specific feature params (based on program->features->MaterialFlags) (called automatically when rendering with cmaterial and using a user program) - virtual void setUniformParams(TProgram program, const CGPUProgramParams ¶ms) = 0; // set all user-provided params from the storage + virtual void setUniformParams(TProgram program, CGPUProgramParams ¶ms) = 0; // set all user-provided params from the storage // @} diff --git a/code/nel/include/nel/3d/gpu_program.h b/code/nel/include/nel/3d/gpu_program.h index 6ff2ed940..01a48614b 100644 --- a/code/nel/include/nel/3d/gpu_program.h +++ b/code/nel/include/nel/3d/gpu_program.h @@ -57,7 +57,7 @@ public: // The virtual dtor is important. virtual ~IGPUProgramDrvInfos(void); - virtual uint getUniformIndex(char *name) const = 0; + virtual uint getUniformIndex(const char *name) const = 0; }; #define NL_GPU_PROGRAM_LIGHTS 8 @@ -261,7 +261,7 @@ public: inline void removeSource(size_t i) { nlassert(!m_Source); m_Sources.erase(m_Sources.begin() + i); } // Get the idx of a parameter (ogl: uniform, d3d: constant, etcetera) by name. Invalid name returns ~0 - inline uint getUniformIndex(char *name) const { return m_DrvInfo->getUniformIndex(name); }; + inline uint getUniformIndex(const char *name) const { return m_DrvInfo->getUniformIndex(name); }; // Get feature information of the current program inline CSource *source() const { return m_Source; }; diff --git a/code/nel/src/3d/driver/direct3d/driver_direct3d.h b/code/nel/src/3d/driver/direct3d/driver_direct3d.h index d360fb915..a7567f0f8 100644 --- a/code/nel/src/3d/driver/direct3d/driver_direct3d.h +++ b/code/nel/src/3d/driver/direct3d/driver_direct3d.h @@ -309,7 +309,7 @@ public: CVertexProgamDrvInfosD3D(IDriver *drv, ItGPUPrgDrvInfoPtrList it); ~CVertexProgamDrvInfosD3D(); - virtual uint getUniformIndex(char *name) const + virtual uint getUniformIndex(const char *name) const { std::map::const_iterator it = ParamIndices.find(name); if (it != ParamIndices.end()) return it->second; @@ -331,7 +331,7 @@ public: CPixelProgramDrvInfosD3D(IDriver *drv, ItGPUPrgDrvInfoPtrList it); ~CPixelProgramDrvInfosD3D(); - virtual uint getUniformIndex(char *name) const + virtual uint getUniformIndex(const char *name) const { std::map::const_iterator it = ParamIndices.find(name); if (it != ParamIndices.end()) return it->second; @@ -1210,7 +1210,7 @@ public: // Set feature parameters virtual bool setUniformDriver(TProgram program); // set all driver-specific features params (based on program->features->DriverFlags) virtual bool setUniformMaterial(TProgram program, CMaterial &material); // set all material-specific feature params (based on program->features->MaterialFlags) - virtual void setUniformParams(TProgram program, const CGPUProgramParams ¶ms); // set all user-provided params from the storage + virtual void setUniformParams(TProgram program, CGPUProgramParams ¶ms); // set all user-provided params from the storage // @} diff --git a/code/nel/src/3d/driver/direct3d/driver_direct3d_uniform.cpp b/code/nel/src/3d/driver/direct3d/driver_direct3d_uniform.cpp index 2ade8cfb4..78799584d 100644 --- a/code/nel/src/3d/driver/direct3d/driver_direct3d_uniform.cpp +++ b/code/nel/src/3d/driver/direct3d/driver_direct3d_uniform.cpp @@ -229,7 +229,7 @@ bool CDriverD3D::setUniformMaterial(TProgram program, const CMaterial &material) return true; } -void CDriverD3D::setUniformParams(TProgram program, const CGPUProgramParams ¶ms) +void CDriverD3D::setUniformParams(TProgram program, CGPUProgramParams ¶ms) { // todo } diff --git a/code/nel/src/3d/driver/opengl/driver_opengl.h b/code/nel/src/3d/driver/opengl/driver_opengl.h index 48e50ab8e..aea5b825b 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl.h +++ b/code/nel/src/3d/driver/opengl/driver_opengl.h @@ -1417,7 +1417,7 @@ private: virtual bool setUniformDriver(TProgram program); // set all driver-specific features params (based on program->features->DriverFlags) virtual bool setUniformMaterial(TProgram program, CMaterial &material); // set all material-specific feature params (based on program->features->MaterialFlags) bool setUniformMaterialInternal(TProgram program, CMaterial &material); // set all material-specific feature params (based on program->features->MaterialFlags) - virtual void setUniformParams(TProgram program, const CGPUProgramParams ¶ms); // set all user-provided params from the storage + virtual void setUniformParams(TProgram program, CGPUProgramParams ¶ms); // set all user-provided params from the storage // @} @@ -1650,7 +1650,7 @@ public: // The gl id is auto created here. CVertexProgamDrvInfosGL (CDriverGL *drv, ItGPUPrgDrvInfoPtrList it); - virtual uint getUniformIndex(char *name) const + virtual uint getUniformIndex(const char *name) const { std::map::const_iterator it = ParamIndices.find(name); if (it != ParamIndices.end()) return it->second; @@ -1670,7 +1670,7 @@ public: // The gl id is auto created here. CPixelProgamDrvInfosGL (CDriverGL *drv, ItGPUPrgDrvInfoPtrList it); - virtual uint getUniformIndex(char *name) const + virtual uint getUniformIndex(const char *name) const { std::map::const_iterator it = ParamIndices.find(name); if (it != ParamIndices.end()) return it->second; diff --git a/code/nel/src/3d/driver/opengl/driver_opengl_uniform.cpp b/code/nel/src/3d/driver/opengl/driver_opengl_uniform.cpp index 6fe7e1f1c..164a40558 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl_uniform.cpp +++ b/code/nel/src/3d/driver/opengl/driver_opengl_uniform.cpp @@ -387,7 +387,7 @@ bool CDriverGL::setUniformMaterialInternal(TProgram program, CMaterial &material return true; } -void CDriverGL::setUniformParams(TProgram program, const CGPUProgramParams ¶ms) +void CDriverGL::setUniformParams(TProgram program, CGPUProgramParams ¶ms) { IGPUProgram *prog = NULL; switch (program) diff --git a/code/nel/src/3d/gpu_program_params.cpp b/code/nel/src/3d/gpu_program_params.cpp index 55b14d41f..e196154f8 100644 --- a/code/nel/src/3d/gpu_program_params.cpp +++ b/code/nel/src/3d/gpu_program_params.cpp @@ -533,6 +533,14 @@ size_t CGPUProgramParams::getOffset(uint index) const return m_Map[index]; } +size_t CGPUProgramParams::getOffset(const std::string &name) const +{ + std::map::const_iterator it = m_MapName.find(name); + if (it == m_MapName.end()) + return s_End; + return it->second; +} + /// Remove by offset void CGPUProgramParams::freeOffset(size_t offset) { From dd490a0cc450e614c741771e47b3cea031baaa15 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Tue, 10 Sep 2013 02:01:57 +0200 Subject: [PATCH 209/313] Set driver parameters --- .../driver/opengl/driver_opengl_uniform.cpp | 53 ++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/code/nel/src/3d/driver/opengl/driver_opengl_uniform.cpp b/code/nel/src/3d/driver/opengl/driver_opengl_uniform.cpp index 164a40558..382d2bd8a 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl_uniform.cpp +++ b/code/nel/src/3d/driver/opengl/driver_opengl_uniform.cpp @@ -309,7 +309,58 @@ bool CDriverGL::setUniformDriver(TProgram program) if (features.DriverFlags) { - // todo + if (features.DriverFlags & CGPUProgramFeatures::ModelView) + { + setUniformMatrix(program, prog->indices().ModelView, ModelView, Identity); + } + if (features.DriverFlags & CGPUProgramFeatures::ModelViewInverse) + { + setUniformMatrix(program, prog->indices().ModelViewInverse, ModelView, Inverse); + } + if (features.DriverFlags & CGPUProgramFeatures::ModelViewTranspose) + { + setUniformMatrix(program, prog->indices().ModelViewTranspose, ModelView, Transpose); + } + if (features.DriverFlags & CGPUProgramFeatures::ModelViewInverseTranspose) + { + setUniformMatrix(program, prog->indices().ModelViewInverseTranspose, ModelView, InverseTranspose); + } + if (features.DriverFlags & CGPUProgramFeatures::Projection) + { + setUniformMatrix(program, prog->indices().Projection, Projection, Identity); + } + if (features.DriverFlags & CGPUProgramFeatures::ProjectionInverse) + { + setUniformMatrix(program, prog->indices().ProjectionInverse, Projection, Inverse); + } + if (features.DriverFlags & CGPUProgramFeatures::ProjectionTranspose) + { + setUniformMatrix(program, prog->indices().ProjectionTranspose, Projection, Transpose); + } + if (features.DriverFlags & CGPUProgramFeatures::ProjectionInverseTranspose) + { + setUniformMatrix(program, prog->indices().ProjectionInverseTranspose, Projection, InverseTranspose); + } + if (features.DriverFlags & CGPUProgramFeatures::ModelViewProjection) + { + setUniformMatrix(program, prog->indices().ModelViewProjection, ModelViewProjection, Identity); + } + if (features.DriverFlags & CGPUProgramFeatures::ModelViewProjectionInverse) + { + setUniformMatrix(program, prog->indices().ModelViewProjectionInverse, ModelViewProjection, Inverse); + } + if (features.DriverFlags & CGPUProgramFeatures::ModelViewProjectionTranspose) + { + setUniformMatrix(program, prog->indices().ModelViewProjectionTranspose, ModelViewProjection, Transpose); + } + if (features.DriverFlags & CGPUProgramFeatures::ModelViewProjectionInverseTranspose) + { + setUniformMatrix(program, prog->indices().ModelViewProjectionInverseTranspose, ModelViewProjection, InverseTranspose); + } + if (features.DriverFlags & CGPUProgramFeatures::Fog) + { + setUniformFog(program, prog->indices().Fog); + } } return true; From 32288eabe884851a432b616bd876c71d892faeaf Mon Sep 17 00:00:00 2001 From: kaetemi Date: Tue, 10 Sep 2013 02:07:16 +0200 Subject: [PATCH 210/313] Use new program interface for water vertex program --- code/nel/include/nel/3d/water_shape.h | 27 ++++++++++- code/nel/src/3d/water_model.cpp | 29 +++++------- code/nel/src/3d/water_shape.cpp | 68 +++++++++++++++++++++++++-- 3 files changed, 101 insertions(+), 23 deletions(-) diff --git a/code/nel/include/nel/3d/water_shape.h b/code/nel/include/nel/3d/water_shape.h index ae0fda5c6..94bdabea1 100644 --- a/code/nel/include/nel/3d/water_shape.h +++ b/code/nel/include/nel/3d/water_shape.h @@ -49,6 +49,29 @@ const NLMISC::CClassId WaveMakerModelClassId = NLMISC::CClassId(0x16da3356, 0x7 const uint WATER_VERTEX_HARD_SIZE = sizeof(float[3]); const uint WATER_VERTEX_SOFT_SIZE = sizeof(float[5]); +// VP Water No Wave +class CVertexProgramWaterVPNoWave : public CVertexProgram +{ +public: + struct CIdx + { + uint BumpMap0Scale; + uint BumpMap0Offset; + uint BumpMap1Scale; + uint BumpMap1Offset; + uint ObserverHeight; + uint ScaleReflectedRay; + uint DiffuseMapVector0; + uint DiffuseMapVector1; + }; + CVertexProgramWaterVPNoWave(bool diffuse); + virtual ~CVertexProgramWaterVPNoWave() { } + virtual void buildInfo(); + inline const CIdx &idx() const { return m_Idx; } +private: + CIdx m_Idx; + bool m_Diffuse; +}; /** * A water shape. @@ -256,8 +279,8 @@ private: static NLMISC::CSmartPtr _VertexProgramNoBump; static NLMISC::CSmartPtr _VertexProgramNoBumpDiffuse; // - static NLMISC::CSmartPtr _VertexProgramNoWave; - static NLMISC::CSmartPtr _VertexProgramNoWaveDiffuse; + static NLMISC::CSmartPtr _VertexProgramNoWave; + static NLMISC::CSmartPtr _VertexProgramNoWaveDiffuse; }; diff --git a/code/nel/src/3d/water_model.cpp b/code/nel/src/3d/water_model.cpp index 7ad9274b0..3614d0905 100644 --- a/code/nel/src/3d/water_model.cpp +++ b/code/nel/src/3d/water_model.cpp @@ -903,15 +903,12 @@ void CWaterModel::setupMaterialNVertexShader(IDriver *drv, CWaterShape *shape, c _WaterMat.setZWrite(true); _WaterMat.setShader(CMaterial::Water); } - const uint cstOffset = 5; // 4 places for the matrix - NLMISC::CVectorH cst[13]; //=========================// // setup Water material // //=========================// shape->initVertexProgram(); - CVertexProgram *program = shape->_ColorMap ? CWaterShape::_VertexProgramNoWaveDiffuse : CWaterShape::_VertexProgramNoWave; + CVertexProgramWaterVPNoWave *program = shape->_ColorMap ? CWaterShape::_VertexProgramNoWaveDiffuse : CWaterShape::_VertexProgramNoWave; drv->activeVertexProgram(program); - // TODO_VP_MATERIAL CWaterModel::_WaterMat.setTexture(0, shape->_BumpMap[0]); CWaterModel::_WaterMat.setTexture(1, shape->_BumpMap[1]); CWaterModel::_WaterMat.setTexture(3, shape->_ColorMap); @@ -957,23 +954,21 @@ void CWaterModel::setupMaterialNVertexShader(IDriver *drv, CWaterShape *shape, c { // setup 2x3 matrix for lookup in diffuse map updateDiffuseMapMatrix(); - cst[11].set(_ColorMapMatColumn0.x, _ColorMapMatColumn1.x, 0, _ColorMapMatColumn0.x * obsPos.x + _ColorMapMatColumn1.x * obsPos.y + _ColorMapMatPos.x); - cst[12].set(_ColorMapMatColumn0.y, _ColorMapMatColumn1.y, 0, _ColorMapMatColumn0.y * obsPos.x + _ColorMapMatColumn1.y * obsPos.y + _ColorMapMatPos.y); + drv->setUniform4f(IDriver::VertexProgram, program->idx().DiffuseMapVector0, _ColorMapMatColumn0.x, _ColorMapMatColumn1.x, 0, _ColorMapMatColumn0.x * obsPos.x + _ColorMapMatColumn1.x * obsPos.y + _ColorMapMatPos.x); + drv->setUniform4f(IDriver::VertexProgram, program->idx().DiffuseMapVector1, _ColorMapMatColumn0.y, _ColorMapMatColumn1.y, 0, _ColorMapMatColumn0.y * obsPos.x + _ColorMapMatColumn1.y * obsPos.y + _ColorMapMatPos.y); } - /// set matrix - drv->setConstantMatrix(0, IDriver::ModelViewProjection, IDriver::Identity); + /// temp + // drv->setUniformMatrix(IDriver::VertexProgram, program->indices().ModelViewProjection, IDriver::ModelViewProjection, IDriver::Identity); // now set by setUniformDriver in setupMaterial + // drv->setUniformFog(IDriver::VertexProgram, program->indices().Fog); // now set by setUniformDriver in setupMaterial // retrieve current time double date = scene->getCurrentTime(); // set bumpmaps pos - cst[6].set(fmodf(obsPos.x * shape->_HeightMapScale[0].x, 1.f) + (float) fmod(date * shape->_HeightMapSpeed[0].x, 1), fmodf(shape->_HeightMapScale[0].y * obsPos.y, 1.f) + (float) fmod(date * shape->_HeightMapSpeed[0].y, 1), 0.f, 1.f); // bump map 0 offset - cst[5].set(shape->_HeightMapScale[0].x, shape->_HeightMapScale[0].y, 0, 0); // bump map 0 scale - cst[8].set(fmodf(shape->_HeightMapScale[1].x * obsPos.x, 1.f) + (float) fmod(date * shape->_HeightMapSpeed[1].x, 1), fmodf(shape->_HeightMapScale[1].y * obsPos.y, 1.f) + (float) fmod(date * shape->_HeightMapSpeed[1].y, 1), 0.f, 1.f); // bump map 1 offset - cst[7].set(shape->_HeightMapScale[1].x, shape->_HeightMapScale[1].y, 0, 0); // bump map 1 scale - cst[9].set(0, 0, obsPos.z - zHeight, 1.f); - cst[10].set(0.5f, 0.5f, 0.f, 1.f); // used to scale reflected ray into the envmap - /// set all our constants in one call - drv->setConstant(cstOffset, sizeof(cst) / sizeof(cst[0]) - cstOffset, (float *) &cst[cstOffset]); - drv->setConstantFog(4); + drv->setUniform4f(IDriver::VertexProgram, program->idx().BumpMap0Offset, fmodf(obsPos.x * shape->_HeightMapScale[0].x, 1.f) + (float) fmod(date * shape->_HeightMapSpeed[0].x, 1), fmodf(shape->_HeightMapScale[0].y * obsPos.y, 1.f) + (float) fmod(date * shape->_HeightMapSpeed[0].y, 1), 0.f, 1.f); // bump map 0 offset + drv->setUniform4f(IDriver::VertexProgram, program->idx().BumpMap0Scale, shape->_HeightMapScale[0].x, shape->_HeightMapScale[0].y, 0, 0); // bump map 0 scale + drv->setUniform4f(IDriver::VertexProgram, program->idx().BumpMap1Offset, fmodf(shape->_HeightMapScale[1].x * obsPos.x, 1.f) + (float) fmod(date * shape->_HeightMapSpeed[1].x, 1), fmodf(shape->_HeightMapScale[1].y * obsPos.y, 1.f) + (float) fmod(date * shape->_HeightMapSpeed[1].y, 1), 0.f, 1.f); // bump map 1 offset + drv->setUniform4f(IDriver::VertexProgram, program->idx().BumpMap1Scale, shape->_HeightMapScale[1].x, shape->_HeightMapScale[1].y, 0, 0); // bump map 1 scale + drv->setUniform4f(IDriver::VertexProgram, program->idx().ObserverHeight, 0, 0, obsPos.z - zHeight, 1.f); + drv->setUniform4f(IDriver::VertexProgram, program->idx().ScaleReflectedRay, 0.5f, 0.5f, 0.f, 1.f); // used to scale reflected ray into the envmap } //================================================ diff --git a/code/nel/src/3d/water_shape.cpp b/code/nel/src/3d/water_shape.cpp index 2376cdfe6..ac586d168 100644 --- a/code/nel/src/3d/water_shape.cpp +++ b/code/nel/src/3d/water_shape.cpp @@ -81,7 +81,67 @@ DP4 o[TEX3].x, v[0], c[11]; #compute uv for diffuse texture \n\ DP4 o[TEX3].y, v[0], c[12]; \n\ END"; +CVertexProgramWaterVPNoWave::CVertexProgramWaterVPNoWave(bool diffuse) +{ + m_Diffuse = diffuse; + // nelvp + { + CSource *source = new CSource(); + source->Profile = nelvp; + source->DisplayName = "WaterVPNoWave/nelvp"; + source->Features.DriverFlags = + CGPUProgramFeatures::ModelViewProjection + | CGPUProgramFeatures::Fog; + source->ParamIndices["modelViewProjection"] = 0; + source->ParamIndices["fog"] = 4; + source->ParamIndices["bumpMap0Scale"] = 5; + source->ParamIndices["bumpMap0Offset"] = 6; + source->ParamIndices["bumpMap1Scale"] = 7; + source->ParamIndices["bumpMap1Offset"] = 8; + source->ParamIndices["observerHeight"] = 9; + source->ParamIndices["scaleReflectedRay"] = 10; + if (diffuse) + { + source->DisplayName += "/diffuse"; + source->ParamIndices["diffuseMapVector0"] = 11; + source->ParamIndices["diffuseMapVector1"] = 12; + source->setSourcePtr(WaterVPNoWaveDiffuse); + } + else + { + source->setSourcePtr(WaterVPNoWave); + } + addSource(source); + } + // glsl330v + { + // TODO_VP_GLSL + // CSource *source = new CSource(); + // source->Profile = glsl330v; + // source->DisplayName = "WaterVPNoWave/glsl330v"; + // if (diffuse) source->DisplayName += "/diffuse"; + // source->Features.DriverFlags = + // CGPUProgramFeatures::ModelViewProjection + // | CGPUProgramFeatures::Fog; + // source->setSource... + // addSource(source); + } +} +void CVertexProgramWaterVPNoWave::buildInfo() +{ + m_Idx.BumpMap0Scale = getUniformIndex("bumpMap0Scale"); + m_Idx.BumpMap0Offset = getUniformIndex("bumpMap0Offset"); + m_Idx.BumpMap1Scale = getUniformIndex("bumpMap1Scale"); + m_Idx.BumpMap1Offset = getUniformIndex("bumpMap1Offset"); + m_Idx.ObserverHeight = getUniformIndex("observerHeight"); + m_Idx.ScaleReflectedRay = getUniformIndex("scaleReflectedRay"); + if (m_Diffuse) + { + m_Idx.DiffuseMapVector0 = getUniformIndex("diffuseMapVector0"); + m_Idx.DiffuseMapVector1 = getUniformIndex("diffuseMapVector1"); + } +} //////////////// // WAVY WATER // @@ -195,8 +255,8 @@ NLMISC::CSmartPtr CWaterShape::_VertexProgramBump2Diffuse; NLMISC::CSmartPtr CWaterShape::_VertexProgramNoBump; NLMISC::CSmartPtr CWaterShape::_VertexProgramNoBumpDiffuse; // water with no waves -NLMISC::CSmartPtr CWaterShape::_VertexProgramNoWave; -NLMISC::CSmartPtr CWaterShape::_VertexProgramNoWaveDiffuse; +NLMISC::CSmartPtr CWaterShape::_VertexProgramNoWave; +NLMISC::CSmartPtr CWaterShape::_VertexProgramNoWaveDiffuse; /** Build a vertex program for water depending on requirements @@ -331,8 +391,8 @@ void CWaterShape::initVertexProgram() _VertexProgramNoBump = BuildWaterVP(false, false, false); _VertexProgramNoBumpDiffuse = BuildWaterVP(true, false, false); // no waves - _VertexProgramNoWave = new CVertexProgram(WaterVPNoWave); // TODO_VP_GLSL - _VertexProgramNoWaveDiffuse = new CVertexProgram(WaterVPNoWaveDiffuse); // TODO_VP_GLSL + _VertexProgramNoWave = new CVertexProgramWaterVPNoWave(false); + _VertexProgramNoWaveDiffuse = new CVertexProgramWaterVPNoWave(true); // TODO_VP_GLSL created = true; } } From 8ab4b6ba84a67821d13c3d1c6dc7bef364266507 Mon Sep 17 00:00:00 2001 From: Quitta Date: Tue, 10 Sep 2013 04:42:36 +0200 Subject: [PATCH 211/313] Just retested the mailing functionality, which seems to still work proper --- .../ryzom/tools/server/ryzom_ams/ams_lib/autoload/helpers.php | 4 ++-- code/ryzom/tools/server/ryzom_ams/ams_lib/translations/en.ini | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/helpers.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/helpers.php index 9bf7f03e1..24bfc5abd 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/helpers.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/helpers.php @@ -91,11 +91,11 @@ class Helpers{ { // if HTTP_USER_AGENT is not set then its ryzom core global $FORCE_INGAME; - if (( strpos($_SERVER['HTTP_USER_AGENT'],"Ryzom") === 0) || $FORCE_INGAME){ + if ( ( isset($_SERVER['HTTP_USER_AGENT']) && (strpos($_SERVER['HTTP_USER_AGENT'],"Ryzom") === 0)) || $FORCE_INGAME){ return true; }else{ return false; - } + } } static public function handle_language(){ diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/translations/en.ini b/code/ryzom/tools/server/ryzom_ams/ams_lib/translations/en.ini index bebed5301..db50a7398 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/translations/en.ini +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/translations/en.ini @@ -161,7 +161,7 @@ email_subject_new_ticket = "New ticket created [Ticket #" email_body_new_ticket_1 = "---------- Ticket #" email_body_new_ticket_2 = " ---------- Your ticket: " -email_body_new_ticket_3 = " is newly created +email_body_new_ticket_3 = " ,is newly created ---------- " email_body_new_ticket_4 = " From 4de5eeb586157d4baba09650c9336edbf073197b Mon Sep 17 00:00:00 2001 From: kaetemi Date: Tue, 10 Sep 2013 15:42:42 +0200 Subject: [PATCH 212/313] Adjust landscape vertex program to use new interface --- code/nel/include/nel/3d/driver.h | 2 + code/nel/include/nel/3d/gpu_program.h | 98 ++++------- .../include/nel/3d/landscapevb_allocator.h | 26 ++- .../src/3d/driver/direct3d/driver_direct3d.h | 1 + .../direct3d/driver_direct3d_uniform.cpp | 2 +- code/nel/src/3d/driver/opengl/driver_opengl.h | 1 + .../driver/opengl/driver_opengl_uniform.cpp | 28 ++-- code/nel/src/3d/gpu_program.cpp | 154 +++--------------- code/nel/src/3d/landscape.cpp | 35 ++-- code/nel/src/3d/landscapevb_allocator.cpp | 96 ++++++++--- code/nel/src/3d/water_model.cpp | 6 +- code/nel/src/3d/water_shape.cpp | 6 - 12 files changed, 199 insertions(+), 256 deletions(-) diff --git a/code/nel/include/nel/3d/driver.h b/code/nel/include/nel/3d/driver.h index 9ad704f81..a97606a97 100644 --- a/code/nel/include/nel/3d/driver.h +++ b/code/nel/include/nel/3d/driver.h @@ -1202,6 +1202,8 @@ public: virtual bool setUniformDriver(TProgram program) = 0; // set all driver-specific features params (based on program->features->DriverFlags) (called automatically when rendering with cmaterial and using a user program) virtual bool setUniformMaterial(TProgram program, CMaterial &material) = 0; // set all material-specific feature params (based on program->features->MaterialFlags) (called automatically when rendering with cmaterial and using a user program) virtual void setUniformParams(TProgram program, CGPUProgramParams ¶ms) = 0; // set all user-provided params from the storage + // Return true if uniforms are kept as program state and switched together with programs, false if uniforms are driver state and stay accross program switches. + virtual bool isUniformProgramState() = 0; // @} diff --git a/code/nel/include/nel/3d/gpu_program.h b/code/nel/include/nel/3d/gpu_program.h index 01a48614b..30fa23c52 100644 --- a/code/nel/include/nel/3d/gpu_program.h +++ b/code/nel/include/nel/3d/gpu_program.h @@ -60,12 +60,13 @@ public: virtual uint getUniformIndex(const char *name) const = 0; }; -#define NL_GPU_PROGRAM_LIGHTS 8 - -/// Features exposed by a program. Used to set builtin parameters on user provided shaders +// Features exposed by a program. Used to set builtin parameters on user provided shaders. +// This is only used for user provided shaders, not for builtin shaders, +// as it is a slow method which has to go through all of the options every time. +// Builtin shaders should set all flags to 0. struct CGPUProgramFeatures { - CGPUProgramFeatures() : DriverFlags(0), MaterialFlags(0) /*, NumLights(0) */ { } + CGPUProgramFeatures() : DriverFlags(0), MaterialFlags(0) { } // Driver builtin parameters enum TDriverFlags @@ -73,11 +74,11 @@ struct CGPUProgramFeatures // Matrices ModelView = 0x00000001, ModelViewInverse = 0x00000002, - ModelViewTranspose = 0x00000004, + ModelViewTranspose = 0x00000004, ModelViewInverseTranspose = 0x00000008, Projection = 0x00000010, - ProjectionInverse = 0x00000020, + ProjectionInverse = 0x00000020, ProjectionTranspose = 0x00000040, ProjectionInverseTranspose = 0x00000080, @@ -88,84 +89,54 @@ struct CGPUProgramFeatures // Fog Fog = 0x00001000, - - // - // Rough example, modify as necessary. - // - - // Lighting (todo) - /// Driver ambient, must be ignored when material ambient is flagged - //DriverAmbient = 0x00001000, - /// Lights, does not set diffuses if material lights is flagged - //DriverLights = 0x00002000, - // etcetera - - // Fog (todo) - // Fog = ..., }; uint32 DriverFlags; - // uint NumLights; // number of lights supported by the program (not used yet, modify as necessary) + // uint NumLights; enum TMaterialFlags { /// Use the CMaterial texture stages as the textures for a Pixel Program - TextureStages = 0x00000001, // <- don't remove this one, it's already used, if you want to split them up into the different stages, then it's ok to change it + TextureStages = 0x00000001, TextureMatrices = 0x00000002, - - // - // Rough example, modify as necessary. - // - - // Lighting (todo) - /// Material ambient premultiplied with driver ambient - //MaterialAmbient = 0x00000002, - /// Premultiply lights diffuse with material diffuse, requires driver lights to be flagged - //MaterialLights = 0x00000004, - // etcetera - - // Add all necessary feature sets used with builtin materials here }; // Material builtin parameters uint32 MaterialFlags; }; -/// Stucture used to cache the indices of builtin parameters -struct CGPUProgramIndices +// Stucture used to cache the indices of builtin parameters which are used by the drivers +// Not used for parameters of specific nl3d programs +struct CGPUProgramIndex { - uint ModelView; - uint ModelViewInverse; - uint ModelViewTranspose; - uint ModelViewInverseTranspose; + enum TName + { + ModelView, + ModelViewInverse, + ModelViewTranspose, + ModelViewInverseTranspose, - uint Projection; - uint ProjectionInverse; - uint ProjectionTranspose; - uint ProjectionInverseTranspose; + Projection, + ProjectionInverse, + ProjectionTranspose, + ProjectionInverseTranspose, - uint ModelViewProjection; - uint ModelViewProjectionInverse; - uint ModelViewProjectionTranspose; - uint ModelViewProjectionInverseTranspose; + ModelViewProjection, + ModelViewProjectionInverse, + ModelViewProjectionTranspose, + ModelViewProjectionInverseTranspose, - uint Fog; + Fog, - // - // Rough example, modify as necessary. - // - //uint Ambient; - - //uint LightType[NL_GPU_PROGRAM_LIGHTS]; - //uint LightAmbient[NL_GPU_PROGRAM_LIGHTS]; - //uint LightDiffuse[NL_GPU_PROGRAM_LIGHTS]; - //uint LightPosition[NL_GPU_PROGRAM_LIGHTS]; - //uint LightDirection[NL_GPU_PROGRAM_LIGHTS]; + NUM_UNIFORMS + }; + static const char *Names[NUM_UNIFORMS]; + uint Indices[NUM_UNIFORMS]; }; /** * \brief IGPUProgram * \date 2013-09-07 15:00GMT * \author Jan Boon (Kaetemi) - * A compiled GPU program + * A generic GPU program */ class IGPUProgram : public NLMISC::CRefCount { @@ -234,6 +205,7 @@ public: const char *SourcePtr; size_t SourceLen; /// Copy the source code string + inline void setSource(const std::string &source) { SourceCopy = source; SourcePtr = &SourceCopy[0]; SourceLen = SourceCopy.size(); } inline void setSource(const char *source) { SourceCopy = source; SourcePtr = &SourceCopy[0]; SourceLen = SourceCopy.size(); } /// Set pointer to source code string without copying the string inline void setSourcePtr(const char *sourcePtr, size_t sourceLen) { SourceCopy.clear(); SourcePtr = sourcePtr; SourceLen = sourceLen; } @@ -262,11 +234,11 @@ public: // Get the idx of a parameter (ogl: uniform, d3d: constant, etcetera) by name. Invalid name returns ~0 inline uint getUniformIndex(const char *name) const { return m_DrvInfo->getUniformIndex(name); }; + inline uint getUniformIndex(CGPUProgramIndex::TName name) const { return m_Index.Indices[name]; } // Get feature information of the current program inline CSource *source() const { return m_Source; }; inline const CGPUProgramFeatures &features() const { return m_Source->Features; }; - inline const CGPUProgramIndices &indices() const { return m_Indices; }; inline TProfile profile() const { return m_Source->Profile; } // Build feature info, called automatically by the driver after compile succeeds @@ -281,7 +253,7 @@ protected: /// The source used for compilation NLMISC::CSmartPtr m_Source; - CGPUProgramIndices m_Indices; + CGPUProgramIndex m_Index; public: /// The driver information. For the driver implementation only. diff --git a/code/nel/include/nel/3d/landscapevb_allocator.h b/code/nel/include/nel/3d/landscapevb_allocator.h index d3a081e7b..0e485e990 100644 --- a/code/nel/include/nel/3d/landscapevb_allocator.h +++ b/code/nel/include/nel/3d/landscapevb_allocator.h @@ -21,6 +21,7 @@ #include "nel/misc/smart_ptr.h" #include "nel/3d/tessellation.h" #include "nel/3d/vertex_buffer.h" +#include "nel/3d/vertex_program.h" namespace NL3D @@ -41,6 +42,7 @@ class CVertexProgram; #define NL3D_LANDSCAPE_VPPOS_DELTAPOS (CVertexBuffer::TexCoord3) #define NL3D_LANDSCAPE_VPPOS_ALPHAINFO (CVertexBuffer::TexCoord4) +class CVertexProgramLandscape; // *************************************************************************** /** @@ -107,6 +109,8 @@ public: * Give a vertexProgram Id to activate. Always 0, but 1 For tile Lightmap Pass. */ void activate(uint vpId); + void activateVP(uint vpId); + inline CVertexProgramLandscape *getVP(uint vpId) const { return _VertexProgram[vpId]; } // @} @@ -151,15 +155,35 @@ private: /// \name Vertex Program mgt . // @{ +public: enum {MaxVertexProgram= 2,}; // Vertex Program , NULL if not enabled. - CVertexProgram *_VertexProgram[MaxVertexProgram]; +private: + NLMISC::CSmartPtr _VertexProgram[MaxVertexProgram]; void deleteVertexProgram(); void setupVBFormatAndVertexProgram(bool withVertexProgram); // @} }; +class CVertexProgramLandscape : public CVertexProgram +{ +public: + struct CIdx + { + uint ProgramConstants0; + uint RefineCenter; + uint TileDist; + uint PZBModelPosition; + }; + CVertexProgramLandscape(CLandscapeVBAllocator::TType type, bool lightMap = false); + virtual ~CVertexProgramLandscape() { } + virtual void buildInfo(); +public: + const CIdx &idx() const { return m_Idx; } + CIdx m_Idx; +}; + } // NL3D diff --git a/code/nel/src/3d/driver/direct3d/driver_direct3d.h b/code/nel/src/3d/driver/direct3d/driver_direct3d.h index a7567f0f8..b3a8f5f37 100644 --- a/code/nel/src/3d/driver/direct3d/driver_direct3d.h +++ b/code/nel/src/3d/driver/direct3d/driver_direct3d.h @@ -1211,6 +1211,7 @@ public: virtual bool setUniformDriver(TProgram program); // set all driver-specific features params (based on program->features->DriverFlags) virtual bool setUniformMaterial(TProgram program, CMaterial &material); // set all material-specific feature params (based on program->features->MaterialFlags) virtual void setUniformParams(TProgram program, CGPUProgramParams ¶ms); // set all user-provided params from the storage + virtual bool isUniformProgramState() { return false; } // @} diff --git a/code/nel/src/3d/driver/direct3d/driver_direct3d_uniform.cpp b/code/nel/src/3d/driver/direct3d/driver_direct3d_uniform.cpp index 78799584d..a77a86549 100644 --- a/code/nel/src/3d/driver/direct3d/driver_direct3d_uniform.cpp +++ b/code/nel/src/3d/driver/direct3d/driver_direct3d_uniform.cpp @@ -222,7 +222,7 @@ bool CDriverD3D::setUniformDriver(TProgram program) return true; } -bool CDriverD3D::setUniformMaterial(TProgram program, const CMaterial &material) +bool CDriverD3D::setUniformMaterial(TProgram program, CMaterial &material) { // todo diff --git a/code/nel/src/3d/driver/opengl/driver_opengl.h b/code/nel/src/3d/driver/opengl/driver_opengl.h index aea5b825b..e20d7d4fa 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl.h +++ b/code/nel/src/3d/driver/opengl/driver_opengl.h @@ -1418,6 +1418,7 @@ private: virtual bool setUniformMaterial(TProgram program, CMaterial &material); // set all material-specific feature params (based on program->features->MaterialFlags) bool setUniformMaterialInternal(TProgram program, CMaterial &material); // set all material-specific feature params (based on program->features->MaterialFlags) virtual void setUniformParams(TProgram program, CGPUProgramParams ¶ms); // set all user-provided params from the storage + virtual bool isUniformProgramState() { return false; } // @} diff --git a/code/nel/src/3d/driver/opengl/driver_opengl_uniform.cpp b/code/nel/src/3d/driver/opengl/driver_opengl_uniform.cpp index 382d2bd8a..a23235d06 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl_uniform.cpp +++ b/code/nel/src/3d/driver/opengl/driver_opengl_uniform.cpp @@ -311,55 +311,55 @@ bool CDriverGL::setUniformDriver(TProgram program) { if (features.DriverFlags & CGPUProgramFeatures::ModelView) { - setUniformMatrix(program, prog->indices().ModelView, ModelView, Identity); + setUniformMatrix(program, prog->getUniformIndex(CGPUProgramIndex::ModelView), ModelView, Identity); } if (features.DriverFlags & CGPUProgramFeatures::ModelViewInverse) { - setUniformMatrix(program, prog->indices().ModelViewInverse, ModelView, Inverse); + setUniformMatrix(program, prog->getUniformIndex(CGPUProgramIndex::ModelViewInverse), ModelView, Inverse); } if (features.DriverFlags & CGPUProgramFeatures::ModelViewTranspose) { - setUniformMatrix(program, prog->indices().ModelViewTranspose, ModelView, Transpose); + setUniformMatrix(program, prog->getUniformIndex(CGPUProgramIndex::ModelViewTranspose), ModelView, Transpose); } if (features.DriverFlags & CGPUProgramFeatures::ModelViewInverseTranspose) { - setUniformMatrix(program, prog->indices().ModelViewInverseTranspose, ModelView, InverseTranspose); + setUniformMatrix(program, prog->getUniformIndex(CGPUProgramIndex::ModelViewInverseTranspose), ModelView, InverseTranspose); } if (features.DriverFlags & CGPUProgramFeatures::Projection) { - setUniformMatrix(program, prog->indices().Projection, Projection, Identity); + setUniformMatrix(program, prog->getUniformIndex(CGPUProgramIndex::Projection), Projection, Identity); } if (features.DriverFlags & CGPUProgramFeatures::ProjectionInverse) { - setUniformMatrix(program, prog->indices().ProjectionInverse, Projection, Inverse); + setUniformMatrix(program, prog->getUniformIndex(CGPUProgramIndex::ProjectionInverse), Projection, Inverse); } if (features.DriverFlags & CGPUProgramFeatures::ProjectionTranspose) { - setUniformMatrix(program, prog->indices().ProjectionTranspose, Projection, Transpose); + setUniformMatrix(program, prog->getUniformIndex(CGPUProgramIndex::ProjectionTranspose), Projection, Transpose); } if (features.DriverFlags & CGPUProgramFeatures::ProjectionInverseTranspose) { - setUniformMatrix(program, prog->indices().ProjectionInverseTranspose, Projection, InverseTranspose); + setUniformMatrix(program, prog->getUniformIndex(CGPUProgramIndex::ProjectionInverseTranspose), Projection, InverseTranspose); } if (features.DriverFlags & CGPUProgramFeatures::ModelViewProjection) { - setUniformMatrix(program, prog->indices().ModelViewProjection, ModelViewProjection, Identity); + setUniformMatrix(program, prog->getUniformIndex(CGPUProgramIndex::ModelViewProjection), ModelViewProjection, Identity); } if (features.DriverFlags & CGPUProgramFeatures::ModelViewProjectionInverse) { - setUniformMatrix(program, prog->indices().ModelViewProjectionInverse, ModelViewProjection, Inverse); + setUniformMatrix(program, prog->getUniformIndex(CGPUProgramIndex::ModelViewProjectionInverse), ModelViewProjection, Inverse); } if (features.DriverFlags & CGPUProgramFeatures::ModelViewProjectionTranspose) { - setUniformMatrix(program, prog->indices().ModelViewProjectionTranspose, ModelViewProjection, Transpose); + setUniformMatrix(program, prog->getUniformIndex(CGPUProgramIndex::ModelViewProjectionTranspose), ModelViewProjection, Transpose); } if (features.DriverFlags & CGPUProgramFeatures::ModelViewProjectionInverseTranspose) { - setUniformMatrix(program, prog->indices().ModelViewProjectionInverseTranspose, ModelViewProjection, InverseTranspose); + setUniformMatrix(program, prog->getUniformIndex(CGPUProgramIndex::ModelViewProjectionInverseTranspose), ModelViewProjection, InverseTranspose); } if (features.DriverFlags & CGPUProgramFeatures::Fog) { - setUniformFog(program, prog->indices().Fog); + setUniformFog(program, prog->getUniformIndex(CGPUProgramIndex::Fog)); } } @@ -432,7 +432,7 @@ bool CDriverGL::setUniformMaterialInternal(TProgram program, CMaterial &material if (features.MaterialFlags & ~(CGPUProgramFeatures::TextureStages | CGPUProgramFeatures::TextureMatrices)) { - // todo + // none } return true; diff --git a/code/nel/src/3d/gpu_program.cpp b/code/nel/src/3d/gpu_program.cpp index 0ffdb49d9..4e5916398 100644 --- a/code/nel/src/3d/gpu_program.cpp +++ b/code/nel/src/3d/gpu_program.cpp @@ -72,6 +72,26 @@ IGPUProgram::~IGPUProgram() m_DrvInfo.kill(); } +const char *CGPUProgramIndex::Names[NUM_UNIFORMS] = +{ + "modelView", + "modelViewInverse", + "modelViewTranspose", + "modelViewInverseTranspose", + + "projection", + "projectionInverse", + "projectionTranspose", + "projectionInverseTranspose", + + "modelViewProjection", + "modelViewProjectionInverse", + "modelViewProjectionTranspose", + "modelViewProjectionInverseTranspose", + + "fog", +}; + void IGPUProgram::buildInfo(CSource *source) { nlassert(!m_Source); @@ -79,140 +99,10 @@ void IGPUProgram::buildInfo(CSource *source) m_Source = source; // Fill index cache - CGPUProgramFeatures &features = m_Source->Features; - TProfile profile = m_Source->Profile; // for special cases - - if (features.DriverFlags & CGPUProgramFeatures::ModelView) + for (int i = 0; i < CGPUProgramIndex::NUM_UNIFORMS; ++i) { - m_Indices.ModelView = getUniformIndex("modelView"); - if (m_Indices.ModelView == ~0) - { - nlwarning("Missing 'modelView' in gpu program '%s', ModelView disabled", source->DisplayName.c_str()); - features.DriverFlags &= ~CGPUProgramFeatures::ModelView; - } + m_Index.Indices[i] = getUniformIndex(m_Index.Names[i]); } - if (features.DriverFlags & CGPUProgramFeatures::ModelViewInverse) - { - m_Indices.ModelViewInverse = getUniformIndex("modelViewInverse"); - if (m_Indices.ModelViewInverse == ~0) - { - nlwarning("Missing 'modelViewInverse' in gpu program '%s', ModelViewInverse disabled", source->DisplayName.c_str()); - features.DriverFlags &= ~CGPUProgramFeatures::ModelViewInverse; - } - } - if (features.DriverFlags & CGPUProgramFeatures::ModelViewTranspose) - { - m_Indices.ModelViewTranspose = getUniformIndex("modelViewTranspose"); - if (m_Indices.ModelViewTranspose == ~0) - { - nlwarning("Missing 'modelViewTranspose' in gpu program '%s', ModelViewTranspose disabled", source->DisplayName.c_str()); - features.DriverFlags &= ~CGPUProgramFeatures::ModelViewTranspose; - } - } - if (features.DriverFlags & CGPUProgramFeatures::ModelViewInverseTranspose) - { - m_Indices.ModelViewInverseTranspose = getUniformIndex("modelViewInverseTranspose"); - if (m_Indices.ModelViewInverseTranspose == ~0) - { - nlwarning("Missing 'modelViewInverseTranspose' in gpu program '%s', ModelViewInverseTranspose disabled", source->DisplayName.c_str()); - features.DriverFlags &= ~CGPUProgramFeatures::ModelViewInverseTranspose; - } - } - if (features.DriverFlags & CGPUProgramFeatures::Projection) - { - m_Indices.Projection = getUniformIndex("projection"); - if (m_Indices.Projection == ~0) - { - nlwarning("Missing 'projection' in gpu program '%s', Projection disabled", source->DisplayName.c_str()); - features.DriverFlags &= ~CGPUProgramFeatures::Projection; - } - } - if (features.DriverFlags & CGPUProgramFeatures::ProjectionInverse) - { - m_Indices.ProjectionInverse = getUniformIndex("projectionInverse"); - if (m_Indices.ProjectionInverse == ~0) - { - nlwarning("Missing 'projectionInverse' in gpu program '%s', ProjectionInverse disabled", source->DisplayName.c_str()); - features.DriverFlags &= ~CGPUProgramFeatures::ProjectionInverse; - } - } - if (features.DriverFlags & CGPUProgramFeatures::ProjectionTranspose) - { - m_Indices.ProjectionTranspose = getUniformIndex("projectionTranspose"); - if (m_Indices.ProjectionTranspose == ~0) - { - nlwarning("Missing 'projectionTranspose' in gpu program '%s', ProjectionTranspose disabled", source->DisplayName.c_str()); - features.DriverFlags &= ~CGPUProgramFeatures::ProjectionTranspose; - } - } - if (features.DriverFlags & CGPUProgramFeatures::ProjectionInverseTranspose) - { - m_Indices.ProjectionInverseTranspose = getUniformIndex("projectionInverseTranspose"); - if (m_Indices.ProjectionInverseTranspose == ~0) - { - nlwarning("Missing 'projectionInverseTranspose' in gpu program '%s', ProjectionInverseTranspose disabled", source->DisplayName.c_str()); - features.DriverFlags &= ~CGPUProgramFeatures::ProjectionInverseTranspose; - } - } - if (features.DriverFlags & CGPUProgramFeatures::ModelViewProjection) - { - m_Indices.ModelViewProjection = getUniformIndex("modelViewProjection"); - if (m_Indices.ModelViewProjection == ~0) - { - nlwarning("Missing 'modelViewProjection' in gpu program '%s', ModelViewProjection disabled", source->DisplayName.c_str()); - features.DriverFlags &= ~CGPUProgramFeatures::ModelViewProjection; - } - } - if (features.DriverFlags & CGPUProgramFeatures::ModelViewProjectionInverse) - { - m_Indices.ModelViewProjectionInverse = getUniformIndex("modelViewProjectionInverse"); - if (m_Indices.ModelViewProjectionInverse == ~0) - { - nlwarning("Missing 'modelViewProjectionInverse' in gpu program '%s', ModelViewProjectionInverse disabled", source->DisplayName.c_str()); - features.DriverFlags &= ~CGPUProgramFeatures::ModelViewProjectionInverse; - } - } - if (features.DriverFlags & CGPUProgramFeatures::ModelViewProjectionTranspose) - { - m_Indices.ModelViewProjectionTranspose = getUniformIndex("modelViewProjectionTranspose"); - if (m_Indices.ModelViewProjectionTranspose == ~0) - { - nlwarning("Missing 'modelViewProjectionTranspose' in gpu program '%s', ModelViewProjectionTranspose disabled", source->DisplayName.c_str()); - features.DriverFlags &= ~CGPUProgramFeatures::ModelViewProjectionTranspose; - } - } - if (features.DriverFlags & CGPUProgramFeatures::ModelViewProjectionInverseTranspose) - { - m_Indices.ModelViewProjectionInverseTranspose = getUniformIndex("modelViewProjectionInverseTranspose"); - if (m_Indices.ModelViewProjectionInverseTranspose == ~0) - { - nlwarning("Missing 'modelViewProjectionInverseTranspose' in gpu program '%s', ModelViewProjectionInverseTranspose disabled", source->DisplayName.c_str()); - features.DriverFlags &= ~CGPUProgramFeatures::ModelViewProjectionInverseTranspose; - } - } - if (features.DriverFlags & CGPUProgramFeatures::Fog) - { - m_Indices.Fog = getUniformIndex("fog"); - if (m_Indices.Fog == ~0) - { - nlwarning("Missing 'fog' in gpu program '%s', Fog disabled", source->DisplayName.c_str()); - features.DriverFlags &= ~CGPUProgramFeatures::Fog; - } - } - - // - // Rough example, modify as necessary. - // - /*if (features.DriverFlags & CGPUProgramFeatures::DriverAmbient || features.MaterialFlags & CGPUProgramFeatures::MaterialAmbient) - { - m_Indices.Ambient = getUniformIndex("nlAmbient"); - if (m_Indices.Ambient == ~0) - { - nlwarning("Missing 'nlAmbient' in gpu program '%s', Ambient disabled", source->DisplayName.c_str()); - features.DriverFlags &= ~CGPUProgramFeatures::DriverAmbient; - features.MaterialFlags &= ~CGPUProgramFeatures::MaterialAmbient; - } - }*/ buildInfo(); } diff --git a/code/nel/src/3d/landscape.cpp b/code/nel/src/3d/landscape.cpp index e842ae369..bf4cad45f 100644 --- a/code/nel/src/3d/landscape.cpp +++ b/code/nel/src/3d/landscape.cpp @@ -1193,20 +1193,29 @@ void CLandscape::render(const CVector &refineCenter, const CVector &frontVecto // If VertexShader enabled, setup VertexProgram Constants. - if(_VertexShaderOk) + if (_VertexShaderOk) { - // c[0..3] take the ModelViewProjection Matrix. - driver->setConstantMatrix(0, IDriver::ModelViewProjection, IDriver::Identity); - // c[4] take useful constants. - driver->setConstant(4, 0, 1, 0.5f, 0); - // c[5] take RefineCenter - driver->setConstant(5, refineCenter); - // c[6] take info for Geomorph trnasition to TileNear. - driver->setConstant(6, CLandscapeGlobals::TileDistFarSqr, CLandscapeGlobals::OOTileDistDeltaSqr, 0, 0); - // c[10] take the fog vector. - driver->setConstantFog(10); - // c[12] take the current landscape Center / delta Pos to apply - driver->setConstant(12, _PZBModelPosition); + bool uprogstate = driver->isUniformProgramState(); + uint nbvp = uprogstate ? CLandscapeVBAllocator::MaxVertexProgram : 1; + for (uint i = 0; i < nbvp; ++i) + { + // activate the program to set the uniforms in the program state for all programs + // note: when uniforms are driver state, the indices must be the same across programs + if (uprogstate) _TileVB.activateVP(i); + CVertexProgramLandscape *program = _TileVB.getVP(i); + // c[0..3] take the ModelViewProjection Matrix. + driver->setUniformMatrix(IDriver::VertexProgram, program->getUniformIndex(CGPUProgramIndex::ModelViewProjection), IDriver::ModelViewProjection, IDriver::Identity); + // c[4] take useful constants. + driver->setUniform4f(IDriver::VertexProgram, program->idx().ProgramConstants0, 0, 1, 0.5f, 0); + // c[5] take RefineCenter + driver->setuniform3f(IDriver::VertexProgram, program->idx().RefineCenter, refineCenter); + // c[6] take info for Geomorph trnasition to TileNear. + driver->setUniform2f(IDriver::VertexProgram, program->idx().TileDist, CLandscapeGlobals::TileDistFarSqr, CLandscapeGlobals::OOTileDistDeltaSqr); + // c[10] take the fog vector. + driver->setUniformFog(IDriver::VertexProgram, program->getUniformIndex(CGPUProgramIndex::Fog)); + // c[12] take the current landscape Center / delta Pos to apply + driver->setUniform3f(IDriver::VertexProgram, program->idx().PZBModelPosition, _PZBModelPosition); + } } diff --git a/code/nel/src/3d/landscapevb_allocator.cpp b/code/nel/src/3d/landscapevb_allocator.cpp index eccfac66e..4af75c729 100644 --- a/code/nel/src/3d/landscapevb_allocator.cpp +++ b/code/nel/src/3d/landscapevb_allocator.cpp @@ -247,14 +247,23 @@ void CLandscapeVBAllocator::activate(uint vpId) nlassert(_Driver); nlassert(!_BufferLocked); + activateVP(vpId); + + _Driver->activeVertexBuffer(_VB); +} + + +// *************************************************************************** +void CLandscapeVBAllocator::activateVP(uint vpId) +{ + nlassert(_Driver); + // If enabled, activate Vertex program first. - if(_VertexProgram[vpId]) + if (_VertexProgram[vpId]) { //nlinfo("\nSTARTVP\n%s\nENDVP\n", _VertexProgram[vpId]->getProgram().c_str()); nlverify(_Driver->activeVertexProgram(_VertexProgram[vpId])); } - - _Driver->activeVertexBuffer(_VB); } @@ -516,12 +525,11 @@ const char* NL3D_LandscapeTileLightMapEndProgram= // *************************************************************************** void CLandscapeVBAllocator::deleteVertexProgram() { - for(uint i=0;iProfile = nelvp; + source->DisplayName = "Landscape/nelvp"; + switch (type) + { + case CLandscapeVBAllocator::Far0: + source->DisplayName += "/far0"; + source->setSource(std::string(NL3D_LandscapeCommonStartProgram) + + std::string(NL3D_LandscapeFar0EndProgram)); + break; + case CLandscapeVBAllocator::Far1: + source->DisplayName += "/far1"; + source->setSource(std::string(NL3D_LandscapeCommonStartProgram) + + std::string(NL3D_LandscapeFar1EndProgram)); + break; + case CLandscapeVBAllocator::Tile: + source->DisplayName += "/tile"; + if (lightMap) + { + source->DisplayName += "/lightmap"; + source->setSource(std::string(NL3D_LandscapeCommonStartProgram) + + std::string(NL3D_LandscapeTileLightMapEndProgram)); + } + else + { + source->setSource(std::string(NL3D_LandscapeCommonStartProgram) + + std::string(NL3D_LandscapeTileEndProgram)); + } + break; + } + source->ParamIndices["modelViewProjection"] = 0; + source->ParamIndices["programConstants0"] = 4; + source->ParamIndices["refineCenter"] = 5; + source->ParamIndices["tileDist"] = 6; + source->ParamIndices["fog"] = 10; + source->ParamIndices["pzbModelPosition"] = 12; + addSource(source); + } + // TODO_VP_GLSL + { + // .... + } +} +void CVertexProgramLandscape::buildInfo() +{ + m_Idx.ProgramConstants0 = getUniformIndex("programConstants0"); + m_Idx.RefineCenter = getUniformIndex("refineCenter"); + m_Idx.TileDist = getUniformIndex("tileDist"); + m_Idx.PZBModelPosition = getUniformIndex("pzbModelPosition"); +} } // NL3D diff --git a/code/nel/src/3d/water_model.cpp b/code/nel/src/3d/water_model.cpp index 3614d0905..6a239b6e0 100644 --- a/code/nel/src/3d/water_model.cpp +++ b/code/nel/src/3d/water_model.cpp @@ -957,9 +957,9 @@ void CWaterModel::setupMaterialNVertexShader(IDriver *drv, CWaterShape *shape, c drv->setUniform4f(IDriver::VertexProgram, program->idx().DiffuseMapVector0, _ColorMapMatColumn0.x, _ColorMapMatColumn1.x, 0, _ColorMapMatColumn0.x * obsPos.x + _ColorMapMatColumn1.x * obsPos.y + _ColorMapMatPos.x); drv->setUniform4f(IDriver::VertexProgram, program->idx().DiffuseMapVector1, _ColorMapMatColumn0.y, _ColorMapMatColumn1.y, 0, _ColorMapMatColumn0.y * obsPos.x + _ColorMapMatColumn1.y * obsPos.y + _ColorMapMatPos.y); } - /// temp - // drv->setUniformMatrix(IDriver::VertexProgram, program->indices().ModelViewProjection, IDriver::ModelViewProjection, IDriver::Identity); // now set by setUniformDriver in setupMaterial - // drv->setUniformFog(IDriver::VertexProgram, program->indices().Fog); // now set by setUniformDriver in setupMaterial + // set builtins + drv->setUniformMatrix(IDriver::VertexProgram, program->getUniformIndex(CGPUProgramIndex::ModelViewProjection), IDriver::ModelViewProjection, IDriver::Identity); + drv->setUniformFog(IDriver::VertexProgram, program->getUniformIndex(CGPUProgramIndex::Fog)); // retrieve current time double date = scene->getCurrentTime(); // set bumpmaps pos diff --git a/code/nel/src/3d/water_shape.cpp b/code/nel/src/3d/water_shape.cpp index ac586d168..2fff229c8 100644 --- a/code/nel/src/3d/water_shape.cpp +++ b/code/nel/src/3d/water_shape.cpp @@ -89,9 +89,6 @@ CVertexProgramWaterVPNoWave::CVertexProgramWaterVPNoWave(bool diffuse) CSource *source = new CSource(); source->Profile = nelvp; source->DisplayName = "WaterVPNoWave/nelvp"; - source->Features.DriverFlags = - CGPUProgramFeatures::ModelViewProjection - | CGPUProgramFeatures::Fog; source->ParamIndices["modelViewProjection"] = 0; source->ParamIndices["fog"] = 4; source->ParamIndices["bumpMap0Scale"] = 5; @@ -120,9 +117,6 @@ CVertexProgramWaterVPNoWave::CVertexProgramWaterVPNoWave(bool diffuse) // source->Profile = glsl330v; // source->DisplayName = "WaterVPNoWave/glsl330v"; // if (diffuse) source->DisplayName += "/diffuse"; - // source->Features.DriverFlags = - // CGPUProgramFeatures::ModelViewProjection - // | CGPUProgramFeatures::Fog; // source->setSource... // addSource(source); } From 26f4073ad71557ec8961ec618e15503dbccfc867 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Tue, 10 Sep 2013 15:59:31 +0200 Subject: [PATCH 213/313] Simplify --- code/nel/include/nel/3d/gpu_program.h | 27 ++--- .../driver/opengl/driver_opengl_uniform.cpp | 100 ++++++++++-------- code/nel/src/3d/landscape.cpp | 2 +- 3 files changed, 65 insertions(+), 64 deletions(-) diff --git a/code/nel/include/nel/3d/gpu_program.h b/code/nel/include/nel/3d/gpu_program.h index 30fa23c52..e7112d410 100644 --- a/code/nel/include/nel/3d/gpu_program.h +++ b/code/nel/include/nel/3d/gpu_program.h @@ -64,6 +64,15 @@ public: // This is only used for user provided shaders, not for builtin shaders, // as it is a slow method which has to go through all of the options every time. // Builtin shaders should set all flags to 0. +// Example: +// User shader flags Matrices in the Vertex Program: +// -> When rendering with a material, the driver will call setUniformDriver, +// which will check if the flag Matrices exists, and if so, it will use +// the index cache to find which matrices are needed by the shader, +// and set those which are found. +// This does not work extremely efficient, but it's the most practical option +// for passing builtin parameters onto user provided shaders. +// Note: May need additional flags related to scene sorting, etcetera. struct CGPUProgramFeatures { CGPUProgramFeatures() : DriverFlags(0), MaterialFlags(0) { } @@ -72,26 +81,12 @@ struct CGPUProgramFeatures enum TDriverFlags { // Matrices - ModelView = 0x00000001, - ModelViewInverse = 0x00000002, - ModelViewTranspose = 0x00000004, - ModelViewInverseTranspose = 0x00000008, - - Projection = 0x00000010, - ProjectionInverse = 0x00000020, - ProjectionTranspose = 0x00000040, - ProjectionInverseTranspose = 0x00000080, - - ModelViewProjection = 0x00000100, - ModelViewProjectionInverse = 0x00000200, - ModelViewProjectionTranspose = 0x00000400, - ModelViewProjectionInverseTranspose = 0x00000800, + Matrices = 0x00000001, // Fog - Fog = 0x00001000, + Fog = 0x00000002, }; uint32 DriverFlags; - // uint NumLights; enum TMaterialFlags { diff --git a/code/nel/src/3d/driver/opengl/driver_opengl_uniform.cpp b/code/nel/src/3d/driver/opengl/driver_opengl_uniform.cpp index a23235d06..658b6739a 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl_uniform.cpp +++ b/code/nel/src/3d/driver/opengl/driver_opengl_uniform.cpp @@ -309,57 +309,63 @@ bool CDriverGL::setUniformDriver(TProgram program) if (features.DriverFlags) { - if (features.DriverFlags & CGPUProgramFeatures::ModelView) + if (features.DriverFlags & CGPUProgramFeatures::Matrices) { - setUniformMatrix(program, prog->getUniformIndex(CGPUProgramIndex::ModelView), ModelView, Identity); - } - if (features.DriverFlags & CGPUProgramFeatures::ModelViewInverse) - { - setUniformMatrix(program, prog->getUniformIndex(CGPUProgramIndex::ModelViewInverse), ModelView, Inverse); - } - if (features.DriverFlags & CGPUProgramFeatures::ModelViewTranspose) - { - setUniformMatrix(program, prog->getUniformIndex(CGPUProgramIndex::ModelViewTranspose), ModelView, Transpose); - } - if (features.DriverFlags & CGPUProgramFeatures::ModelViewInverseTranspose) - { - setUniformMatrix(program, prog->getUniformIndex(CGPUProgramIndex::ModelViewInverseTranspose), ModelView, InverseTranspose); - } - if (features.DriverFlags & CGPUProgramFeatures::Projection) - { - setUniformMatrix(program, prog->getUniformIndex(CGPUProgramIndex::Projection), Projection, Identity); - } - if (features.DriverFlags & CGPUProgramFeatures::ProjectionInverse) - { - setUniformMatrix(program, prog->getUniformIndex(CGPUProgramIndex::ProjectionInverse), Projection, Inverse); - } - if (features.DriverFlags & CGPUProgramFeatures::ProjectionTranspose) - { - setUniformMatrix(program, prog->getUniformIndex(CGPUProgramIndex::ProjectionTranspose), Projection, Transpose); - } - if (features.DriverFlags & CGPUProgramFeatures::ProjectionInverseTranspose) - { - setUniformMatrix(program, prog->getUniformIndex(CGPUProgramIndex::ProjectionInverseTranspose), Projection, InverseTranspose); - } - if (features.DriverFlags & CGPUProgramFeatures::ModelViewProjection) - { - setUniformMatrix(program, prog->getUniformIndex(CGPUProgramIndex::ModelViewProjection), ModelViewProjection, Identity); - } - if (features.DriverFlags & CGPUProgramFeatures::ModelViewProjectionInverse) - { - setUniformMatrix(program, prog->getUniformIndex(CGPUProgramIndex::ModelViewProjectionInverse), ModelViewProjection, Inverse); - } - if (features.DriverFlags & CGPUProgramFeatures::ModelViewProjectionTranspose) - { - setUniformMatrix(program, prog->getUniformIndex(CGPUProgramIndex::ModelViewProjectionTranspose), ModelViewProjection, Transpose); - } - if (features.DriverFlags & CGPUProgramFeatures::ModelViewProjectionInverseTranspose) - { - setUniformMatrix(program, prog->getUniformIndex(CGPUProgramIndex::ModelViewProjectionInverseTranspose), ModelViewProjection, InverseTranspose); + if (prog->getUniformIndex(CGPUProgramIndex::ModelView) != ~0) + { + setUniformMatrix(program, prog->getUniformIndex(CGPUProgramIndex::ModelView), ModelView, Identity); + } + if (prog->getUniformIndex(CGPUProgramIndex::ModelViewInverse) != ~0) + { + setUniformMatrix(program, prog->getUniformIndex(CGPUProgramIndex::ModelViewInverse), ModelView, Inverse); + } + if (prog->getUniformIndex(CGPUProgramIndex::ModelViewTranspose) != ~0) + { + setUniformMatrix(program, prog->getUniformIndex(CGPUProgramIndex::ModelViewTranspose), ModelView, Transpose); + } + if (prog->getUniformIndex(CGPUProgramIndex::ModelViewInverseTranspose) != ~0) + { + setUniformMatrix(program, prog->getUniformIndex(CGPUProgramIndex::ModelViewInverseTranspose), ModelView, InverseTranspose); + } + if (prog->getUniformIndex(CGPUProgramIndex::Projection) != ~0) + { + setUniformMatrix(program, prog->getUniformIndex(CGPUProgramIndex::Projection), Projection, Identity); + } + if (prog->getUniformIndex(CGPUProgramIndex::ProjectionInverse) != ~0) + { + setUniformMatrix(program, prog->getUniformIndex(CGPUProgramIndex::ProjectionInverse), Projection, Inverse); + } + if (prog->getUniformIndex(CGPUProgramIndex::ProjectionTranspose) != ~0) + { + setUniformMatrix(program, prog->getUniformIndex(CGPUProgramIndex::ProjectionTranspose), Projection, Transpose); + } + if (prog->getUniformIndex(CGPUProgramIndex::ProjectionInverseTranspose) != ~0) + { + setUniformMatrix(program, prog->getUniformIndex(CGPUProgramIndex::ProjectionInverseTranspose), Projection, InverseTranspose); + } + if (prog->getUniformIndex(CGPUProgramIndex::ModelViewProjection) != ~0) + { + setUniformMatrix(program, prog->getUniformIndex(CGPUProgramIndex::ModelViewProjection), ModelViewProjection, Identity); + } + if (prog->getUniformIndex(CGPUProgramIndex::ModelViewProjectionInverse) != ~0) + { + setUniformMatrix(program, prog->getUniformIndex(CGPUProgramIndex::ModelViewProjectionInverse), ModelViewProjection, Inverse); + } + if (prog->getUniformIndex(CGPUProgramIndex::ModelViewProjectionTranspose) != ~0) + { + setUniformMatrix(program, prog->getUniformIndex(CGPUProgramIndex::ModelViewProjectionTranspose), ModelViewProjection, Transpose); + } + if (prog->getUniformIndex(CGPUProgramIndex::ModelViewProjectionInverseTranspose) != ~0) + { + setUniformMatrix(program, prog->getUniformIndex(CGPUProgramIndex::ModelViewProjectionInverseTranspose), ModelViewProjection, InverseTranspose); + } } if (features.DriverFlags & CGPUProgramFeatures::Fog) { - setUniformFog(program, prog->getUniformIndex(CGPUProgramIndex::Fog)); + if (prog->getUniformIndex(CGPUProgramIndex::Fog) != ~0) + { + setUniformFog(program, prog->getUniformIndex(CGPUProgramIndex::Fog)); + } } } diff --git a/code/nel/src/3d/landscape.cpp b/code/nel/src/3d/landscape.cpp index bf4cad45f..c6da6ec27 100644 --- a/code/nel/src/3d/landscape.cpp +++ b/code/nel/src/3d/landscape.cpp @@ -1208,7 +1208,7 @@ void CLandscape::render(const CVector &refineCenter, const CVector &frontVecto // c[4] take useful constants. driver->setUniform4f(IDriver::VertexProgram, program->idx().ProgramConstants0, 0, 1, 0.5f, 0); // c[5] take RefineCenter - driver->setuniform3f(IDriver::VertexProgram, program->idx().RefineCenter, refineCenter); + driver->setUniform3f(IDriver::VertexProgram, program->idx().RefineCenter, refineCenter); // c[6] take info for Geomorph trnasition to TileNear. driver->setUniform2f(IDriver::VertexProgram, program->idx().TileDist, CLandscapeGlobals::TileDistFarSqr, CLandscapeGlobals::OOTileDistDeltaSqr); // c[10] take the fog vector. From 7462d731f1fc30952db72b3e573acde621c63130 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Tue, 10 Sep 2013 16:24:45 +0200 Subject: [PATCH 214/313] Small adjustment to landscape vp parameter setting --- code/nel/src/3d/landscape.cpp | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/code/nel/src/3d/landscape.cpp b/code/nel/src/3d/landscape.cpp index c6da6ec27..7f9898504 100644 --- a/code/nel/src/3d/landscape.cpp +++ b/code/nel/src/3d/landscape.cpp @@ -1199,22 +1199,26 @@ void CLandscape::render(const CVector &refineCenter, const CVector &frontVecto uint nbvp = uprogstate ? CLandscapeVBAllocator::MaxVertexProgram : 1; for (uint i = 0; i < nbvp; ++i) { - // activate the program to set the uniforms in the program state for all programs - // note: when uniforms are driver state, the indices must be the same across programs - if (uprogstate) _TileVB.activateVP(i); CVertexProgramLandscape *program = _TileVB.getVP(i); - // c[0..3] take the ModelViewProjection Matrix. - driver->setUniformMatrix(IDriver::VertexProgram, program->getUniformIndex(CGPUProgramIndex::ModelViewProjection), IDriver::ModelViewProjection, IDriver::Identity); - // c[4] take useful constants. - driver->setUniform4f(IDriver::VertexProgram, program->idx().ProgramConstants0, 0, 1, 0.5f, 0); - // c[5] take RefineCenter - driver->setUniform3f(IDriver::VertexProgram, program->idx().RefineCenter, refineCenter); - // c[6] take info for Geomorph trnasition to TileNear. - driver->setUniform2f(IDriver::VertexProgram, program->idx().TileDist, CLandscapeGlobals::TileDistFarSqr, CLandscapeGlobals::OOTileDistDeltaSqr); - // c[10] take the fog vector. - driver->setUniformFog(IDriver::VertexProgram, program->getUniformIndex(CGPUProgramIndex::Fog)); - // c[12] take the current landscape Center / delta Pos to apply - driver->setUniform3f(IDriver::VertexProgram, program->idx().PZBModelPosition, _PZBModelPosition); + if (program) + { + // activate the program to set the uniforms in the program state for all programs + // note: when uniforms are driver state, the indices must be the same across programs + if (uprogstate) _TileVB.activateVP(i); + + // c[0..3] take the ModelViewProjection Matrix. + driver->setUniformMatrix(IDriver::VertexProgram, program->getUniformIndex(CGPUProgramIndex::ModelViewProjection), IDriver::ModelViewProjection, IDriver::Identity); + // c[4] take useful constants. + driver->setUniform4f(IDriver::VertexProgram, program->idx().ProgramConstants0, 0, 1, 0.5f, 0); + // c[5] take RefineCenter + driver->setUniform3f(IDriver::VertexProgram, program->idx().RefineCenter, refineCenter); + // c[6] take info for Geomorph trnasition to TileNear. + driver->setUniform2f(IDriver::VertexProgram, program->idx().TileDist, CLandscapeGlobals::TileDistFarSqr, CLandscapeGlobals::OOTileDistDeltaSqr); + // c[10] take the fog vector. + driver->setUniformFog(IDriver::VertexProgram, program->getUniformIndex(CGPUProgramIndex::Fog)); + // c[12] take the current landscape Center / delta Pos to apply + driver->setUniform3f(IDriver::VertexProgram, program->idx().PZBModelPosition, _PZBModelPosition); + } } } From 776f198df3be4cdd554d73e3ff7c8033e2f71ada Mon Sep 17 00:00:00 2001 From: kaetemi Date: Tue, 10 Sep 2013 16:42:51 +0200 Subject: [PATCH 215/313] Update some test code --- code/nel/src/3d/water_env_map.cpp | 49 ++++++++++++++++++++++++++----- 1 file changed, 42 insertions(+), 7 deletions(-) diff --git a/code/nel/src/3d/water_env_map.cpp b/code/nel/src/3d/water_env_map.cpp index 3d2f64a1f..65f66e622 100644 --- a/code/nel/src/3d/water_env_map.cpp +++ b/code/nel/src/3d/water_env_map.cpp @@ -227,8 +227,7 @@ void CWaterEnvMap::doInit() } } - -static CVertexProgram testMeshVP( +static const char *testMeshVPstr = "!!VP1.0\n\ DP4 o[HPOS].x, c[0], v[0]; \n\ DP4 o[HPOS].y, c[1], v[0]; \n\ @@ -236,8 +235,45 @@ static CVertexProgram testMeshVP( DP4 o[HPOS].w, c[3], v[0]; \n\ MAD o[COL0], v[8], c[4].xxxx, c[4].yyyy; \n\ MOV o[TEX0], v[8]; \n\ - END" -); + END"; + + +class CVertexProgramTestMeshVP : public CVertexProgram +{ +public: + struct CIdx + { + uint ProgramConstant0; + }; + CVertexProgramTestMeshVP() + { + // nelvp + { + CSource *source = new CSource(); + source->Profile = nelvp; + source->DisplayName = "testMeshVP/nelvp"; + source->setSourcePtr(testMeshVPstr); + source->ParamIndices["modelViewProjection"] = 0; + source->ParamIndices["programConstant0"] = 4; + addSource(source); + } + // TODO_VP_GLSL + } + virtual ~CVertexProgramTestMeshVP() + { + + } + virtual void buildInfo() + { + m_Idx.ProgramConstant0 = getUniformIndex("programConstant0"); + } + inline const CIdx &idx() { return m_Idx; } +private: + CIdx m_Idx; +}; + + +static CVertexProgramTestMeshVP testMeshVP; @@ -260,10 +296,9 @@ void CWaterEnvMap::renderTestMesh(IDriver &driver) driver.activeVertexProgram(&testMeshVP); driver.activeVertexBuffer(_TestVB); driver.activeIndexBuffer(_TestIB); - driver.setConstantMatrix(0, IDriver::ModelViewProjection, IDriver::Identity); // tmp _MaterialPassThruZTest.setTexture(0, _EnvCubic); - driver.setConstantMatrix(0, IDriver::ModelViewProjection, IDriver::Identity); - driver.setConstant(4, 2.f, 1.f, 0.f, 0.f); + driver.setUniformMatrix(IDriver::VertexProgram, testMeshVP.getUniformIndex(CGPUProgramIndex::ModelViewProjection), IDriver::ModelViewProjection, IDriver::Identity); + driver.setUniform2f(IDriver::VertexProgram, testMeshVP.idx().ProgramConstant0, 2.f, 1.f); //driver.renderTriangles(testMat, 0, TEST_VB_NUM_TRIS); driver.renderTriangles(_MaterialPassThruZTest, 0, TEST_VB_NUM_TRIS); driver.activeVertexProgram(NULL); From e6658bdef0b65963cc5569a7025afef38d9c1000 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Tue, 10 Sep 2013 17:01:33 +0200 Subject: [PATCH 216/313] Update decal vp --- code/ryzom/client/src/decal.cpp | 74 +++++++++++++++++++++++++++------ 1 file changed, 62 insertions(+), 12 deletions(-) diff --git a/code/ryzom/client/src/decal.cpp b/code/ryzom/client/src/decal.cpp index da0fdcb82..bac27ebb6 100644 --- a/code/ryzom/client/src/decal.cpp +++ b/code/ryzom/client/src/decal.cpp @@ -84,9 +84,59 @@ static const char *DecalAttenuationVertexProgramCode = MUL o[COL0].w, v[3], R0.w; \n\ END \n"; -static NL3D::CVertexProgram DecalAttenuationVertexProgram(DecalAttenuationVertexProgramCode); +class CVertexProgramDecalAttenuation : public CVertexProgram +{ +public: + struct CIdx + { + // 0-3 mvp + uint WorldToUV0; // 4 + uint WorldToUV1; // 5 + uint RefCamDist; // 6 + uint DistScaleBias; // 7 + uint Diffuse; // 8 + // 9 + // 10 + uint BlendScale; // 11 + }; + CVertexProgramDecalAttenuation() + { + // nelvp + { + CSource *source = new CSource(); + source->Profile = nelvp; + source->DisplayName = "nelvp/DecalAttenuation"; + source->setSourcePtr(DecalAttenuationVertexProgramCode); + source->ParamIndices["modelViewProjection"] = 0; + source->ParamIndices["worldToUV0"] = 4; + source->ParamIndices["worldToUV1"] = 5; + source->ParamIndices["refCamDist"] = 6; + source->ParamIndices["distScaleBias"] = 7; + source->ParamIndices["diffuse"] = 8; + source->ParamIndices["blendScale"] = 11; + addSource(source); + } + // TODO_VP_GLSL + } + ~CVertexProgramDecalAttenuation() + { + + } + virtual void buildInfo() + { + m_Idx.WorldToUV0 = getUniformIndex("worldToUV0"); + m_Idx.WorldToUV1 = getUniformIndex("worldToUV1"); + m_Idx.RefCamDist = getUniformIndex("refCamDist"); + m_Idx.DistScaleBias = getUniformIndex("distScaleBias"); + m_Idx.Diffuse = getUniformIndex("diffuse"); + m_Idx.BlendScale = getUniformIndex("blendScale"); + } + inline const CIdx &idx() const { return m_Idx; } +private: + CIdx m_Idx; +}; -// TODO_VP_GLSL +static CVertexProgramDecalAttenuation DecalAttenuationVertexProgram; typedef CShadowPolyReceiver::CRGBAVertex CRGBAVertex; @@ -312,16 +362,16 @@ void CDecal::renderTriCache(NL3D::IDriver &drv, NL3D::CShadowPolyReceiver &/* memcpy(vba.getVertexCoordPointer(), &_TriCache[0], sizeof(CRGBAVertex) * _TriCache.size()); } drv.activeVertexBuffer(_VB); - drv.setConstantMatrix(0, NL3D::IDriver::ModelViewProjection, NL3D::IDriver::Identity); - drv.setConstant(4, _WorldToUVMatrix[0][0], _WorldToUVMatrix[1][0], _WorldToUVMatrix[2][0], _WorldToUVMatrix[3][0]); - drv.setConstant(5, _WorldToUVMatrix[0][1], _WorldToUVMatrix[1][1], _WorldToUVMatrix[2][1], _WorldToUVMatrix[3][1]); - drv.setConstant(8, _Diffuse.R * (1.f / 255.f), _Diffuse.G * (1.f / 255.f), _Diffuse.B * (1.f / 255.f), 1.f); + drv.setUniformMatrix(IDriver::VertexProgram, DecalAttenuationVertexProgram.getUniformIndex(CGPUProgramIndex::ModelViewProjection), NL3D::IDriver::ModelViewProjection, NL3D::IDriver::Identity); + drv.setUniform4f(IDriver::VertexProgram, DecalAttenuationVertexProgram.idx().WorldToUV0, _WorldToUVMatrix[0][0], _WorldToUVMatrix[1][0], _WorldToUVMatrix[2][0], _WorldToUVMatrix[3][0]); + drv.setUniform4f(IDriver::VertexProgram, DecalAttenuationVertexProgram.idx().WorldToUV1, _WorldToUVMatrix[0][1], _WorldToUVMatrix[1][1], _WorldToUVMatrix[2][1], _WorldToUVMatrix[3][1]); + drv.setUniform4f(IDriver::VertexProgram, DecalAttenuationVertexProgram.idx().Diffuse, _Diffuse.R * (1.f / 255.f), _Diffuse.G * (1.f / 255.f), _Diffuse.B * (1.f / 255.f), 1.f); const NLMISC::CVector &camPos = MainCam.getMatrix().getPos(); - drv.setConstant(6, camPos.x - _RefPosition.x, camPos.y - _RefPosition.y, camPos.z - _RefPosition.z, 1.f); + drv.setUniform4f(IDriver::VertexProgram, DecalAttenuationVertexProgram.idx().RefCamDist, camPos.x - _RefPosition.x, camPos.y - _RefPosition.y, camPos.z - _RefPosition.z, 1.f); // bottom & top blend float bottomBlendScale = 1.f / favoid0(_BottomBlendZMax - _BottomBlendZMin); float topBlendScale = 1.f / favoid0(_TopBlendZMin - _TopBlendZMax); - drv.setConstant(11, bottomBlendScale, bottomBlendScale * (_RefPosition.z - _BottomBlendZMin), + drv.setUniform4f(IDriver::VertexProgram, DecalAttenuationVertexProgram.idx().BlendScale, bottomBlendScale, bottomBlendScale * (_RefPosition.z - _BottomBlendZMin), topBlendScale, topBlendScale * (_RefPosition.z - _TopBlendZMax)); // static volatile bool wantSimpleMat = false; @@ -562,12 +612,12 @@ void CDecalRenderList::renderAllDecals() NL3D::IDriver *drvInternal = ((CDriverUser *) Driver)->getDriver(); // static volatile bool forceNoVertexProgram = false; - if (drvInternal->supportVertexProgram() && !forceNoVertexProgram) + if (!forceNoVertexProgram && drvInternal->compileVertexProgram(&DecalAttenuationVertexProgram)) { - //drvInternal->setConstantMatrix(0, NL3D::IDriver::ModelViewProjection, NL3D::IDriver::Identity); - drvInternal->setConstant(7, _DistScale, _DistBias, 0.f, 1.f); - useVertexProgram = true; drvInternal->activeVertexProgram(&DecalAttenuationVertexProgram); + //drvInternal->setConstantMatrix(0, NL3D::IDriver::ModelViewProjection, NL3D::IDriver::Identity); + drvInternal->setUniform4f(IDriver::VertexProgram, DecalAttenuationVertexProgram.idx().DistScaleBias, _DistScale, _DistBias, 0.f, 1.f); + useVertexProgram = true; } else { From a798aecd71221a6298d69ebb1d0499e814639773 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Tue, 10 Sep 2013 19:52:14 +0200 Subject: [PATCH 217/313] Partial update of veget vp --- code/nel/include/nel/3d/gpu_program.h | 1 + code/nel/include/nel/3d/vegetable_manager.h | 4 +- code/nel/src/3d/vegetable_manager.cpp | 212 +++++++++++++++----- 3 files changed, 164 insertions(+), 53 deletions(-) diff --git a/code/nel/include/nel/3d/gpu_program.h b/code/nel/include/nel/3d/gpu_program.h index e7112d410..b7114a3bd 100644 --- a/code/nel/include/nel/3d/gpu_program.h +++ b/code/nel/include/nel/3d/gpu_program.h @@ -229,6 +229,7 @@ public: // Get the idx of a parameter (ogl: uniform, d3d: constant, etcetera) by name. Invalid name returns ~0 inline uint getUniformIndex(const char *name) const { return m_DrvInfo->getUniformIndex(name); }; + inline uint getUniformIndex(const std::string &name) const { return m_DrvInfo->getUniformIndex(name.c_str()); }; inline uint getUniformIndex(CGPUProgramIndex::TName name) const { return m_Index.Indices[name]; } // Get feature information of the current program diff --git a/code/nel/include/nel/3d/vegetable_manager.h b/code/nel/include/nel/3d/vegetable_manager.h index 71ed235e4..8b3026794 100644 --- a/code/nel/include/nel/3d/vegetable_manager.h +++ b/code/nel/include/nel/3d/vegetable_manager.h @@ -48,6 +48,7 @@ class CVegetableLightEx; // default distance is 60 meters. #define NL3D_VEGETABLE_DEFAULT_DIST_MAX 60.f +class CVertexProgramVeget; // *************************************************************************** /** @@ -306,7 +307,8 @@ private: // The same, but no VBHard. CVegetableVBAllocator _VBSoftAllocator[CVegetableVBAllocator::VBTypeCount]; // Vertex Program. One VertexProgram for each rdrPass (with / without fog) - CVertexProgram *_VertexProgram[NL3D_VEGETABLE_NRDRPASS][2]; + CSmartPtr _VertexProgram[NL3D_VEGETABLE_NRDRPASS][2]; + CRefPtr _ActiveVertexProgram; // Material. Useful for texture and alphaTest diff --git a/code/nel/src/3d/vegetable_manager.cpp b/code/nel/src/3d/vegetable_manager.cpp index 6d9e15752..d8a5aa198 100644 --- a/code/nel/src/3d/vegetable_manager.cpp +++ b/code/nel/src/3d/vegetable_manager.cpp @@ -560,49 +560,144 @@ const char* NL3D_SimpleStartVegetableProgram= MOV o[COL0].xyz, v[3]; # col.RGBA= vertex color \n\ "; +class CVertexProgramVeget : public CVertexProgram +{ +public: + struct CIdx + { + // 0-3 modelViewProjection + // 4 + // 5 + // 6 fog + // 7 + uint ProgramConstants0; // 8 + uint DirectionalLight; // 9 + uint ViewCenter; // 10 + uint NegInvTransDist; // 11 + // 12 + // 13 + // 14 + // 15 + uint AngleAxis; // 16 + uint Wind; // 17 + uint CosCoeff0; // 18 + uint CosCoeff1; // 19 + uint CosCoeff2; // 20 + uint QuatConstants; // 21 + uint PiConstants; // 22 + uint LUTSize; // 23 (value = 64) + uint LUT[NL3D_VEGETABLE_VP_LUT_SIZE]; // 32+ + }; + CVertexProgramVeget(uint vpType, bool fogEnabled) + { + // nelvp + { + CSource *source = new CSource(); + source->Profile = nelvp; + source->DisplayName = "nelvp/Veget"; + + // Init the Vertex Program. + string vpgram; + // start always with Bend. + if( vpType==NL3D_VEGETABLE_RDRPASS_LIGHTED || vpType==NL3D_VEGETABLE_RDRPASS_LIGHTED_2SIDED ) + { + source->DisplayName += "/Bend"; + vpgram= NL3D_BendProgram; + } + else + { + source->DisplayName += "/FastBend"; + vpgram= NL3D_FastBendProgram; + } + // combine the VP according to Type + switch(vpType) + { + case NL3D_VEGETABLE_RDRPASS_LIGHTED: + case NL3D_VEGETABLE_RDRPASS_LIGHTED_2SIDED: + source->DisplayName += "/Lighted"; + vpgram+= string(NL3D_LightedStartVegetableProgram); + break; + case NL3D_VEGETABLE_RDRPASS_UNLIT: + case NL3D_VEGETABLE_RDRPASS_UNLIT_2SIDED: + source->DisplayName += "/Unlit"; + vpgram+= string(NL3D_UnlitVegetableProgram); + break; + case NL3D_VEGETABLE_RDRPASS_UNLIT_2SIDED_ZSORT: + source->DisplayName += "/UnlitAlphaBlend"; + vpgram+= string(NL3D_UnlitAlphaBlendVegetableProgram); + break; + } + + // common end of VP + vpgram+= string(NL3D_CommonEndVegetableProgram); + + if (fogEnabled) + { + source->DisplayName += "/Fog"; + vpgram+= string(NL3D_VegetableProgramFog); + } + + vpgram+="\nEND\n"; + + source->setSource(vpgram); + + source->ParamIndices["programConstants0"] = 8; + source->ParamIndices["directionalLight"] = 9; + source->ParamIndices["viewCenter"] = 10; + source->ParamIndices["negInvTransDist"] = 11; + source->ParamIndices["angleAxis"] = 16; + source->ParamIndices["wind"] = 17; + source->ParamIndices["cosCoeff0"] = 18; + source->ParamIndices["cosCoeff1"] = 19; + source->ParamIndices["cosCoeff2"] = 20; + source->ParamIndices["quatConstants"] = 21; + source->ParamIndices["piConstants"] = 22; + source->ParamIndices["lutSize"] = 23; + for (uint i = 0; i < NL3D_VEGETABLE_VP_LUT_SIZE; ++i) + { + source->ParamIndices[NLMISC::toString("lut[%i]", i)] = 32 + i; + } + + addSource(source); + } + // TODO_VP_GLSL + } + virtual ~CVertexProgramVeget() + { + + } + virtual void buildInfo() + { + m_Idx.ProgramConstants0 = getUniformIndex("programConstants0"); + m_Idx.DirectionalLight = getUniformIndex("directionalLight"); + m_Idx.ViewCenter = getUniformIndex("viewCenter"); + m_Idx.NegInvTransDist = getUniformIndex("negInvTransDist"); + m_Idx.AngleAxis = getUniformIndex("angleAxis"); + m_Idx.Wind = getUniformIndex("wind"); + m_Idx.CosCoeff0 = getUniformIndex("cosCoeff0"); + m_Idx.CosCoeff1 = getUniformIndex("cosCoeff1"); + m_Idx.CosCoeff2 = getUniformIndex("cosCoeff2"); + m_Idx.QuatConstants = getUniformIndex("quatConstants"); + m_Idx.PiConstants = getUniformIndex("piConstants"); + m_Idx.LUTSize = getUniformIndex("lutSize"); + for (uint i = 0; i < NL3D_VEGETABLE_VP_LUT_SIZE; ++i) + { + m_Idx.LUT[i] = getUniformIndex(NLMISC::toString("lut[%i]", i)); + } + } + const CIdx &idx() const { return m_Idx; } +private: + CIdx m_Idx; +}; // *************************************************************************** void CVegetableManager::initVertexProgram(uint vpType, bool fogEnabled) { nlassert(_LastDriver); // update driver should have been called at least once ! - // Init the Vertex Program. - string vpgram; - // start always with Bend. - if( vpType==NL3D_VEGETABLE_RDRPASS_LIGHTED || vpType==NL3D_VEGETABLE_RDRPASS_LIGHTED_2SIDED ) - vpgram= NL3D_BendProgram; - else - vpgram= NL3D_FastBendProgram; - - // combine the VP according to Type - switch(vpType) - { - case NL3D_VEGETABLE_RDRPASS_LIGHTED: - case NL3D_VEGETABLE_RDRPASS_LIGHTED_2SIDED: - vpgram+= string(NL3D_LightedStartVegetableProgram); - break; - case NL3D_VEGETABLE_RDRPASS_UNLIT: - case NL3D_VEGETABLE_RDRPASS_UNLIT_2SIDED: - vpgram+= string(NL3D_UnlitVegetableProgram); - break; - case NL3D_VEGETABLE_RDRPASS_UNLIT_2SIDED_ZSORT: - vpgram+= string(NL3D_UnlitAlphaBlendVegetableProgram); - break; - } - - // common end of VP - vpgram+= string(NL3D_CommonEndVegetableProgram); - - if (fogEnabled) - { - vpgram+= string(NL3D_VegetableProgramFog); - } - - vpgram+="\nEND\n"; - + // create VP. - _VertexProgram[vpType][fogEnabled ? 1 : 0] = new CVertexProgram(vpgram.c_str()); - // TODO_VP_GLSL + _VertexProgram[vpType][fogEnabled ? 1 : 0] = new CVertexProgramVeget(vpType, fogEnabled); } @@ -1758,6 +1853,9 @@ public: // *************************************************************************** void CVegetableManager::setupVertexProgramConstants(IDriver *driver) { + nlassert(_ActiveVertexProgram); + + // Standard // setup VertexProgram constants. // c[0..3] take the ModelViewProjection Matrix. After setupModelMatrix(); @@ -1925,10 +2023,6 @@ void CVegetableManager::render(const CVector &viewCenter, const CVector &front } - // setup VP constants. - setupVertexProgramConstants(driver); - - // Setup TexEnvs for Dynamic lightmapping //-------------------- // if the dynamic lightmap is provided @@ -1968,6 +2062,12 @@ void CVegetableManager::render(const CVector &viewCenter, const CVector &front _VegetableMaterial.setZWrite(true); _VegetableMaterial.setAlphaTestThreshold(0.5f); + bool uprogst = driver->isUniformProgramState(); + bool progstateset[NL3D_VEGETABLE_NRDRPASS]; + for (sint rdrPass=0; rdrPass < NL3D_VEGETABLE_NRDRPASS; rdrPass++) + { + progstateset[rdrPass] = !uprogst; + } /* Prefer sort with Soft / Hard first. @@ -1995,14 +2095,20 @@ void CVegetableManager::render(const CVector &viewCenter, const CVector &front // set the 2Sided flag in the material _VegetableMaterial.setDoubleSided( doubleSided ); - - // Activate the unique material. - driver->setupMaterial(_VegetableMaterial); - // activate Vertex program first. //nlinfo("\nSTARTVP\n%s\nENDVP\n", _VertexProgram[rdrPass]->getProgram().c_str()); - nlverify(driver->activeVertexProgram(_VertexProgram[rdrPass][fogged ? 1 : 0])); + _ActiveVertexProgram = _VertexProgram[rdrPass][fogged ? 1 : 0]; + nlverify(driver->activeVertexProgram(_ActiveVertexProgram)); + + // Set VP constants + if (!progstateset[uprogst ? 0 : rdrPass]) + { + setupVertexProgramConstants(driver); + } + + // Activate the unique material. + driver->setupMaterial(_VegetableMaterial); // Activate the good VBuffer vbAllocator.activate(); @@ -2222,6 +2328,7 @@ void CVegetableManager::render(const CVector &viewCenter, const CVector &front // disable VertexProgram. driver->activeVertexProgram(NULL); + _ActiveVertexProgram = NULL; // restore Fog. @@ -2261,25 +2368,25 @@ void CVegetableManager::setupRenderStateForBlendLayerModel(IDriver *driver) // set model matrix to the manager matrix. driver->setupModelMatrix(_ManagerMatrix); - // setup VP constants. - setupVertexProgramConstants(driver); - // Setup RdrPass. //============= uint rdrPass= NL3D_VEGETABLE_RDRPASS_UNLIT_2SIDED_ZSORT; - // Activate the unique material (correclty setuped for AlphaBlend in render()). - driver->setupMaterial(_VegetableMaterial); - // activate Vertex program first. //nlinfo("\nSTARTVP\n%s\nENDVP\n", _VertexProgram[rdrPass]->getProgram().c_str()); - nlverify(driver->activeVertexProgram(_VertexProgram[rdrPass][fogged ? 1 : 0])); + _ActiveVertexProgram = _VertexProgram[rdrPass][fogged ? 1 : 0]; + nlverify(driver->activeVertexProgram(_ActiveVertexProgram)); + + // setup VP constants. + setupVertexProgramConstants(driver); if (fogged) { driver->setConstantFog(6); } + // Activate the unique material (correclty setuped for AlphaBlend in render()). + driver->setupMaterial(_VegetableMaterial); } @@ -2302,6 +2409,7 @@ void CVegetableManager::exitRenderStateForBlendLayerModel(IDriver *driver) { // disable VertexProgram. driver->activeVertexProgram(NULL); + _ActiveVertexProgram = NULL; // restore Fog. driver->enableFog(_BkupFog); From f0177268b457b0c4cb01cd3f1b77f2d244b7aca5 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Tue, 10 Sep 2013 19:53:41 +0200 Subject: [PATCH 218/313] Rename of a d3d specific class --- .../src/3d/driver/direct3d/driver_direct3d.h | 58 +++++++++---------- .../direct3d/driver_direct3d_shader.cpp | 16 ++--- 2 files changed, 37 insertions(+), 37 deletions(-) diff --git a/code/nel/src/3d/driver/direct3d/driver_direct3d.h b/code/nel/src/3d/driver/direct3d/driver_direct3d.h index b3a8f5f37..3e2d2cdc9 100644 --- a/code/nel/src/3d/driver/direct3d/driver_direct3d.h +++ b/code/nel/src/3d/driver/direct3d/driver_direct3d.h @@ -215,11 +215,11 @@ public: * ********************************** */ // -------------------------------------------------- -class CShader +class CD3DShaderFX { public: - CShader(); - ~CShader(); + CD3DShaderFX(); + ~CD3DShaderFX(); // Load a shader file bool loadShaderFile (const char *filename); @@ -1236,7 +1236,7 @@ public: * ColorOp[n] = DISABLE; * AlphaOp[n] = DISABLE; */ - bool activeShader(CShader *shd); + bool activeShader(CD3DShaderFX *shd); // Bench virtual void startBench (bool wantStandardDeviation = false, bool quick = false, bool reset = true); @@ -2165,7 +2165,7 @@ public: void releaseInternalShaders(); bool setShaderTexture (uint textureHandle, ITexture *texture, CFXCache *cache); - bool validateShader(CShader *shader); + bool validateShader(CD3DShaderFX *shader); void activePass (uint pass) { @@ -2573,7 +2573,7 @@ private: CIndexBuffer _QuadIndexesAGP; // The last setuped shader - CShader *_CurrentShader; + CD3DShaderFX *_CurrentShader; UINT _CurrentShaderPassCount; public: struct CTextureRef @@ -2598,29 +2598,29 @@ private: CRenderVariable *_ModifiedRenderState; // Internal shaders - CShader _ShaderLightmap0; - CShader _ShaderLightmap1; - CShader _ShaderLightmap2; - CShader _ShaderLightmap3; - CShader _ShaderLightmap4; - CShader _ShaderLightmap0Blend; - CShader _ShaderLightmap1Blend; - CShader _ShaderLightmap2Blend; - CShader _ShaderLightmap3Blend; - CShader _ShaderLightmap4Blend; - CShader _ShaderLightmap0X2; - CShader _ShaderLightmap1X2; - CShader _ShaderLightmap2X2; - CShader _ShaderLightmap3X2; - CShader _ShaderLightmap4X2; - CShader _ShaderLightmap0BlendX2; - CShader _ShaderLightmap1BlendX2; - CShader _ShaderLightmap2BlendX2; - CShader _ShaderLightmap3BlendX2; - CShader _ShaderLightmap4BlendX2; - CShader _ShaderCloud; - CShader _ShaderWaterNoDiffuse; - CShader _ShaderWaterDiffuse; + CD3DShaderFX _ShaderLightmap0; + CD3DShaderFX _ShaderLightmap1; + CD3DShaderFX _ShaderLightmap2; + CD3DShaderFX _ShaderLightmap3; + CD3DShaderFX _ShaderLightmap4; + CD3DShaderFX _ShaderLightmap0Blend; + CD3DShaderFX _ShaderLightmap1Blend; + CD3DShaderFX _ShaderLightmap2Blend; + CD3DShaderFX _ShaderLightmap3Blend; + CD3DShaderFX _ShaderLightmap4Blend; + CD3DShaderFX _ShaderLightmap0X2; + CD3DShaderFX _ShaderLightmap1X2; + CD3DShaderFX _ShaderLightmap2X2; + CD3DShaderFX _ShaderLightmap3X2; + CD3DShaderFX _ShaderLightmap4X2; + CD3DShaderFX _ShaderLightmap0BlendX2; + CD3DShaderFX _ShaderLightmap1BlendX2; + CD3DShaderFX _ShaderLightmap2BlendX2; + CD3DShaderFX _ShaderLightmap3BlendX2; + CD3DShaderFX _ShaderLightmap4BlendX2; + CD3DShaderFX _ShaderCloud; + CD3DShaderFX _ShaderWaterNoDiffuse; + CD3DShaderFX _ShaderWaterDiffuse; // Backup frame buffer diff --git a/code/nel/src/3d/driver/direct3d/driver_direct3d_shader.cpp b/code/nel/src/3d/driver/direct3d/driver_direct3d_shader.cpp index 300cfe4b7..5cc6c283c 100644 --- a/code/nel/src/3d/driver/direct3d/driver_direct3d_shader.cpp +++ b/code/nel/src/3d/driver/direct3d/driver_direct3d_shader.cpp @@ -29,7 +29,7 @@ namespace NL3D // *************************************************************************** -CShader::~CShader() +CD3DShaderFX::~CD3DShaderFX() { // Must kill the drv mirror of this shader. _DrvInfo.kill(); @@ -37,14 +37,14 @@ CShader::~CShader() // *************************************************************************** -CShader::CShader() +CD3DShaderFX::CD3DShaderFX() { _ShaderChanged = true; } // *************************************************************************** -void CShader::setText (const char *text) +void CD3DShaderFX::setText (const char *text) { _Text = text; _ShaderChanged = true; @@ -52,7 +52,7 @@ void CShader::setText (const char *text) // *************************************************************************** -void CShader::setName (const char *name) +void CD3DShaderFX::setName (const char *name) { _Name = name; _ShaderChanged = true; @@ -60,7 +60,7 @@ void CShader::setName (const char *name) // *************************************************************************** -bool CShader::loadShaderFile (const char *filename) +bool CD3DShaderFX::loadShaderFile (const char *filename) { _Text = ""; // Lookup @@ -354,7 +354,7 @@ CShaderDrvInfosD3D::~CShaderDrvInfosD3D() // *************************************************************************** -bool CDriverD3D::validateShader(CShader *shader) +bool CDriverD3D::validateShader(CD3DShaderFX *shader) { H_AUTO_D3D(CDriverD3D_validateShader) CShaderDrvInfosD3D *shaderInfo = static_cast((IShaderDrvInfos*)shader->_DrvInfo); @@ -416,7 +416,7 @@ bool CDriverD3D::validateShader(CShader *shader) // *************************************************************************** -bool CDriverD3D::activeShader(CShader *shd) +bool CDriverD3D::activeShader(CD3DShaderFX *shd) { H_AUTO_D3D(CDriverD3D_activeShader) if (_DisableHardwarePixelShader) @@ -482,7 +482,7 @@ bool CDriverD3D::activeShader(CShader *shd) } -static void setFX(CShader &s, const char *name, const char *prog, CDriverD3D *drv) +static void setFX(CD3DShaderFX &s, const char *name, const char *prog, CDriverD3D *drv) { H_AUTO_D3D(setFX) From e07bd3e2a5bcac8a96c801e5578e702657e92b81 Mon Sep 17 00:00:00 2001 From: Quitta Date: Tue, 10 Sep 2013 21:26:03 +0200 Subject: [PATCH 219/313] added documentation for assigned, dblayer and forwarded class --- .hgignore | 10 +- .../ryzom_ams/ams_lib/autoload/assigned.php | 96 ++++++++++++++++--- .../ryzom_ams/ams_lib/autoload/dblayer.php | 30 +++++- .../ryzom_ams/ams_lib/autoload/forwarded.php | 79 +++++++++++++-- 4 files changed, 189 insertions(+), 26 deletions(-) diff --git a/.hgignore b/.hgignore index 5b6bebb35..f0a8e0993 100644 --- a/.hgignore +++ b/.hgignore @@ -200,7 +200,15 @@ code/ryzom/common/data_leveldesign/leveldesign/game_element/xp_table/skills.skil code/ryzom/common/data_leveldesign/leveldesign/game_element/xp_table/xptable.xp_table code/ryzom/tools/server/sql/ryzom_admin_default_data.sql code/ryzom/tools/server/ryzom_ams/drupal -code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib +code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/autoload +code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/configs +code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/cron +code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/img +code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/plugins +code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/smarty +code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/translations +code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/libinclude.php + # Linux server compile code/ryzom/server/src/entities_game_service/entities_game_service code/ryzom/server/src/frontend_service/frontend_service diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/assigned.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/assigned.php index 6d2a558c3..4989767aa 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/assigned.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/assigned.php @@ -1,13 +1,25 @@ execute("SELECT ticket_user.ExternId FROM `assigned` JOIN `ticket_user` ON assigned.User = ticket_user.TUserId WHERE `Ticket` = :ticket_id", Array('ticket_id' => $ticket_id)); @@ -48,6 +71,12 @@ class Assigned{ } + /** + * Check if a ticket is already assigned (in case the user_id param is used, it will check if it's assigned to that user) + * @param $ticket_id the Id of the ticket that's being queried + * @param $user_id the id of the user, default parameter = 0, by using a user_id, it will check if that user is assigned to the ticket. + * @return true in case it's assigned, false in case it isn't. + */ public static function isAssigned( $ticket_id, $user_id = 0) { $dbl = new DBLayer("lib"); //check if ticket is already assigned @@ -62,16 +91,30 @@ class Assigned{ } ////////////////////////////////////////////Methods//////////////////////////////////////////////////// - + + + /** + * A constructor. + * Empty constructor + */ public function __construct() { } - //set values + + /** + * sets the object's attributes. + * @param $values should be an array of the form array('User' => user_id, 'Ticket' => ticket_id). + */ public function set($values) { $this->setUser($values['User']); $this->setTicket($values['Ticket']); } + + /** + * creates a new 'assigned' entry. + * this method will use the object's attributes for creating a new 'assigned' entry in the database. + */ public function create() { $dbl = new DBLayer("lib"); $query = "INSERT INTO `assigned` (`User`,`Ticket`) VALUES (:user, :ticket)"; @@ -79,7 +122,11 @@ class Assigned{ $dbl->execute($query, $values); } - //delete entry + + /** + * deletes an existing 'assigned' entry. + * this method will use the object's attributes for deleting an existing 'assigned' entry in the database. + */ public function delete() { $dbl = new DBLayer("lib"); $query = "DELETE FROM `assigned` WHERE `User` = :user_id and `Ticket` = :ticket_id"; @@ -87,10 +134,14 @@ class Assigned{ $dbl->execute($query, $values); } - //Load with sGroupId - public function load( $user_id, $user_id) { + /** + * loads the object's attributes. + * loads the object's attributes by giving a ticket_id, it will put the matching user_id and the ticket_id into the attributes. + * @param $ticket_id the id of the ticket that should be loaded + */ + public function load($ticket_id) { $dbl = new DBLayer("lib"); - $statement = $dbl->execute("SELECT * FROM `assigned` WHERE `Ticket` = :ticket_id AND `User` = :user_id", Array('ticket_id' => $ticket_id, 'user_id' => $user_id)); + $statement = $dbl->execute("SELECT * FROM `assigned` WHERE `Ticket` = :ticket_id", Array('ticket_id' => $ticket_id)); $row = $statement->fetch(); $this->set($row); } @@ -98,22 +149,37 @@ class Assigned{ ////////////////////////////////////////////Getters//////////////////////////////////////////////////// + /** + * get user attribute of the object. + */ public function getUser(){ return $this->user; } + + /** + * get ticket attribute of the object. + */ public function getTicket(){ return $this->ticket; } ////////////////////////////////////////////Setters//////////////////////////////////////////////////// + /** + * set user attribute of the object. + * @param $u integer id of the user + */ public function setUser($u){ $this->user = $u; } - public function setTicket($g){ - $this->ticket = $g; + /** + * set ticket attribute of the object. + * @param $t integer id of the ticket + */ + public function setTicket($t){ + $this->ticket = $t; } diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/dblayer.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/dblayer.php index c5b7375c3..534eea1b9 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/dblayer.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/dblayer.php @@ -1,8 +1,19 @@ PDO->prepare($query); $statement->execute(); return $statement; } + /** + * execute a query that has parameters + * @param $query the mysql query + * @param $params the parameters that are being used by the query + * @return returns a PDOStatement object + */ public function execute($query,$params){ $statement = $this->PDO->prepare($query); $statement->execute($params); return $statement; } + /** + * execute a query (an insertion query) that has parameters and return the id of it's insertion + * @param $query the mysql query + * @param $params the parameters that are being used by the query + * @return returns the id of the last inserted element. + */ public function executeReturnId($query,$params){ $statement = $this->PDO->prepare($query); $this->PDO->beginTransaction(); diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/forwarded.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/forwarded.php index 003811fe9..54fece58c 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/forwarded.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/forwarded.php @@ -1,13 +1,26 @@ load($ticket_id); @@ -29,6 +48,11 @@ class Forwarded{ } + /** + * check if the ticket is forwarded + * @param $ticket_id the id of the ticket. + * @return returns true if the ticket is forwarded, else return false; + */ public static function isForwarded( $ticket_id) { $dbl = new DBLayer("lib"); if( $dbl->execute(" SELECT * FROM `forwarded` WHERE `Ticket` = :ticket_id", array('ticket_id' => $ticket_id))->rowCount()){ @@ -41,15 +65,29 @@ class Forwarded{ ////////////////////////////////////////////Methods//////////////////////////////////////////////////// + + /** + * A constructor. + * Empty constructor + */ public function __construct() { } - //set values + + /** + * sets the object's attributes. + * @param $values should be an array of the form array('Group' => group_id, 'Ticket' => ticket_id). + */ public function set($values) { $this->setGroup($values['Group']); $this->setTicket($values['Ticket']); } + + /** + * creates a new 'forwarded' entry. + * this method will use the object's attributes for creating a new 'forwarded' entry in the database. + */ public function create() { $dbl = new DBLayer("lib"); $query = "INSERT INTO `forwarded` (`Group`,`Ticket`) VALUES (:group, :ticket)"; @@ -57,7 +95,11 @@ class Forwarded{ $dbl->execute($query, $values); } - //delete entry + + /** + * deletes an existing 'forwarded' entry. + * this method will use the object's attributes for deleting an existing 'forwarded' entry in the database. + */ public function delete() { $dbl = new DBLayer("lib"); $query = "DELETE FROM `forwarded` WHERE `Group` = :group_id and `Ticket` = :ticket_id"; @@ -65,7 +107,12 @@ class Forwarded{ $dbl->execute($query, $values); } - //Load with sGroupId + + /** + * loads the object's attributes. + * loads the object's attributes by giving a ticket_id, it will put the matching group_id and the ticket_id into the attributes. + * @param $ticket_id the id of the ticket that should be loaded + */ public function load( $ticket_id) { $dbl = new DBLayer("lib"); $statement = $dbl->execute("SELECT * FROM `forwarded` WHERE `Ticket` = :ticket_id", Array('ticket_id' => $ticket_id)); @@ -75,21 +122,35 @@ class Forwarded{ ////////////////////////////////////////////Getters//////////////////////////////////////////////////// - + + /** + * get group attribute of the object. + */ public function getGroup(){ return $this->group; } + /** + * get ticket attribute of the object. + */ public function getTicket(){ return $this->ticket; } ////////////////////////////////////////////Setters//////////////////////////////////////////////////// + /** + * set group attribute of the object. + * @param $g integer id of the group + */ public function setGroup($g){ $this->group = $g; } + /** + * set ticket attribute of the object. + * @param $t integer id of the ticket + */ public function setTicket($t){ $this->ticket = $t; } From b77101cdd476206c9671132520b65d56b71db3a1 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 11 Sep 2013 01:12:37 +0200 Subject: [PATCH 220/313] Update d3d implementation and add some debugging code --- .../src/3d/driver/direct3d/driver_direct3d.h | 4 +- .../direct3d/driver_direct3d_material.cpp | 10 ++- .../driver_direct3d_pixel_program.cpp | 4 +- .../driver_direct3d_vertex_program.cpp | 4 +- code/nel/src/3d/landscape.cpp | 2 +- code/nel/src/3d/landscapevb_allocator.cpp | 4 + code/nel/src/3d/stereo_debugger.cpp | 82 +++++++++++++++---- code/nel/src/3d/vegetable_manager.cpp | 15 ++++ code/nel/src/3d/water_env_map.cpp | 1 + code/nel/src/3d/water_shape.cpp | 8 ++ code/ryzom/client/src/decal.cpp | 6 ++ 11 files changed, 112 insertions(+), 28 deletions(-) diff --git a/code/nel/src/3d/driver/direct3d/driver_direct3d.h b/code/nel/src/3d/driver/direct3d/driver_direct3d.h index 3e2d2cdc9..80b39f786 100644 --- a/code/nel/src/3d/driver/direct3d/driver_direct3d.h +++ b/code/nel/src/3d/driver/direct3d/driver_direct3d.h @@ -2542,8 +2542,8 @@ private: // The last vertex buffer needs vertex color bool _FogEnabled; - bool _VertexProgramUser; - bool _PixelProgramUser; + NLMISC::CRefPtr _VertexProgramUser; + NLMISC::CRefPtr _PixelProgramUser; // *** Internal resources diff --git a/code/nel/src/3d/driver/direct3d/driver_direct3d_material.cpp b/code/nel/src/3d/driver/direct3d/driver_direct3d_material.cpp index 07ec5187b..b8d2fa41b 100644 --- a/code/nel/src/3d/driver/direct3d/driver_direct3d_material.cpp +++ b/code/nel/src/3d/driver/direct3d/driver_direct3d_material.cpp @@ -328,7 +328,7 @@ bool CDriverD3D::setupMaterial(CMaterial &mat) pShader = static_cast((IMaterialDrvInfos*)(mat._MatDrvInfo)); // Now we can get the supported shader from the cache. - CMaterial::TShader matShader = mat.getShader(); + CMaterial::TShader matShader = _PixelProgramUser ? CMaterial::Program : mat.getShader(); if (_CurrentMaterialSupportedShader != CMaterial::Normal) { @@ -648,7 +648,9 @@ bool CDriverD3D::setupMaterial(CMaterial &mat) // Must separate texture setup and texture activation in 2 "for"... // because setupTexture() may disable all stage. - if (matShader == CMaterial::Normal || matShader == CMaterial::PostProcessing) + if (matShader == CMaterial::Normal + || ((matShader == CMaterial::Program) && (_PixelProgramUser->features().MaterialFlags & CGPUProgramFeatures::TextureStages)) + ) { uint stage; for(stage=0 ; stagefeatures().MaterialFlags & CGPUProgramFeatures::TextureStages)) + ) { uint stage; for(stage=0 ; stage((IGPUProgramDrvInfos*)program->m_DrvInfo); - _PixelProgramUser = true; + _PixelProgramUser = program; setPixelShader(info->Shader); } else { setPixelShader(NULL); - _PixelProgramUser = false; + _PixelProgramUser = NULL; } return true; diff --git a/code/nel/src/3d/driver/direct3d/driver_direct3d_vertex_program.cpp b/code/nel/src/3d/driver/direct3d/driver_direct3d_vertex_program.cpp index 74ddf9011..3f069edd5 100644 --- a/code/nel/src/3d/driver/direct3d/driver_direct3d_vertex_program.cpp +++ b/code/nel/src/3d/driver/direct3d/driver_direct3d_vertex_program.cpp @@ -379,7 +379,7 @@ bool CDriverD3D::activeVertexProgram (CVertexProgram *program) if (!CDriverD3D::compileVertexProgram(program)) return false; CVertexProgamDrvInfosD3D *info = NLMISC::safe_cast((IGPUProgramDrvInfos*)program->m_DrvInfo); - _VertexProgramUser = true; + _VertexProgramUser = program; setVertexProgram (info->Shader, program); /* D3DRS_FOGSTART and D3DRS_FOGEND must be set with [1, 0] else the fog doesn't work properly on VertexShader and non-VertexShader objects @@ -395,7 +395,7 @@ bool CDriverD3D::activeVertexProgram (CVertexProgram *program) else { setVertexProgram (NULL, NULL); - _VertexProgramUser = false; + _VertexProgramUser = NULL; // Set the old fog range setRenderState (D3DRS_FOGSTART, *((DWORD*) (&_FogStart))); diff --git a/code/nel/src/3d/landscape.cpp b/code/nel/src/3d/landscape.cpp index 7f9898504..06054a48d 100644 --- a/code/nel/src/3d/landscape.cpp +++ b/code/nel/src/3d/landscape.cpp @@ -1204,7 +1204,7 @@ void CLandscape::render(const CVector &refineCenter, const CVector &frontVecto { // activate the program to set the uniforms in the program state for all programs // note: when uniforms are driver state, the indices must be the same across programs - if (uprogstate) _TileVB.activateVP(i); + _TileVB.activateVP(i); // c[0..3] take the ModelViewProjection Matrix. driver->setUniformMatrix(IDriver::VertexProgram, program->getUniformIndex(CGPUProgramIndex::ModelViewProjection), IDriver::ModelViewProjection, IDriver::Identity); diff --git a/code/nel/src/3d/landscapevb_allocator.cpp b/code/nel/src/3d/landscapevb_allocator.cpp index 4af75c729..79f56ab42 100644 --- a/code/nel/src/3d/landscapevb_allocator.cpp +++ b/code/nel/src/3d/landscapevb_allocator.cpp @@ -659,9 +659,13 @@ CVertexProgramLandscape::CVertexProgramLandscape(CLandscapeVBAllocator::TType ty void CVertexProgramLandscape::buildInfo() { m_Idx.ProgramConstants0 = getUniformIndex("programConstants0"); + nlassert(m_Idx.ProgramConstants0 != ~0); m_Idx.RefineCenter = getUniformIndex("refineCenter"); + nlassert(m_Idx.RefineCenter != ~0); m_Idx.TileDist = getUniformIndex("tileDist"); + nlassert(m_Idx.TileDist != ~0); m_Idx.PZBModelPosition = getUniformIndex("pzbModelPosition"); + nlassert(m_Idx.PZBModelPosition != ~0); } } // NL3D diff --git a/code/nel/src/3d/stereo_debugger.cpp b/code/nel/src/3d/stereo_debugger.cpp index 4dc9a39bb..ccb2a6e96 100644 --- a/code/nel/src/3d/stereo_debugger.cpp +++ b/code/nel/src/3d/stereo_debugger.cpp @@ -78,6 +78,48 @@ const char *a_arbfp1 = "MOV result.color.yzw, R0;\n" "END\n"; +const char *a_ps_2_0 = + "ps_2_0\n" + // cgc version 3.1.0013, build date Apr 18 2012 + // command line args: -profile ps_2_0 + // source file: pp_stereo_debug.cg + //vendor NVIDIA Corporation + //version 3.1.0.13 + //profile ps_2_0 + //program pp_stereo_debug + //semantic pp_stereo_debug.cTex0 : TEX0 + //semantic pp_stereo_debug.cTex1 : TEX1 + //var float2 texCoord : $vin.TEXCOORD0 : TEX0 : 0 : 1 + //var sampler2D cTex0 : TEX0 : texunit 0 : 1 : 1 + //var sampler2D cTex1 : TEX1 : texunit 1 : 2 : 1 + //var float4 oCol : $vout.COLOR : COL : 3 : 1 + //const c[0] = 0 1 0.5 + "dcl_2d s0\n" + "dcl_2d s1\n" + "def c0, 0.00000000, 1.00000000, 0.50000000, 0\n" + "dcl t0.xy\n" + "texld r1, t0, s1\n" + "texld r2, t0, s0\n" + "add r0, r2, -r1\n" + "add r1, r2, r1\n" + "mul r1, r1, c0.z\n" + "abs r0, r0\n" + "cmp r0, -r0, c0.x, c0.y\n" + "add_pp_sat r0.x, r0, r0.y\n" + "add_pp_sat r0.x, r0, r0.z\n" + "add_pp_sat r0.x, r0, r0.w\n" + "abs_pp r0.x, r0\n" + "cmp_pp r0.x, -r0, c0.y, c0\n" + "abs_pp r0.x, r0\n" + "mov r2.xzw, r1\n" + "mad r2.y, r1, c0.z, c0.z\n" + "cmp r2, -r0.x, r1, r2\n" + "mad r1.x, r2, c0.z, c0.z\n" + "mov r0.yzw, r2\n" + "cmp r0.x, -r0, r1, r2\n" + "mov oC0, r0\n"; +; + class CStereoDebuggerFactory : public IStereoDeviceFactory { public: @@ -116,27 +158,31 @@ void CStereoDebugger::setDriver(NL3D::UDriver *driver) NL3D::IDriver *drvInternal = (static_cast(driver))->getDriver(); - IGPUProgram::CSource *source = new IGPUProgram::CSource(); - source->Features.MaterialFlags = CGPUProgramFeatures::TextureStages; - - /*if (drvInternal->supportPixelProgram(CPixelProgram::fp40) && drvInternal->supportBloomEffect() && drvInternal->supportNonPowerOfTwoTextures()) + if (drvInternal->supportBloomEffect() && drvInternal->supportNonPowerOfTwoTextures()) { - nldebug("VR: fp40"); - m_PixelProgram = new CPixelProgram(a_fp40); - } - else*/ if (drvInternal->supportPixelProgram(CPixelProgram::arbfp1) && drvInternal->supportBloomEffect() && drvInternal->supportNonPowerOfTwoTextures()) - { - nldebug("VR: arbfp1"); - source->Profile = IGPUProgram::arbfp1; - source->setSourcePtr(a_arbfp1); m_PixelProgram = new CPixelProgram(); - m_PixelProgram->addSource(source); + // arbfp1 + { + IGPUProgram::CSource *source = new IGPUProgram::CSource(); + source->Features.MaterialFlags = CGPUProgramFeatures::TextureStages; + source->Profile = IGPUProgram::arbfp1; + source->setSourcePtr(a_arbfp1); + m_PixelProgram->addSource(source); + } + // ps_2_0 + { + IGPUProgram::CSource *source = new IGPUProgram::CSource(); + source->Features.MaterialFlags = CGPUProgramFeatures::TextureStages; + source->Profile = IGPUProgram::ps_2_0; + source->setSourcePtr(a_ps_2_0); + m_PixelProgram->addSource(source); + } + if (!drvInternal->compilePixelProgram(m_PixelProgram)) + { + delete m_PixelProgram; + m_PixelProgram = NULL; + } } - /*else if (drvInternal->supportPixelProgram(CPixelProgram::ps_2_0)) - { - nldebug("VR: ps_2_0"); - m_PixelProgram = new CPixelProgram(a_ps_2_0); - }*/ if (m_PixelProgram) { diff --git a/code/nel/src/3d/vegetable_manager.cpp b/code/nel/src/3d/vegetable_manager.cpp index d8a5aa198..2fcc25144 100644 --- a/code/nel/src/3d/vegetable_manager.cpp +++ b/code/nel/src/3d/vegetable_manager.cpp @@ -642,6 +642,8 @@ public: source->setSource(vpgram); + source->ParamIndices["modelViewProjection"] = 0; + source->ParamIndices["fog"] = 6; source->ParamIndices["programConstants0"] = 8; source->ParamIndices["directionalLight"] = 9; source->ParamIndices["viewCenter"] = 10; @@ -670,20 +672,33 @@ public: virtual void buildInfo() { m_Idx.ProgramConstants0 = getUniformIndex("programConstants0"); + nlassert(m_Idx.ProgramConstants0 != ~0); m_Idx.DirectionalLight = getUniformIndex("directionalLight"); + nlassert(m_Idx.DirectionalLight != ~0); m_Idx.ViewCenter = getUniformIndex("viewCenter"); + nlassert(m_Idx.ViewCenter != ~0); m_Idx.NegInvTransDist = getUniformIndex("negInvTransDist"); + nlassert(m_Idx.NegInvTransDist != ~0); m_Idx.AngleAxis = getUniformIndex("angleAxis"); + nlassert(m_Idx.AngleAxis != ~0); m_Idx.Wind = getUniformIndex("wind"); + nlassert(m_Idx.Wind != ~0); m_Idx.CosCoeff0 = getUniformIndex("cosCoeff0"); + nlassert(m_Idx.CosCoeff0 != ~0); m_Idx.CosCoeff1 = getUniformIndex("cosCoeff1"); + nlassert(m_Idx.CosCoeff1 != ~0); m_Idx.CosCoeff2 = getUniformIndex("cosCoeff2"); + nlassert(m_Idx.CosCoeff2 != ~0); m_Idx.QuatConstants = getUniformIndex("quatConstants"); + nlassert(m_Idx.QuatConstants != ~0); m_Idx.PiConstants = getUniformIndex("piConstants"); + nlassert(m_Idx.PiConstants != ~0); m_Idx.LUTSize = getUniformIndex("lutSize"); + nlassert(m_Idx.LUTSize != ~0); for (uint i = 0; i < NL3D_VEGETABLE_VP_LUT_SIZE; ++i) { m_Idx.LUT[i] = getUniformIndex(NLMISC::toString("lut[%i]", i)); + nlassert(m_Idx.LUT[i] != ~0); } } const CIdx &idx() const { return m_Idx; } diff --git a/code/nel/src/3d/water_env_map.cpp b/code/nel/src/3d/water_env_map.cpp index 65f66e622..e75fd172a 100644 --- a/code/nel/src/3d/water_env_map.cpp +++ b/code/nel/src/3d/water_env_map.cpp @@ -266,6 +266,7 @@ public: virtual void buildInfo() { m_Idx.ProgramConstant0 = getUniformIndex("programConstant0"); + nlassert(m_Idx.ProgramConstant0 != ~0); } inline const CIdx &idx() { return m_Idx; } private: diff --git a/code/nel/src/3d/water_shape.cpp b/code/nel/src/3d/water_shape.cpp index 2fff229c8..33f9bdfa4 100644 --- a/code/nel/src/3d/water_shape.cpp +++ b/code/nel/src/3d/water_shape.cpp @@ -125,15 +125,23 @@ CVertexProgramWaterVPNoWave::CVertexProgramWaterVPNoWave(bool diffuse) void CVertexProgramWaterVPNoWave::buildInfo() { m_Idx.BumpMap0Scale = getUniformIndex("bumpMap0Scale"); + nlassert(m_Idx.BumpMap0Scale != ~0); m_Idx.BumpMap0Offset = getUniformIndex("bumpMap0Offset"); + nlassert(m_Idx.BumpMap0Offset != ~0); m_Idx.BumpMap1Scale = getUniformIndex("bumpMap1Scale"); + nlassert(m_Idx.BumpMap1Scale != ~0); m_Idx.BumpMap1Offset = getUniformIndex("bumpMap1Offset"); + nlassert(m_Idx.BumpMap1Offset != ~0); m_Idx.ObserverHeight = getUniformIndex("observerHeight"); + nlassert(m_Idx.ObserverHeight != ~0); m_Idx.ScaleReflectedRay = getUniformIndex("scaleReflectedRay"); + nlassert(m_Idx.ScaleReflectedRay != ~0); if (m_Diffuse) { m_Idx.DiffuseMapVector0 = getUniformIndex("diffuseMapVector0"); + nlassert(m_Idx.DiffuseMapVector0 != ~0); m_Idx.DiffuseMapVector1 = getUniformIndex("diffuseMapVector1"); + nlassert(m_Idx.DiffuseMapVector1 != ~0); } } diff --git a/code/ryzom/client/src/decal.cpp b/code/ryzom/client/src/decal.cpp index bac27ebb6..5649b9b57 100644 --- a/code/ryzom/client/src/decal.cpp +++ b/code/ryzom/client/src/decal.cpp @@ -125,11 +125,17 @@ public: virtual void buildInfo() { m_Idx.WorldToUV0 = getUniformIndex("worldToUV0"); + nlassert(m_Idx.WorldToUV0 != ~0); m_Idx.WorldToUV1 = getUniformIndex("worldToUV1"); + nlassert(m_Idx.WorldToUV1 != ~0); m_Idx.RefCamDist = getUniformIndex("refCamDist"); + nlassert(m_Idx.RefCamDist != ~0); m_Idx.DistScaleBias = getUniformIndex("distScaleBias"); + nlassert(m_Idx.DistScaleBias != ~0); m_Idx.Diffuse = getUniformIndex("diffuse"); + nlassert(m_Idx.Diffuse != ~0); m_Idx.BlendScale = getUniformIndex("blendScale"); + nlassert(m_Idx.BlendScale != ~0); } inline const CIdx &idx() const { return m_Idx; } private: From bef4d1d029d3b6300815457d2f3a9edfc23f74cd Mon Sep 17 00:00:00 2001 From: Quitta Date: Wed, 11 Sep 2013 01:38:53 +0200 Subject: [PATCH 221/313] added more documentation.. --- .../ryzom_ams/ams_lib/autoload/assigned.php | 2 - .../ams_lib/autoload/gui_elements.php | 30 ++++++- .../ryzom_ams/ams_lib/autoload/helpers.php | 57 ++++++++++-- .../ams_lib/autoload/in_support_group.php | 62 +++++++++++-- .../ams_lib/autoload/mail_handler.php | 90 +++++++++++++++---- .../ryzom_ams/ams_lib/autoload/mycrypt.php | 38 +++++++- 6 files changed, 240 insertions(+), 39 deletions(-) diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/assigned.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/assigned.php index 4989767aa..8de17a9e2 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/assigned.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/assigned.php @@ -3,7 +3,6 @@ * Handles the assigning of a ticket to a user. This is being used to make someone responsible for the handling and solving of a ticket. * The idea is that someone can easily assign a ticket to himself and by doing that, he makes aware to the other moderators that he will deal with the ticket in the future. * @author Daan Janssens, mentored by Matthew Lagoe -* */ class Assigned{ @@ -92,7 +91,6 @@ class Assigned{ ////////////////////////////////////////////Methods//////////////////////////////////////////////////// - /** * A constructor. * Empty constructor diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/gui_elements.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/gui_elements.php index e9748fc04..e09de1621 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/gui_elements.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/gui_elements.php @@ -1,7 +1,21 @@ getTimestamp(); diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/helpers.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/helpers.php index 24bfc5abd..24aed5483 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/helpers.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/helpers.php @@ -1,8 +1,20 @@ caching = false; $smarty -> cache_lifetime = 120; + //needed by smarty. helpers :: create_folders (); global $FORCE_INGAME; + + //if ingame, then use the ingame templates if ( helpers::check_if_game_client() or $FORCE_INGAME ){ $smarty -> template_dir = $AMS_LIB . '/ingame_templates/'; $smarty -> setConfigDir( $AMS_LIB . '/configs' ); @@ -41,12 +56,13 @@ class Helpers{ $smarty -> assign( $key, $value ); } - + //load page specific variables that are language dependent $variables = Helpers::handle_language(); foreach ( $variables[$template] as $key => $value ){ $smarty -> assign( $key, $value ); } + //smarty inheritance for loading the matching wrapper layout (with the matching menu bar) if( isset($vars['permission']) && $vars['permission'] == 3 ){ $inherited = "extends:layout_admin.tpl|"; }else if( isset($vars['permission']) && $vars['permission'] == 2){ @@ -57,8 +73,7 @@ class Helpers{ $inherited =""; } - - + //if $returnHTML is set to true, return the html by fetching the template else display the template. if($returnHTML == true){ return $smarty ->fetch($inherited . $template . '.tpl' ); }else{ @@ -66,6 +81,11 @@ class Helpers{ } } + + /** + * creates the folders that are needed for smarty. + * @todo for the drupal module it might be possible that drupal_mkdir needs to be used instead of mkdir, also this should be in the install.php instead. + */ static public function create_folders(){ global $AMS_LIB; global $SITEBASE; @@ -78,8 +98,8 @@ class Helpers{ $SITEBASE . '/configs' ); foreach ( $arr as & $value ){ + if ( !file_exists( $value ) ){ - //echo $value; print($value); mkdir($value); } @@ -87,6 +107,11 @@ class Helpers{ } + + /** + * check if the http request is sent ingame or not. + * @return returns true in case it's sent ingame, else false is returned. + */ static public function check_if_game_client() { // if HTTP_USER_AGENT is not set then its ryzom core @@ -98,6 +123,13 @@ class Helpers{ } } + + /** + * Handles the language specific aspect. + * The language can be changed by setting the $_GET['Language'] & $_GET['setLang'] together. This will also change the language entry of the user in the db. + * Cookies are also being used in case the user isn't logged in. + * @return returns the parsed content of the language .ini file related to the users language setting. + */ static public function handle_language(){ global $DEFAULT_LANGUAGE; global $AMS_TRANS; @@ -149,8 +181,11 @@ class Helpers{ } - - //Time output function for handling the time display function. + + /** + * Time output function for handling the time display. + * @return returns the time in the format specified in the $TIME_FORMAT global variable. + */ static public function outputTime($time, $str = 1){ global $TIME_FORMAT; if($str){ @@ -160,6 +195,12 @@ class Helpers{ } } + /** + * Auto login function for ingame use. + * This function will allow users who access the website ingame, to log in without entering the username and password. It uses the COOKIE entry in the open_ring db. + * it checks if the cookie sent by the http request matches the one in the db. This cookie in the db is changed everytime the user relogs. + * @return returns "FALSE" if the cookies didn't match, else it returns an array with the user's id and name. + */ static public function check_login_ingame(){ if ( helpers :: check_if_game_client () or $forcelibrender = false ){ $dbr = new DBLayer("ring"); diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/in_support_group.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/in_support_group.php index b01517db2..bf10d3d9a 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/in_support_group.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/in_support_group.php @@ -1,13 +1,23 @@ user_id, 'Group' => support_groups_id). + */ public function set($values) { $this->setUser($values['User']); $this->setGroup($values['Group']); } + + /** + * creates a new 'in_support_group' entry. + * this method will use the object's attributes for creating a new 'in_support_group' entry in the database. + */ public function create() { $dbl = new DBLayer("lib"); $query = "INSERT INTO `in_support_group` (`User`,`Group`) VALUES (:user, :group)"; @@ -36,7 +59,11 @@ class In_Support_Group{ $dbl->execute($query, $values); } - //delete entry + + /** + * deletes an existing 'in_support_group' entry. + * this method will use the object's attributes for deleting an existing 'in_support_group' entry in the database. + */ public function delete() { $dbl = new DBLayer("lib"); $query = "DELETE FROM `in_support_group` WHERE `User` = :user_id and `Group` = :group_id"; @@ -44,31 +71,48 @@ class In_Support_Group{ $dbl->execute($query, $values); } - //Load with sGroupId - public function load( $user_id, $group_id) { + /* + public function load($group_id) { $dbl = new DBLayer("lib"); $statement = $dbl->execute("SELECT * FROM `in_support_group` WHERE `Group` = :group_id", Array('group_id' => $group_id)); $row = $statement->fetch(); $this->set($row); } + */ ////////////////////////////////////////////Getters//////////////////////////////////////////////////// + /** + * get user attribute of the object. + */ public function getUser(){ return $this->user; } + + /** + * get group attribute of the object. + */ public function getGroup(){ return $this->group; } ////////////////////////////////////////////Setters//////////////////////////////////////////////////// + /** + * set user attribute of the object. + * @param $u integer id of the user + */ public function setUser($u){ $this->user = $u; } + + /** + * set group attribute of the object. + * @param $g integer id of the support group + */ public function setGroup($g){ $this->group = $g; } diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php index 4434c62db..18422474f 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php @@ -1,23 +1,42 @@ config = $cryptinfo; } - + /** + * encrypts by using the given enc_method and hash_method. + * It will first check if the methods are supported, if not it will throw an error, if so it will encrypt the $data + * @param $data the string that we want to encrypt. + * @return the encrypted string. + */ public function encrypt($data) { self::check_methods($this->config['enc_method'], $this->config['hash_method']); @@ -17,6 +31,11 @@ class MyCrypt{ return $infostr . openssl_encrypt($data, $this->config['enc_method'], $this->config['key'], false, $iv); } + /** + * decrypts by using the given enc_method and hash_method. + * @param $edata the encrypted string that we want to decrypt + * @return the decrypted string. + */ public function decrypt($edata) { $e_arr = explode('$', $edata); if( count($e_arr) != 4 ) { @@ -29,6 +48,13 @@ class MyCrypt{ return openssl_decrypt($e_arr[3], $this->config['enc_method'], $this->config['key'], false, $iv); } + /** + * hashes the key by using a hash method specified. + * @param $key the key to be hashed + * @param $method the metho of hashing to be used + * @param $iv_size the size of the initialization vector. + * @return return the hashed key up till the size of the iv_size param. + */ private static function hashIV($key, $method, $iv_size) { $myhash = hash($method, $key, TRUE); while( strlen($myhash) < $iv_size ) { @@ -37,6 +63,12 @@ class MyCrypt{ return substr($myhash, 0, $iv_size); } + /** + * checks if the encryption and hash methods are supported + * @param $enc the encryption method. + * @param $hash the hash method. + * @throw Exception in case a method is not supported. + */ private static function check_methods($enc, $hash) { if( ! function_exists('openssl_encrypt') ) { From 135e57720c1de22c21fb007b8683ccf8641c204d Mon Sep 17 00:00:00 2001 From: Quitta Date: Wed, 11 Sep 2013 16:56:00 +0200 Subject: [PATCH 222/313] Even more docs! writing documentation sucks.. --- .../ryzom_ams/ams_lib/autoload/pagination.php | 51 ++++- .../ryzom_ams/ams_lib/autoload/querycache.php | 69 +++++-- .../ams_lib/autoload/support_group.php | 186 +++++++++++++++--- 3 files changed, 267 insertions(+), 39 deletions(-) diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/pagination.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/pagination.php index 3c4473170..182d9f5af 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/pagination.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/pagination.php @@ -1,12 +1,28 @@ current= 1; @@ -52,22 +68,47 @@ class Pagination{ } + /** + * return the number of the 'last' object attribute + * @return the number of the last page + */ public function getLast(){ return $this->last; } + + /** + * return the number of the 'current' object attribute + * @return the number of the current page + */ public function getCurrent(){ return $this->current; } + + /** + * return the elements array of the object + * @return the elements of a specific page (these are instantiations of the class passed as parameter ($resultClass) to the constructor) + */ public function getElements(){ return $this->element_array; } + + /** + * return total amount of rows for the original query + * @return the total amount of rows for the original query + */ public function getAmountOfRows(){ return $this->amountOfRows; } + /** + * return the page links. + * (for browsing the pages, placed under a table for example) the $nrOfLinks parameter specifies the amount of links you want to return. + * it will show the links closest to the current page on both sides (in case one side can't show more, it will show more on the other side) + * @return an array of integerswhich refer to the clickable pagenumbers for browsing other pages. + */ public function getLinks($nrOfLinks){ $pageLinks = Array(); //if amount of showable links is greater than the amount of pages: show all! diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/querycache.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/querycache.php index 9ca6c627d..6be551225 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/querycache.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/querycache.php @@ -1,21 +1,37 @@ sid, 'type' => type, 'query' => query, 'db' => db). + */ public function set($values) { $this->setSID($values['SID']); $this->setType($values['type']); @@ -24,7 +40,11 @@ class Querycache{ } - //return constructed element based on SID + /** + * loads the object's attributes. + * loads the object's attributes by giving a SID as parameter + * @param $id the id of the querycaches row + */ public function load_With_SID( $id) { $dbl = new DBLayer("lib"); $statement = $dbl->execute("SELECT * FROM ams_querycache WHERE SID=:id", array('id' => $id)); @@ -33,7 +53,9 @@ class Querycache{ } - //update private data to DB. + /** + * updates the entry. + */ public function update(){ $dbl = new DBLayer("lib"); $query = "UPDATE ams_querycache SET type= :t, query = :q, db = :d WHERE SID=:id"; @@ -43,39 +65,64 @@ class Querycache{ ////////////////////////////////////////////Getters//////////////////////////////////////////////////// + /** + * get SID attribute of the object. + */ public function getSID(){ return $this->SID; } - + /** + * get type attribute of the object. + */ public function getType(){ return $this->type; } - + /** + * get query attribute of the object. + */ public function getQuery(){ return $this->query; } + /** + * get db attribute of the object. + */ public function getDb(){ return $this->db; } ////////////////////////////////////////////Setters//////////////////////////////////////////////////// + /** + * set SID attribute of the object. + * @param $s integer id + */ public function setSID($s){ $this->SID = $s; } - + /** + * set type attribute of the object. + * @param $t type of the query, could be changePassword, changePermissions, changeEmail, createUser + */ public function setType($t){ $this->type = $t; } + /** + * set query attribute of the object. + * @param $q query string + */ public function setQuery($q){ $this->query= $q; } + /** + * set db attribute of the object. + * @param $d the name of the database in the config global var that we want to use. + */ public function setDb($d){ $this->db= $d; } diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/support_group.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/support_group.php index 6975cb183..919f82608 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/support_group.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/support_group.php @@ -1,18 +1,27 @@ execute("SELECT * FROM support_group WHERE SGroupId = :id", array('id' => $id)); @@ -23,7 +32,11 @@ class Support_Group{ } - //return all groups + + /** + * return all support_group objects. + * @return an array containing all support_group objects. + */ public static function getGroups() { $dbl = new DBLayer("lib"); $statement = $dbl->executeWithoutParams("SELECT * FROM support_group ORDER BY Name ASC"); @@ -38,7 +51,14 @@ class Support_Group{ return $result; } - //wrapper for creating a support group + + /** + * Wrapper for creating a support group. + * It will check if the support group doesn't exist yet, if the tag or name already exists then NAME_TAKEN or TAG_TAKEN will be returned. + * If the name is bigger than 20 characters or smaller than 4 and the tag greater than 7 or smaller than 2 a SIZE_ERROR will be returned. + * Else it will return SUCCESS + * @return a string that specifies if it was a success or not (SUCCESS, SIZE_ERROR, NAME_TAKEN or TAG_TAKEN ) + */ public static function createSupportGroup( $name, $tag, $groupemail, $imap_mailserver, $imap_username, $imap_password) { if(strlen($name) < 21 && strlen($name) > 4 &&strlen($tag) < 8 && strlen($tag) > 1 ){ @@ -70,7 +90,12 @@ class Support_Group{ } } - //check if group exists + /** + * check if support group name/tag doesn't exist yet. + * @param $name the name of the group we want to check + * @param $tag the tag of the group we want to check + * @return if name is already taken return NAME_TAKEN, else if tag is already taken return TAG_TAKEN, else return success. + */ public static function supportGroup_EntryNotExists( $name, $tag) { $dbl = new DBLayer("lib"); //check if name is already used @@ -85,7 +110,11 @@ class Support_Group{ } - //check if group exists + /** + * check if support group entry coupled to a given id exist or not. + * @param $id the id of the group we want to check + * @return true or false. + */ public static function supportGroup_Exists( $id) { $dbl = new DBLayer("lib"); //check if supportgroup id exist @@ -97,14 +126,23 @@ class Support_Group{ } - //return constructed element based on SGroupId + /** + * construct an object based on the SGroupId. + * @param $id the id of the group we want to construct + * @return the constructed support group object + */ public static function constr_SGroupId( $id) { $instance = new self(); $instance->setSGroup($id); return $instance; } - //returns list of all users that are enlisted to a support group + + /** + * get list of all users that are enlisted to a support group. + * @param $id the id of the group we want to query + * @return an array of ticket_user objects that are in the support group. + */ public static function getAllUsersOfSupportGroup($group_id) { $dbl = new DBLayer("lib"); $statement = $dbl->execute("SELECT * FROM `in_support_group` INNER JOIN `ticket_user` ON ticket_user.TUserId = in_support_group.User WHERE in_support_group.Group=:id", array('id' => $group_id)); @@ -120,7 +158,13 @@ class Support_Group{ return $result; } - //wrapper for adding user to a support group + + /** + * wrapper for deleting a support group. + * We will first check if the group really exists, if not than "GROUP_NOT_EXISING" will be returned. + * @param $group_id the id of the group we want to delete + * @return an array of ticket_user objects that are in the support group. + */ public static function deleteSupportGroup($group_id) { //check if group id exists @@ -135,7 +179,15 @@ class Support_Group{ } - //wrapper for adding user to a support group + /** + * wrapper for deleting a user that's in a specified support group. + * We will first check if the group really exists, if not than "GROUP_NOT_EXISING" will be returned. + * Afterwards we will check if the user exists in the support group, if not "USER_NOT_IN_GROUP" will be returned. + * Else the users entry in the in_support_group table will be deleted and "SUCCESS" will be returned. + * @param $user_id the id of the user we want to remove out of the group. + * @param $group_id the id of the group the user should be in + * @return a string (SUCCESS, USER_NOT_IN_GROUP or GROUP_NOT_EXISTING) + */ public static function deleteUserOfSupportGroup( $user_id, $group_id) { //check if group id exists @@ -164,7 +216,16 @@ class Support_Group{ } - //wrapper for adding user to a support group + + /** + * wrapper for adding a user to a specified support group. + * We will first check if the group really exists, if not than "GROUP_NOT_EXISING" will be returned. + * Afterwards we will check if the user exists in the support group, if so "ALREADY_ADDED" will be returned. + * Else the user will be added to the in_support_group table and "SUCCESS" will be returned. + * @param $user_id the id of the user we want to add to the group. + * @param $group_id the id of the group the user wants to be in + * @return a string (SUCCESS, ALREADY_ADDED or GROUP_NOT_EXISTING) + */ public static function addUserToSupportGroup( $user_id, $group_id) { //check if group id exists if (self::supportGroup_Exists($group_id)){ @@ -191,7 +252,12 @@ class Support_Group{ } - //returns list of all category objects + + /** + * return all support_group objects. + * @return an array containing all support_group objects. + * @deprecated should be removed in the future, because getGroups does the same. + */ public static function getAllSupportGroups() { $dbl = new DBLayer("lib"); $statement = $dbl->executeWithoutParams("SELECT * FROM `support_group`"); @@ -206,10 +272,19 @@ class Support_Group{ } ////////////////////////////////////////////Methods//////////////////////////////////////////////////// + + /** + * A constructor. + * Empty constructor + */ public function __construct() { } - //set values + + /** + * sets the object's attributes. + * @param $values should be an array of the form array('SGroupId' => groupid, 'Name' => name, 'Tag' => tag, 'GroupEmail' => mail, 'IMAP_MailServer' => server, 'IMAP_Username' => username,'IMAP_Password' => pass). + */ public function set($values) { $this->setSGroupId($values['SGroupId']); $this->setName($values['Name']); @@ -220,6 +295,11 @@ class Support_Group{ $this->setIMAP_Password($values['IMAP_Password']); } + + /** + * creates a new 'support_group' entry. + * this method will use the object's attributes for creating a new 'support_group' entry in the database. + */ public function create() { $dbl = new DBLayer("lib"); $query = "INSERT INTO support_group (Name, Tag, GroupEmail, IMAP_MailServer, IMAP_Username, IMAP_Password) VALUES (:name, :tag, :groupemail, :imap_mailserver, :imap_username, :imap_password)"; @@ -227,7 +307,12 @@ class Support_Group{ $dbl->execute($query, $values); } - //Load with sGroupId + + /** + * loads the object's attributes. + * loads the object's attributes by giving a group id, it will put the matching groups attributes in the object. + * @param $id the id of the support group that should be loaded + */ public function load_With_SGroupId( $id) { $dbl = new DBLayer("lib"); $statement = $dbl->execute("SELECT * FROM `support_group` WHERE `SGroupId` = :id", array('id' => $id)); @@ -236,7 +321,9 @@ class Support_Group{ } - //update private data to DB. + /** + * update the objects attributes to the db. + */ public function update(){ $dbl = new DBLayer("lib"); $query = "UPDATE `support_group` SET `Name` = :name, `Tag` = :tag, `GroupEmail` = :groupemail, `IMAP_MailServer` = :mailserver, `IMAP_Username` = :username, `IMAP_Password` = :password WHERE `SGroupId` = :id"; @@ -244,7 +331,11 @@ class Support_Group{ $statement = $dbl->execute($query, $values); } - //delete entry + + /** + * deletes an existing 'support_group' entry. + * this method will use the object's attributes for deleting an existing 'support_group' entry in the database. + */ public function delete(){ $dbl = new DBLayer("lib"); $query = "DELETE FROM `support_group` WHERE `SGroupId` = :id"; @@ -254,59 +345,108 @@ class Support_Group{ ////////////////////////////////////////////Getters//////////////////////////////////////////////////// + /** + * get sGroupId attribute of the object. + */ public function getSGroupId(){ return $this->sGroupId; } + /** + * get name attribute of the object. + */ public function getName(){ return $this->name; } + /** + * get tag attribute of the object. + */ public function getTag(){ return $this->tag; } + /** + * get groupEmail attribute of the object. + */ public function getGroupEmail(){ return $this->groupEmail; } + /** + * get iMAP_MailServer attribute of the object. + */ public function getIMAP_MailServer(){ return $this->iMAP_MailServer; } + /** + * get iMAP_Username attribute of the object. + */ public function getIMAP_Username(){ return $this->iMAP_Username; } + /** + * get iMAP_Password attribute of the object. + */ public function getIMAP_Password(){ return $this->iMap_Password; } ////////////////////////////////////////////Setters//////////////////////////////////////////////////// + /** + * set sGroupId attribute of the object. + * @param $id integer id of the group + */ public function setSGroupId($id){ $this->sGroupId = $id; } + /** + * set name attribute of the object. + * @param $n name of the group + */ public function setName($n){ $this->name = $n; } + /** + * set tag attribute of the object. + * @param $t tag of the group + */ public function setTag($t){ $this->tag = $t; } + /** + * set groupEmail attribute of the object. + * @param $ge email of the group + */ public function setGroupEmail($ge){ $this->groupEmail = $ge; } + /** + * set iMAP_MailServer attribute of the object. + * @param $ms mailserver of the group + */ public function setIMAP_MailServer($ms){ $this->iMAP_MailServer = $ms; } + /** + * set iMAP_Username attribute of the object. + * @param $u imap username of the group + */ public function setIMAP_Username($u){ $this->iMAP_Username = $u; } + /** + * set iMAP_Password attribute of the object. + * @param $p imap password of the group + */ public function setIMAP_Password($p){ $this->iMap_Password = $p; } From ce36454383afc4f8a56c0626a529b288baea934f Mon Sep 17 00:00:00 2001 From: Quitta Date: Wed, 11 Sep 2013 23:51:23 +0200 Subject: [PATCH 223/313] added extra documentation. Man, writing documentation for the ticket class was quite a job :D --- .../ryzom_ams/ams_lib/autoload/querycache.php | 2 +- .../ryzom_ams/ams_lib/autoload/sync.php | 19 +- .../ryzom_ams/ams_lib/autoload/ticket.php | 276 ++++++++++++++---- .../ams_lib/autoload/ticket_category.php | 56 +++- .../ams_lib/autoload/ticket_content.php | 53 +++- 5 files changed, 326 insertions(+), 80 deletions(-) diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/querycache.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/querycache.php index 6be551225..3da0887c9 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/querycache.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/querycache.php @@ -1,7 +1,7 @@ execute(" SELECT * FROM `ticket` WHERE `TId` = :ticket_id", array('ticket_id' => $id) )->rowCount() ){ return true; }else{ @@ -26,26 +34,31 @@ class Ticket{ } } - /*FUNCTION: getStatusArray - * returns all possible statusses - * + + /** + * return an array of the possible statuses + * @return an array containing the string values that represent the different statuses. */ public static function getStatusArray() { return Array("Waiting on user reply","Waiting on support","Waiting on Dev reply","Closed"); } - - /*FUNCTION: getPriorityArray - * returns all possible statusses - * + + + /** + * return an array of the possible priorities + * @return an array containing the string values that represent the different priorities. */ public static function getPriorityArray() { return Array("Low","Normal","High","Super Dupa High"); } - /*FUNCTION: getEntireTicket - * return all ticket of the given author's id. - * + /** + * return an entire ticket. + * returns the ticket object and an array of all replies to that ticket. + * @param $id the id of the ticket. + * @param $view_as_admin true if the viewer of the ticket is a mod, else false (depending on this it will also show the hidden comments) + * @return an array containing the 'ticket_obj' and a 'reply_array', which is an array containing all replies to that ticket. */ public static function getEntireTicket($id,$view_as_admin) { $ticket = new Ticket(); @@ -55,10 +68,12 @@ class Ticket{ } - /*FUNCTION: getTicketTitlesOf - * return all ticket of the given author's id. - * - */ + /** + * return all tickets of a specific user. + * an array of all tickets created by a specific user are returned by this function. + * @param $author the id of the user of whom we want all tickets from. + * @return an array containing all ticket objects related to a user. + */ public static function getTicketsOf($author) { $dbl = new DBLayer("lib"); $statement = $dbl->execute("SELECT * FROM ticket INNER JOIN ticket_user ON ticket.Author = ticket_user.TUserId and ticket_user.ExternId=:id", array('id' => $author)); @@ -79,10 +94,21 @@ class Ticket{ } - /*FUNCTION: create_Ticket() - * creates a ticket + first initial reply and fills in the content of it! - * for_support_group defines to which support group the ticket has to be forwarded - */ + + /** + * function that creates a new ticket. + * A new ticket will be created, in case the extra_info != 0 and the http request came from ingame, then a ticket_info page will be created. + * A log entry will be written, depending on the $real_authors value. In case the for_support_group parameter is set, the ticket will be forwarded immediately. + * Also the mail handler will create a new email that will be sent to the author to notify him that his ticket is freshly created. + * @param $title the title we want to give to the ticket. + * @param $content the content we want to give to the starting post of the ticket. + * @param $category the id of the category that should be related to the ticket. + * @param $author the person who's id will be stored in the database as creator of the ticket. + * @param $real_author should be the same id, or a moderator/admin who creates a ticket for another user (this is used for logging purposes). + * @param $for_support_group in case you directly want to forward the ticket after creating it. (default value = 0 = don't forward) + * @param $extra_info used for creating an ticket_info page related to the ticket, this only happens when the ticket is made ingame. + * @return the created tickets id. + */ public static function create_Ticket( $title, $content, $category, $author, $real_author, $for_support_group = 0, $extra_info = 0) { //create the new ticket! @@ -117,10 +143,14 @@ class Ticket{ } - /*FUNCTION: updateTicketStatus() - * - * - */ + + /** + * updates the ticket's status. + * A log entry about this will be created only if the newStatus is different from the current status. + * @param $ticket_id the id of the ticket of which we want to change the status. + * @param $newStatus the new status value (integer) + * @param $author the user (id) that performed the update status action + */ public static function updateTicketStatus( $ticket_id, $newStatus, $author) { $ticket = new Ticket(); @@ -134,10 +164,15 @@ class Ticket{ } - /*FUNCTION: updateTicketStatusAndPriority() - * creates a ticket + first initial reply and fills in the content of it! - * - */ + /** + * updates the ticket's status & priority. + * A log entry about this will be created only if the newStatus is different from the current status and also when the newPriority is different from the current priority. + * @todo break this function up into a updateStatus (already exists) and updatePriority function and perhaps write a wrapper function for the combo. + * @param $ticket_id the id of the ticket of which we want to change the status & priority + * @param $newStatus the new status value (integer) + * @param $newPriority the new priority value (integer) + * @param $author the user (id) that performed the update + */ public static function updateTicketStatusAndPriority( $ticket_id, $newStatus, $newPriority, $author) { $ticket = new Ticket(); @@ -154,7 +189,12 @@ class Ticket{ } - //return the latest reply. + + /** + * return the latest reply of a ticket + * @param $ticket_id the id of the ticket. + * @return a ticket_reply object. + */ public static function getLatestReply( $ticket_id) { $dbl = new DBLayer("lib"); $statement = $dbl->execute("SELECT * FROM ticket_reply WHERE Ticket =:id ORDER BY TReplyId DESC LIMIT 1 ", array('id' => $ticket_id)); @@ -163,6 +203,16 @@ class Ticket{ return $reply; } + + /** + * create a new reply for a ticket. + * A reply will only be added if the content isn't empty and if the ticket isn't closed. + * The ticket creator will be notified by email that someone else replied on his ticket. + * @param $content the content of the reply + * @param $author the author of the reply + * @param $ticket_id the id of the ticket to which we want to add the reply. + * @param $hidden boolean that specifies if the reply should only be shown to mods/admins or all users. + */ public static function createReply($content, $author, $ticket_id, $hidden){ //if not empty if(! ( Trim ( $content ) === '' )){ @@ -187,7 +237,14 @@ class Ticket{ } } - //returns SUCCESS_ASSIGNED, TICKET_NOT_EXISTING or ALREADY_ASSIGNED + + /** + * assign a ticket to a user. + * Checks if the ticket exists, if so then it will try to assign the user to it, a log entry will be written about this. + * @param $user_id the id of user trying to be assigned to the ticket. + * @param $ticket_id the id of the ticket that we try to assign to the user. + * @return SUCCESS_ASSIGNED, TICKET_NOT_EXISTING or ALREADY_ASSIGNED + */ public static function assignTicket($user_id, $ticket_id){ if(self::ticketExists($ticket_id)){ $returnvalue = Assigned::assignTicket($user_id, $ticket_id); @@ -198,7 +255,14 @@ class Ticket{ } } - //returns SUCCESS_UNASSIGNED, TICKET_NOT_EXISTING or NOT_ASSIGNED + + /** + * unassign a ticket of a user. + * Checks if the ticket exists, if so then it will try to unassign the user of it, a log entry will be written about this. + * @param $user_id the id of user trying to be assigned to the ticket. + * @param $ticket_id the id of the ticket that we try to assign to the user. + * @return SUCCESS_UNASSIGNED, TICKET_NOT_EXISTING or NOT_ASSIGNED + */ public static function unAssignTicket($user_id, $ticket_id){ if(self::ticketExists($ticket_id)){ $returnvalue = Assigned::unAssignTicket($user_id, $ticket_id); @@ -209,6 +273,16 @@ class Ticket{ } } + + /** + * forward a ticket to a specific support group. + * Checks if the ticket exists, if so then it will try to forward the ticket to the support group specified, a log entry will be written about this. + * if no log entry should be written then the user_id should be 0, else te $user_id will be used in the log to specify who forwarded it. + * @param $user_id the id of user trying to forward the ticket. + * @param $ticket_id the id of the ticket that we try to forward to a support group. + * @param $group_id the id of the support group. + * @return SUCCESS_FORWARDED, TICKET_NOT_EXISTING or INVALID_SGROUP + */ public static function forwardTicket($user_id, $ticket_id, $group_id){ if(self::ticketExists($ticket_id)){ if(isset($group_id) && $group_id != ""){ @@ -234,12 +308,21 @@ class Ticket{ ////////////////////////////////////////////Methods//////////////////////////////////////////////////// + + /** + * A constructor. + * Empty constructor + */ public function __construct() { } - //Set ticket object + /** + * sets the object's attributes. + * @param $values should be an array of the form array('TId' => ticket_id, 'Title' => title, 'Status'=> status, 'Timestamp' => ts, 'Queue' => queue, + * 'Ticket_Category' => tc, 'Author' => author, 'Priority' => priority). + */ public function set($values){ if(isset($values['TId'])){ $this->tId = $values['TId']; @@ -253,7 +336,11 @@ class Ticket{ $this->priority = $values['Priority']; } - //create ticket by writing private data to DB. + + /** + * creates a new 'ticket' entry. + * this method will use the object's attributes for creating a new 'ticket' entry in the database. + */ public function create(){ $dbl = new DBLayer("lib"); $query = "INSERT INTO ticket (Timestamp, Title, Status, Queue, Ticket_Category, Author, Priority) VALUES (now(), :title, :status, :queue, :tcat, :author, :priority)"; @@ -261,7 +348,12 @@ class Ticket{ $this->tId = $dbl->executeReturnId($query, $values); ; } - //return constructed element based on TId + + /** + * loads the object's attributes. + * loads the object's attributes by giving a TId (ticket id). + * @param $id the id of the ticket that should be loaded + */ public function load_With_TId( $id) { $dbl = new DBLayer("lib"); $statement = $dbl->execute("SELECT * FROM ticket WHERE TId=:id", array('id' => $id)); @@ -276,7 +368,10 @@ class Ticket{ $this->priority = $row['Priority']; } - //update private data to DB. + + /** + * update the objects attributes to the db. + */ public function update(){ $dbl = new DBLayer("lib"); $query = "UPDATE ticket SET Timestamp = :timestamp, Title = :title, Status = :status, Queue = :queue, Ticket_Category = :tcat, Author = :author, Priority = :priority WHERE TId=:id"; @@ -284,67 +379,102 @@ class Ticket{ $statement = $dbl->execute($query, $values); } - //hasInfo + + /** + * check if a ticket has a ticket_info page or not. + * @return true or false + */ public function hasInfo(){ return Ticket_Info::TicketHasInfo($this->getTId()); } - /*FUNCTION: postreply - * returns all possible statusses - * - * - public function postReply() { - return Array("Waiting on user reply","Waiting on support","Waiting on Dev reply","Closed"); - }*/ + ////////////////////////////////////////////Getters//////////////////////////////////////////////////// + /** + * get tId attribute of the object. + */ public function getTId(){ return $this->tId; } + /** + * get timestamp attribute of the object in the format defined in the outputTime function of the Helperclass. + */ public function getTimestamp(){ return Helpers::outputTime($this->timestamp); } + /** + * get title attribute of the object. + */ public function getTitle(){ return $this->title; } + /** + * get status attribute of the object. + */ public function getStatus(){ return $this->status; } + /** + * get status attribute of the object in the form of text (string). + */ public function getStatusText(){ $statusArray = Ticket::getStatusArray(); return $statusArray[$this->getStatus()]; } + /** + * get category attribute of the object in the form of text (string). + */ public function getCategoryName(){ $category = Ticket_Category::constr_TCategoryId($this->getTicket_Category()); return $category->getName(); } + /** + * get queue attribute of the object. + */ public function getQueue(){ return $this->queue; } + /** + * get ticket_category attribute of the object (int). + */ public function getTicket_Category(){ return $this->ticket_category; } + /** + * get author attribute of the object (int). + */ public function getAuthor(){ return $this->author; } + /** + * get priority attribute of the object (int). + */ public function getPriority(){ return $this->priority; } + /** + * get priority attribute of the object in the form of text (string). + */ public function getPriorityText(){ $priorityArray = Ticket::getPriorityArray(); return $priorityArray[$this->getPriority()]; } + /** + * get the user assigned to the ticket. + * or return 0 in case not assigned. + */ public function getAssigned(){ $user_id = Assigned::getUserAssignedToTicket($this->getTId()); if ($user_id == ""){ @@ -354,6 +484,10 @@ class Ticket{ } } + /** + * get the name of the support group to whom the ticket is forwarded + * or return 0 in case not forwarded. + */ public function getForwardedGroupName(){ $group_id = Forwarded::getSGroupOfTicket($this->getTId()); if ($group_id == ""){ @@ -363,6 +497,10 @@ class Ticket{ } } + /** + * get the id of the support group to whom the ticket is forwarded + * or return 0 in case not forwarded. + */ public function getForwardedGroupId(){ $group_id = Forwarded::getSGroupOfTicket($this->getTId()); if ($group_id == ""){ @@ -373,34 +511,66 @@ class Ticket{ } ////////////////////////////////////////////Setters//////////////////////////////////////////////////// + /** + * set tId attribute of the object. + * @param $id integer id of the ticket + */ public function setTId($id){ $this->tId = $id; } + /** + * set timestamp attribute of the object. + * @param $ts timestamp of the ticket + */ public function setTimestamp($ts){ $this->timestamp = $ts; } + /** + * set title attribute of the object. + * @param $t title of the ticket + */ public function setTitle($t){ $this->title = $t; } + /** + * set status attribute of the object. + * @param $s status of the ticket(int) + */ public function setStatus($s){ $this->status = $s; } + /** + * set queue attribute of the object. + * @param $q queue of the ticket + */ public function setQueue($q){ $this->queue = $q; } + /** + * set ticket_category attribute of the object. + * @param $tc ticket_category id of the ticket(int) + */ public function setTicket_Category($tc){ $this->ticket_category = $tc; } + /** + * set author attribute of the object. + * @param $a author of the ticket + */ public function setAuthor($a){ $this->author = $a; } + /** + * set priority attribute of the object. + * @param $p priority of the ticket + */ public function setPriority($p){ $this->priority = $p; } diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_category.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_category.php index c2fd6a5d4..92e603d12 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_category.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_category.php @@ -1,14 +1,19 @@ setTCategoryId($id); return $instance; } - //returns list of all category objects + + /** + * return a list of all category objects. + * @return an array consisting of all category objects. + */ public static function getAllCategories() { $dbl = new DBLayer("lib"); $statement = $dbl->executeWithoutParams("SELECT * FROM ticket_category"); @@ -43,10 +55,19 @@ class Ticket_Category{ ////////////////////////////////////////////Methods//////////////////////////////////////////////////// + /** + * A constructor. + * Empty constructor + */ public function __construct() { } - //return constructed element based on TCategoryId + + /** + * loads the object's attributes. + * loads the object's attributes by giving a categories id. + * @param $id the id of the ticket_category that should be loaded + */ public function load_With_TCategoryId( $id) { $dbl = new DBLayer("lib"); $statement = $dbl->execute("SELECT * FROM ticket_category WHERE TCategoryId=:id", array('id' => $id)); @@ -56,7 +77,9 @@ class Ticket_Category{ } - //update private data to DB. + /** + * update object attributes to the DB. + */ public function update(){ $dbl = new DBLayer("lib"); $query = "UPDATE ticket_category SET Name = :name WHERE TCategoryId=:id"; @@ -66,6 +89,9 @@ class Ticket_Category{ ////////////////////////////////////////////Getters//////////////////////////////////////////////////// + /** + * get name attribute of the object. + */ public function getName(){ if ($this->name == ""){ $this->load_With_TCategoryId($this->tCategoryId); @@ -73,7 +99,9 @@ class Ticket_Category{ return $this->name; } - + /** + * get tCategoryId attribute of the object. + */ public function getTCategoryId(){ return $this->tCategoryId; } @@ -81,10 +109,18 @@ class Ticket_Category{ ////////////////////////////////////////////Setters//////////////////////////////////////////////////// + /** + * set name attribute of the object. + * @param $n name of the category + */ public function setName($n){ $this->name = $n; } + /** + * set tCategoryId attribute of the object. + * @param $id integer id of the category + */ public function setTCategoryId($id){ $this->tCategoryId = $id; } diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_content.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_content.php index bc9ca8398..445cad867 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_content.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_content.php @@ -1,14 +1,25 @@ setTContentId($id); @@ -18,10 +29,18 @@ class Ticket_Content{ ////////////////////////////////////////////Methods//////////////////////////////////////////////////// + /** + * A constructor. + * Empty constructor + */ public function __construct() { } - //Creates a ticket_content entry in the DB + + /** + * creates a new 'tickt_content' entry. + * this method will use the object's attributes for creating a new 'ticket_content' entry in the database. + */ public function create() { $dbl = new DBLayer("lib"); $query = "INSERT INTO ticket_content (Content) VALUES (:content)"; @@ -29,7 +48,12 @@ class Ticket_Content{ $this->tContentId = $dbl->executeReturnId($query, $values); ; } - //return constructed element based on TContentId + + /** + * loads the object's attributes. + * loads the object's attributes by giving a ticket_content's id, + * @param $id the id of the ticket_content entry that should be loaded + */ public function load_With_TContentId( $id) { $dbl = new DBLayer("lib"); $statement = $dbl->execute("SELECT * FROM ticket_content WHERE TContentId=:id", array('id' => $id)); @@ -38,7 +62,9 @@ class Ticket_Content{ $this->content = $row['Content']; } - //update private data to DB. + /** + * update the object's attributes to the database. + */ public function update(){ $dbl = new DBLayer("lib"); $query = "UPDATE ticket_content SET Content = :content WHERE TContentId=:id"; @@ -48,6 +74,9 @@ class Ticket_Content{ ////////////////////////////////////////////Getters//////////////////////////////////////////////////// + /** + * get content attribute of the object. + */ public function getContent(){ if ($this->content == ""){ $this->load_With_TContentId($this->tContentId); @@ -55,7 +84,9 @@ class Ticket_Content{ return $this->content; } - + /** + * get tContentId attribute of the object. + */ public function getTContentId(){ return $this->tContentId; } @@ -63,10 +94,18 @@ class Ticket_Content{ ////////////////////////////////////////////Setters//////////////////////////////////////////////////// + /** + * set content attribute of the object. + * @param $c content of a reply + */ public function setContent($c){ $this->content = $c; } + /** + * set tContentId attribute of the object. + * @param $c integer id of ticket_content entry + */ public function setTContentId($c){ $this->tContentId = $c; } From 3030d4c6503c7573066cdbf756aba223faae2fab Mon Sep 17 00:00:00 2001 From: Quitta Date: Thu, 12 Sep 2013 06:13:28 +0200 Subject: [PATCH 224/313] some more documented classes.. --- .../ams_lib/autoload/ticket_info.php | 220 +++++++++++++++--- .../ryzom_ams/ams_lib/autoload/ticket_log.php | 121 +++++++++- 2 files changed, 300 insertions(+), 41 deletions(-) diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_info.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_info.php index 513967c99..fc852d093 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_info.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_info.php @@ -1,38 +1,54 @@ set($info_array); $ticket_info->create(); } + + /** + * check if a specific ticket has extra info or not. + * Not all tickets have extra info, only tickets made ingame do. This function checks if a specific ticket does have a ticket_info entry linked to it. + * @param $ticket_id the id of the ticket that we want to query + * @return true or false + */ public static function TicketHasInfo($ticket_id) { $dbl = new DBLayer("lib"); //check if ticket is already assigned @@ -42,12 +58,22 @@ class Ticket_Info{ return false; } } + + ////////////////////////////////////////////Methods//////////////////////////////////////////////////// + /** + * A constructor. + * Empty constructor + */ public function __construct() { } - //set values + + /** + * sets the object's attributes. + * @param $values should be an array. + */ public function set($values) { $this->setTicket($values['Ticket']); $this->setShardId($values['ShardId']); @@ -69,7 +95,11 @@ class Ticket_Info{ } - //Load with tInfoId + /** + * loads the object's attributes by using a ticket_info id. + * loads the object's attributes by giving a ticket_info's entry id. + * @param $id the id of the ticket_info entry that should be loaded + */ public function load_With_TInfoId( $id) { $dbl = new DBLayer("lib"); $statement = $dbl->execute("SELECT * FROM ticket_info WHERE TInfoId=:id", array('id' => $id)); @@ -77,7 +107,12 @@ class Ticket_Info{ $this->set($row); } - //Load with ticket Id + + /** + * loads the object's attributes by using a ticket's id. + * loads the object's attributes by giving a ticket's entry id. + * @param $id the id of the ticket, the ticket_info entry of that ticket should be loaded. + */ public function load_With_Ticket( $id) { $dbl = new DBLayer("lib"); $statement = $dbl->execute("SELECT * FROM ticket_info WHERE Ticket=:id", array('id' => $id)); @@ -85,7 +120,11 @@ class Ticket_Info{ $this->set($row); } - //create ticket info + + /** + * creates a new 'ticket_info' entry. + * this method will use the object's attributes for creating a new 'ticket_info' entry in the database. + */ public function create() { $dbl = new DBLayer("lib"); $query = "INSERT INTO ticket_info ( Ticket, ShardId, UserPosition,ViewPosition, ClientVersion, PatchVersion,ServerTick, ConnectState, LocalAddress, Memory, OS, @@ -99,75 +138,128 @@ Processor, CPUID, CpuMask, HT, NeL3D, UserId) VALUES ( :ticket, :shardid, :user ////////////////////////////////////////////Getters//////////////////////////////////////////////////// + /** + * get tInfoId attribute of the object. + */ public function getTInfoId(){ return $this->tInfoId; } + /** + * get ticket attribute of the object. + */ public function getTicket(){ return $this->ticket; } + /** + * get shardid attribute of the object. + */ public function getShardId(){ return $this->shardid; } + /** + * get user_position attribute of the object. + */ public function getUser_Position(){ return $this->user_position; } + /** + * get view_position attribute of the object. + */ public function getView_Position(){ return $this->view_position; } + /** + * get client_version attribute of the object. + */ public function getClient_Version(){ return $this->client_version; } + /** + * get patch_version attribute of the object. + */ public function getPatch_Version(){ return $this->patch_version; } + /** + * get server_tick attribute of the object. + */ public function getServer_Tick(){ return $this->server_tick; } + /** + * get connect_state attribute of the object. + */ public function getConnect_State(){ return $this->connect_state; } + /** + * get local_address attribute of the object. + */ public function getLocal_Address(){ return $this->local_address; } + /** + * get memory attribute of the object. + */ public function getMemory(){ return $this->memory; } + /** + * get os attribute of the object. + */ public function getOS(){ return $this->os; } + /** + * get processor attribute of the object. + */ public function getProcessor(){ return $this->processor; } - + /** + * get cpu_id attribute of the object. + */ public function getCPUId(){ return $this->cpu_id; } + /** + * get cpu_mask attribute of the object. + */ public function getCPU_Mask(){ return $this->cpu_mask; } + /** + * get ht attribute of the object. + */ public function getHT(){ return $this->ht; } + /** + * get nel3d attribute of the object. + */ public function getNel3D(){ return $this->nel3d; } + /** + * get user_id attribute of the object. + */ public function getUser_Id(){ return $this->user_id; } @@ -175,75 +267,145 @@ Processor, CPUID, CpuMask, HT, NeL3D, UserId) VALUES ( :ticket, :shardid, :user ////////////////////////////////////////////Setters//////////////////////////////////////////////////// + /** + * set tInfoId attribute of the object. + * @param $id integer id of ticket_info object itself + */ public function setTInfoId($id){ $this->tInfoId = $id; } - + + /** + * set ticket attribute of the object. + * @param $t integer id of the ticket linked to the info object + */ public function setTicket($t){ $this->ticket = $t; } + /** + * set shardid attribute of the object. + * @param $s (integer) shard id + */ public function setShardId($s){ $this->shardid = $s; } + /** + * set user_position attribute of the object. + * @param $u the users position + */ public function setUser_Position($u){ $this->user_position = $u; } - + + /** + * set view_position attribute of the object. + * @param $v the view position + */ public function setView_Position($v){ $this->view_position = $v; } + /** + * set client_version attribute of the object. + * @param $c client version number + */ public function setClient_Version($c){ $this->client_version = $c; } + /** + * set patch_version attribute of the object. + * @param $p patch version number + */ public function setPatch_Version($p){ $this->patch_version = $p; } + /** + * set server_tick attribute of the object. + * @param $s integer that resembles the server tick + */ public function setServer_Tick($s){ $this->server_tick = $s; } + /** + * set connect_state attribute of the object. + * @param $c string that defines the connect state. + */ public function setConnect_State($c){ $this->connect_state = $c; } + /** + * set local_address attribute of the object. + * @param $l local address + */ public function setLocal_Address($l){ $this->local_address = $l; } + /** + * set memory attribute of the object. + * @param $m memory usage + */ public function setMemory($m){ $this->memory = $m; } + /** + * set os attribute of the object. + * @param $o set os version information + */ public function setOS($o){ $this->os = $o; } + /** + * set processor attribute of the object. + * @param $p processor information + */ public function setProcessor($p){ $this->processor = $p; } - + /** + * set cpu_id attribute of the object. + * @param $c cpu id information + */ public function setCPUId($c){ $this->cpu_id = $c; } + /** + * set cpu_mask attribute of the object. + * @param $c mask of the cpu + */ public function setCPU_Mask($c){ $this->cpu_mask = $c; } + /** + * set ht attribute of the object. + */ public function setHT($h){ $this->ht = $h; } + /** + * set nel3d attribute of the object. + * @param $n version information about NeL3D + */ public function setNel3D($n){ $this->nel3d = $n; } + /** + * set user_id attribute of the object. + * @param $u the user_id. + */ public function setUser_Id($u){ $this->user_id = $u; } diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_log.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_log.php index ee6f6b9ef..70eef6b91 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_log.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_log.php @@ -1,12 +1,29 @@ execute("SELECT * FROM ticket_log INNER JOIN ticket_user ON ticket_log.Author = ticket_user.TUserId and ticket_log.Ticket=:id ORDER BY ticket_log.TLogId ASC", array('id' => $ticket_id)); @@ -47,7 +68,16 @@ class Ticket_Log{ return $result; } - //Creates a log entry + + /** + * create a new log entry. + * It will check if the $TICKET_LOGGING global var is true, this var is used to turn logging on and off. In case it's on, the log message will be stored. + * the action id and argument (which is -1 by default), will be json encoded and stored in the query field in the db. + * @param $ticket_id the id of the ticket related to the new log entry + * @param $author_id the id of the user that instantiated the logging. + * @param $action the action id (see the list in the class description) + * @param $arg argument for the action (default = -1) + */ public static function createLogEntry( $ticket_id, $author_id, $action, $arg = -1) { global $TICKET_LOGGING; if($TICKET_LOGGING){ @@ -59,14 +89,23 @@ class Ticket_Log{ } - //return constructed element based on TLogId + /** + * return constructed element based on TLogId + * @param $ticket_log id of the entry that we want to load into our object. + * @return constructed ticket_log object. + */ public static function constr_TLogId( $id) { $instance = new self(); $instance->setTLogId($id); return $instance; } - //returns list of all logs of a ticket + /** + * return all log entries related to a ticket. + * @param $ticket_id the id of the ticket of which we want all related log entries returned. + * @return an array of ticket_log objects, here the author is an integer. + * @todo only use one of the 2 comparable functions in the future and make the other depricated. + */ public static function getAllLogs($ticket_id) { $dbl = new DBLayer("lib"); $statement = $dbl->execute("SELECT * FROM ticket_log INNER JOIN ticket_user ON ticket_log.Author = ticket_user.TUserId and ticket_log.Ticket=:id", array('id' => $ticket_id)); @@ -83,10 +122,17 @@ class Ticket_Log{ ////////////////////////////////////////////Methods//////////////////////////////////////////////////// + /** + * A constructor. + * Empty constructor + */ public function __construct() { } - //set values + /** + * sets the object's attributes. + * @param $values should be an array. + */ public function set($values) { $this->setTLogId($values['TLogId']); $this->setTimestamp($values['Timestamp']); @@ -95,7 +141,11 @@ class Ticket_Log{ $this->setAuthor($values['Author']); } - //Load with tlogId + /** + * loads the object's attributes. + * loads the object's attributes by giving a ticket_log entries ID (TLogId). + * @param id the id of the ticket_log entry that should be loaded + */ public function load_With_TLogId( $id) { $dbl = new DBLayer("lib"); $statement = $dbl->execute("SELECT * FROM ticket_log WHERE TLogId=:id", array('id' => $id)); @@ -104,7 +154,9 @@ class Ticket_Log{ } - //update private data to DB. + /** + * update attributes of the object to the DB. + */ public function update(){ $dbl = new DBLayer("lib"); $query = "UPDATE ticket_log SET Timestamp = :timestamp, Query = :query, Author = :author, Ticket = :ticket WHERE TLogId=:id"; @@ -114,36 +166,61 @@ class Ticket_Log{ ////////////////////////////////////////////Getters//////////////////////////////////////////////////// + /** + * get tLogId attribute of the object. + */ public function getTLogId(){ return $this->tLogId; } + /** + * get timestamp attribute of the object. + */ public function getTimestamp(){ return Helpers::outputTime($this->timestamp); } + /** + * get query attribute of the object. + */ public function getQuery(){ return $this->query; } + /** + * get author attribute of the object. + */ public function getAuthor(){ return $this->author; } + /** + * get ticket attribute of the object. + */ public function getTicket(){ return $this->ticket; } + /** + * get the action id out of the query by decoding it. + */ public function getAction(){ $decodedQuery = json_decode($this->query); return $decodedQuery[0]; } + /** + * get the argument out of the query by decoding it. + */ public function getArgument(){ $decodedQuery = json_decode($this->query); return $decodedQuery[1]; } + /** + * get the action text(string) array. + * this is being read from the language .ini files. + */ public function getActionTextArray(){ $variables = Helpers::handle_language(); $result = array(); @@ -155,22 +232,42 @@ class Ticket_Log{ ////////////////////////////////////////////Setters//////////////////////////////////////////////////// + /** + * set tLogId attribute of the object. + * @param $id integer id of the log entry + */ public function setTLogId($id){ $this->tLogId = $id; } + /** + * set timestamp attribute of the object. + * @param $t timestamp of the log entry + */ public function setTimestamp($t){ $this->timestamp = $t; } + /** + * set query attribute of the object. + * @param $q the encoded query + */ public function setQuery($q){ $this->query = $q; } + /** + * set author attribute of the object. + * @param $a integer id of the user who created the log entry + */ public function setAuthor($a){ $this->author = $a; } + /** + * set ticket attribute of the object. + * @param $t integer id of ticket of which the log entry is related to. + */ public function setTicket($t){ $this->ticket = $t; } From 75dcaa9ec3646b0fb412aee50374f2feef1ef5ea Mon Sep 17 00:00:00 2001 From: Quitta Date: Thu, 12 Sep 2013 19:28:56 +0200 Subject: [PATCH 225/313] the lib is entirely documented, still webusers and func/inc folders to go :D --- .../ams_lib/autoload/ticket_queue.php | 58 +++++++- .../ams_lib/autoload/ticket_queue_handler.php | 49 ++++++- .../ams_lib/autoload/ticket_reply.php | 121 ++++++++++++++--- .../ams_lib/autoload/ticket_user.php | 124 ++++++++++++++++-- .../ryzom_ams/ams_lib/autoload/users.php | 110 ++++++++++------ 5 files changed, 376 insertions(+), 86 deletions(-) diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_queue.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_queue.php index ef0b15bd2..2d6752057 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_queue.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_queue.php @@ -1,35 +1,56 @@ query = "SELECT ticket . * FROM ticket LEFT JOIN assigned ON ticket.TId = assigned.Ticket WHERE assigned.Ticket IS NULL"; $this->params = array(); } + /** + * loads the 'all' tickets query into the objects attributes. + */ public function loadAllTickets(){ $this->query = "SELECT * FROM `ticket`"; $this->params = array(); } + /** + * loads the 'all open' tickets query into the objects attributes. + */ public function loadAllOpenTickets(){ $this->query = "SELECT * FROM ticket INNER JOIN ticket_user ON ticket.Author = ticket_user.TUserId and ticket.Status!=3"; $this->params = array(); } + /** + * loads the 'closed' tickets query into the objects attributes. + */ public function loadAllClosedTickets(){ $this->query = "SELECT * FROM ticket INNER JOIN ticket_user ON ticket.Author = ticket_user.TUserId and ticket.Status=3"; $this->params = array(); } + /** + * loads the 'todo' tickets query & params into the objects attributes. + * first: find the tickets assigned to the user with status = waiting on support, + * second find all not assigned tickets that aren't forwarded yet. + * find all tickets assigned to someone else witht status waiting on support, with timestamp of last reply > 1 day, + * find all non-assigned tickets forwarded to the support groups to which that user belongs + * @param $user_id the user's id to whom the tickets should be assigned + */ public function loadToDoTickets($user_id){ - //first: find the tickets assigned to the user with status = waiting on support - //second find all not assigned tickets that aren't forwarded yet. - //find all tickets assigned to someone else witht status waiting on support, with timestamp of last reply > 1 day - //find all non-assigned tickets forwarded to the support groups to which that user belongs $this->query = "SELECT * FROM `ticket` t LEFT JOIN `assigned` a ON t.TId = a.Ticket LEFT JOIN `ticket_user` tu ON tu.TUserId = a.User LEFT JOIN `forwarded` f ON t.TId = f.Ticket WHERE (tu.ExternId = :user_id AND t.Status = 1) OR (a.Ticket IS NULL AND f.Group IS NULL) @@ -39,12 +60,26 @@ class Ticket_Queue{ $this->params = array('user_id' => $user_id); } + /** + * loads the 'tickets asssigned to a user and waiting on support' query & params into the objects attributes. + * @param $user_id the user's id to whom the tickets should be assigned + */ public function loadAssignedandWaiting($user_id){ $this->query = "SELECT * FROM `ticket` t LEFT JOIN `assigned` a ON t.TId = a.Ticket LEFT JOIN `ticket_user` tu ON tu.TUserId = a.User WHERE (tu.ExternId = :user_id AND t.Status = 1)"; $this->params = array('user_id' => $user_id); } + + /** + * loads the 'created' query & params into the objects attributes. + * This function creates dynamically a query based on the selected features. + * @param $who specifies if we want to user the user_id or group_id to form the query. + * @param $user_id the user's id to whom the tickets should be assigned/not assigned + * @param $group_id the group's id to whom the tickets should be forwarded/not forwarded + * @param $what specifies what kind of tickets we want to return: waiting for support, waiting on user, closed + * @param $how specifies if the tickets should be or shouldn't be assigned/forwarded to the group/user selected. + */ public function createQueue($userid, $groupid, $what, $how, $who){ if($who == "user"){ @@ -83,12 +118,21 @@ class Ticket_Queue{ } $this->query = $query; $this->params = $params; - } + } + + ////////////////////////////////////////////Getters//////////////////////////////////////////////////// + + /** + * get query attribute of the object. + */ public function getQuery(){ return $this->query; } + /** + * get params attribute of the object. + */ public function getParams(){ return $this->params; } diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_queue_handler.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_queue_handler.php index 142dc8912..5ea8d8781 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_queue_handler.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_queue_handler.php @@ -1,14 +1,31 @@ queue = new Ticket_Queue(); } + /** + * returns the tickets that are related in someway defined by $input. + * The $input parameter should be a string that defines what kind of queue should be loaded. A new pagination object will be instantiated and will load 10 entries, + * related to the $_GET['pagenum'] variable. + * @param $input identifier that defines what queue to load. + * @param $user_id the id of the user that browses the queues, some queues can be depending on this. + * @return an array consisting of ticket objects, beware, the author & category of a ticket, are objects on their own (no integers are used this time). + */ public function getTickets($input, $user_id){ switch ($input){ @@ -51,17 +68,29 @@ class Ticket_Queue_Handler{ } + + /** + * get pagination attribute of the object. + */ public function getPagination(){ return $this->pagination; } + /** + * creates the queue. + * afterwards the getTickets function should be called, else a lot of extra parameters had to be added to the getTickets function.. + */ public function createQueue($userid, $groupid, $what, $how, $who){ $this->queue->createQueue($userid, $groupid, $what, $how, $who); } - //================================================================================== - //Info retrievers about ticket statistics + ////////////////////////////////////////////Info retrievers about ticket statistics//////////////////////////////////////////////////// + + /** + * get the number of tickets in the todo queue for a specific user. + * @param $user_id the user being queried + */ public static function getNrOfTicketsToDo($user_id){ $queueHandler = new Ticket_Queue_Handler(); $queueHandler->queue->loadToDoTickets($user_id); @@ -71,6 +100,10 @@ class Ticket_Queue_Handler{ return $dbl->execute($query,$params)->rowCount(); } + /** + * get the number of tickets assigned to a specific user and waiting for support. + * @param $user_id the user being queried + */ public static function getNrOfTicketsAssignedWaiting($user_id){ $queueHandler = new Ticket_Queue_Handler(); $queueHandler->queue->loadAssignedandWaiting($user_id); @@ -80,6 +113,9 @@ class Ticket_Queue_Handler{ return $dbl->execute($query,$params)->rowCount(); } + /** + * get the total number of tickets. + */ public static function getNrOfTickets(){ $queueHandler = new Ticket_Queue_Handler(); $queueHandler->queue->loadAllTickets(); @@ -89,6 +125,9 @@ class Ticket_Queue_Handler{ return $dbl->execute($query,$params)->rowCount(); } + /** + * get the ticket object of the latest added ticket. + */ public static function getNewestTicket(){ $dbl = new DBLayer("lib"); $statement = $dbl->executeWithoutParams("SELECT * FROM `ticket` ORDER BY `TId` DESC LIMIT 1 "); diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_reply.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_reply.php index dafbdf74b..8e784543d 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_reply.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_reply.php @@ -1,16 +1,23 @@ setTReplyId($id); @@ -18,22 +25,31 @@ class Ticket_Reply{ } - //return constructed element based on TCategoryId + /** + * return all replies on a specific ticket. + * @param $ticket_id the id of the ticket of which we want the replies. + * @param $view_as_admin if the browsing user is an admin/mod it should be 1, this will also show the hidden replies. + * @return an array with ticket_reply objects (beware the author and content are objects on their own, not integers!) + */ public static function getRepliesOfTicket( $ticket_id, $view_as_admin) { $dbl = new DBLayer("lib"); $statement = $dbl->execute("SELECT * FROM ticket_reply INNER JOIN ticket_content INNER JOIN ticket_user ON ticket_reply.Content = ticket_content.TContentId and ticket_reply.Ticket=:id and ticket_user.TUserId = ticket_reply.Author ORDER BY ticket_reply.TReplyId ASC", array('id' => $ticket_id)); $row = $statement->fetchAll(); $result = Array(); foreach($row as $tReply){ + //only add hidden replies if the user is a mod/admin if(! $tReply['Hidden'] || $view_as_admin){ + //load author $instanceAuthor = Ticket_User::constr_TUserId($tReply['Author']); $instanceAuthor->setExternId($tReply['ExternId']); $instanceAuthor->setPermission($tReply['Permission']); + //load content $instanceContent = new Ticket_Content(); $instanceContent->setTContentId($tReply['TContentId']); $instanceContent->setContent($tReply['Content']); + //load reply and add the author and content object in it. $instanceReply = new self(); $instanceReply->setTReplyId($tReply['TReplyId']); $instanceReply->setTimestamp($tReply['Timestamp']); @@ -47,6 +63,16 @@ class Ticket_Reply{ return $result; } + /** + * creates a new reply on a ticket. + * Creates a ticket_content entry and links it with a new created ticket_reply, a log entry will be written about this. + * In case the ticket creator replies on a ticket, he will set the status by default to 'waiting on support'. + * @param $content the content of the reply + * @param $author the id of the reply creator. + * @param $ticket_id the id of the ticket of which we want the replies. + * @param $hidden should be 0 or 1 + * @param $ticket_creator the ticket's starter his id. + */ public static function createReply($content, $author, $ticket_id , $hidden, $ticket_creator){ $ticket_content = new Ticket_Content(); $ticket_content->setContent($content); @@ -66,12 +92,19 @@ class Ticket_Reply{ } ////////////////////////////////////////////Methods//////////////////////////////////////////////////// - + + /** + * A constructor. + * Empty constructor + */ public function __construct() { } - //Set ticket_reply object + /** + * sets the object's attributes. + * @param $values should be an array. + */ public function set($values){ $this->setTicket($values['Ticket']); $this->setContent($values['Content']); @@ -84,7 +117,10 @@ class Ticket_Reply{ } } - //create ticket by writing private data to DB. + /** + * creates a new 'ticket_reply' entry. + * this method will use the object's attributes for creating a new 'ticket_reply' entry in the database (the now() function will create the timestamp). + */ public function create(){ $dbl = new DBLayer("lib"); $query = "INSERT INTO ticket_reply (Ticket, Content, Author, Timestamp, Hidden) VALUES (:ticket, :content, :author, now(), :hidden)"; @@ -92,7 +128,11 @@ class Ticket_Reply{ $this->tReplyId = $dbl->executeReturnId($query, $values); } - //return constructed element based on TId + /** + * loads the object's attributes. + * loads the object's attributes by giving a ticket_reply's id. + * @param $id the id of the ticket_reply that should be loaded + */ public function load_With_TReplyId( $id) { $dbl = new DBLayer("lib"); $statement = $dbl->execute("SELECT * FROM ticket_reply WHERE TReplyId=:id", array('id' => $id)); @@ -105,7 +145,9 @@ class Ticket_Reply{ $this->hidden = $row['Hidden']; } - //update private data to DB. + /** + * updates a ticket_reply entry based on the objects attributes. + */ public function update(){ $dbl = new DBLayer("lib"); $query = "UPDATE ticket SET Ticket = :ticket, Content = :content, Author = :author, Timestamp = :timestamp, Hidden = :hidden WHERE TReplyId=:id"; @@ -115,56 +157,95 @@ class Ticket_Reply{ ////////////////////////////////////////////Getters//////////////////////////////////////////////////// + /** + * get ticket attribute of the object. + */ public function getTicket(){ return $this->ticket; } - - + + /** + * get content attribute of the object. + */ public function getContent(){ return $this->content; } - + + /** + * get author attribute of the object. + */ public function getAuthor(){ return $this->author; } + /** + * get timestamp attribute of the object. + * The output format is defined by the Helpers class function, outputTime(). + */ public function getTimestamp(){ return Helpers::outputTime($this->timestamp); } - + /** + * get tReplyId attribute of the object. + */ public function getTReplyId(){ return $this->tReplyId; } + /** + * get hidden attribute of the object. + */ public function getHidden(){ return $this->hidden; } ////////////////////////////////////////////Setters//////////////////////////////////////////////////// + /** + * set ticket attribute of the object. + * @param $t integer id of the ticket + */ public function setTicket($t){ $this->ticket = $t; } - + /** + * set content attribute of the object. + * @param $c integer id of the ticket_content entry + */ public function setContent($c){ $this->content = $c; } + /** + * set author attribute of the object. + * @param $a integer id of the user + */ public function setAuthor($a){ $this->author = $a; } + /** + * set timestamp attribute of the object. + * @param $t timestamp of the reply + */ public function setTimestamp($t){ $this->timestamp = $t; } - + /** + * set tReplyId attribute of the object. + * @param $i integer id of the ticket_reply + */ public function setTReplyId($i){ $this->tReplyId = $i; } + /** + * set hidden attribute of the object. + * @param $h should be 0 or 1 + */ public function setHidden($h){ $this->hidden = $h; } diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_user.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_user.php index 6772dd216..46125e284 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_user.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_user.php @@ -1,13 +1,24 @@ getPermission() > 1){ return true; @@ -23,6 +40,12 @@ class Ticket_User{ return false; } + + /** + * check if a ticket_user object is an admin or not. + * @param $user the ticket_user object itself + * @return true or false + */ public static function isAdmin($user){ if(isset($user) && $user->getPermission() == 3){ return true; @@ -30,7 +53,12 @@ class Ticket_User{ return false; } - //return constructed element based on TUserId + + /** + * return constructed ticket_user object based on TUserId. + * @param $id the TUserId of the entry. + * @return constructed ticket_user object + */ public static function constr_TUserId( $id) { $instance = new self(); $instance->setTUserId($id); @@ -38,7 +66,11 @@ class Ticket_User{ } - //return all mods/admins + + /** + * return a list of all mods/admins. + * @return an array consisting of ticket_user objects that are mods & admins. + */ public static function getModsAndAdmins() { $dbl = new DBLayer("lib"); $statement = $dbl->executeWithoutParams("SELECT * FROM `ticket_user` WHERE `Permission` > 1"); @@ -52,7 +84,12 @@ class Ticket_User{ return $result; } - //return constructed element based on ExternId + + /** + * return constructed ticket_user object based on ExternId. + * @param $id the ExternId of the entry. + * @return constructed ticket_user object + */ public static function constr_ExternId( $id) { $instance = new self(); $dbl = new DBLayer("lib"); @@ -62,9 +99,14 @@ class Ticket_User{ $instance->permission = $row['Permission']; $instance->externId = $row['ExternId']; return $instance; - } + + /** + * change the permission of a ticket_user. + * @param $user_id the TUserId of the entry. + * @param $perm the new permission value. + */ public static function change_permission($user_id, $perm){ $user = new Ticket_User(); $user->load_With_TUserId($user_id); @@ -73,6 +115,11 @@ class Ticket_User{ } + /** + * return the email address of a ticket_user. + * @param $id the TUserId of the entry. + * @return string containing the email address of that user. + */ public static function get_email_by_user_id($id){ $user = new Ticket_User(); $user->load_With_TUserId($id); @@ -81,6 +128,11 @@ class Ticket_User{ } + /** + * return the username of a ticket_user. + * @param $id the TUserId of the entry. + * @return string containing username of that user. + */ public static function get_username_from_id($id){ $user = new Ticket_User(); $user->load_With_TUserId($id); @@ -89,13 +141,22 @@ class Ticket_User{ } + /** + * return the TUserId of a ticket_user by giving a username. + * @param $username the username of a user. + * @return the TUserId related to that username. + */ public static function get_id_from_username($username){ $externId = WebUsers::getId($username); $user = Ticket_User::constr_ExternId($externId); return $user->getTUserId(); } - + /** + * return the ticket_user id from an email address. + * @param $email the emailaddress of a user. + * @return the ticket_user id related to that email address, in case none, return "FALSE". + */ public static function get_id_from_email($email){ $webUserId = WebUsers::getIdFromEmail($email); if($webUserId != "FALSE"){ @@ -108,10 +169,19 @@ class Ticket_User{ ////////////////////////////////////////////Methods//////////////////////////////////////////////////// + + /** + * A constructor. + * Empty constructor + */ public function __construct() { } - //set values + + /** + * sets the object's attributes. + * @param $values should be an array of the form array('TUserId' => id, 'Permission' => perm, 'ExternId' => ext_id). + */ public function set($values) { $this->setTUserId($values['TUserId']); $this->setPermission($values['Permission']); @@ -119,7 +189,11 @@ class Ticket_User{ } - //return constructed element based on TUserId + /** + * loads the object's attributes. + * loads the object's attributes by giving a TUserId. + * @param $id the id of the ticket_user that should be loaded + */ public function load_With_TUserId( $id) { $dbl = new DBLayer("lib"); $statement = $dbl->execute("SELECT * FROM ticket_user WHERE TUserId=:id", array('id' => $id)); @@ -129,7 +203,10 @@ class Ticket_User{ $this->externId = $row['ExternId']; } - //update private data to DB. + + /** + * update the object's attributes to the db. + */ public function update(){ $dbl = new DBLayer("lib"); $query = "UPDATE ticket_user SET Permission = :perm, ExternId = :ext_id WHERE TUserId=:id"; @@ -139,16 +216,23 @@ class Ticket_User{ ////////////////////////////////////////////Getters//////////////////////////////////////////////////// + /** + * get permission attribute of the object. + */ public function getPermission(){ return $this->permission; } - + /** + * get externId attribute of the object. + */ public function getExternId(){ return $this->externId; } - + /** + * get tUserId attribute of the object. + */ public function getTUserId(){ return $this->tUserId; } @@ -156,15 +240,27 @@ class Ticket_User{ ////////////////////////////////////////////Setters//////////////////////////////////////////////////// + /** + * set permission attribute of the object. + * @param $perm integer that indicates the permission level. (1= user, 2= mod, 3= admin) + */ public function setPermission($perm){ $this->permission = $perm; } + /** + * set externId attribute of the object. + * @param $id the external id. + */ public function setExternId($id){ $this->externId = $id; } + /** + * set tUserId attribute of the object. + * @param $id the ticket_user id + */ public function setTUserId($id){ $this->tUserId= $id; } diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/users.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/users.php index 3eb67b97e..1768f62ce 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/users.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/users.php @@ -1,10 +1,15 @@ $user, 'pass' => $pass); @@ -413,6 +436,13 @@ class Users{ } } + /** + * sets the shards email. + * in case the shard is offline, the entry will be stored in the ams_querycache. + * @param $user the usersname of the account of which we want to change the emailaddress. + * @param $mail the new email address + * @return ok if it worked, if the lib or shard is offline it will return liboffline or shardoffline. + */ protected function setAmsEmail($user, $mail){ $values = Array('user' => $user, 'mail' => $mail); From ef3f24d8df03feb2e9d0832bb02425c4d1c1c80e Mon Sep 17 00:00:00 2001 From: Quitta Date: Fri, 13 Sep 2013 00:27:49 +0200 Subject: [PATCH 226/313] Added the doxygen generated pages + config file in ryzom/tools/server/ryzom_ams_docs --- .../server/ryzom_ams/ams_lib/libinclude.php | 4 +- .../ryzommanage/autoload/webusers.php | 452 +++-- .../ryzom_ams/www/html/autoload/webusers.php | 481 +++-- .../server/ryzom_ams_docs/doxygen/Doxyfile | 1781 +++++++++++++++++ .../server/ryzom_ams_docs/doxygen/info.php | 13 + .../server/ryzom_ams_docs/html/annotated.html | 134 ++ .../tools/server/ryzom_ams_docs/html/bc_s.png | Bin 0 -> 677 bytes .../html/classAssigned-members.html | 125 ++ .../ryzom_ams_docs/html/classAssigned.html | 466 +++++ .../html/classDBLayer-members.html | 115 ++ .../ryzom_ams_docs/html/classDBLayer.html | 269 +++ .../html/classForwarded-members.html | 124 ++ .../ryzom_ams_docs/html/classForwarded.html | 415 ++++ .../html/classGui__Elements-members.html | 113 ++ .../html/classGui__Elements.html | 250 +++ .../html/classHelpers-members.html | 116 ++ .../ryzom_ams_docs/html/classHelpers.html | 292 +++ .../html/classIn__Support__Group-members.html | 121 ++ .../html/classIn__Support__Group.html | 330 +++ .../html/classMail__Handler-members.html | 121 ++ .../html/classMail__Handler.html | 480 +++++ .../html/classMyCrypt-members.html | 116 ++ .../ryzom_ams_docs/html/classMyCrypt.html | 312 +++ .../html/classPagination-members.html | 120 ++ .../ryzom_ams_docs/html/classPagination.html | 311 +++ .../html/classQuerycache-members.html | 126 ++ .../ryzom_ams_docs/html/classQuerycache.html | 343 ++++ .../html/classSupport__Group-members.html | 148 ++ .../html/classSupport__Group.html | 847 ++++++++ .../html/classSync-members.html | 111 + .../server/ryzom_ams_docs/html/classSync.html | 149 ++ .../html/classTicket-members.html | 159 ++ .../ryzom_ams_docs/html/classTicket.html | 1078 ++++++++++ .../html/classTicket__Category-members.html | 122 ++ .../html/classTicket__Category.html | 322 +++ .../html/classTicket__Content-members.html | 121 ++ .../html/classTicket__Content.html | 302 +++ .../html/classTicket__Info-members.html | 171 ++ .../html/classTicket__Info.html | 886 ++++++++ .../html/classTicket__Log-members.html | 136 ++ .../ryzom_ams_docs/html/classTicket__Log.html | 556 +++++ .../html/classTicket__Queue-members.html | 121 ++ .../html/classTicket__Queue.html | 270 +++ .../classTicket__Queue__Handler-members.html | 120 ++ .../html/classTicket__Queue__Handler.html | 330 +++ .../html/classTicket__Reply-members.html | 136 ++ .../html/classTicket__Reply.html | 577 ++++++ .../html/classTicket__User-members.html | 134 ++ .../html/classTicket__User.html | 634 ++++++ .../html/classUsers-members.html | 125 ++ .../ryzom_ams_docs/html/classUsers.html | 639 ++++++ .../server/ryzom_ams_docs/html/classUsers.png | Bin 0 -> 341 bytes .../html/classWebUsers-members.html | 151 ++ .../ryzom_ams_docs/html/classWebUsers.html | 712 +++++++ .../ryzom_ams_docs/html/classWebUsers.png | Bin 0 -> 338 bytes .../server/ryzom_ams_docs/html/classes.html | 139 ++ .../server/ryzom_ams_docs/html/closed.png | Bin 0 -> 126 bytes .../ryzom_ams_docs/html/deprecated.html | 105 + .../dir_4e66b119222255be04e87a0dd87a3317.html | 126 ++ .../dir_732e39ca668b77f3e13244f204e87535.html | 128 ++ .../dir_7add0e0eba863b010b52e5fb777019f1.html | 126 ++ .../dir_ba1cfc2c2eb4ebe254e0d7aa1e01cfcb.html | 127 ++ .../dir_c7daa497b9d1b9c868775e0964568556.html | 126 ++ .../dir_d0847756a6376d36128ff0811ac4d74f.html | 148 ++ .../server/ryzom_ams_docs/html/dirs.html | 117 ++ .../server/ryzom_ams_docs/html/doxygen.css | 949 +++++++++ .../server/ryzom_ams_docs/html/doxygen.png | Bin 0 -> 3942 bytes .../server/ryzom_ams_docs/html/functions.html | 349 ++++ .../ryzom_ams_docs/html/functions_0x5f.html | 157 ++ .../ryzom_ams_docs/html/functions_0x61.html | 145 ++ .../ryzom_ams_docs/html/functions_0x63.html | 252 +++ .../ryzom_ams_docs/html/functions_0x64.html | 156 ++ .../ryzom_ams_docs/html/functions_0x65.html | 150 ++ .../ryzom_ams_docs/html/functions_0x66.html | 142 ++ .../ryzom_ams_docs/html/functions_0x67.html | 456 +++++ .../ryzom_ams_docs/html/functions_0x68.html | 147 ++ .../ryzom_ams_docs/html/functions_0x69.html | 156 ++ .../ryzom_ams_docs/html/functions_0x6c.html | 193 ++ .../ryzom_ams_docs/html/functions_0x6d.html | 147 ++ .../ryzom_ams_docs/html/functions_0x6e.html | 141 ++ .../ryzom_ams_docs/html/functions_0x6f.html | 141 ++ .../ryzom_ams_docs/html/functions_0x73.html | 341 ++++ .../ryzom_ams_docs/html/functions_0x74.html | 147 ++ .../ryzom_ams_docs/html/functions_0x75.html | 161 ++ .../ryzom_ams_docs/html/functions_0x76.html | 141 ++ .../ryzom_ams_docs/html/functions_func.html | 156 ++ .../html/functions_func_0x61.html | 144 ++ .../html/functions_func_0x63.html | 251 +++ .../html/functions_func_0x64.html | 155 ++ .../html/functions_func_0x65.html | 149 ++ .../html/functions_func_0x66.html | 141 ++ .../html/functions_func_0x67.html | 455 +++++ .../html/functions_func_0x68.html | 146 ++ .../html/functions_func_0x69.html | 155 ++ .../html/functions_func_0x6c.html | 192 ++ .../html/functions_func_0x6d.html | 146 ++ .../html/functions_func_0x6e.html | 140 ++ .../html/functions_func_0x6f.html | 140 ++ .../html/functions_func_0x73.html | 340 ++++ .../html/functions_func_0x74.html | 146 ++ .../html/functions_func_0x75.html | 160 ++ .../html/functions_func_0x76.html | 140 ++ .../ryzom_ams_docs/html/functions_vars.html | 332 +++ .../server/ryzom_ams_docs/html/hierarchy.html | 136 ++ .../server/ryzom_ams_docs/html/index.html | 108 + .../server/ryzom_ams_docs/html/installdox | 112 ++ .../server/ryzom_ams_docs/html/jquery.js | 64 + .../server/ryzom_ams_docs/html/nav_f.png | Bin 0 -> 159 bytes .../server/ryzom_ams_docs/html/nav_h.png | Bin 0 -> 97 bytes .../tools/server/ryzom_ams_docs/html/open.png | Bin 0 -> 118 bytes .../server/ryzom_ams_docs/html/pages.html | 107 + .../ryzom_ams_docs/html/search/all_24.html | 25 + .../ryzom_ams_docs/html/search/all_24.js | 68 + .../ryzom_ams_docs/html/search/all_5f.html | 25 + .../ryzom_ams_docs/html/search/all_5f.js | 4 + .../ryzom_ams_docs/html/search/all_61.html | 25 + .../ryzom_ams_docs/html/search/all_61.js | 6 + .../ryzom_ams_docs/html/search/all_63.html | 25 + .../ryzom_ams_docs/html/search/all_63.js | 37 + .../ryzom_ams_docs/html/search/all_64.html | 25 + .../ryzom_ams_docs/html/search/all_64.js | 9 + .../ryzom_ams_docs/html/search/all_65.html | 25 + .../ryzom_ams_docs/html/search/all_65.js | 7 + .../ryzom_ams_docs/html/search/all_66.html | 25 + .../ryzom_ams_docs/html/search/all_66.js | 5 + .../ryzom_ams_docs/html/search/all_67.html | 25 + .../ryzom_ams_docs/html/search/all_67.js | 105 + .../ryzom_ams_docs/html/search/all_68.html | 25 + .../ryzom_ams_docs/html/search/all_68.js | 7 + .../ryzom_ams_docs/html/search/all_69.html | 25 + .../ryzom_ams_docs/html/search/all_69.js | 10 + .../ryzom_ams_docs/html/search/all_6c.html | 25 + .../ryzom_ams_docs/html/search/all_6c.js | 21 + .../ryzom_ams_docs/html/search/all_6d.html | 25 + .../ryzom_ams_docs/html/search/all_6d.js | 8 + .../ryzom_ams_docs/html/search/all_6e.html | 25 + .../ryzom_ams_docs/html/search/all_6e.js | 4 + .../ryzom_ams_docs/html/search/all_6f.html | 25 + .../ryzom_ams_docs/html/search/all_6f.js | 4 + .../ryzom_ams_docs/html/search/all_70.html | 25 + .../ryzom_ams_docs/html/search/all_70.js | 4 + .../ryzom_ams_docs/html/search/all_71.html | 25 + .../ryzom_ams_docs/html/search/all_71.js | 4 + .../ryzom_ams_docs/html/search/all_73.html | 25 + .../ryzom_ams_docs/html/search/all_73.js | 65 + .../ryzom_ams_docs/html/search/all_74.html | 25 + .../ryzom_ams_docs/html/search/all_74.js | 15 + .../ryzom_ams_docs/html/search/all_75.html | 25 + .../ryzom_ams_docs/html/search/all_75.js | 9 + .../ryzom_ams_docs/html/search/all_76.html | 25 + .../ryzom_ams_docs/html/search/all_76.js | 4 + .../ryzom_ams_docs/html/search/all_77.html | 25 + .../ryzom_ams_docs/html/search/all_77.js | 4 + .../html/search/classes_61.html | 25 + .../ryzom_ams_docs/html/search/classes_61.js | 4 + .../html/search/classes_64.html | 25 + .../ryzom_ams_docs/html/search/classes_64.js | 4 + .../html/search/classes_66.html | 25 + .../ryzom_ams_docs/html/search/classes_66.js | 4 + .../html/search/classes_67.html | 25 + .../ryzom_ams_docs/html/search/classes_67.js | 4 + .../html/search/classes_68.html | 25 + .../ryzom_ams_docs/html/search/classes_68.js | 4 + .../html/search/classes_69.html | 25 + .../ryzom_ams_docs/html/search/classes_69.js | 4 + .../html/search/classes_6d.html | 25 + .../ryzom_ams_docs/html/search/classes_6d.js | 5 + .../html/search/classes_70.html | 25 + .../ryzom_ams_docs/html/search/classes_70.js | 4 + .../html/search/classes_71.html | 25 + .../ryzom_ams_docs/html/search/classes_71.js | 4 + .../html/search/classes_73.html | 25 + .../ryzom_ams_docs/html/search/classes_73.js | 5 + .../html/search/classes_74.html | 25 + .../ryzom_ams_docs/html/search/classes_74.js | 12 + .../html/search/classes_75.html | 25 + .../ryzom_ams_docs/html/search/classes_75.js | 4 + .../html/search/classes_77.html | 25 + .../ryzom_ams_docs/html/search/classes_77.js | 4 + .../ryzom_ams_docs/html/search/close.png | Bin 0 -> 273 bytes .../html/search/functions_5f.html | 25 + .../html/search/functions_5f.js | 4 + .../html/search/functions_61.html | 25 + .../html/search/functions_61.js | 5 + .../html/search/functions_63.html | 25 + .../html/search/functions_63.js | 37 + .../html/search/functions_64.html | 25 + .../html/search/functions_64.js | 8 + .../html/search/functions_65.html | 25 + .../html/search/functions_65.js | 7 + .../html/search/functions_66.html | 25 + .../html/search/functions_66.js | 4 + .../html/search/functions_67.html | 25 + .../html/search/functions_67.js | 104 + .../html/search/functions_68.html | 25 + .../html/search/functions_68.js | 6 + .../html/search/functions_69.html | 25 + .../html/search/functions_69.js | 9 + .../html/search/functions_6c.html | 25 + .../html/search/functions_6c.js | 21 + .../html/search/functions_6d.html | 25 + .../html/search/functions_6d.js | 6 + .../html/search/functions_6e.html | 25 + .../html/search/functions_6e.js | 4 + .../html/search/functions_6f.html | 25 + .../html/search/functions_6f.js | 4 + .../html/search/functions_73.html | 25 + .../html/search/functions_73.js | 63 + .../html/search/functions_74.html | 25 + .../html/search/functions_74.js | 6 + .../html/search/functions_75.html | 25 + .../html/search/functions_75.js | 8 + .../html/search/functions_76.html | 25 + .../html/search/functions_76.js | 4 + .../ryzom_ams_docs/html/search/mag_sel.png | Bin 0 -> 563 bytes .../ryzom_ams_docs/html/search/nomatches.html | 12 + .../ryzom_ams_docs/html/search/search.css | 238 +++ .../ryzom_ams_docs/html/search/search.js | 801 ++++++++ .../ryzom_ams_docs/html/search/search_l.png | Bin 0 -> 604 bytes .../ryzom_ams_docs/html/search/search_m.png | Bin 0 -> 158 bytes .../ryzom_ams_docs/html/search/search_r.png | Bin 0 -> 612 bytes .../html/search/variables_24.html | 25 + .../html/search/variables_24.js | 68 + .../server/ryzom_ams_docs/html/tab_a.png | Bin 0 -> 140 bytes .../server/ryzom_ams_docs/html/tab_b.png | Bin 0 -> 178 bytes .../server/ryzom_ams_docs/html/tab_h.png | Bin 0 -> 192 bytes .../server/ryzom_ams_docs/html/tab_s.png | Bin 0 -> 189 bytes .../tools/server/ryzom_ams_docs/html/tabs.css | 59 + .../server/ryzom_ams_docs/html/todo.html | 113 ++ 229 files changed, 29205 insertions(+), 363 deletions(-) create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/doxygen/Doxyfile create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/doxygen/info.php create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/annotated.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/bc_s.png create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classAssigned-members.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classAssigned.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classDBLayer-members.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classDBLayer.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classForwarded-members.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classForwarded.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classGui__Elements-members.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classGui__Elements.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classHelpers-members.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classHelpers.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classIn__Support__Group-members.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classIn__Support__Group.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classMail__Handler-members.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classMail__Handler.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classMyCrypt-members.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classMyCrypt.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classPagination-members.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classPagination.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classQuerycache-members.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classQuerycache.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classSupport__Group-members.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classSupport__Group.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classSync-members.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classSync.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classTicket-members.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classTicket.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__Category-members.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__Category.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__Content-members.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__Content.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__Info-members.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__Info.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__Log-members.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__Log.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__Queue-members.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__Queue.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__Queue__Handler-members.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__Queue__Handler.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__Reply-members.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__Reply.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__User-members.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__User.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classUsers-members.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classUsers.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classUsers.png create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classWebUsers-members.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classWebUsers.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classWebUsers.png create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classes.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/closed.png create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/deprecated.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/dir_4e66b119222255be04e87a0dd87a3317.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/dir_732e39ca668b77f3e13244f204e87535.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/dir_7add0e0eba863b010b52e5fb777019f1.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/dir_ba1cfc2c2eb4ebe254e0d7aa1e01cfcb.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/dir_c7daa497b9d1b9c868775e0964568556.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/dir_d0847756a6376d36128ff0811ac4d74f.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/dirs.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/doxygen.css create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/doxygen.png create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/functions.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x5f.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x61.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x63.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x64.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x65.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x66.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x67.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x68.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x69.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x6c.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x6d.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x6e.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x6f.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x73.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x74.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x75.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x76.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/functions_func.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/functions_func_0x61.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/functions_func_0x63.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/functions_func_0x64.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/functions_func_0x65.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/functions_func_0x66.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/functions_func_0x67.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/functions_func_0x68.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/functions_func_0x69.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/functions_func_0x6c.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/functions_func_0x6d.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/functions_func_0x6e.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/functions_func_0x6f.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/functions_func_0x73.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/functions_func_0x74.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/functions_func_0x75.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/functions_func_0x76.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/functions_vars.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/hierarchy.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/index.html create mode 100755 code/ryzom/tools/server/ryzom_ams_docs/html/installdox create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/jquery.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/nav_f.png create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/nav_h.png create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/open.png create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/pages.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/all_24.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/all_24.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/all_5f.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/all_5f.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/all_61.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/all_61.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/all_63.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/all_63.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/all_64.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/all_64.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/all_65.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/all_65.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/all_66.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/all_66.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/all_67.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/all_67.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/all_68.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/all_68.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/all_69.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/all_69.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/all_6c.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/all_6c.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/all_6d.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/all_6d.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/all_6e.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/all_6e.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/all_6f.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/all_6f.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/all_70.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/all_70.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/all_71.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/all_71.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/all_73.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/all_73.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/all_74.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/all_74.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/all_75.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/all_75.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/all_76.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/all_76.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/all_77.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/all_77.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_61.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_61.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_64.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_64.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_66.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_66.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_67.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_67.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_68.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_68.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_69.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_69.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_6d.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_6d.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_70.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_70.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_71.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_71.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_73.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_73.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_74.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_74.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_75.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_75.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_77.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_77.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/close.png create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_5f.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_5f.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_61.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_61.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_63.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_63.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_64.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_64.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_65.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_65.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_66.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_66.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_67.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_67.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_68.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_68.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_69.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_69.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_6c.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_6c.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_6d.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_6d.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_6e.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_6e.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_6f.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_6f.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_73.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_73.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_74.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_74.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_75.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_75.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_76.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_76.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/mag_sel.png create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/nomatches.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/search.css create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/search.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/search_l.png create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/search_m.png create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/search_r.png create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/variables_24.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/variables_24.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/tab_a.png create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/tab_b.png create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/tab_h.png create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/tab_s.png create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/tabs.css create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/todo.html diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/libinclude.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/libinclude.php index b0031de73..4ac64cc5e 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/libinclude.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/libinclude.php @@ -5,11 +5,13 @@ function __autoload( $className ){ global $AMS_LIB; global $SITEBASE; + //if the class exists in the lib's autload dir, load that one if(file_exists( $AMS_LIB.'/autoload/' . strtolower ( $className ) . '.php')){ require_once 'autoload/' . strtolower ( $className ) . '.php'; } + //if the classname is WebUsers, use the sitebase location for the autoload dir. if($className == "WebUsers"){ - require_once $SITEBASE.'/autoload/' . strtolower ( $className ) . '.php'; + require_once $SITEBASE.'/autoload/' . strtolower ( $className ) . '.php'; } } diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/autoload/webusers.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/autoload/webusers.php index a99a4a295..9d462b38e 100644 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/autoload/webusers.php +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/autoload/webusers.php @@ -1,21 +1,36 @@ uId = $UId; } + + /** + * sets the object's attributes. + * @param $values should be an array. + */ public function set($values){ $this->uId = $values['uid']; $this->login = $values['name']; @@ -28,49 +43,60 @@ class WebUsers extends Users{ //$this->language = $values['Language']; } - /** - * Function checkUserNameExists - * - * @takes $username - * @return string Info: Returns true or false if the user is in the web db. - */ - protected function checkUserNameExists($username){ - return db_query("SELECT COUNT(*) FROM {users} WHERE name = :name", array(':name' => $username))->fetchField(); - } - - - /** - * Function checkEmailExists - * - * @takes $username - * @return string Info: Returns true or false if the user is in the www db. - */ - protected function checkEmailExists($email){ - return db_query("SELECT COUNT(*) FROM {users} WHERE mail = :mail", array(':mail' => $email))->fetchField(); - } - - - /** - * Function checkUserPassMatch - * - * @takes $username,$password - * @return string Info: Returns true or false if a login match is found in the web db - */ - public function checkLoginMatch($username,$password){ - if(!user_authenticate($username, $password)){ - return 'fail'; - }else{ - return db_query("SELECT * FROM {users} WHERE name = :name", array(':name' => $username))->fetchAssoc(); + + /** + * function that checks if a username exists already or not. + * This function overrides the function of the base class. + * @takes $username the username in question + * @return string Info: Returns 0 if the user is not in the web db, else a positive number is returned. + */ + protected function checkUserNameExists($username){ + return db_query("SELECT COUNT(*) FROM {users} WHERE name = :name", array(':name' => $username))->fetchField(); + } + + + /** + * function that checks if a email exists already or not. + * This function overrides the function of the base class. + * @takes $email the email address in question. + * @return string Info: Returns 0 if the email address is not in the web db, else a positive number is returned. + */ + protected function checkEmailExists($email){ + return db_query("SELECT COUNT(*) FROM {users} WHERE mail = :mail", array(':mail' => $email))->fetchField(); + } + + + /** + * check if the login username and password match the db. + * @param $username the inserted username + * @param $password the inserted password (unhashed) + * @return the logged in user's db row as array if login was a success, else "fail" will be returned. + */ + public function checkLoginMatch($username,$password){ + if(!user_authenticate($username, $password)){ + return 'fail'; + }else{ + return db_query("SELECT * FROM {users} WHERE name = :name", array(':name' => $username))->fetchAssoc(); + } } - } - //returns te id for a given username + + /** + * returns te id for a given username + * @param $username the username + * @return the user's id linked to the username + */ public static function getId($username){ $row = db_query("SELECT * FROM {users} WHERE name = :name", array(':name' => $username))->fetchAssoc(); return $row['uid']; } - //returns te id for a given username + + /** + * returns te id for a given emailaddress + * @param $email the emailaddress + * @return the user's id linked to the emailaddress + */ public static function getIdFromEmail($email){ $dbw = new DBLayer("web"); $statement = $dbw->execute("SELECT * FROM ams_user WHERE Email=:email", array('email' => $email)); @@ -82,142 +108,220 @@ class WebUsers extends Users{ } } + + /** + * get uId attribute of the object. + */ public function getUId(){ return $this->uId; } - - public function getUsername(){ - - if(! isset($this->login) || $this->login == ""){ - $row = db_query("SELECT * FROM {users} WHERE uid = :id", array(':id' => $this->uId))->fetchAssoc(); - $this->set($row); - } - return $this->login; - } - - public function getEmail(){ - if(! isset($this->email) || $this->email == ""){ - $row = db_query("SELECT * FROM {users} WHERE uid = :id", array(':id' => $this->uId))->fetchAssoc(); - $this->set($row); - } - return $this->email; - } - - public function getInfo(){ - $dbw = new DBLayer("web"); - if(! (isset($this->firstname) && isset($this->lastname) && isset($this->gender) && isset($this->country) && isset($this->receiveMail) ) || - $this->firstname == "" || $this->lastname == "" || $this->gender == "" || $this->country == "" || $this->receiveMail == ""){ - $statement = $dbw->execute("SELECT * FROM ams_user WHERE UId=:id", array('id' => $this->uId)); - $row = $statement->fetch(); - $this->set($row); - } - $result = Array('FirstName' => $this->firstname, 'LastName' => $this->lastname, 'Gender' => $this->gender, 'Country' => $this->country, 'ReceiveMail' => $this->receiveMail); - return $result; - } - - public function getReceiveMail(){ - $dbw = new DBLayer("web"); - if(! isset($this->receiveMail) || $this->receiveMail == ""){ - $statement = $dbw->execute("SELECT * FROM ams_user WHERE UId=:id", array('id' => $this->uId)); - $row = $statement->fetch(); - $this->set($row); - } - return $this->receiveMail; - } - - public function getLanguage(){ - return $DEFAULT_LANGUAGE; - } - - public function isLoggedIn(){ - if(isset($_SESSION['user'])){ - return true; - } - return false; - } - - public function setPassword($user, $pass){ - $hashpass = crypt($pass, WebUsers::generateSALT()); - $reply = WebUsers::setAmsPassword($user, $hashpass); - $drupal_pass = user_hash_password($pass); - $values = Array('user' => $user, 'pass' => $drupal_pass); - try { - //make connection with and put into shard db - db_query("UPDATE {users} SET pass = :pass WHERE name = :user", $values); + + + /** + * get login attribute of the object.(username) + */ + public function getUsername(){ + + if(! isset($this->login) || $this->login == ""){ + $row = db_query("SELECT * FROM {users} WHERE uid = :id", array(':id' => $this->uId))->fetchAssoc(); + $this->set($row); } - catch (PDOException $e) { - //ERROR: the web DB is offline - } - return $reply; - } - - public function setEmail($user, $mail){ - $reply = WebUsers::setAmsEmail($user, $mail); - $values = Array('user' => $user, 'mail' => $mail); - try { - //make connection with and put into shard db - db_query("UPDATE {users} SET mail = :mail WHERE name = :user", $values); - - } - catch (PDOException $e) { - //ERROR: the web DB is offline - } - return $reply; - } - - public static function setReceiveMail($user, $receivemail){ - $values = Array('user' => $user, 'receivemail' => $receivemail); - try { - //make connection with and put into shard db - $dbw = new DBLayer("web"); - $dbw->execute("UPDATE ams_user SET ReceiveMail = :receivemail WHERE UId = :user ",$values); - } - catch (PDOException $e) { - //ERROR: the web DB is offline - } - } - - public static function setLanguage($user, $language){ - $values = Array('user' => $user, 'language' => $language); - try { - //make connection with and put into shard db - $dbw = new DBLayer("web"); - $dbw->execute("UPDATE ams_user SET Language = :language WHERE UId = :user ",$values); - } - catch (PDOException $e) { - //ERROR: the web DB is offline - } - } - - public function getUsers(){ - $dbl = new DBLayer("web"); - $data = $dbl->executeWithoutParams("SELECT * FROM ams_user"); - return $data; - } - - public static function getAllUsersQuery(){ - return "SELECT * FROM users WHERE `uid` > 0"; - } - - public static function createWebuser($name, $pass, $mail){ - - //register account with the correct language (check if cookie is already set)! - if ( isset( $_COOKIE['Language'] ) ) { - $lang = $_COOKIE['Language']; - }else{ - global $DEFAULT_LANGUAGE; - $lang = $DEFAULT_LANGUAGE; + return $this->login; } - $values = Array('name' => $name, 'pass' => $pass, 'mail' => $mail, 'lang' => $lang); - try { + /** + * get email attribute of the object. + */ + public function getEmail(){ + if(! isset($this->email) || $this->email == ""){ + $row = db_query("SELECT * FROM {users} WHERE uid = :id", array(':id' => $this->uId))->fetchAssoc(); + $this->set($row); + } + return $this->email; + } + + + /** + * get basic info of the object. + * @return returns an array in the form of Array('FirstName' => $this->firstname, 'LastName' => $this->lastname, 'Gender' => $this->gender, 'Country' => $this->country, 'ReceiveMail' => $this->receiveMail) + */ + public function getInfo(){ $dbw = new DBLayer("web"); - return $dbw->executeReturnId("INSERT INTO ams_user (Login, Password, Email, Language) VALUES (:name, :pass, :mail, :lang)",$values); + if(! (isset($this->firstname) && isset($this->lastname) && isset($this->gender) && isset($this->country) && isset($this->receiveMail) ) || + $this->firstname == "" || $this->lastname == "" || $this->gender == "" || $this->country == "" || $this->receiveMail == ""){ + $statement = $dbw->execute("SELECT * FROM ams_user WHERE UId=:id", array('id' => $this->uId)); + $row = $statement->fetch(); + $this->set($row); + } + $result = Array('FirstName' => $this->firstname, 'LastName' => $this->lastname, 'Gender' => $this->gender, 'Country' => $this->country, 'ReceiveMail' => $this->receiveMail); + return $result; } - catch (PDOException $e) { - //ERROR: the web DB is offline - } - } + + /** + * get receiveMail attribute of the object. + */ + public function getReceiveMail(){ + $dbw = new DBLayer("web"); + if(! isset($this->receiveMail) || $this->receiveMail == ""){ + $statement = $dbw->execute("SELECT * FROM ams_user WHERE UId=:id", array('id' => $this->uId)); + $row = $statement->fetch(); + $this->set($row); + } + return $this->receiveMail; + } + + + /** + * get language attribute of the object. + */ + public function getLanguage(){ + return $DEFAULT_LANGUAGE; + } + + + /** + * check if the user is logged in. + * @return true or false + */ + public function isLoggedIn(){ + if(isset($_SESSION['user'])){ + return true; + } + return false; + } + + + /** + * update the password. + * update the password in the shard + update the password in the www/CMS version. + * @param $user the username + * @param $pass the new password. + * @return ok if it worked, if the lib or shard is offline it will return liboffline or shardoffline. + */ + public function setPassword($user, $pass){ + $hashpass = crypt($pass, WebUsers::generateSALT()); + $reply = WebUsers::setAmsPassword($user, $hashpass); + $drupal_pass = user_hash_password($pass); + $values = Array('user' => $user, 'pass' => $drupal_pass); + try { + //make connection with and put into shard db + db_query("UPDATE {users} SET pass = :pass WHERE name = :user", $values); + } + catch (PDOException $e) { + //ERROR: the web DB is offline + } + return $reply; + } + + + /** + * update the emailaddress. + * update the emailaddress in the shard + update the emailaddress in the www/CMS version. + * @param $user the username + * @param $mail the new emailaddress. + * @return ok if it worked, if the lib or shard is offline it will return liboffline or shardoffline. + */ + public function setEmail($user, $mail){ + $reply = WebUsers::setAmsEmail($user, $mail); + $values = Array('user' => $user, 'mail' => $mail); + try { + //make connection with and put into shard db + db_query("UPDATE {users} SET mail = :mail WHERE name = :user", $values); + + } + catch (PDOException $e) { + //ERROR: the web DB is offline + } + return $reply; + } + + + /** + * update the setReceiveMail value in the db. + * update the receiveMail in the www/CMS version. + * @param $user the username + * @param $receivemail the receivemail setting . + */ + public static function setReceiveMail($user, $receivemail){ + $values = Array('user' => $user, 'receivemail' => $receivemail); + try { + //make connection with and put into shard db + $dbw = new DBLayer("web"); + $dbw->execute("UPDATE ams_user SET ReceiveMail = :receivemail WHERE UId = :user ",$values); + } + catch (PDOException $e) { + //ERROR: the web DB is offline + } + } + + + /** + * update the language value in the db. + * update the language in the www/CMS version. + * @param $user the username + * @param $language the new language value. + */ + public static function setLanguage($user, $language){ + $values = Array('user' => $user, 'language' => $language); + try { + //make connection with and put into shard db + $dbw = new DBLayer("web"); + $dbw->execute("UPDATE ams_user SET Language = :language WHERE UId = :user ",$values); + } + catch (PDOException $e) { + //ERROR: the web DB is offline + } + } + + + /** + * return all users. + * @return return an array of users + */ + public function getUsers(){ + $dbl = new DBLayer("web"); + $data = $dbl->executeWithoutParams("SELECT * FROM ams_user"); + return $data; + } + + + /** + * return the query that should get all users. + * @return string: the query to receive all users. + */ + public static function getAllUsersQuery(){ + return "SELECT * FROM users WHERE `uid` > 0"; + } + + + /** + * creates a webuser. + * it will set the language matching to the language cookie setting and add it to the www/CMS's DB. + * @param $name the username + * @param $pass the unhashed password + * @param $mail the email address + */ + public static function createWebuser($name, $pass, $mail){ + + //register account with the correct language (check if cookie is already set)! + if ( isset( $_COOKIE['Language'] ) ) { + $lang = $_COOKIE['Language']; + }else{ + global $DEFAULT_LANGUAGE; + $lang = $DEFAULT_LANGUAGE; + } + + $values = Array('name' => $name, 'pass' => $pass, 'mail' => $mail, 'lang' => $lang); + + try { + $dbw = new DBLayer("web"); + return $dbw->executeReturnId("INSERT INTO ams_user (Login, Password, Email, Language) VALUES (:name, :pass, :mail, :lang)",$values); + } + catch (PDOException $e) { + //ERROR: the web DB is offline + } + } + } \ No newline at end of file diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/autoload/webusers.php b/code/ryzom/tools/server/ryzom_ams/www/html/autoload/webusers.php index 4e9ae845a..11afef80d 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/autoload/webusers.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/autoload/webusers.php @@ -1,21 +1,37 @@ uId = $UId; } + + /** + * sets the object's attributes. + * @param $values should be an array. + */ public function set($values){ $this->uId = $values['UId']; $this->login = $values['Login']; @@ -28,51 +44,57 @@ class WebUsers extends Users{ $this->language = $values['Language']; } - /** - * Function checkUserNameExists - * - * @takes $username - * @return string Info: Returns true or false if the user is in the web db. - */ - protected function checkUserNameExists($username){ - $dbw = new DBLayer("web"); - return $dbw->execute("SELECT * FROM ams_user WHERE Login = :name",array('name' => $username))->rowCount(); - } + + /** + * function that checks if a username exists already or not. + * This function overrides the function of the base class. + * @takes $username the username in question + * @return string Info: Returns 0 if the user is not in the web db, else a positive number is returned. + */ + protected function checkUserNameExists($username){ + $dbw = new DBLayer("web"); + return $dbw->execute("SELECT * FROM ams_user WHERE Login = :name",array('name' => $username))->rowCount(); + } - /** - * Function checkEmailExists - * - * @takes $username - * @return string Info: Returns true or false if the user is in the www db. - */ - protected function checkEmailExists($email){ - $dbw = new DBLayer("web"); - return $dbw->execute("SELECT * FROM ams_user WHERE Email = :email",array('email' => $email))->rowCount(); - } + /** + * function that checks if a email exists already or not. + * This function overrides the function of the base class. + * @takes $email the email address in question. + * @return string Info: Returns 0 if the email address is not in the web db, else a positive number is returned. + */ + protected function checkEmailExists($email){ + $dbw = new DBLayer("web"); + return $dbw->execute("SELECT * FROM ams_user WHERE Email = :email",array('email' => $email))->rowCount(); + } - /** - * Function checkUserPassMatch - * - * @takes $username,$password - * @return string Info: Returns true or false if a login match is found in the web db - */ - public function checkLoginMatch($username,$password){ - - $dbw = new DBLayer("web"); - $statement = $dbw->execute("SELECT * FROM ams_user WHERE Login=:user", array('user' => $username)); - $row = $statement->fetch(); - $salt = substr($row['Password'],0,2); - $hashed_input_pass = crypt($password, $salt); - if($hashed_input_pass == $row['Password']){ - return $row; - }else{ - return "fail"; - } - } + /** + * check if the login username and password match the db. + * @param $username the inserted username + * @param $password the inserted password (unhashed) + * @return the logged in user's db row as array if login was a success, else "fail" will be returned. + */ + public function checkLoginMatch($username,$password){ + + $dbw = new DBLayer("web"); + $statement = $dbw->execute("SELECT * FROM ams_user WHERE Login=:user", array('user' => $username)); + $row = $statement->fetch(); + $salt = substr($row['Password'],0,2); + $hashed_input_pass = crypt($password, $salt); + if($hashed_input_pass == $row['Password']){ + return $row; + }else{ + return "fail"; + } + } - //returns te id for a given username + + /** + * returns te id for a given username + * @param $username the username + * @return the user's id linked to the username + */ public static function getId($username){ $dbw = new DBLayer("web"); $statement = $dbw->execute("SELECT * FROM ams_user WHERE Login=:username", array('username' => $username)); @@ -80,7 +102,12 @@ class WebUsers extends Users{ return $row['UId']; } - //returns te id for a given username + + /** + * returns te id for a given emailaddress + * @param $email the emailaddress + * @return the user's id linked to the emailaddress + */ public static function getIdFromEmail($email){ $dbw = new DBLayer("web"); $statement = $dbw->execute("SELECT * FROM ams_user WHERE Email=:email", array('email' => $email)); @@ -92,150 +119,228 @@ class WebUsers extends Users{ } } + + /** + * get uId attribute of the object. + */ public function getUId(){ return $this->uId; } - public function getUsername(){ - $dbw = new DBLayer("web"); - if(! isset($this->login) || $this->login == ""){ - $statement = $dbw->execute("SELECT * FROM ams_user WHERE UId=:id", array('id' => $this->uId)); - $row = $statement->fetch(); - $this->set($row); - } - return $this->login; - } - public function getEmail(){ - $dbw = new DBLayer("web"); - if(! isset($this->email) || $this->email == ""){ - $statement = $dbw->execute("SELECT * FROM ams_user WHERE UId=:id", array('id' => $this->uId)); - $row = $statement->fetch(); - $this->set($row); - } - return $this->email; - } - - public function getInfo(){ - $dbw = new DBLayer("web"); - if(! (isset($this->firstname) && isset($this->lastname) && isset($this->gender) && isset($this->country) && isset($this->receiveMail) ) || - $this->firstname == "" || $this->lastname == "" || $this->gender == "" || $this->country == "" || $this->receiveMail == ""){ - $statement = $dbw->execute("SELECT * FROM ams_user WHERE UId=:id", array('id' => $this->uId)); - $row = $statement->fetch(); - $this->set($row); - } - $result = Array('FirstName' => $this->firstname, 'LastName' => $this->lastname, 'Gender' => $this->gender, 'Country' => $this->country, 'ReceiveMail' => $this->receiveMail); - return $result; - } - - public function getReceiveMail(){ - $dbw = new DBLayer("web"); - if(! isset($this->receiveMail) || $this->receiveMail == ""){ - $statement = $dbw->execute("SELECT * FROM ams_user WHERE UId=:id", array('id' => $this->uId)); - $row = $statement->fetch(); - $this->set($row); - } - return $this->receiveMail; - } - - public function getLanguage(){ - $dbw = new DBLayer("web"); - if(! isset($this->language) || $this->language == ""){ - $statement = $dbw->execute("SELECT * FROM ams_user WHERE UId=:id", array('id' => $this->uId)); - $row = $statement->fetch(); - $this->set($row); - } - return $this->language; - } - - public function isLoggedIn(){ - if(isset($_SESSION['user'])){ - return true; - } - return false; - } - - public function setPassword($user, $pass){ - $reply = WebUsers::setAmsPassword($user, $pass); - $values = Array('user' => $user, 'pass' => $pass); - try { - //make connection with and put into shard db - $dbw = new DBLayer("web"); - $dbw->execute("UPDATE ams_user SET Password = :pass WHERE Login = :user ",$values); - } - catch (PDOException $e) { - //ERROR: the web DB is offline - } - return $reply; - } - - public function setEmail($user, $mail){ - $reply = WebUsers::setAmsEmail($user, $mail); - $values = Array('user' => $user, 'mail' => $mail); - try { - //make connection with and put into shard db - $dbw = new DBLayer("web"); - $dbw->execute("UPDATE ams_user SET Email = :mail WHERE Login = :user ",$values); - } - catch (PDOException $e) { - //ERROR: the web DB is offline - } - return $reply; - } - - public static function setReceiveMail($user, $receivemail){ - $values = Array('user' => $user, 'receivemail' => $receivemail); - try { - //make connection with and put into shard db - $dbw = new DBLayer("web"); - $dbw->execute("UPDATE ams_user SET ReceiveMail = :receivemail WHERE UId = :user ",$values); - } - catch (PDOException $e) { - //ERROR: the web DB is offline - } - } - - public static function setLanguage($user, $language){ - $values = Array('user' => $user, 'language' => $language); - try { - //make connection with and put into shard db - $dbw = new DBLayer("web"); - $dbw->execute("UPDATE ams_user SET Language = :language WHERE UId = :user ",$values); - } - catch (PDOException $e) { - //ERROR: the web DB is offline - } - } - - public function getUsers(){ - $dbl = new DBLayer("web"); - $data = $dbl->executeWithoutParams("SELECT * FROM ams_user"); - return $data; - } - - public static function getAllUsersQuery(){ - return "SELECT * FROM ams_user"; - } - - public static function createWebuser($name, $pass, $mail){ - - //register account with the correct language (check if cookie is already set)! - if ( isset( $_COOKIE['Language'] ) ) { - $lang = $_COOKIE['Language']; - }else{ - global $DEFAULT_LANGUAGE; - $lang = $DEFAULT_LANGUAGE; - } - - $values = Array('name' => $name, 'pass' => $pass, 'mail' => $mail, 'lang' => $lang); - - try { + /** + * get login attribute of the object.(username) + */ + public function getUsername(){ $dbw = new DBLayer("web"); - return $dbw->executeReturnId("INSERT INTO ams_user (Login, Password, Email, Language) VALUES (:name, :pass, :mail, :lang)",$values); + if(! isset($this->login) || $this->login == ""){ + $statement = $dbw->execute("SELECT * FROM ams_user WHERE UId=:id", array('id' => $this->uId)); + $row = $statement->fetch(); + $this->set($row); + } + return $this->login; } - catch (PDOException $e) { - //ERROR: the web DB is offline + + + /** + * get email attribute of the object. + */ + public function getEmail(){ + $dbw = new DBLayer("web"); + if(! isset($this->email) || $this->email == ""){ + $statement = $dbw->execute("SELECT * FROM ams_user WHERE UId=:id", array('id' => $this->uId)); + $row = $statement->fetch(); + $this->set($row); + } + return $this->email; + } + + + /** + * get basic info of the object. + * @return returns an array in the form of Array('FirstName' => $this->firstname, 'LastName' => $this->lastname, 'Gender' => $this->gender, 'Country' => $this->country, 'ReceiveMail' => $this->receiveMail) + */ + public function getInfo(){ + $dbw = new DBLayer("web"); + if(! (isset($this->firstname) && isset($this->lastname) && isset($this->gender) && isset($this->country) && isset($this->receiveMail) ) || + $this->firstname == "" || $this->lastname == "" || $this->gender == "" || $this->country == "" || $this->receiveMail == ""){ + $statement = $dbw->execute("SELECT * FROM ams_user WHERE UId=:id", array('id' => $this->uId)); + $row = $statement->fetch(); + $this->set($row); + } + $result = Array('FirstName' => $this->firstname, 'LastName' => $this->lastname, 'Gender' => $this->gender, 'Country' => $this->country, 'ReceiveMail' => $this->receiveMail); + return $result; + } + + + /** + * get receiveMail attribute of the object. + */ + public function getReceiveMail(){ + $dbw = new DBLayer("web"); + if(! isset($this->receiveMail) || $this->receiveMail == ""){ + $statement = $dbw->execute("SELECT * FROM ams_user WHERE UId=:id", array('id' => $this->uId)); + $row = $statement->fetch(); + $this->set($row); + } + return $this->receiveMail; + } + + + /** + * get language attribute of the object. + */ + public function getLanguage(){ + $dbw = new DBLayer("web"); + if(! isset($this->language) || $this->language == ""){ + $statement = $dbw->execute("SELECT * FROM ams_user WHERE UId=:id", array('id' => $this->uId)); + $row = $statement->fetch(); + $this->set($row); + } + return $this->language; + } + + + /** + * check if the user is logged in. + * @return true or false + */ + public function isLoggedIn(){ + if(isset($_SESSION['user'])){ + return true; + } + return false; + } + + + /** + * update the password. + * update the password in the shard + update the password in the www/CMS version. + * @param $user the username + * @param $pass the new password. + * @return ok if it worked, if the lib or shard is offline it will return liboffline or shardoffline. + */ + public function setPassword($user, $pass){ + $reply = WebUsers::setAmsPassword($user, $pass); + $values = Array('user' => $user, 'pass' => $pass); + try { + //make connection with and put into shard db + $dbw = new DBLayer("web"); + $dbw->execute("UPDATE ams_user SET Password = :pass WHERE Login = :user ",$values); + } + catch (PDOException $e) { + //ERROR: the web DB is offline + } + return $reply; + } + + + /** + * update the emailaddress. + * update the emailaddress in the shard + update the emailaddress in the www/CMS version. + * @param $user the username + * @param $mail the new emailaddress. + * @return ok if it worked, if the lib or shard is offline it will return liboffline or shardoffline. + */ + public function setEmail($user, $mail){ + $reply = WebUsers::setAmsEmail($user, $mail); + $values = Array('user' => $user, 'mail' => $mail); + try { + //make connection with and put into shard db + $dbw = new DBLayer("web"); + $dbw->execute("UPDATE ams_user SET Email = :mail WHERE Login = :user ",$values); + } + catch (PDOException $e) { + //ERROR: the web DB is offline + } + return $reply; + } + + + /** + * update the setReceiveMail value in the db. + * update the receiveMail in the www/CMS version. + * @param $user the username + * @param $receivemail the receivemail setting . + */ + public static function setReceiveMail($user, $receivemail){ + $values = Array('user' => $user, 'receivemail' => $receivemail); + try { + //make connection with and put into shard db + $dbw = new DBLayer("web"); + $dbw->execute("UPDATE ams_user SET ReceiveMail = :receivemail WHERE UId = :user ",$values); + } + catch (PDOException $e) { + //ERROR: the web DB is offline + } + } + + + /** + * update the language value in the db. + * update the language in the www/CMS version. + * @param $user the username + * @param $language the new language value. + */ + public static function setLanguage($user, $language){ + $values = Array('user' => $user, 'language' => $language); + try { + //make connection with and put into shard db + $dbw = new DBLayer("web"); + $dbw->execute("UPDATE ams_user SET Language = :language WHERE UId = :user ",$values); + } + catch (PDOException $e) { + //ERROR: the web DB is offline + } + } + + + /** + * return all users. + * @return return an array of users + */ + public function getUsers(){ + $dbl = new DBLayer("web"); + $data = $dbl->executeWithoutParams("SELECT * FROM ams_user"); + return $data; + } + + + /** + * return the query that should get all users. + * @return string: the query to receive all users. + */ + public static function getAllUsersQuery(){ + return "SELECT * FROM ams_user"; + } + + + /** + * creates a webuser. + * it will set the language matching to the language cookie setting and add it to the www/CMS's DB. + * @param $name the username + * @param $pass the unhashed password + * @param $mail the email address + */ + public static function createWebuser($name, $pass, $mail){ + + //register account with the correct language (check if cookie is already set)! + if ( isset( $_COOKIE['Language'] ) ) { + $lang = $_COOKIE['Language']; + }else{ + global $DEFAULT_LANGUAGE; + $lang = $DEFAULT_LANGUAGE; + } + + $values = Array('name' => $name, 'pass' => $pass, 'mail' => $mail, 'lang' => $lang); + + try { + $dbw = new DBLayer("web"); + return $dbw->executeReturnId("INSERT INTO ams_user (Login, Password, Email, Language) VALUES (:name, :pass, :mail, :lang)",$values); + } + catch (PDOException $e) { + //ERROR: the web DB is offline + } } - } } \ No newline at end of file diff --git a/code/ryzom/tools/server/ryzom_ams_docs/doxygen/Doxyfile b/code/ryzom/tools/server/ryzom_ams_docs/doxygen/Doxyfile new file mode 100644 index 000000000..5f5327ce5 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/doxygen/Doxyfile @@ -0,0 +1,1781 @@ +# Doxyfile 1.7.6.1 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" "). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# http://www.gnu.org/software/libiconv for the list of possible encodings. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or sequence of words) that should +# identify the project. Note that if you do not use Doxywizard you need +# to put quotes around the project name if it contains spaces. + +PROJECT_NAME = "Ryzom Account Management System" + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer +# a quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = + +# With the PROJECT_LOGO tag one can specify an logo or icon that is +# included in the documentation. The maximum height of the logo should not +# exceed 55 pixels and the maximum width should not exceed 200 pixels. +# Doxygen will copy the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = "../" + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, +# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English +# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, +# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, +# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful if your file system +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments +# (thus requiring an explicit @brief command for a brief description.) + +JAVADOC_AUTOBRIEF = YES + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring +# an explicit \brief command for a brief description.) + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding +# "class=itcl::class" will allow you to use the command class in the +# itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for +# Java. For instance, namespaces will be presented as packages, qualified +# scopes will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources only. Doxygen will then generate output that is more tailored for +# Fortran. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for +# VHDL. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given extension. +# Doxygen has a built-in mapping, but you can override or extend it using this +# tag. The format is ext=language, where ext is a file extension, and language +# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, +# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make +# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C +# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions +# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also makes the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. +# Doxygen will parse them like normal C++ but will assume all classes use public +# instead of private inheritance when no explicit protection keyword is present. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate getter +# and setter methods for a property. Setting this option to YES (the default) +# will make doxygen replace the get and set methods by a property in the +# documentation. This will only work if the methods are indeed getting or +# setting a simple type. If this is not the case, or you want to show the +# methods anyway, you should set this option to NO. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and +# unions are shown inside the group in which they are included (e.g. using +# @ingroup) instead of on a separate page (for HTML and Man pages) or +# section (for LaTeX and RTF). + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and +# unions with only public data fields will be shown inline in the documentation +# of the scope in which they are defined (i.e. file, namespace, or group +# documentation), provided this scope is documented. If set to NO (the default), +# structs, classes, and unions are shown on a separate page (for HTML and Man +# pages) or section (for LaTeX and RTF). + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum +# is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically +# be useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. + +TYPEDEF_HIDES_STRUCT = NO + +# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to +# determine which symbols to keep in memory and which to flush to disk. +# When the cache is full, less often used symbols will be written to disk. +# For small to medium size projects (<1000 input files) the default value is +# probably good enough. For larger projects a too small cache size can cause +# doxygen to be busy swapping symbols to and from disk most of the time +# causing a significant performance penalty. +# If the system has enough physical memory increasing the cache will improve the +# performance by keeping more symbols in memory. Note that the value works on +# a logarithmic scale so increasing the size by one will roughly double the +# memory usage. The cache size is given by this formula: +# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols. + +SYMBOL_CACHE_SIZE = 0 + +# Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be +# set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given +# their name and scope. Since this can be an expensive process and often the +# same symbol appear multiple times in the code, doxygen keeps a cache of +# pre-resolved symbols. If the cache is too small doxygen will become slower. +# If the cache is too large, memory is wasted. The cache size is given by this +# formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = YES + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = YES + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base +# name of the file that contains the anonymous namespace. By default +# anonymous namespaces are hidden. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen +# will list include files with double quotes in the documentation +# rather than with sharp brackets. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen +# will sort the (brief and detailed) documentation of class members so that +# constructors and destructors are listed first. If set to NO (the default) +# the constructors will appear in the respective orders defined by +# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. +# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO +# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the +# hierarchy of group names into alphabetical order. If set to NO (the default) +# the group names will appear in their defined order. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to +# do proper type resolution of all parameters of a function it will reject a +# match between the prototype and the implementation of a member function even +# if there is only one candidate or it is obvious which candidate to choose +# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen +# will still accept a match between prototype and implementation in such cases. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or macro consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and macros in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. The default is NO. + +SHOW_DIRECTORIES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. +# This will remove the Files entry from the Quick Index and from the +# Folder Tree View (if specified). The default is YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the +# Namespaces page. +# This will remove the Namespaces entry from the Quick Index +# and from the Folder Tree View (if specified). The default is YES. + +SHOW_NAMESPACES = NO + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. The create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. +# You can optionally specify a file name after the option, if omitted +# DoxygenLayout.xml will be used as the name of the layout file. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files +# containing the references data. This must be a list of .bib files. The +# .bib extension is automatically appended if omitted. Using this command +# requires the bibtex tool to be installed. See also +# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style +# of the bibliography can be controlled using LATEX_BIB_STYLE. To use this +# feature you need bibtex and perl available in the search path. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# The WARN_NO_PARAMDOC option can be enabled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = ../../ryzom_ams/ams_lib/autoload ../../ryzom_ams/www/html/autoload info.php + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is +# also the default input encoding. Doxygen uses libiconv (or the iconv built +# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for +# the list of possible encodings. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh +# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py +# *.f90 *.f *.for *.vhd *.vhdl + +FILE_PATTERNS = *.php + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. +# If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. +# Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. +# The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty or if +# non of the patterns match the file name, INPUT_FILTER is applied. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) +# and it is also possible to disable source filtering for a specific pattern +# using *.ext= (so without naming a filter). This option only has effect when +# FILTER_SOURCE_FILES is enabled. + +FILTER_SOURCE_PATTERNS = + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. +# Otherwise they will link to the documentation. + +REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = YES + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. Note that when using a custom header you are responsible +# for the proper inclusion of any scripts and style sheets that doxygen +# needs, which is dependent on the configuration options used. +# It is advised to generate a default header using "doxygen -w html +# header.html footer.html stylesheet.css YourConfigFile" and then modify +# that header. Note that the header is subject to change so you typically +# have to redo this when upgrading to a newer version of doxygen or when +# changing the value of configuration settings such as GENERATE_TREEVIEW! + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# style sheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that +# the files will be copied as-is; there are no commands or markers available. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. +# Doxygen will adjust the colors in the style sheet and background images +# according to this color. Hue is specified as an angle on a colorwheel, +# see http://en.wikipedia.org/wiki/Hue for more information. +# For instance the value 0 represents red, 60 is yellow, 120 is green, +# 180 is cyan, 240 is blue, 300 purple, and 360 is red again. +# The allowed range is 0 to 359. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of +# the colors in the HTML output. For a value of 0 the output will use +# grayscales only. A value of 255 will produce the most vivid colors. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to +# the luminance component of the colors in the HTML output. Values below +# 100 gradually make the output lighter, whereas values above 100 make +# the output darker. The value divided by 100 is the actual gamma applied, +# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, +# and 100 does not change the gamma. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting +# this to NO can help when comparing the output of multiple runs. + +HTML_TIMESTAMP = YES + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. For this to work a browser that supports +# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox +# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). + +HTML_DYNAMIC_SECTIONS = NO + +# If the GENERATE_DOCSET tag is set to YES, additional index files +# will be generated that can be used as input for Apple's Xcode 3 +# integrated development environment, introduced with OSX 10.5 (Leopard). +# To create a documentation set, doxygen will generate a Makefile in the +# HTML output directory. Running make will produce the docset in that +# directory and running "make install" will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find +# it at startup. +# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. + +GENERATE_DOCSET = NO + +# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the +# feed. A documentation feed provides an umbrella under which multiple +# documentation sets from a single provider (such as a company or product suite) +# can be grouped. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that +# should uniquely identify the documentation set bundle. This should be a +# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen +# will append .docset to the name. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING +# is used to encode HtmlHelp index (hhk), content (hhc) and project file +# content. + +CHM_INDEX_ENCODING = + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated +# that can be used as input for Qt's qhelpgenerator to generate a +# Qt Compressed Help (.qch) of the generated HTML documentation. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can +# be used to specify the file name of the resulting .qch file. +# The path specified is relative to the HTML output folder. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#namespace + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#virtual-folders + +QHP_VIRTUAL_FOLDER = doc + +# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to +# add. For more information please see +# http://doc.trolltech.com/qthelpproject.html#custom-filters + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see +# +# Qt Help Project / Custom Filters. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's +# filter section matches. +# +# Qt Help Project / Filter Attributes. + +QHP_SECT_FILTER_ATTRS = + +# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can +# be used to specify the location of Qt's qhelpgenerator. +# If non-empty doxygen will try to run qhelpgenerator on the generated +# .qhp file. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files +# will be generated, which together with the HTML files, form an Eclipse help +# plugin. To install this plugin and make it available under the help contents +# menu in Eclipse, the contents of the directory containing the HTML and XML +# files needs to be copied into the plugins directory of eclipse. The name of +# the directory within the plugins directory should be the same as +# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before +# the help appears. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have +# this name. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) +# at top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. Since the tabs have the same information as the +# navigation tree you can set this option to NO if you already set +# GENERATE_TREEVIEW to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. +# If the tag value is set to YES, a side panel will be generated +# containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). +# Windows users are probably better off using the HTML help feature. +# Since the tree basically has the same information as the tab index you +# could consider to set DISABLE_INDEX to NO when enabling this option. + +GENERATE_TREEVIEW = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values +# (range [0,1..20]) that doxygen will group on one line in the generated HTML +# documentation. Note that a value of 0 will completely suppress the enum +# values from appearing in the overview section. + +ENUM_VALUES_PER_LINE = 4 + +# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, +# and Class Hierarchy pages using a tree view instead of an ordered list. + +USE_INLINE_TREES = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open +# links to external symbols imported via tag files in a separate window. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of Latex formulas included +# as images in the HTML documentation. The default is 10. Note that +# when you change the font size after a successful doxygen run you need +# to manually remove any form_*.png images from the HTML output directory +# to force them to be regenerated. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are +# not supported properly for IE 6.0, but are supported on all modern browsers. +# Note that when changing this option you need to delete any form_*.png files +# in the HTML output before the changes have effect. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax +# (see http://www.mathjax.org) which uses client side Javascript for the +# rendering instead of using prerendered bitmaps. Use this if you do not +# have LaTeX installed or if you want to formulas look prettier in the HTML +# output. When enabled you also need to install MathJax separately and +# configure the path to it using the MATHJAX_RELPATH option. + +USE_MATHJAX = NO + +# When MathJax is enabled you need to specify the location relative to the +# HTML output directory using the MATHJAX_RELPATH option. The destination +# directory should contain the MathJax.js script. For instance, if the mathjax +# directory is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the +# mathjax.org site, so you can quickly see the result without installing +# MathJax, but it is strongly recommended to install a local copy of MathJax +# before deployment. + +MATHJAX_RELPATH = http://www.mathjax.org/mathjax + +# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension +# names that should be enabled during MathJax rendering. + +MATHJAX_EXTENSIONS = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box +# for the HTML output. The underlying search engine uses javascript +# and DHTML and should work on any modern browser. Note that when using +# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets +# (GENERATE_DOCSET) there is already a search function so this one should +# typically be disabled. For large projects the javascript based search engine +# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. + +SEARCHENGINE = YES + +# When the SERVER_BASED_SEARCH tag is enabled the search engine will be +# implemented using a PHP enabled web server instead of at the web client +# using Javascript. Doxygen will generate the search PHP script and index +# file to put on the web server. The advantage of the server +# based approach is that it scales better to large projects and allows +# full text search. The disadvantages are that it is more difficult to setup +# and does not have live searching capabilities. + +SERVER_BASED_SEARCH = NO + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. +# Note that when enabling USE_PDFLATEX this option is only used for +# generating bitmaps for formulas in the HTML output, but not in the +# Makefile that is written to the output directory. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4 + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for +# the generated latex document. The footer should contain everything after +# the last chapter. If it is left blank doxygen will generate a +# standard footer. Notice: only use this tag if you know what you are doing! + +LATEX_FOOTER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +# If LATEX_SOURCE_CODE is set to YES then doxygen will include +# source code with syntax highlighting in the LaTeX output. +# Note that which sources are shown also depends on other settings +# such as SOURCE_BROWSER. + +LATEX_SOURCE_CODE = NO + +# The LATEX_BIB_STYLE tag can be used to specify the style to use for the +# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See +# http://en.wikipedia.org/wiki/BibTeX for more info. + +LATEX_BIB_STYLE = plain + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load style sheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. +# This is useful +# if you want to understand what is going on. +# On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# pointed to by INCLUDE_PATH will be searched when a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition that +# overrules the definition found in the source code. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all references to function-like macros +# that are alone on a line, have an all uppercase name, and do not end with a +# semicolon, because these will confuse the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option also works with HAVE_DOT disabled, but it is recommended to +# install and use dot, since it yields more powerful graphs. + +CLASS_DIAGRAMS = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see +# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = NO + +# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is +# allowed to run in parallel. When set to 0 (the default) doxygen will +# base this on the number of processors available in the system. You can set it +# explicitly to a value larger than 0 to get control over the balance +# between CPU load and processing speed. + +DOT_NUM_THREADS = 0 + +# By default doxygen will use the Helvetica font for all dot files that +# doxygen generates. When you want a differently looking font you can specify +# the font name using DOT_FONTNAME. You need to make sure dot is able to find +# the font, which can be done by putting it in a standard location or by setting +# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the +# directory containing the font. + +DOT_FONTNAME = Helvetica + +# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. +# The default size is 10pt. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the Helvetica font. +# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to +# set the path where dot can find it. + +DOT_FONTPATH = + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT options are set to YES then +# doxygen will generate a call dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable call graphs +# for selected functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then +# doxygen will generate a caller dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable caller +# graphs for selected functions only using the \callergraph command. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will generate a graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are svg, png, jpg, or gif. +# If left blank png will be used. If you choose svg you need to set +# HTML_FILE_EXTENSION to xhtml in order to make the SVG files +# visible in IE 9+ (other browsers do not have this requirement). + +DOT_IMAGE_FORMAT = png + +# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to +# enable generation of interactive SVG images that allow zooming and panning. +# Note that this requires a modern browser other than Internet Explorer. +# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you +# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files +# visible. Older versions of IE do not have SVG support. + +INTERACTIVE_SVG = NO + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MSCFILE_DIRS tag can be used to specify one or more directories that +# contain msc files that are included in the documentation (see the +# \mscfile command). + +MSCFILE_DIRS = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the +# number of direct children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not +# seem to support this out of the box. Warning: Depending on the platform used, +# enabling this option may lead to badly anti-aliased labels on the edges of +# a graph (i.e. they become hard to read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = YES + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES diff --git a/code/ryzom/tools/server/ryzom_ams_docs/doxygen/info.php b/code/ryzom/tools/server/ryzom_ams_docs/doxygen/info.php new file mode 100644 index 000000000..b5c3088cb --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/doxygen/info.php @@ -0,0 +1,13 @@ + + + + + +Ryzom Account Management System: Class List + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + +
    +
    +
    +
    Class List
    +
    +
    +
    Here are the classes, structs, unions and interfaces with brief descriptions:
    + + + + + + + + + + + + + + + + + + + + + + + +
    AssignedHandles the assigning of a ticket to a user
    DBLayerHandles the database connections
    ForwardedHandles the forwarding of a ticket to a support_group
    Gui_ElementsHelper class for generating gui related elements
    HelpersHelper class for more site specific functions
    In_Support_GroupHandles the linkage of users being in a support group
    Mail_HandlerHandles the mailing functionality
    MyCryptBasic encryption/decryption class
    PaginationHandles returning arrays based on a given pagenumber
    QuerycacheClass for storing changes when shard is offline
    Support_GroupGroups moderators & admins together
    SyncHandler for performing changes when shard is back online after being offline
    TicketClass that handles most ticket related functions
    Ticket_CategoryClass related to the ticket categories
    Ticket_ContentClass that handles the content of a reply
    Ticket_InfoClass that handles additional info sent by ticket creation ingame
    Ticket_LogClass that handles the logging
    Ticket_QueueData class that holds a lot of queries that load specific tickets
    Ticket_Queue_HandlerReturns tickets (queues) that are related in some way
    Ticket_ReplyHandles functions related to replies on tickets
    Ticket_UserUser entry point in the ticket system
    UsersHandles basic user registration & management functions (shard related)
    WebUsersHandles CMS/WWW related functions regarding user management & registration
    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/bc_s.png b/code/ryzom/tools/server/ryzom_ams_docs/html/bc_s.png new file mode 100644 index 0000000000000000000000000000000000000000..e4018628b5b45cb4301037485a29d7d74ac22138 GIT binary patch literal 677 zcmV;W0$TlvP)X?0Pv5h+5!wElpi=&YL!gfY!djl#UDdPKy97F|A-deTa@qo3BWh1YQIvzmHR^g zFjV4I6pLB7_*vEZk^%p7c7Bh>0`4r^X#gpJE_Vz9fSHKqclcZaV^k3gX%h+1`u||O zZ+BY?7(R=ayr^kXE=E0Dw=$Ud3VJ?9^Cz@hP?388Cw5>9TloOJ>^KczCgj zns2=|0!a|)Yq3{hjL{xyy7|Tk0N}Pe+g9PUTL!4{#;eUhrNd@!_T<>Vu+35c)h>sq ztgb?(6W3oFLz#%?OMEV@{j#4LuDvjVGZ~6hpQT8li5b0yjvK8c4efl+vSz5)P6 zle78)00_Iv5)&E~hnOdcd}L}i+MU>k+Q8#@KjqJJN`gRj(~)RmNrck9ht@LelPtVO zwp(J;k!T=gC#%o(13-^E+g@aqc()pf{+j|0w)AH*Mq$54UjLv#jV$RYpz3Vjg$$=u z>yjfBQOhL=^@+#4#$l|{~}HZ-?1Yy{lI*$N}*YDC`<{+;>_#gMXZdz4NI00000 LNkvXXu0mjfx86dR literal 0 HcmV?d00001 diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/classAssigned-members.html b/code/ryzom/tools/server/ryzom_ams_docs/html/classAssigned-members.html new file mode 100644 index 000000000..c18cd720c --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/classAssigned-members.html @@ -0,0 +1,125 @@ + + + + + +Ryzom Account Management System: Member List + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + +
    +
    +
    +
    Assigned Member List
    +
    +
    +This is the complete list of members for Assigned, including all inherited members. + + + + + + + + + + + + + + + +
    $ticketAssigned [private]
    $userAssigned [private]
    __construct()Assigned
    assignTicket($user_id, $ticket_id)Assigned [static]
    create()Assigned
    delete()Assigned
    getTicket()Assigned
    getUser()Assigned
    getUserAssignedToTicket($ticket_id)Assigned [static]
    isAssigned($ticket_id, $user_id=0)Assigned [static]
    load($ticket_id)Assigned
    set($values)Assigned
    setTicket($t)Assigned
    setUser($u)Assigned
    unAssignTicket($user_id, $ticket_id)Assigned [static]
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/classAssigned.html b/code/ryzom/tools/server/ryzom_ams_docs/html/classAssigned.html new file mode 100644 index 000000000..c27cd4e70 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/classAssigned.html @@ -0,0 +1,466 @@ + + + + + +Ryzom Account Management System: Assigned Class Reference + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + +
    + +
    + +

    Handles the assigning of a ticket to a user. + More...

    + +

    List of all members.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    +Public Member Functions

     __construct ()
     A constructor.
     set ($values)
     sets the object's attributes.
     create ()
     creates a new 'assigned' entry.
     delete ()
     deletes an existing 'assigned' entry.
     load ($ticket_id)
     loads the object's attributes.
    getUser ()
     get user attribute of the object.
    getTicket ()
     get ticket attribute of the object.
     setUser ($u)
     set user attribute of the object.
     setTicket ($t)
     set ticket attribute of the object.

    +Static Public Member Functions

    static assignTicket ($user_id, $ticket_id)
     Assigns a ticket to a user or returns an error message.
    static unAssignTicket ($user_id, $ticket_id)
     Unassign a ticket being coupled to a user or return an error message.
    static getUserAssignedToTicket ($ticket_id)
     Get the (external) id of the user assigned to a ticket.
    static isAssigned ($ticket_id, $user_id=0)
     Check if a ticket is already assigned (in case the user_id param is used, it will check if it's assigned to that user)

    +Private Attributes

    $user
     The id of the user being assigned.
    $ticket
     The id of the ticket being assigned.
    +

    Detailed Description

    +

    Handles the assigning of a ticket to a user.

    +

    This is being used to make someone responsible for the handling and solving of a ticket. The idea is that someone can easily assign a ticket to himself and by doing that, he makes aware to the other moderators that he will deal with the ticket in the future.

    +
    Author:
    Daan Janssens, mentored by Matthew Lagoe
    +

    Constructor & Destructor Documentation

    + +
    +
    + + + + + + + +
    Assigned::__construct ()
    +
    +
    + +

    A constructor.

    +

    Empty constructor

    + +
    +
    +

    Member Function Documentation

    + +
    +
    + + + + + + + + + + + + + + + + + + +
    static Assigned::assignTicket (user_id,
    ticket_id 
    ) [static]
    +
    +
    + +

    Assigns a ticket to a user or returns an error message.

    +

    It will first check if the ticket isn't already assigned, if not, it will create a new 'assigned' entry.

    +
    Parameters:
    + + + +
    $user_idthe id of the user we want to assign to the ticket
    $ticket_idthe id of the ticket.
    +
    +
    +
    Returns:
    A string, if assigning succeedded "SUCCESS_ASSIGNED" will be returned, else "ALREADY_ASSIGNED" will be returned.
    + +
    +
    + +
    +
    + + + + + + + +
    Assigned::create ()
    +
    +
    + +

    creates a new 'assigned' entry.

    +

    this method will use the object's attributes for creating a new 'assigned' entry in the database.

    + +
    +
    + +
    +
    + + + + + + + +
    Assigned::delete ()
    +
    +
    + +

    deletes an existing 'assigned' entry.

    +

    this method will use the object's attributes for deleting an existing 'assigned' entry in the database.

    + +
    +
    + +
    +
    + + + + + + + + +
    static Assigned::getUserAssignedToTicket (ticket_id) [static]
    +
    +
    + +

    Get the (external) id of the user assigned to a ticket.

    +
    Parameters:
    + + +
    $ticket_idthe Id of the ticket that's being queried
    +
    +
    +
    Returns:
    The (external)id of the user being assigned to the ticket
    + +
    +
    + +
    +
    + + + + + + + + + + + + + + + + + + +
    static Assigned::isAssigned (ticket_id,
    user_id = 0 
    ) [static]
    +
    +
    + +

    Check if a ticket is already assigned (in case the user_id param is used, it will check if it's assigned to that user)

    +
    Parameters:
    + + + +
    $ticket_idthe Id of the ticket that's being queried
    $user_idthe id of the user, default parameter = 0, by using a user_id, it will check if that user is assigned to the ticket.
    +
    +
    +
    Returns:
    true in case it's assigned, false in case it isn't.
    + +
    +
    + +
    +
    + + + + + + + + +
    Assigned::load (ticket_id)
    +
    +
    + +

    loads the object's attributes.

    +

    loads the object's attributes by giving a ticket_id, it will put the matching user_id and the ticket_id into the attributes.

    +
    Parameters:
    + + +
    $ticket_idthe id of the ticket that should be loaded
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Assigned::set (values)
    +
    +
    + +

    sets the object's attributes.

    +
    Parameters:
    + + +
    $valuesshould be an array of the form array('User' => user_id, 'Ticket' => ticket_id).
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Assigned::setTicket (t)
    +
    +
    + +

    set ticket attribute of the object.

    +
    Parameters:
    + + +
    $tinteger id of the ticket
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Assigned::setUser (u)
    +
    +
    + +

    set user attribute of the object.

    +
    Parameters:
    + + +
    $uinteger id of the user
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + + + + + + + + + + + +
    static Assigned::unAssignTicket (user_id,
    ticket_id 
    ) [static]
    +
    +
    + +

    Unassign a ticket being coupled to a user or return an error message.

    +

    It will first check if the ticket is assigned, if this is indeed the case it will delete the 'assigned' entry.

    +
    Parameters:
    + + + +
    $user_idthe id of the user we want to unassign from the ticket
    $ticket_idthe id of the ticket.
    +
    +
    +
    Returns:
    A string, if unassigning succeedded "SUCCESS_UNASSIGNED" will be returned, else "NOT_ASSIGNED" will be returned.
    + +
    +
    +
    The documentation for this class was generated from the following file:
      +
    • /home/daan/ryzom/ryzomcore/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/assigned.php
    • +
    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/classDBLayer-members.html b/code/ryzom/tools/server/ryzom_ams_docs/html/classDBLayer-members.html new file mode 100644 index 000000000..bd993f903 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/classDBLayer-members.html @@ -0,0 +1,115 @@ + + + + + +Ryzom Account Management System: Member List + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + +
    +
    +
    +
    DBLayer Member List
    +
    +
    +This is the complete list of members for DBLayer, including all inherited members. + + + + + +
    $PDODBLayer [private]
    __construct($db)DBLayer
    execute($query, $params)DBLayer
    executeReturnId($query, $params)DBLayer
    executeWithoutParams($query)DBLayer
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/classDBLayer.html b/code/ryzom/tools/server/ryzom_ams_docs/html/classDBLayer.html new file mode 100644 index 000000000..e5f879bce --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/classDBLayer.html @@ -0,0 +1,269 @@ + + + + + +Ryzom Account Management System: DBLayer Class Reference + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + +
    +
    + +
    +
    DBLayer Class Reference
    +
    +
    + +

    Handles the database connections. + More...

    + +

    List of all members.

    + + + + + + + + + + + + + +

    +Public Member Functions

     __construct ($db)
     The constructor.
     executeWithoutParams ($query)
     execute a query that doesn't have any parameters
     execute ($query, $params)
     execute a query that has parameters
     executeReturnId ($query, $params)
     execute a query (an insertion query) that has parameters and return the id of it's insertion

    +Private Attributes

    $PDO
     The PDO object, instantiated by the constructor.
    +

    Detailed Description

    +

    Handles the database connections.

    +

    It uses PDO to connect to the different databases. It will use the argument of the constructor to setup a connection to the database with the matching entry in the $cfg global variable.

    +
    Author:
    Daan Janssens, mentored by Matthew Lagoe
    +

    Constructor & Destructor Documentation

    + +
    +
    + + + + + + + + +
    DBLayer::__construct (db)
    +
    +
    + +

    The constructor.

    +

    Instantiates the PDO object attribute by connecting to the arguments matching database(the db info is stored in the $cfg global var)

    +
    Parameters:
    + + +
    String,thename of the databases entry in the $cfg global var.
    +
    +
    + +
    +
    +

    Member Function Documentation

    + +
    +
    + + + + + + + + + + + + + + + + + + +
    DBLayer::execute (query,
    params 
    )
    +
    +
    + +

    execute a query that has parameters

    +
    Parameters:
    + + + +
    $querythe mysql query
    $paramsthe parameters that are being used by the query
    +
    +
    +
    Returns:
    returns a PDOStatement object
    + +
    +
    + +
    +
    + + + + + + + + + + + + + + + + + + +
    DBLayer::executeReturnId (query,
    params 
    )
    +
    +
    + +

    execute a query (an insertion query) that has parameters and return the id of it's insertion

    +
    Parameters:
    + + + +
    $querythe mysql query
    $paramsthe parameters that are being used by the query
    +
    +
    +
    Returns:
    returns the id of the last inserted element.
    + +
    +
    + +
    +
    + + + + + + + + +
    DBLayer::executeWithoutParams (query)
    +
    +
    + +

    execute a query that doesn't have any parameters

    +
    Parameters:
    + + +
    $querythe mysql query
    +
    +
    +
    Returns:
    returns a PDOStatement object
    + +
    +
    +
    The documentation for this class was generated from the following file:
      +
    • /home/daan/ryzom/ryzomcore/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/dblayer.php
    • +
    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/classForwarded-members.html b/code/ryzom/tools/server/ryzom_ams_docs/html/classForwarded-members.html new file mode 100644 index 000000000..71e9559d7 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/classForwarded-members.html @@ -0,0 +1,124 @@ + + + + + +Ryzom Account Management System: Member List + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + +
    +
    +
    +
    Forwarded Member List
    +
    +
    +This is the complete list of members for Forwarded, including all inherited members. + + + + + + + + + + + + + + +
    $groupForwarded [private]
    $ticketForwarded [private]
    __construct()Forwarded
    create()Forwarded
    delete()Forwarded
    forwardTicket($group_id, $ticket_id)Forwarded [static]
    getGroup()Forwarded
    getSGroupOfTicket($ticket_id)Forwarded [static]
    getTicket()Forwarded
    isForwarded($ticket_id)Forwarded [static]
    load($ticket_id)Forwarded
    set($values)Forwarded
    setGroup($g)Forwarded
    setTicket($t)Forwarded
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/classForwarded.html b/code/ryzom/tools/server/ryzom_ams_docs/html/classForwarded.html new file mode 100644 index 000000000..93dc9878c --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/classForwarded.html @@ -0,0 +1,415 @@ + + + + + +Ryzom Account Management System: Forwarded Class Reference + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + +
    + +
    + +

    Handles the forwarding of a ticket to a support_group. + More...

    + +

    List of all members.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    +Public Member Functions

     __construct ()
     A constructor.
     set ($values)
     sets the object's attributes.
     create ()
     creates a new 'forwarded' entry.
     delete ()
     deletes an existing 'forwarded' entry.
     load ($ticket_id)
     loads the object's attributes.
    getGroup ()
     get group attribute of the object.
    getTicket ()
     get ticket attribute of the object.
     setGroup ($g)
     set group attribute of the object.
     setTicket ($t)
     set ticket attribute of the object.

    +Static Public Member Functions

    static forwardTicket ($group_id, $ticket_id)
     Forward a ticket to a group, also removes the previous group where it was forwarded to.
    static getSGroupOfTicket ($ticket_id)
     get the id of the group a ticket is forwarded to.
    static isForwarded ($ticket_id)
     check if the ticket is forwarded

    +Private Attributes

    $group
     The id of the group to which the ticket is being forwarded.
    $ticket
     The id of the ticket being forwarded.
    +

    Detailed Description

    +

    Handles the forwarding of a ticket to a support_group.

    +

    This is being used to transfer tickets to different groups (eg Developers, Website-Team, SupportGroup etc..) The idea is that someone can easily forward a ticket to a group and by doing that, the moderators that are in that group will receive the ticket in their todo queue.

    +
    Author:
    Daan Janssens, mentored by Matthew Lagoe
    +

    Constructor & Destructor Documentation

    + +
    +
    + + + + + + + +
    Forwarded::__construct ()
    +
    +
    + +

    A constructor.

    +

    Empty constructor

    + +
    +
    +

    Member Function Documentation

    + +
    +
    + + + + + + + +
    Forwarded::create ()
    +
    +
    + +

    creates a new 'forwarded' entry.

    +

    this method will use the object's attributes for creating a new 'forwarded' entry in the database.

    + +
    +
    + +
    +
    + + + + + + + +
    Forwarded::delete ()
    +
    +
    + +

    deletes an existing 'forwarded' entry.

    +

    this method will use the object's attributes for deleting an existing 'forwarded' entry in the database.

    + +
    +
    + +
    +
    + + + + + + + + + + + + + + + + + + +
    static Forwarded::forwardTicket (group_id,
    ticket_id 
    ) [static]
    +
    +
    + +

    Forward a ticket to a group, also removes the previous group where it was forwarded to.

    +

    It will first check if the ticket is already forwarded, if that's the case, it will delete that entry. Afterwards it creates the new forward entry

    +
    Parameters:
    + + + +
    $group_idthe id of the support group we want to forward the ticket to.
    $ticket_idthe id of the ticket.
    +
    +
    +
    Returns:
    A string, if assigning succeedded "SUCCESS_FORWARDED" will be returned.
    + +
    +
    + +
    +
    + + + + + + + + +
    static Forwarded::getSGroupOfTicket (ticket_id) [static]
    +
    +
    + +

    get the id of the group a ticket is forwarded to.

    +
    Parameters:
    + + +
    $ticket_idthe id of the ticket.
    +
    +
    +
    Returns:
    the id of the group
    + +
    +
    + +
    +
    + + + + + + + + +
    static Forwarded::isForwarded (ticket_id) [static]
    +
    +
    + +

    check if the ticket is forwarded

    +
    Parameters:
    + + +
    $ticket_idthe id of the ticket.
    +
    +
    +
    Returns:
    returns true if the ticket is forwarded, else return false;
    + +
    +
    + +
    +
    + + + + + + + + +
    Forwarded::load (ticket_id)
    +
    +
    + +

    loads the object's attributes.

    +

    loads the object's attributes by giving a ticket_id, it will put the matching group_id and the ticket_id into the attributes.

    +
    Parameters:
    + + +
    $ticket_idthe id of the ticket that should be loaded
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Forwarded::set (values)
    +
    +
    + +

    sets the object's attributes.

    +
    Parameters:
    + + +
    $valuesshould be an array of the form array('Group' => group_id, 'Ticket' => ticket_id).
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Forwarded::setGroup (g)
    +
    +
    + +

    set group attribute of the object.

    +
    Parameters:
    + + +
    $ginteger id of the group
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Forwarded::setTicket (t)
    +
    +
    + +

    set ticket attribute of the object.

    +
    Parameters:
    + + +
    $tinteger id of the ticket
    +
    +
    + +
    +
    +
    The documentation for this class was generated from the following file:
      +
    • /home/daan/ryzom/ryzomcore/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/forwarded.php
    • +
    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/classGui__Elements-members.html b/code/ryzom/tools/server/ryzom_ams_docs/html/classGui__Elements-members.html new file mode 100644 index 000000000..a4e7b9269 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/classGui__Elements-members.html @@ -0,0 +1,113 @@ + + + + + +Ryzom Account Management System: Member List + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + +
    +
    +
    +
    Gui_Elements Member List
    +
    +
    +This is the complete list of members for Gui_Elements, including all inherited members. + + + +
    make_table($inputList, $funcArray, $fieldArray)Gui_Elements [static]
    make_table_with_key_is_id($inputList, $funcArray, $idFunction)Gui_Elements [static]
    time_elapsed_string($ptime)Gui_Elements [static]
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/classGui__Elements.html b/code/ryzom/tools/server/ryzom_ams_docs/html/classGui__Elements.html new file mode 100644 index 000000000..4753e9aed --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/classGui__Elements.html @@ -0,0 +1,250 @@ + + + + + +Ryzom Account Management System: Gui_Elements Class Reference + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + +
    +
    + +
    +
    Gui_Elements Class Reference
    +
    +
    + +

    Helper class for generating gui related elements. + More...

    + +

    List of all members.

    + + + + + + + + +

    +Static Public Member Functions

    static make_table ($inputList, $funcArray, $fieldArray)
     creates an array of information out of a list of objects which can be used to form a table.
    static make_table_with_key_is_id ($inputList, $funcArray, $idFunction)
     creates an array of information out of a list of objects which can be used to form a table with a key as id.
    static time_elapsed_string ($ptime)
     returns the elapsed time from a timestamp up till now.
    +

    Detailed Description

    +

    Helper class for generating gui related elements.

    +

    This class contains functions that generate data-arrays for tables, or other visual entities

    +
    Author:
    Daan Janssens, mentored by Matthew Lagoe
    +

    Member Function Documentation

    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + +
    static Gui_Elements::make_table (inputList,
    funcArray,
    fieldArray 
    ) [static]
    +
    +
    + +

    creates an array of information out of a list of objects which can be used to form a table.

    +

    The idea of this is that you can pass an array of objects, an array of functions to perform on each object and a name for the index of the returning array to store the result.

    +
    Parameters:
    + + + + +
    $inputListthe list of objects of which we want to make a table.
    $funcArraya list of methods of that object we want to perform.
    $fieldArraya list of strings, that will be used to store our result into.
    +
    +
    +
    Returns:
    an array with the indexes listed in $fieldArray and which holds the results of the methods in $funcArray on each object in the $inputList
    + +
    +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + +
    static Gui_Elements::make_table_with_key_is_id (inputList,
    funcArray,
    idFunction 
    ) [static]
    +
    +
    + +

    creates an array of information out of a list of objects which can be used to form a table with a key as id.

    +

    The idea is comparable to the make_table() function, though this time the results are stored in the index that is returned by the idFunction()

    +
    Parameters:
    + + + + +
    $inputListthe list of objects of which we want to make a table.
    $funcArraya list of methods of that object we want to perform.
    $idFunctiona function that returns an id that will be used as index to store our result
    +
    +
    +
    Returns:
    an array which holds the results of the methods in $funcArray on each object in the $inputList, though thearrays indexes are formed by using the idFunction.
    + +
    +
    + +
    +
    + + + + + + + + +
    static Gui_Elements::time_elapsed_string (ptime) [static]
    +
    +
    + +

    returns the elapsed time from a timestamp up till now.

    +
    Parameters:
    + + +
    $ptimea timestamp.
    +
    +
    +
    Returns:
    a string in the form of A years, B months, C days, D hours, E minutes, F seconds ago.
    + +
    +
    +
    The documentation for this class was generated from the following file:
      +
    • /home/daan/ryzom/ryzomcore/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/gui_elements.php
    • +
    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/classHelpers-members.html b/code/ryzom/tools/server/ryzom_ams_docs/html/classHelpers-members.html new file mode 100644 index 000000000..fa7879da1 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/classHelpers-members.html @@ -0,0 +1,116 @@ + + + + + +Ryzom Account Management System: Member List + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + +
    +
    +
    +
    Helpers Member List
    +
    +
    +This is the complete list of members for Helpers, including all inherited members. + + + + + + +
    check_if_game_client()Helpers [static]
    check_login_ingame()Helpers [static]
    create_folders()Helpers [static]
    handle_language()Helpers [static]
    loadTemplate($template, $vars=array(), $returnHTML=false)Helpers [static]
    outputTime($time, $str=1)Helpers [static]
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/classHelpers.html b/code/ryzom/tools/server/ryzom_ams_docs/html/classHelpers.html new file mode 100644 index 000000000..f6a32caf0 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/classHelpers.html @@ -0,0 +1,292 @@ + + + + + +Ryzom Account Management System: Helpers Class Reference + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + +
    +
    + +
    +
    Helpers Class Reference
    +
    +
    + +

    Helper class for more site specific functions. + More...

    + +

    List of all members.

    + + + + + + + + + + + + + + +

    +Static Public Member Functions

    static loadTemplate ($template, $vars=array(), $returnHTML=false)
     workhorse of the website, it loads the template and shows it or returns th html.
    static create_folders ()
     creates the folders that are needed for smarty.
    static check_if_game_client ()
     check if the http request is sent ingame or not.
    static handle_language ()
     Handles the language specific aspect.
    static outputTime ($time, $str=1)
     Time output function for handling the time display.
    static check_login_ingame ()
     Auto login function for ingame use.
    +

    Detailed Description

    +

    Helper class for more site specific functions.

    +
    Author:
    Daan Janssens, mentored by Matthew Lagoe
    +

    Member Function Documentation

    + +
    +
    + + + + + + + +
    static Helpers::check_if_game_client () [static]
    +
    +
    + +

    check if the http request is sent ingame or not.

    +
    Returns:
    returns true in case it's sent ingame, else false is returned.
    + +
    +
    + +
    +
    + + + + + + + +
    static Helpers::check_login_ingame () [static]
    +
    +
    + +

    Auto login function for ingame use.

    +

    This function will allow users who access the website ingame, to log in without entering the username and password. It uses the COOKIE entry in the open_ring db. it checks if the cookie sent by the http request matches the one in the db. This cookie in the db is changed everytime the user relogs.

    +
    Returns:
    returns "FALSE" if the cookies didn't match, else it returns an array with the user's id and name.
    + +
    +
    + +
    +
    + + + + + + + +
    static Helpers::create_folders () [static]
    +
    +
    + +

    creates the folders that are needed for smarty.

    +
    Todo:
    for the drupal module it might be possible that drupal_mkdir needs to be used instead of mkdir, also this should be in the install.php instead.
    + +
    +
    + +
    +
    + + + + + + + +
    static Helpers::handle_language () [static]
    +
    +
    + +

    Handles the language specific aspect.

    +

    The language can be changed by setting the $_GET['Language'] & $_GET['setLang'] together. This will also change the language entry of the user in the db. Cookies are also being used in case the user isn't logged in.

    +
    Returns:
    returns the parsed content of the language .ini file related to the users language setting.
    + +
    +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + +
    static Helpers::loadTemplate (template,
    vars = array (),
    returnHTML = false 
    ) [static]
    +
    +
    + +

    workhorse of the website, it loads the template and shows it or returns th html.

    +

    it uses smarty to load the $template, but before displaying the template it will pass the $vars to smarty. Also based on your language settings a matching array of words & sentences for that page will be loaded. In case the $returnHTML parameter is set to true, it will return the html instead of displaying the template.

    +
    Parameters:
    + + + + +
    $templatethe name of the template(page) that we want to load.
    $varsan array of variables that should be loaded by smarty before displaying or returning the html.
    $returnHTML(default=false) if set to true, the html that should have been displayed, will be returned.
    +
    +
    +
    Returns:
    in case $returnHTML=true, it returns the html of the template being loaded.
    + +
    +
    + +
    +
    + + + + + + + + + + + + + + + + + + +
    static Helpers::outputTime (time,
    str = 1 
    ) [static]
    +
    +
    + +

    Time output function for handling the time display.

    +
    Returns:
    returns the time in the format specified in the $TIME_FORMAT global variable.
    + +
    +
    +
    The documentation for this class was generated from the following file:
      +
    • /home/daan/ryzom/ryzomcore/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/helpers.php
    • +
    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/classIn__Support__Group-members.html b/code/ryzom/tools/server/ryzom_ams_docs/html/classIn__Support__Group-members.html new file mode 100644 index 000000000..e1f503802 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/classIn__Support__Group-members.html @@ -0,0 +1,121 @@ + + + + + +Ryzom Account Management System: Member List + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + +
    +
    +
    +
    In_Support_Group Member List
    +
    + + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/classIn__Support__Group.html b/code/ryzom/tools/server/ryzom_ams_docs/html/classIn__Support__Group.html new file mode 100644 index 000000000..19e7c628b --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/classIn__Support__Group.html @@ -0,0 +1,330 @@ + + + + + +Ryzom Account Management System: In_Support_Group Class Reference + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + +
    +
    + +
    +
    In_Support_Group Class Reference
    +
    +
    + +

    Handles the linkage of users being in a support group. + More...

    + +

    List of all members.

    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    +Public Member Functions

     __construct ()
     A constructor.
     set ($values)
     sets the object's attributes.
     create ()
     creates a new 'in_support_group' entry.
     delete ()
     deletes an existing 'in_support_group' entry.
    getUser ()
     get user attribute of the object.
    getGroup ()
     get group attribute of the object.
     setUser ($u)
     set user attribute of the object.
     setGroup ($g)
     set group attribute of the object.

    +Static Public Member Functions

    static userExistsInSGroup ($user_id, $group_id)
     Check if user is in in_support_group.

    +Private Attributes

    $user
     The id of the user being in a support group.
    $group
     The id of the support group.
    +

    Detailed Description

    +

    Handles the linkage of users being in a support group.

    +

    Moderators and Admins can be part of a support group, this class offers functionality to check if a link between a user and group is existing.

    +
    Author:
    Daan Janssens, mentored by Matthew Lagoe
    +

    Constructor & Destructor Documentation

    + +
    +
    + + + + + + + +
    In_Support_Group::__construct ()
    +
    +
    + +

    A constructor.

    +

    Empty constructor

    + +
    +
    +

    Member Function Documentation

    + +
    +
    + + + + + + + +
    In_Support_Group::create ()
    +
    +
    + +

    creates a new 'in_support_group' entry.

    +

    this method will use the object's attributes for creating a new 'in_support_group' entry in the database.

    + +
    +
    + +
    +
    + + + + + + + +
    In_Support_Group::delete ()
    +
    +
    + +

    deletes an existing 'in_support_group' entry.

    +

    this method will use the object's attributes for deleting an existing 'in_support_group' entry in the database.

    + +
    +
    + +
    +
    + + + + + + + + +
    In_Support_Group::set (values)
    +
    +
    + +

    sets the object's attributes.

    +
    Parameters:
    + + +
    $valuesshould be an array of the form array('User' => user_id, 'Group' => support_groups_id).
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    In_Support_Group::setGroup (g)
    +
    +
    + +

    set group attribute of the object.

    +
    Parameters:
    + + +
    $ginteger id of the support group
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    In_Support_Group::setUser (u)
    +
    +
    + +

    set user attribute of the object.

    +
    Parameters:
    + + +
    $uinteger id of the user
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + + + + + + + + + + + +
    static In_Support_Group::userExistsInSGroup (user_id,
    group_id 
    ) [static]
    +
    +
    + +

    Check if user is in in_support_group.

    +
    Parameters:
    + + + +
    $user_idthe id of the user.
    $group_idthe id of the support group.
    +
    +
    +
    Returns:
    true is returned in case the user is in the support group, else false is returned.
    + +
    +
    +
    The documentation for this class was generated from the following file:
      +
    • /home/daan/ryzom/ryzomcore/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/in_support_group.php
    • +
    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/classMail__Handler-members.html b/code/ryzom/tools/server/ryzom_ams_docs/html/classMail__Handler-members.html new file mode 100644 index 000000000..0f3d590a7 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/classMail__Handler-members.html @@ -0,0 +1,121 @@ + + + + + +Ryzom Account Management System: Member List + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + +
    +
    +
    +
    Mail_Handler Member List
    +
    +
    +This is the complete list of members for Mail_Handler, including all inherited members. + + + + + + + + + + + +
    $dbMail_Handler [private]
    cron()Mail_Handler
    decode_utf8($str)Mail_Handler
    get_mime_type(&$structure)Mail_Handler
    get_part($stream, $msg_number, $mime_type, $structure=false, $part_number=false) (defined in Mail_Handler)Mail_Handler
    get_ticket_id_from_subject($subject)Mail_Handler
    incoming_mail_handler($mbox, $i, $group)Mail_Handler
    mail_fork()Mail_Handler [private]
    new_message_id($ticketId)Mail_Handler
    send_mail($recipient, $subject, $body, $ticket_id=0, $from=NULL)Mail_Handler [static]
    send_ticketing_mail($receiver, $ticketObj, $content, $type, $sender=0)Mail_Handler [static]
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/classMail__Handler.html b/code/ryzom/tools/server/ryzom_ams_docs/html/classMail__Handler.html new file mode 100644 index 000000000..cff653b56 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/classMail__Handler.html @@ -0,0 +1,480 @@ + + + + + +Ryzom Account Management System: Mail_Handler Class Reference + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + +
    + +
    + +

    Handles the mailing functionality. + More...

    + +

    List of all members.

    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    +Public Member Functions

     cron ()
     the cron funtion (workhorse of the mailing system).
     new_message_id ($ticketId)
     creates a new message id for a email about to send.
     get_ticket_id_from_subject ($subject)
     try to fetch the ticket_id out of the subject.
     incoming_mail_handler ($mbox, $i, $group)
     Handles an incomming email Read the content of one email by using imap's functionality.
     decode_utf8 ($str)
     decode utf8
     get_mime_type (&$structure)
     returns the mime type of a structure of a email
    get_part ($stream, $msg_number, $mime_type, $structure=false, $part_number=false)

    +Static Public Member Functions

    static send_ticketing_mail ($receiver, $ticketObj, $content, $type, $sender=0)
     Wrapper for sending emails, creates the content of the email Based on the type of the ticketing mail it will create a specific email, it will use the language.ini files to load the correct language of the email for the receiver.
    static send_mail ($recipient, $subject, $body, $ticket_id=0, $from=NULL)
     send mail function that will add the email to the db.

    +Private Member Functions

     mail_fork ()
     Start a new child process and return the process id this is used because imap might take some time, we dont want the cron parent process waiting on that.

    +Private Attributes

     $db
     db object used by various methods.
    +

    Detailed Description

    +

    Handles the mailing functionality.

    +

    This class covers the reading of the mail boxes of the support_groups, handling those emails, updating tickets accoring to the content & title of the emails, but also the sending of emails after creating a new ticket and when someone else replies on your ticket.

    +
    Author:
    Daan Janssens, mentored by Matthew Lagoe
    +

    Member Function Documentation

    + +
    +
    + + + + + + + +
    Mail_Handler::cron ()
    +
    +
    + +

    the cron funtion (workhorse of the mailing system).

    +

    The cron job will create a child process, which will first send the emails that are in the email table in the database, we use some kind of semaphore (a temp file) to make sure that if the cron job is called multiple times, it wont email those mails multiple times. After this, we will read the mail inboxes of the support groups and the default group using IMAP and we will add new tickets or new replies according to the incoming emails.

    + +
    +
    + +
    +
    + + + + + + + + +
    Mail_Handler::decode_utf8 (str)
    +
    +
    + +

    decode utf8

    +
    Parameters:
    + + +
    $strstr to be decoded
    +
    +
    +
    Returns:
    decoded string
    + +
    +
    + +
    +
    + + + + + + + + +
    Mail_Handler::get_mime_type (&$ structure)
    +
    +
    + +

    returns the mime type of a structure of a email

    +
    Parameters:
    + + +
    &$structurethe structure of an email message.
    +
    +
    +
    Returns:
    "TEXT", "MULTIPART","MESSAGE", "APPLICATION", "AUDIO","IMAGE", "VIDEO", "OTHER","TEXT/PLAIN"
    +
    Todo:
    take care of the HTML part of incoming emails.
    + +
    +
    + +
    +
    + + + + + + + + +
    Mail_Handler::get_ticket_id_from_subject (subject)
    +
    +
    + +

    try to fetch the ticket_id out of the subject.

    +

    The subject should have a substring of the form [Ticket #ticket_id], where ticket_id should be the integer ID of the ticket.

    +
    Parameters:
    + + +
    $subject,thesubject of an incomming email.
    +
    +
    +
    Returns:
    if the ticket's id is succesfully parsed, it will return the ticket_id, else it returns 0.
    + +
    +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + +
    Mail_Handler::incoming_mail_handler (mbox,
    i,
    group 
    )
    +
    +
    + +

    Handles an incomming email Read the content of one email by using imap's functionality.

    +

    If a ticket id is found inside the message_id or else in the subject line, then a reply will be added (if the email is not being sent from the authors email address it won't be added though and a warning will be sent to both parties). If no ticket id is found, then a new ticket will be created.

    +
    Parameters:
    + + + + +
    $mboxa mailbox object
    $ithe email's id in the mailbox (integer)
    $groupthe group object that owns the inbox.
    +
    +
    +
    Returns:
    a string based on the found ticket i and timestamp (will be used to store a copy of the email locally)
    + +
    +
    + +
    +
    + + + + + + + +
    Mail_Handler::mail_fork () [private]
    +
    +
    + +

    Start a new child process and return the process id this is used because imap might take some time, we dont want the cron parent process waiting on that.

    +
    Returns:
    return the child process id
    + +
    +
    + +
    +
    + + + + + + + + +
    Mail_Handler::new_message_id (ticketId)
    +
    +
    + +

    creates a new message id for a email about to send.

    +
    Parameters:
    + + +
    $ticket_idthe ticket id of the ticket that is mentioned in the email.
    +
    +
    +
    Returns:
    returns a string, that consist out of some variable parts, a consistent part and the ticket_id. The ticket_id will be used lateron, if someone replies on the message, to see to which ticket the reply should be added.
    + +
    +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    static Mail_Handler::send_mail (recipient,
    subject,
    body,
    ticket_id = 0,
    from = NULL 
    ) [static]
    +
    +
    + +

    send mail function that will add the email to the db.

    +

    this function is being used by the send_ticketing_mail() function. It adds the email as an entry to the `email` table in the database, which will be sent later on when we run the cron job.

    +
    Parameters:
    + + + + + + +
    $recipientif integer, then it refers to the id of the user to whom we want to mail, if it's a string(email-address) then we will use that.
    $subjectthe subject of the email
    $bodythe body of the email
    $ticket_idthe id of the ticket
    $fromthe sending support_group's id (NULL in case the default group is sending))
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    static Mail_Handler::send_ticketing_mail (receiver,
    ticketObj,
    content,
    type,
    sender = 0 
    ) [static]
    +
    +
    + +

    Wrapper for sending emails, creates the content of the email Based on the type of the ticketing mail it will create a specific email, it will use the language.ini files to load the correct language of the email for the receiver.

    +

    Also if the $TICKET_MAILING_SUPPORT is set to false or if the user's personal 'ReceiveMail' entry is set to false then no mail will be sent.

    +
    Parameters:
    + + + + + + +
    $receiverif integer, then it refers to the id of the user to whom we want to mail, if it's a string(email-address) then we will use that.
    $ticketObjthe ticket object itself, this is being used for including ticket related information into the email.
    $contentthe content of a reply or new ticket
    $typeREPLY, NEW, WARNAUTHOR, WARNSENDER, WARNUNKNOWNSENDER
    $sender(default = 0 (if it is not forwarded)) else use the id of the support group to which the ticket is currently forwarded, the support groups email address will be used to send the ticket.
    +
    +
    + +
    +
    +

    Member Data Documentation

    + +
    +
    + + + + +
    Mail_Handler::$db [private]
    +
    +
    + +

    db object used by various methods.

    + +
    +
    +
    The documentation for this class was generated from the following file:
      +
    • /home/daan/ryzom/ryzomcore/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php
    • +
    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/classMyCrypt-members.html b/code/ryzom/tools/server/ryzom_ams_docs/html/classMyCrypt-members.html new file mode 100644 index 000000000..e5439a748 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/classMyCrypt-members.html @@ -0,0 +1,116 @@ + + + + + +Ryzom Account Management System: Member List + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + +
    +
    +
    +
    MyCrypt Member List
    +
    +
    +This is the complete list of members for MyCrypt, including all inherited members. + + + + + + +
    $configMyCrypt [private]
    __construct($cryptinfo)MyCrypt
    check_methods($enc, $hash)MyCrypt [private, static]
    decrypt($edata)MyCrypt
    encrypt($data)MyCrypt
    hashIV($key, $method, $iv_size)MyCrypt [private, static]
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/classMyCrypt.html b/code/ryzom/tools/server/ryzom_ams_docs/html/classMyCrypt.html new file mode 100644 index 000000000..fb1b2e051 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/classMyCrypt.html @@ -0,0 +1,312 @@ + + + + + +Ryzom Account Management System: MyCrypt Class Reference + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + +
    + +
    + +

    Basic encryption/decryption class. + More...

    + +

    List of all members.

    + + + + + + + + + + + + + + + + +

    +Public Member Functions

     __construct ($cryptinfo)
     constructor.
     encrypt ($data)
     encrypts by using the given enc_method and hash_method.
     decrypt ($edata)
     decrypts by using the given enc_method and hash_method.

    +Static Private Member Functions

    static hashIV ($key, $method, $iv_size)
     hashes the key by using a hash method specified.
    static check_methods ($enc, $hash)
     checks if the encryption and hash methods are supported

    +Private Attributes

    $config
     array that should contain the enc_method & hash_method & key
    +

    Detailed Description

    +

    Basic encryption/decryption class.

    +

    We use this class atm for encrypting & decrypting the imap passwords.

    +

    Constructor & Destructor Documentation

    + +
    +
    + + + + + + + + +
    MyCrypt::__construct (cryptinfo)
    +
    +
    + +

    constructor.

    +

    loads the config array with the given argument.

    +
    Parameters:
    + + +
    $cryptinfoan array containing the info needed to encrypt & decrypt.(enc_method & hash_method & key)
    +
    +
    + +
    +
    +

    Member Function Documentation

    + +
    +
    + + + + + + + + + + + + + + + + + + +
    static MyCrypt::check_methods (enc,
    hash 
    ) [static, private]
    +
    +
    + +

    checks if the encryption and hash methods are supported

    +
    Parameters:
    + + + +
    $encthe encryption method.
    $hashthe hash method.
    +
    +
    +
    Exceptions:
    + + +
    Exceptionin case a method is not supported.
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    MyCrypt::decrypt (edata)
    +
    +
    + +

    decrypts by using the given enc_method and hash_method.

    +
    Parameters:
    + + +
    $edatathe encrypted string that we want to decrypt
    +
    +
    +
    Returns:
    the decrypted string.
    + +
    +
    + +
    +
    + + + + + + + + +
    MyCrypt::encrypt (data)
    +
    +
    + +

    encrypts by using the given enc_method and hash_method.

    +

    It will first check if the methods are supported, if not it will throw an error, if so it will encrypt the $data

    +
    Parameters:
    + + +
    $datathe string that we want to encrypt.
    +
    +
    +
    Returns:
    the encrypted string.
    + +
    +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + +
    static MyCrypt::hashIV (key,
    method,
    iv_size 
    ) [static, private]
    +
    +
    + +

    hashes the key by using a hash method specified.

    +
    Parameters:
    + + + + +
    $keythe key to be hashed
    $methodthe metho of hashing to be used
    $iv_sizethe size of the initialization vector.
    +
    +
    +
    Returns:
    return the hashed key up till the size of the iv_size param.
    + +
    +
    +
    The documentation for this class was generated from the following file:
      +
    • /home/daan/ryzom/ryzomcore/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mycrypt.php
    • +
    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/classPagination-members.html b/code/ryzom/tools/server/ryzom_ams_docs/html/classPagination-members.html new file mode 100644 index 000000000..847c73f5b --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/classPagination-members.html @@ -0,0 +1,120 @@ + + + + + +Ryzom Account Management System: Member List + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + +
    +
    +
    +
    Pagination Member List
    +
    +
    +This is the complete list of members for Pagination, including all inherited members. + + + + + + + + + + +
    $amountOfRowsPagination [private]
    $currentPagination [private]
    $element_arrayPagination [private]
    $lastPagination [private]
    __construct($query, $db, $nrDisplayed, $resultClass, $params=array())Pagination
    getAmountOfRows()Pagination
    getCurrent()Pagination
    getElements()Pagination
    getLast()Pagination
    getLinks($nrOfLinks)Pagination
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/classPagination.html b/code/ryzom/tools/server/ryzom_ams_docs/html/classPagination.html new file mode 100644 index 000000000..471d8ad60 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/classPagination.html @@ -0,0 +1,311 @@ + + + + + +Ryzom Account Management System: Pagination Class Reference + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + +
    +
    + +
    +
    Pagination Class Reference
    +
    +
    + +

    Handles returning arrays based on a given pagenumber. + More...

    + +

    List of all members.

    + + + + + + + + + + + + + + + + + + + + + + + +

    +Public Member Functions

     __construct ($query, $db, $nrDisplayed, $resultClass, $params=array())
     Constructor.
     getLast ()
     return the number of the 'last' object attribute
     getCurrent ()
     return the number of the 'current' object attribute
     getElements ()
     return the elements array of the object
     getAmountOfRows ()
     return total amount of rows for the original query
     getLinks ($nrOfLinks)
     return the page links.

    +Private Attributes

    $element_array
     Array containing the elements that are extracted for that specific page number.
    $last
     The last page number.
    $current
     The current page number (read from $_GET['pagenum'])
    $amountOfRows
     Total amount of rows that a query would return (if no limits would be used)
    +

    Detailed Description

    +

    Handles returning arrays based on a given pagenumber.

    +

    By specifing a $_GET['pagenum'] or if not(page = 1 will be used) a few elements from a specific query will be returned. Not all elements have to be loaded into objects, only the elements needed for that specific page, this is a good thing performance wise. This is done by passign the query to the constructor and specifying how many you want to display.

    +
    Author:
    Daan Janssens, mentored by Matthew Lagoe
    +

    Constructor & Destructor Documentation

    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Pagination::__construct (query,
    db,
    nrDisplayed,
    resultClass,
    params = array() 
    )
    +
    +
    + +

    Constructor.

    +

    will fetch the correct elements that match to a specific page (specified by the $_GET['pagenum'] variable). The query has to be passed as a string to the function that way it will only load the specific elements that are related to the pagenumber. The $params, parameter is optional and is used to pass the parameters for the query. The result class will be used to instantiate the found elements with, their set() function will be called. The class its getters can be later used to get the info out of the object.

    +
    Parameters:
    + + + + + + +
    $querythe query to be paginated
    $dbthe db on which the query should be performed
    $nrDisplayedthe amount of elements that should be displayed /page
    $resultClassthe elements that should be returned should be of that specific class.
    $paramsthe parameters used by the query (optional)
    +
    +
    + +
    +
    +

    Member Function Documentation

    + +
    +
    + + + + + + + +
    Pagination::getAmountOfRows ()
    +
    +
    + +

    return total amount of rows for the original query

    +
    Returns:
    the total amount of rows for the original query
    + +
    +
    + +
    +
    + + + + + + + +
    Pagination::getCurrent ()
    +
    +
    + +

    return the number of the 'current' object attribute

    +
    Returns:
    the number of the current page
    + +
    +
    + +
    +
    + + + + + + + +
    Pagination::getElements ()
    +
    +
    + +

    return the elements array of the object

    +
    Returns:
    the elements of a specific page (these are instantiations of the class passed as parameter ($resultClass) to the constructor)
    + +
    +
    + +
    +
    + + + + + + + +
    Pagination::getLast ()
    +
    +
    + +

    return the number of the 'last' object attribute

    +
    Returns:
    the number of the last page
    + +
    +
    + +
    +
    + + + + + + + + +
    Pagination::getLinks (nrOfLinks)
    +
    +
    + +

    return the page links.

    +

    (for browsing the pages, placed under a table for example) the $nrOfLinks parameter specifies the amount of links you want to return. it will show the links closest to the current page on both sides (in case one side can't show more, it will show more on the other side)

    +
    Returns:
    an array of integerswhich refer to the clickable pagenumbers for browsing other pages.
    + +
    +
    +
    The documentation for this class was generated from the following file:
      +
    • /home/daan/ryzom/ryzomcore/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/pagination.php
    • +
    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/classQuerycache-members.html b/code/ryzom/tools/server/ryzom_ams_docs/html/classQuerycache-members.html new file mode 100644 index 000000000..1fb29465a --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/classQuerycache-members.html @@ -0,0 +1,126 @@ + + + + + +Ryzom Account Management System: Member List + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + +
    +
    +
    +
    Querycache Member List
    +
    +
    +This is the complete list of members for Querycache, including all inherited members. + + + + + + + + + + + + + + + + +
    $dbQuerycache [private]
    $queryQuerycache [private]
    $SIDQuerycache [private]
    $typeQuerycache [private]
    __construct()Querycache
    getDb()Querycache
    getQuery()Querycache
    getSID()Querycache
    getType()Querycache
    load_With_SID($id)Querycache
    set($values)Querycache
    setDb($d)Querycache
    setQuery($q)Querycache
    setSID($s)Querycache
    setType($t)Querycache
    update()Querycache
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/classQuerycache.html b/code/ryzom/tools/server/ryzom_ams_docs/html/classQuerycache.html new file mode 100644 index 000000000..3e69ac4b3 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/classQuerycache.html @@ -0,0 +1,343 @@ + + + + + +Ryzom Account Management System: Querycache Class Reference + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + +
    +
    + +
    +
    Querycache Class Reference
    +
    +
    + +

    class for storing changes when shard is offline. + More...

    + +

    List of all members.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    +Public Member Functions

     __construct ()
     A constructor.
     set ($values)
     sets the object's attributes.
     load_With_SID ($id)
     loads the object's attributes.
    update ()
     updates the entry.
    getSID ()
     get SID attribute of the object.
    getType ()
     get type attribute of the object.
    getQuery ()
     get query attribute of the object.
    getDb ()
     get db attribute of the object.
     setSID ($s)
     set SID attribute of the object.
     setType ($t)
     set type attribute of the object.
     setQuery ($q)
     set query attribute of the object.
     setDb ($d)
     set db attribute of the object.

    +Private Attributes

    $SID
     The queries ID.
    $type
     The type of query.
    $query
     The query itself (json encoded)
    $db
     the db where the query should be performed
    +

    Detailed Description

    +

    class for storing changes when shard is offline.

    +
    Todo:
    make sure that the querycache class is being used by the sync class and also for inserting the queries themselfs into it. Atm this class isn't used yet if I remember correctly
    +
    Author:
    Daan Janssens, mentored by Matthew Lagoe
    +

    Constructor & Destructor Documentation

    + +
    +
    + + + + + + + +
    Querycache::__construct ()
    +
    +
    + +

    A constructor.

    +

    Empty constructor

    + +
    +
    +

    Member Function Documentation

    + +
    +
    + + + + + + + + +
    Querycache::load_With_SID (id)
    +
    +
    + +

    loads the object's attributes.

    +

    loads the object's attributes by giving a SID as parameter

    +
    Parameters:
    + + +
    $idthe id of the querycaches row
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Querycache::set (values)
    +
    +
    + +

    sets the object's attributes.

    +
    Parameters:
    + + +
    $valuesshould be an array of the form array('SID' => sid, 'type' => type, 'query' => query, 'db' => db).
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Querycache::setDb (d)
    +
    +
    + +

    set db attribute of the object.

    +
    Parameters:
    + + +
    $dthe name of the database in the config global var that we want to use.
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Querycache::setQuery (q)
    +
    +
    + +

    set query attribute of the object.

    +
    Parameters:
    + + +
    $qquery string
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Querycache::setSID (s)
    +
    +
    + +

    set SID attribute of the object.

    +
    Parameters:
    + + +
    $sinteger id
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Querycache::setType (t)
    +
    +
    + +

    set type attribute of the object.

    +
    Parameters:
    + + +
    $ttype of the query, could be changePassword, changePermissions, changeEmail, createUser
    +
    +
    + +
    +
    +
    The documentation for this class was generated from the following file:
      +
    • /home/daan/ryzom/ryzomcore/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/querycache.php
    • +
    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/classSupport__Group-members.html b/code/ryzom/tools/server/ryzom_ams_docs/html/classSupport__Group-members.html new file mode 100644 index 000000000..181f7ec05 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/classSupport__Group-members.html @@ -0,0 +1,148 @@ + + + + + +Ryzom Account Management System: Member List + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + +
    +
    +
    +
    Support_Group Member List
    +
    +
    +This is the complete list of members for Support_Group, including all inherited members. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    $groupEmailSupport_Group [private]
    $iMAP_MailServerSupport_Group [private]
    $iMAP_PasswordSupport_Group [private]
    $iMAP_UsernameSupport_Group [private]
    $nameSupport_Group [private]
    $sGroupIdSupport_Group [private]
    $tagSupport_Group [private]
    __construct()Support_Group
    addUserToSupportGroup($user_id, $group_id)Support_Group [static]
    constr_SGroupId($id)Support_Group [static]
    create()Support_Group
    createSupportGroup($name, $tag, $groupemail, $imap_mailserver, $imap_username, $imap_password)Support_Group [static]
    delete()Support_Group
    deleteSupportGroup($group_id)Support_Group [static]
    deleteUserOfSupportGroup($user_id, $group_id)Support_Group [static]
    getAllSupportGroups()Support_Group [static]
    getAllUsersOfSupportGroup($group_id)Support_Group [static]
    getGroup($id)Support_Group [static]
    getGroupEmail()Support_Group
    getGroups()Support_Group [static]
    getIMAP_MailServer()Support_Group
    getIMAP_Password()Support_Group
    getIMAP_Username()Support_Group
    getName()Support_Group
    getSGroupId()Support_Group
    getTag()Support_Group
    load_With_SGroupId($id)Support_Group
    set($values)Support_Group
    setGroupEmail($ge)Support_Group
    setIMAP_MailServer($ms)Support_Group
    setIMAP_Password($p)Support_Group
    setIMAP_Username($u)Support_Group
    setName($n)Support_Group
    setSGroupId($id)Support_Group
    setTag($t)Support_Group
    supportGroup_EntryNotExists($name, $tag)Support_Group [static]
    supportGroup_Exists($id)Support_Group [static]
    update()Support_Group
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/classSupport__Group.html b/code/ryzom/tools/server/ryzom_ams_docs/html/classSupport__Group.html new file mode 100644 index 000000000..9a6374b5e --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/classSupport__Group.html @@ -0,0 +1,847 @@ + + + + + +Ryzom Account Management System: Support_Group Class Reference + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + +
    + +
    + +

    groups moderators & admins together. + More...

    + +

    List of all members.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    +Public Member Functions

     __construct ()
     A constructor.
     set ($values)
     sets the object's attributes.
     create ()
     creates a new 'support_group' entry.
     load_With_SGroupId ($id)
     loads the object's attributes.
    update ()
     update the objects attributes to the db.
     delete ()
     deletes an existing 'support_group' entry.
    getSGroupId ()
     get sGroupId attribute of the object.
    getName ()
     get name attribute of the object.
    getTag ()
     get tag attribute of the object.
    getGroupEmail ()
     get groupEmail attribute of the object.
    getIMAP_MailServer ()
     get iMAP_MailServer attribute of the object.
    getIMAP_Username ()
     get iMAP_Username attribute of the object.
    getIMAP_Password ()
     get iMAP_Password attribute of the object.
     setSGroupId ($id)
     set sGroupId attribute of the object.
     setName ($n)
     set name attribute of the object.
     setTag ($t)
     set tag attribute of the object.
     setGroupEmail ($ge)
     set groupEmail attribute of the object.
     setIMAP_MailServer ($ms)
     set iMAP_MailServer attribute of the object.
     setIMAP_Username ($u)
     set iMAP_Username attribute of the object.
     setIMAP_Password ($p)
     set iMAP_Password attribute of the object.

    +Static Public Member Functions

    static getGroup ($id)
     return a specific support_group object.
    static getGroups ()
     return all support_group objects.
    static createSupportGroup ($name, $tag, $groupemail, $imap_mailserver, $imap_username, $imap_password)
     Wrapper for creating a support group.
    static supportGroup_EntryNotExists ($name, $tag)
     check if support group name/tag doesn't exist yet.
    static supportGroup_Exists ($id)
     check if support group entry coupled to a given id exist or not.
    static constr_SGroupId ($id)
     construct an object based on the SGroupId.
    static getAllUsersOfSupportGroup ($group_id)
     get list of all users that are enlisted to a support group.
    static deleteSupportGroup ($group_id)
     wrapper for deleting a support group.
    static deleteUserOfSupportGroup ($user_id, $group_id)
     wrapper for deleting a user that's in a specified support group.
    static addUserToSupportGroup ($user_id, $group_id)
     wrapper for adding a user to a specified support group.
    static getAllSupportGroups ()
     return all support_group objects.

    +Private Attributes

    $sGroupId
     The id of the support group.
    $name
     The name of the support group.
    $tag
     The tag of the support group, a tag is max 4 letters big, and will be used in the future as easy reference to indicate what group it is refered to (eg [DEV])
    $groupEmail
     The email address of the group.
    $iMAP_MailServer
     The imap server connection string.
    $iMAP_Username
     The imap username of the account.
    $iMAP_Password
     The imap matching password.
    +

    Detailed Description

    +

    groups moderators & admins together.

    +

    A Support Group is a group of people with the same skills or knowledge. A typical example will be the (Developers group, webteam group, management, etc..) The idea is that tickets can be forwarded to a group of persons that might be able to answer that specific question. Support Groups are also the key of handling the emails, because the email addresses of the support groups will be used by the Mail_Handler class.

    +
    Author:
    Daan Janssens, mentored by Matthew Lagoe
    +

    Constructor & Destructor Documentation

    + +
    +
    + + + + + + + +
    Support_Group::__construct ()
    +
    +
    + +

    A constructor.

    +

    Empty constructor

    + +
    +
    +

    Member Function Documentation

    + +
    +
    + + + + + + + + + + + + + + + + + + +
    static Support_Group::addUserToSupportGroup (user_id,
    group_id 
    ) [static]
    +
    +
    + +

    wrapper for adding a user to a specified support group.

    +

    We will first check if the group really exists, if not than "GROUP_NOT_EXISING" will be returned. Afterwards we will check if the user exists in the support group, if so "ALREADY_ADDED" will be returned. Else the user will be added to the in_support_group table and "SUCCESS" will be returned.

    +
    Parameters:
    + + + +
    $user_idthe id of the user we want to add to the group.
    $group_idthe id of the group the user wants to be in
    +
    +
    +
    Returns:
    a string (SUCCESS, ALREADY_ADDED or GROUP_NOT_EXISTING)
    + +
    +
    + +
    +
    + + + + + + + + +
    static Support_Group::constr_SGroupId (id) [static]
    +
    +
    + +

    construct an object based on the SGroupId.

    +
    Parameters:
    + + +
    $idthe id of the group we want to construct
    +
    +
    +
    Returns:
    the constructed support group object
    + +
    +
    + +
    +
    + + + + + + + +
    Support_Group::create ()
    +
    +
    + +

    creates a new 'support_group' entry.

    +

    this method will use the object's attributes for creating a new 'support_group' entry in the database.

    + +
    +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    static Support_Group::createSupportGroup (name,
    tag,
    groupemail,
    imap_mailserver,
    imap_username,
    imap_password 
    ) [static]
    +
    +
    + +

    Wrapper for creating a support group.

    +

    It will check if the support group doesn't exist yet, if the tag or name already exists then NAME_TAKEN or TAG_TAKEN will be returned. If the name is bigger than 20 characters or smaller than 4 and the tag greater than 7 or smaller than 2 a SIZE_ERROR will be returned. Else it will return SUCCESS

    +
    Returns:
    a string that specifies if it was a success or not (SUCCESS, SIZE_ERROR, NAME_TAKEN or TAG_TAKEN )
    + +
    +
    + +
    +
    + + + + + + + +
    Support_Group::delete ()
    +
    +
    + +

    deletes an existing 'support_group' entry.

    +

    this method will use the object's attributes for deleting an existing 'support_group' entry in the database.

    + +
    +
    + +
    +
    + + + + + + + + +
    static Support_Group::deleteSupportGroup (group_id) [static]
    +
    +
    + +

    wrapper for deleting a support group.

    +

    We will first check if the group really exists, if not than "GROUP_NOT_EXISING" will be returned.

    +
    Parameters:
    + + +
    $group_idthe id of the group we want to delete
    +
    +
    +
    Returns:
    an array of ticket_user objects that are in the support group.
    + +
    +
    + +
    +
    + + + + + + + + + + + + + + + + + + +
    static Support_Group::deleteUserOfSupportGroup (user_id,
    group_id 
    ) [static]
    +
    +
    + +

    wrapper for deleting a user that's in a specified support group.

    +

    We will first check if the group really exists, if not than "GROUP_NOT_EXISING" will be returned. Afterwards we will check if the user exists in the support group, if not "USER_NOT_IN_GROUP" will be returned. Else the users entry in the in_support_group table will be deleted and "SUCCESS" will be returned.

    +
    Parameters:
    + + + +
    $user_idthe id of the user we want to remove out of the group.
    $group_idthe id of the group the user should be in
    +
    +
    +
    Returns:
    a string (SUCCESS, USER_NOT_IN_GROUP or GROUP_NOT_EXISTING)
    + +
    +
    + +
    +
    + + + + + + + +
    static Support_Group::getAllSupportGroups () [static]
    +
    +
    + +

    return all support_group objects.

    +
    Returns:
    an array containing all support_group objects.
    +
    Deprecated:
    should be removed in the future, because getGroups does the same.
    + +
    +
    + +
    +
    + + + + + + + + +
    static Support_Group::getAllUsersOfSupportGroup (group_id) [static]
    +
    +
    + +

    get list of all users that are enlisted to a support group.

    +
    Parameters:
    + + +
    $idthe id of the group we want to query
    +
    +
    +
    Returns:
    an array of ticket_user objects that are in the support group.
    + +
    +
    + +
    +
    + + + + + + + + +
    static Support_Group::getGroup (id) [static]
    +
    +
    + +

    return a specific support_group object.

    +
    Parameters:
    + + +
    $idthe id of the support group that we want to return
    +
    +
    +
    Returns:
    a support_group object.
    + +
    +
    + +
    +
    + + + + + + + +
    static Support_Group::getGroups () [static]
    +
    +
    + +

    return all support_group objects.

    +
    Returns:
    an array containing all support_group objects.
    + +
    +
    + +
    +
    + + + + + + + + +
    Support_Group::load_With_SGroupId (id)
    +
    +
    + +

    loads the object's attributes.

    +

    loads the object's attributes by giving a group id, it will put the matching groups attributes in the object.

    +
    Parameters:
    + + +
    $idthe id of the support group that should be loaded
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Support_Group::set (values)
    +
    +
    + +

    sets the object's attributes.

    +
    Parameters:
    + + +
    $valuesshould be an array of the form array('SGroupId' => groupid, 'Name' => name, 'Tag' => tag, 'GroupEmail' => mail, 'IMAP_MailServer' => server, 'IMAP_Username' => username,'IMAP_Password' => pass).
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Support_Group::setGroupEmail (ge)
    +
    +
    + +

    set groupEmail attribute of the object.

    +
    Parameters:
    + + +
    $geemail of the group
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Support_Group::setIMAP_MailServer (ms)
    +
    +
    + +

    set iMAP_MailServer attribute of the object.

    +
    Parameters:
    + + +
    $msmailserver of the group
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Support_Group::setIMAP_Password (p)
    +
    +
    + +

    set iMAP_Password attribute of the object.

    +
    Parameters:
    + + +
    $pimap password of the group
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Support_Group::setIMAP_Username (u)
    +
    +
    + +

    set iMAP_Username attribute of the object.

    +
    Parameters:
    + + +
    $uimap username of the group
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Support_Group::setName (n)
    +
    +
    + +

    set name attribute of the object.

    +
    Parameters:
    + + +
    $nname of the group
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Support_Group::setSGroupId (id)
    +
    +
    + +

    set sGroupId attribute of the object.

    +
    Parameters:
    + + +
    $idinteger id of the group
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Support_Group::setTag (t)
    +
    +
    + +

    set tag attribute of the object.

    +
    Parameters:
    + + +
    $ttag of the group
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + + + + + + + + + + + +
    static Support_Group::supportGroup_EntryNotExists (name,
    tag 
    ) [static]
    +
    +
    + +

    check if support group name/tag doesn't exist yet.

    +
    Parameters:
    + + + +
    $namethe name of the group we want to check
    $tagthe tag of the group we want to check
    +
    +
    +
    Returns:
    if name is already taken return NAME_TAKEN, else if tag is already taken return TAG_TAKEN, else return success.
    + +
    +
    + +
    +
    + + + + + + + + +
    static Support_Group::supportGroup_Exists (id) [static]
    +
    +
    + +

    check if support group entry coupled to a given id exist or not.

    +
    Parameters:
    + + +
    $idthe id of the group we want to check
    +
    +
    +
    Returns:
    true or false.
    + +
    +
    +
    The documentation for this class was generated from the following file:
      +
    • /home/daan/ryzom/ryzomcore/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/support_group.php
    • +
    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/classSync-members.html b/code/ryzom/tools/server/ryzom_ams_docs/html/classSync-members.html new file mode 100644 index 000000000..a4320dc81 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/classSync-members.html @@ -0,0 +1,111 @@ + + + + + +Ryzom Account Management System: Member List + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + +
    +
    +
    +
    Sync Member List
    +
    +
    +This is the complete list of members for Sync, including all inherited members. + +
    syncdata()Sync [static]
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/classSync.html b/code/ryzom/tools/server/ryzom_ams_docs/html/classSync.html new file mode 100644 index 000000000..99478263e --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/classSync.html @@ -0,0 +1,149 @@ + + + + + +Ryzom Account Management System: Sync Class Reference + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + +
    +
    + +
    +
    Sync Class Reference
    +
    +
    + +

    handler for performing changes when shard is back online after being offline. + More...

    + +

    List of all members.

    + + + + +

    +Static Public Member Functions

    static syncdata ()
     performs the actions listed in the querycache.
    +

    Detailed Description

    +

    handler for performing changes when shard is back online after being offline.

    +

    the sync class is responsible for the syncdata function, which will synchronise the website with the shard (when the shard is offline, users can still change their password, email or even register)

    +
    Author:
    Daan Janssens, mentored by Matthew Lagoe
    +

    Member Function Documentation

    + +
    +
    + + + + + + + +
    static Sync::syncdata () [static]
    +
    +
    + +

    performs the actions listed in the querycache.

    +

    All entries in the querycache will be read and performed depending on their type. This is done because the shard could have been offline and we want changes made on the website (which is still online) to eventually hit the shard. These changes are: createPermissions, createUser, change_pass, change_mail

    + +
    +
    +
    The documentation for this class was generated from the following file:
      +
    • /home/daan/ryzom/ryzomcore/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/sync.php
    • +
    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/classTicket-members.html b/code/ryzom/tools/server/ryzom_ams_docs/html/classTicket-members.html new file mode 100644 index 000000000..26865ac82 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/classTicket-members.html @@ -0,0 +1,159 @@ + + + + + +Ryzom Account Management System: Member List + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + +
    +
    +
    +
    Ticket Member List
    +
    +
    +This is the complete list of members for Ticket, including all inherited members. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    $authorTicket [private]
    $priorityTicket [private]
    $queueTicket [private]
    $statusTicket [private]
    $ticket_categoryTicket [private]
    $tIdTicket [private]
    $timestampTicket [private]
    $titleTicket [private]
    __construct()Ticket
    assignTicket($user_id, $ticket_id)Ticket [static]
    create()Ticket
    create_Ticket($title, $content, $category, $author, $real_author, $for_support_group=0, $extra_info=0)Ticket [static]
    createReply($content, $author, $ticket_id, $hidden)Ticket [static]
    forwardTicket($user_id, $ticket_id, $group_id)Ticket [static]
    getAssigned()Ticket
    getAuthor()Ticket
    getCategoryName()Ticket
    getEntireTicket($id, $view_as_admin)Ticket [static]
    getForwardedGroupId()Ticket
    getForwardedGroupName()Ticket
    getLatestReply($ticket_id)Ticket [static]
    getPriority()Ticket
    getPriorityArray()Ticket [static]
    getPriorityText()Ticket
    getQueue()Ticket
    getStatus()Ticket
    getStatusArray()Ticket [static]
    getStatusText()Ticket
    getTicket_Category()Ticket
    getTicketsOf($author)Ticket [static]
    getTId()Ticket
    getTimestamp()Ticket
    getTitle()Ticket
    hasInfo()Ticket
    load_With_TId($id)Ticket
    set($values)Ticket
    setAuthor($a)Ticket
    setPriority($p)Ticket
    setQueue($q)Ticket
    setStatus($s)Ticket
    setTicket_Category($tc)Ticket
    setTId($id)Ticket
    setTimestamp($ts)Ticket
    setTitle($t)Ticket
    ticketExists($id)Ticket [static]
    unAssignTicket($user_id, $ticket_id)Ticket [static]
    update()Ticket
    updateTicketStatus($ticket_id, $newStatus, $author)Ticket [static]
    updateTicketStatusAndPriority($ticket_id, $newStatus, $newPriority, $author)Ticket [static]
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/classTicket.html b/code/ryzom/tools/server/ryzom_ams_docs/html/classTicket.html new file mode 100644 index 000000000..206496cba --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/classTicket.html @@ -0,0 +1,1078 @@ + + + + + +Ryzom Account Management System: Ticket Class Reference + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + +
    + +
    + +

    class that handles most ticket related functions. + More...

    + +

    List of all members.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    +Public Member Functions

     __construct ()
     A constructor.
     set ($values)
     sets the object's attributes.
     create ()
     creates a new 'ticket' entry.
     load_With_TId ($id)
     loads the object's attributes.
    update ()
     update the objects attributes to the db.
     hasInfo ()
     check if a ticket has a ticket_info page or not.
    getTId ()
     get tId attribute of the object.
    getTimestamp ()
     get timestamp attribute of the object in the format defined in the outputTime function of the Helperclass.
    getTitle ()
     get title attribute of the object.
    getStatus ()
     get status attribute of the object.
    getStatusText ()
     get status attribute of the object in the form of text (string).
    getCategoryName ()
     get category attribute of the object in the form of text (string).
    getQueue ()
     get queue attribute of the object.
    getTicket_Category ()
     get ticket_category attribute of the object (int).
    getAuthor ()
     get author attribute of the object (int).
    getPriority ()
     get priority attribute of the object (int).
    getPriorityText ()
     get priority attribute of the object in the form of text (string).
     getAssigned ()
     get the user assigned to the ticket.
    getForwardedGroupName ()
     get the name of the support group to whom the ticket is forwarded or return 0 in case not forwarded.
    getForwardedGroupId ()
     get the id of the support group to whom the ticket is forwarded or return 0 in case not forwarded.
     setTId ($id)
     set tId attribute of the object.
     setTimestamp ($ts)
     set timestamp attribute of the object.
     setTitle ($t)
     set title attribute of the object.
     setStatus ($s)
     set status attribute of the object.
     setQueue ($q)
     set queue attribute of the object.
     setTicket_Category ($tc)
     set ticket_category attribute of the object.
     setAuthor ($a)
     set author attribute of the object.
     setPriority ($p)
     set priority attribute of the object.

    +Static Public Member Functions

    static ticketExists ($id)
     check if a ticket exists.
    static getStatusArray ()
     return an array of the possible statuses
    static getPriorityArray ()
     return an array of the possible priorities
    static getEntireTicket ($id, $view_as_admin)
     return an entire ticket.
    static getTicketsOf ($author)
     return all tickets of a specific user.
    static create_Ticket ($title, $content, $category, $author, $real_author, $for_support_group=0, $extra_info=0)
     function that creates a new ticket.
    static updateTicketStatus ($ticket_id, $newStatus, $author)
     updates the ticket's status.
    static updateTicketStatusAndPriority ($ticket_id, $newStatus, $newPriority, $author)
     updates the ticket's status & priority.
    static getLatestReply ($ticket_id)
     return the latest reply of a ticket
    static createReply ($content, $author, $ticket_id, $hidden)
     create a new reply for a ticket.
    static assignTicket ($user_id, $ticket_id)
     assign a ticket to a user.
    static unAssignTicket ($user_id, $ticket_id)
     unassign a ticket of a user.
    static forwardTicket ($user_id, $ticket_id, $group_id)
     forward a ticket to a specific support group.

    +Private Attributes

    $tId
     The id of ticket.
    $timestamp
     Timestamp of the ticket.
    $title
     Title of the ticket.
    $status
     Status of the ticket (0 = waiting on user reply, 1 = waiting on support, (2= not used atm), 3 = closed.
    $queue
     (not in use atm)
    $ticket_category
     the id of the category belonging to the ticket
    $author
     The ticket_users id.
    $priority
     The priority of the ticket where 0 = low, 3= supadupahigh.
    +

    Detailed Description

    +

    class that handles most ticket related functions.

    +

    the ticket class is used for most ticketing related functions, it also holds some wrapper functions.

    +
    Author:
    Daan Janssens, mentored by Matthew Lagoe
    +

    Constructor & Destructor Documentation

    + +
    +
    + + + + + + + +
    Ticket::__construct ()
    +
    +
    + +

    A constructor.

    +

    Empty constructor

    + +
    +
    +

    Member Function Documentation

    + +
    +
    + + + + + + + + + + + + + + + + + + +
    static Ticket::assignTicket (user_id,
    ticket_id 
    ) [static]
    +
    +
    + +

    assign a ticket to a user.

    +

    Checks if the ticket exists, if so then it will try to assign the user to it, a log entry will be written about this.

    +
    Parameters:
    + + + +
    $user_idthe id of user trying to be assigned to the ticket.
    $ticket_idthe id of the ticket that we try to assign to the user.
    +
    +
    +
    Returns:
    SUCCESS_ASSIGNED, TICKET_NOT_EXISTING or ALREADY_ASSIGNED
    + +
    +
    + +
    +
    + + + + + + + +
    Ticket::create ()
    +
    +
    + +

    creates a new 'ticket' entry.

    +

    this method will use the object's attributes for creating a new 'ticket' entry in the database.

    + +
    +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    static Ticket::create_Ticket (title,
    content,
    category,
    author,
    real_author,
    for_support_group = 0,
    extra_info = 0 
    ) [static]
    +
    +
    + +

    function that creates a new ticket.

    +

    A new ticket will be created, in case the extra_info != 0 and the http request came from ingame, then a ticket_info page will be created. A log entry will be written, depending on the $real_authors value. In case the for_support_group parameter is set, the ticket will be forwarded immediately. Also the mail handler will create a new email that will be sent to the author to notify him that his ticket is freshly created.

    +
    Parameters:
    + + + + + + + + +
    $titlethe title we want to give to the ticket.
    $contentthe content we want to give to the starting post of the ticket.
    $categorythe id of the category that should be related to the ticket.
    $authorthe person who's id will be stored in the database as creator of the ticket.
    $real_authorshould be the same id, or a moderator/admin who creates a ticket for another user (this is used for logging purposes).
    $for_support_groupin case you directly want to forward the ticket after creating it. (default value = 0 = don't forward)
    $extra_infoused for creating an ticket_info page related to the ticket, this only happens when the ticket is made ingame.
    +
    +
    +
    Returns:
    the created tickets id.
    + +
    +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    static Ticket::createReply (content,
    author,
    ticket_id,
    hidden 
    ) [static]
    +
    +
    + +

    create a new reply for a ticket.

    +

    A reply will only be added if the content isn't empty and if the ticket isn't closed. The ticket creator will be notified by email that someone else replied on his ticket.

    +
    Parameters:
    + + + + + +
    $contentthe content of the reply
    $authorthe author of the reply
    $ticket_idthe id of the ticket to which we want to add the reply.
    $hiddenboolean that specifies if the reply should only be shown to mods/admins or all users.
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + +
    static Ticket::forwardTicket (user_id,
    ticket_id,
    group_id 
    ) [static]
    +
    +
    + +

    forward a ticket to a specific support group.

    +

    Checks if the ticket exists, if so then it will try to forward the ticket to the support group specified, a log entry will be written about this. if no log entry should be written then the user_id should be 0, else te $user_id will be used in the log to specify who forwarded it.

    +
    Parameters:
    + + + + +
    $user_idthe id of user trying to forward the ticket.
    $ticket_idthe id of the ticket that we try to forward to a support group.
    $group_idthe id of the support group.
    +
    +
    +
    Returns:
    SUCCESS_FORWARDED, TICKET_NOT_EXISTING or INVALID_SGROUP
    + +
    +
    + +
    +
    + + + + + + + +
    Ticket::getAssigned ()
    +
    +
    + +

    get the user assigned to the ticket.

    +

    or return 0 in case not assigned.

    + +
    +
    + +
    +
    + + + + + + + + + + + + + + + + + + +
    static Ticket::getEntireTicket (id,
    view_as_admin 
    ) [static]
    +
    +
    + +

    return an entire ticket.

    +

    returns the ticket object and an array of all replies to that ticket.

    +
    Parameters:
    + + + +
    $idthe id of the ticket.
    $view_as_admintrue if the viewer of the ticket is a mod, else false (depending on this it will also show the hidden comments)
    +
    +
    +
    Returns:
    an array containing the 'ticket_obj' and a 'reply_array', which is an array containing all replies to that ticket.
    + +
    +
    + +
    +
    + + + + + + + + +
    static Ticket::getLatestReply (ticket_id) [static]
    +
    +
    + +

    return the latest reply of a ticket

    +
    Parameters:
    + + +
    $ticket_idthe id of the ticket.
    +
    +
    +
    Returns:
    a ticket_reply object.
    + +
    +
    + +
    +
    + + + + + + + +
    static Ticket::getPriorityArray () [static]
    +
    +
    + +

    return an array of the possible priorities

    +
    Returns:
    an array containing the string values that represent the different priorities.
    + +
    +
    + +
    +
    + + + + + + + +
    static Ticket::getStatusArray () [static]
    +
    +
    + +

    return an array of the possible statuses

    +
    Returns:
    an array containing the string values that represent the different statuses.
    + +
    +
    + +
    +
    + + + + + + + + +
    static Ticket::getTicketsOf (author) [static]
    +
    +
    + +

    return all tickets of a specific user.

    +

    an array of all tickets created by a specific user are returned by this function.

    +
    Parameters:
    + + +
    $authorthe id of the user of whom we want all tickets from.
    +
    +
    +
    Returns:
    an array containing all ticket objects related to a user.
    + +
    +
    + +
    +
    + + + + + + + +
    Ticket::hasInfo ()
    +
    +
    + +

    check if a ticket has a ticket_info page or not.

    +
    Returns:
    true or false
    + +
    +
    + +
    +
    + + + + + + + + +
    Ticket::load_With_TId (id)
    +
    +
    + +

    loads the object's attributes.

    +

    loads the object's attributes by giving a TId (ticket id).

    +
    Parameters:
    + + +
    $idthe id of the ticket that should be loaded
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Ticket::set (values)
    +
    +
    + +

    sets the object's attributes.

    +
    Parameters:
    + + +
    $valuesshould be an array of the form array('TId' => ticket_id, 'Title' => title, 'Status'=> status, 'Timestamp' => ts, 'Queue' => queue, 'Ticket_Category' => tc, 'Author' => author, 'Priority' => priority).
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Ticket::setAuthor (a)
    +
    +
    + +

    set author attribute of the object.

    +
    Parameters:
    + + +
    $aauthor of the ticket
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Ticket::setPriority (p)
    +
    +
    + +

    set priority attribute of the object.

    +
    Parameters:
    + + +
    $ppriority of the ticket
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Ticket::setQueue (q)
    +
    +
    + +

    set queue attribute of the object.

    +
    Parameters:
    + + +
    $qqueue of the ticket
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Ticket::setStatus (s)
    +
    +
    + +

    set status attribute of the object.

    +
    Parameters:
    + + +
    $sstatus of the ticket(int)
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Ticket::setTicket_Category (tc)
    +
    +
    + +

    set ticket_category attribute of the object.

    +
    Parameters:
    + + +
    $tcticket_category id of the ticket(int)
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Ticket::setTId (id)
    +
    +
    + +

    set tId attribute of the object.

    +
    Parameters:
    + + +
    $idinteger id of the ticket
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Ticket::setTimestamp (ts)
    +
    +
    + +

    set timestamp attribute of the object.

    +
    Parameters:
    + + +
    $tstimestamp of the ticket
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Ticket::setTitle (t)
    +
    +
    + +

    set title attribute of the object.

    +
    Parameters:
    + + +
    $ttitle of the ticket
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    static Ticket::ticketExists (id) [static]
    +
    +
    + +

    check if a ticket exists.

    +
    Parameters:
    + + +
    $idthe id of the ticket to be checked.
    +
    +
    +
    Returns:
    true if the ticket exists, else false.
    + +
    +
    + +
    +
    + + + + + + + + + + + + + + + + + + +
    static Ticket::unAssignTicket (user_id,
    ticket_id 
    ) [static]
    +
    +
    + +

    unassign a ticket of a user.

    +

    Checks if the ticket exists, if so then it will try to unassign the user of it, a log entry will be written about this.

    +
    Parameters:
    + + + +
    $user_idthe id of user trying to be assigned to the ticket.
    $ticket_idthe id of the ticket that we try to assign to the user.
    +
    +
    +
    Returns:
    SUCCESS_UNASSIGNED, TICKET_NOT_EXISTING or NOT_ASSIGNED
    + +
    +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + +
    static Ticket::updateTicketStatus (ticket_id,
    newStatus,
    author 
    ) [static]
    +
    +
    + +

    updates the ticket's status.

    +

    A log entry about this will be created only if the newStatus is different from the current status.

    +
    Parameters:
    + + + + +
    $ticket_idthe id of the ticket of which we want to change the status.
    $newStatusthe new status value (integer)
    $authorthe user (id) that performed the update status action
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    static Ticket::updateTicketStatusAndPriority (ticket_id,
    newStatus,
    newPriority,
    author 
    ) [static]
    +
    +
    + +

    updates the ticket's status & priority.

    +

    A log entry about this will be created only if the newStatus is different from the current status and also when the newPriority is different from the current priority.

    +
    Todo:
    break this function up into a updateStatus (already exists) and updatePriority function and perhaps write a wrapper function for the combo.
    +
    Parameters:
    + + + + + +
    $ticket_idthe id of the ticket of which we want to change the status & priority
    $newStatusthe new status value (integer)
    $newPrioritythe new priority value (integer)
    $authorthe user (id) that performed the update
    +
    +
    + +
    +
    +
    The documentation for this class was generated from the following file:
      +
    • /home/daan/ryzom/ryzomcore/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket.php
    • +
    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__Category-members.html b/code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__Category-members.html new file mode 100644 index 000000000..b921f00c2 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__Category-members.html @@ -0,0 +1,122 @@ + + + + + +Ryzom Account Management System: Member List + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + +
    +
    +
    +
    Ticket_Category Member List
    +
    + + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__Category.html b/code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__Category.html new file mode 100644 index 000000000..a153b0a38 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__Category.html @@ -0,0 +1,322 @@ + + + + + +Ryzom Account Management System: Ticket_Category Class Reference + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + +
    +
    + +
    +
    Ticket_Category Class Reference
    +
    +
    + +

    Class related to the ticket categories. + More...

    + +

    List of all members.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    +Public Member Functions

     __construct ()
     A constructor.
     load_With_TCategoryId ($id)
     loads the object's attributes.
    update ()
     update object attributes to the DB.
    getName ()
     get name attribute of the object.
    getTCategoryId ()
     get tCategoryId attribute of the object.
     setName ($n)
     set name attribute of the object.
     setTCategoryId ($id)
     set tCategoryId attribute of the object.

    +Static Public Member Functions

    static createTicketCategory ($name)
     creates a ticket_Catergory in the DB.
    static constr_TCategoryId ($id)
     construct a category object based on the TCategoryId.
    static getAllCategories ()
     return a list of all category objects.

    +Private Attributes

    $tCategoryId
     The id of the category.
    $name
     The name of the category.
    +

    Detailed Description

    +

    Class related to the ticket categories.

    +
    Author:
    Daan Janssens, mentored by Matthew Lagoe
    +

    Constructor & Destructor Documentation

    + +
    +
    + + + + + + + +
    Ticket_Category::__construct ()
    +
    +
    + +

    A constructor.

    +

    Empty constructor

    + +
    +
    +

    Member Function Documentation

    + +
    +
    + + + + + + + + +
    static Ticket_Category::constr_TCategoryId (id) [static]
    +
    +
    + +

    construct a category object based on the TCategoryId.

    +
    Returns:
    constructed element based on TCategoryId
    + +
    +
    + +
    +
    + + + + + + + + +
    static Ticket_Category::createTicketCategory (name) [static]
    +
    +
    + +

    creates a ticket_Catergory in the DB.

    +
    Parameters:
    + + +
    $namename we want to give to the new category.
    +
    +
    + +
    +
    + +
    +
    + + + + + + + +
    static Ticket_Category::getAllCategories () [static]
    +
    +
    + +

    return a list of all category objects.

    +
    Returns:
    an array consisting of all category objects.
    + +
    +
    + +
    +
    + + + + + + + + +
    Ticket_Category::load_With_TCategoryId (id)
    +
    +
    + +

    loads the object's attributes.

    +

    loads the object's attributes by giving a categories id.

    +
    Parameters:
    + + +
    $idthe id of the ticket_category that should be loaded
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Ticket_Category::setName (n)
    +
    +
    + +

    set name attribute of the object.

    +
    Parameters:
    + + +
    $nname of the category
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Ticket_Category::setTCategoryId (id)
    +
    +
    + +

    set tCategoryId attribute of the object.

    +
    Parameters:
    + + +
    $idinteger id of the category
    +
    +
    + +
    +
    +
    The documentation for this class was generated from the following file:
      +
    • /home/daan/ryzom/ryzomcore/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_category.php
    • +
    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__Content-members.html b/code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__Content-members.html new file mode 100644 index 000000000..2445ca390 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__Content-members.html @@ -0,0 +1,121 @@ + + + + + +Ryzom Account Management System: Member List + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + +
    +
    +
    +
    Ticket_Content Member List
    +
    + + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__Content.html b/code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__Content.html new file mode 100644 index 000000000..c38ddf98f --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__Content.html @@ -0,0 +1,302 @@ + + + + + +Ryzom Account Management System: Ticket_Content Class Reference + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + +
    +
    + +
    +
    Ticket_Content Class Reference
    +
    +
    + +

    Class that handles the content of a reply. + More...

    + +

    List of all members.

    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    +Public Member Functions

     __construct ()
     A constructor.
     create ()
     creates a new 'tickt_content' entry.
     load_With_TContentId ($id)
     loads the object's attributes.
    update ()
     update the object's attributes to the database.
    getContent ()
     get content attribute of the object.
    getTContentId ()
     get tContentId attribute of the object.
     setContent ($c)
     set content attribute of the object.
     setTContentId ($c)
     set tContentId attribute of the object.

    +Static Public Member Functions

    static constr_TContentId ($id)
     return constructed element based on TContentId.

    +Private Attributes

    $tContentId
     The id of ticket_content entry.
    $content
     The content of an entry.
    +

    Detailed Description

    +

    Class that handles the content of a reply.

    +

    The Ticket_Content has a one-to-one relation with a ticket_reply, it contains the content of a reply, this way the content doesn't always have to be loaded when we query the database when we only need information regarding to the replies basic information.

    +
    Author:
    Daan Janssens, mentored by Matthew Lagoe
    +

    Constructor & Destructor Documentation

    + +
    +
    + + + + + + + +
    Ticket_Content::__construct ()
    +
    +
    + +

    A constructor.

    +

    Empty constructor

    + +
    +
    +

    Member Function Documentation

    + +
    +
    + + + + + + + + +
    static Ticket_Content::constr_TContentId (id) [static]
    +
    +
    + +

    return constructed element based on TContentId.

    +
    Parameters:
    + + +
    $idthe id of ticket_content entry.
    +
    +
    +
    Returns:
    a constructed ticket_content object by specifying the TContentId.
    + +
    +
    + +
    +
    + + + + + + + +
    Ticket_Content::create ()
    +
    +
    + +

    creates a new 'tickt_content' entry.

    +

    this method will use the object's attributes for creating a new 'ticket_content' entry in the database.

    + +
    +
    + +
    +
    + + + + + + + + +
    Ticket_Content::load_With_TContentId (id)
    +
    +
    + +

    loads the object's attributes.

    +

    loads the object's attributes by giving a ticket_content's id,

    +
    Parameters:
    + + +
    $idthe id of the ticket_content entry that should be loaded
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Ticket_Content::setContent (c)
    +
    +
    + +

    set content attribute of the object.

    +
    Parameters:
    + + +
    $ccontent of a reply
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Ticket_Content::setTContentId (c)
    +
    +
    + +

    set tContentId attribute of the object.

    +
    Parameters:
    + + +
    $cinteger id of ticket_content entry
    +
    +
    + +
    +
    +
    The documentation for this class was generated from the following file:
      +
    • /home/daan/ryzom/ryzomcore/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_content.php
    • +
    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__Info-members.html b/code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__Info-members.html new file mode 100644 index 000000000..d9545d426 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__Info-members.html @@ -0,0 +1,171 @@ + + + + + +Ryzom Account Management System: Member List + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + +
    +
    +
    +
    Ticket_Info Member List
    +
    +
    +This is the complete list of members for Ticket_Info, including all inherited members. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    $client_versionTicket_Info [private]
    $connect_stateTicket_Info [private]
    $cpu_idTicket_Info [private]
    $cpu_maskTicket_Info [private]
    $htTicket_Info [private]
    $local_addressTicket_Info [private]
    $memoryTicket_Info [private]
    $nel3dTicket_Info [private]
    $osTicket_Info [private]
    $patch_versionTicket_Info [private]
    $processorTicket_Info [private]
    $server_tickTicket_Info [private]
    $shardidTicket_Info [private]
    $ticketTicket_Info [private]
    $tInfoIdTicket_Info [private]
    $user_idTicket_Info [private]
    $user_positionTicket_Info [private]
    $view_positionTicket_Info [private]
    __construct()Ticket_Info
    create()Ticket_Info
    create_Ticket_Info($info_array)Ticket_Info [static]
    getClient_Version()Ticket_Info
    getConnect_State()Ticket_Info
    getCPU_Mask()Ticket_Info
    getCPUId()Ticket_Info
    getHT()Ticket_Info
    getLocal_Address()Ticket_Info
    getMemory()Ticket_Info
    getNel3D()Ticket_Info
    getOS()Ticket_Info
    getPatch_Version()Ticket_Info
    getProcessor()Ticket_Info
    getServer_Tick()Ticket_Info
    getShardId()Ticket_Info
    getTicket()Ticket_Info
    getTInfoId()Ticket_Info
    getUser_Id()Ticket_Info
    getUser_Position()Ticket_Info
    getView_Position()Ticket_Info
    load_With_Ticket($id)Ticket_Info
    load_With_TInfoId($id)Ticket_Info
    set($values)Ticket_Info
    setClient_Version($c)Ticket_Info
    setConnect_State($c)Ticket_Info
    setCPU_Mask($c)Ticket_Info
    setCPUId($c)Ticket_Info
    setHT($h)Ticket_Info
    setLocal_Address($l)Ticket_Info
    setMemory($m)Ticket_Info
    setNel3D($n)Ticket_Info
    setOS($o)Ticket_Info
    setPatch_Version($p)Ticket_Info
    setProcessor($p)Ticket_Info
    setServer_Tick($s)Ticket_Info
    setShardId($s)Ticket_Info
    setTicket($t)Ticket_Info
    setTInfoId($id)Ticket_Info
    setUser_Id($u)Ticket_Info
    setUser_Position($u)Ticket_Info
    setView_Position($v)Ticket_Info
    TicketHasInfo($ticket_id)Ticket_Info [static]
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__Info.html b/code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__Info.html new file mode 100644 index 000000000..e0ce3b11e --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__Info.html @@ -0,0 +1,886 @@ + + + + + +Ryzom Account Management System: Ticket_Info Class Reference + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + +
    + +
    + +

    Class that handles additional info sent by ticket creation ingame. + More...

    + +

    List of all members.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    +Public Member Functions

     __construct ()
     A constructor.
     set ($values)
     sets the object's attributes.
     load_With_TInfoId ($id)
     loads the object's attributes by using a ticket_info id.
     load_With_Ticket ($id)
     loads the object's attributes by using a ticket's id.
     create ()
     creates a new 'ticket_info' entry.
    getTInfoId ()
     get tInfoId attribute of the object.
    getTicket ()
     get ticket attribute of the object.
    getShardId ()
     get shardid attribute of the object.
    getUser_Position ()
     get user_position attribute of the object.
    getView_Position ()
     get view_position attribute of the object.
    getClient_Version ()
     get client_version attribute of the object.
    getPatch_Version ()
     get patch_version attribute of the object.
    getServer_Tick ()
     get server_tick attribute of the object.
    getConnect_State ()
     get connect_state attribute of the object.
    getLocal_Address ()
     get local_address attribute of the object.
    getMemory ()
     get memory attribute of the object.
    getOS ()
     get os attribute of the object.
    getProcessor ()
     get processor attribute of the object.
    getCPUId ()
     get cpu_id attribute of the object.
    getCPU_Mask ()
     get cpu_mask attribute of the object.
    getHT ()
     get ht attribute of the object.
    getNel3D ()
     get nel3d attribute of the object.
    getUser_Id ()
     get user_id attribute of the object.
     setTInfoId ($id)
     set tInfoId attribute of the object.
     setTicket ($t)
     set ticket attribute of the object.
     setShardId ($s)
     set shardid attribute of the object.
     setUser_Position ($u)
     set user_position attribute of the object.
     setView_Position ($v)
     set view_position attribute of the object.
     setClient_Version ($c)
     set client_version attribute of the object.
     setPatch_Version ($p)
     set patch_version attribute of the object.
     setServer_Tick ($s)
     set server_tick attribute of the object.
     setConnect_State ($c)
     set connect_state attribute of the object.
     setLocal_Address ($l)
     set local_address attribute of the object.
     setMemory ($m)
     set memory attribute of the object.
     setOS ($o)
     set os attribute of the object.
     setProcessor ($p)
     set processor attribute of the object.
     setCPUId ($c)
     set cpu_id attribute of the object.
     setCPU_Mask ($c)
     set cpu_mask attribute of the object.
    setHT ($h)
     set ht attribute of the object.
     setNel3D ($n)
     set nel3d attribute of the object.
     setUser_Id ($u)
     set user_id attribute of the object.

    +Static Public Member Functions

    static create_Ticket_Info ($info_array)
     create a ticket_info entry.
    static TicketHasInfo ($ticket_id)
     check if a specific ticket has extra info or not.

    +Private Attributes

    $tInfoId
     The id of ticket_info entry.
    $ticket
     The ticket linked to this ticket_info entry.
    $shardid
     The shard id.
    $user_position
     The user's character position.
    $view_position
     The view position of the character.
    $client_version
     The client version in use.
    $patch_version
     The patch version in use.
    $server_tick
     The current server tick.
    $connect_state
     The connect state.
    $local_address
     local ip
    $memory
     memory usage information
    $os
     os information
    $processor
     processor information
    $cpu_id
     the cpu id
    $cpu_mask
     the cpu mask
    $ht
     tbh I have no idea :D
    $nel3d
     the nel3d version
    $user_id
     The users id.
    +

    Detailed Description

    +

    Class that handles additional info sent by ticket creation ingame.

    +

    If a user creates a ticket ingame, there are a lot of extra $_GET parameters being sent inside the http request that might have something todo with the ticket. for example the OS the user uses or the processor of it's computer, but also the current client version etc. This information can be stored and retrieved by using the ticket_info class.

    +
    Author:
    Daan Janssens, mentored by Matthew Lagoe
    +

    Constructor & Destructor Documentation

    + +
    +
    + + + + + + + +
    Ticket_Info::__construct ()
    +
    +
    + +

    A constructor.

    +

    Empty constructor

    + +
    +
    +

    Member Function Documentation

    + +
    +
    + + + + + + + +
    Ticket_Info::create ()
    +
    +
    + +

    creates a new 'ticket_info' entry.

    +

    this method will use the object's attributes for creating a new 'ticket_info' entry in the database.

    + +
    +
    + +
    +
    + + + + + + + + +
    static Ticket_Info::create_Ticket_Info (info_array) [static]
    +
    +
    + +

    create a ticket_info entry.

    +
    Parameters:
    + + +
    $info_arraythe info array (this can be the entire $_GET array being sent by the ingame browser)
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Ticket_Info::load_With_Ticket (id)
    +
    +
    + +

    loads the object's attributes by using a ticket's id.

    +

    loads the object's attributes by giving a ticket's entry id.

    +
    Parameters:
    + + +
    $idthe id of the ticket, the ticket_info entry of that ticket should be loaded.
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Ticket_Info::load_With_TInfoId (id)
    +
    +
    + +

    loads the object's attributes by using a ticket_info id.

    +

    loads the object's attributes by giving a ticket_info's entry id.

    +
    Parameters:
    + + +
    $idthe id of the ticket_info entry that should be loaded
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Ticket_Info::set (values)
    +
    +
    + +

    sets the object's attributes.

    +
    Parameters:
    + + +
    $valuesshould be an array.
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Ticket_Info::setClient_Version (c)
    +
    +
    + +

    set client_version attribute of the object.

    +
    Parameters:
    + + +
    $cclient version number
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Ticket_Info::setConnect_State (c)
    +
    +
    + +

    set connect_state attribute of the object.

    +
    Parameters:
    + + +
    $cstring that defines the connect state.
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Ticket_Info::setCPU_Mask (c)
    +
    +
    + +

    set cpu_mask attribute of the object.

    +
    Parameters:
    + + +
    $cmask of the cpu
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Ticket_Info::setCPUId (c)
    +
    +
    + +

    set cpu_id attribute of the object.

    +
    Parameters:
    + + +
    $ccpu id information
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Ticket_Info::setLocal_Address (l)
    +
    +
    + +

    set local_address attribute of the object.

    +
    Parameters:
    + + +
    $llocal address
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Ticket_Info::setMemory (m)
    +
    +
    + +

    set memory attribute of the object.

    +
    Parameters:
    + + +
    $mmemory usage
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Ticket_Info::setNel3D (n)
    +
    +
    + +

    set nel3d attribute of the object.

    +
    Parameters:
    + + +
    $nversion information about NeL3D
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Ticket_Info::setOS (o)
    +
    +
    + +

    set os attribute of the object.

    +
    Parameters:
    + + +
    $oset os version information
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Ticket_Info::setPatch_Version (p)
    +
    +
    + +

    set patch_version attribute of the object.

    +
    Parameters:
    + + +
    $ppatch version number
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Ticket_Info::setProcessor (p)
    +
    +
    + +

    set processor attribute of the object.

    +
    Parameters:
    + + +
    $pprocessor information
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Ticket_Info::setServer_Tick (s)
    +
    +
    + +

    set server_tick attribute of the object.

    +
    Parameters:
    + + +
    $sinteger that resembles the server tick
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Ticket_Info::setShardId (s)
    +
    +
    + +

    set shardid attribute of the object.

    +
    Parameters:
    + + +
    $s(integer) shard id
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Ticket_Info::setTicket (t)
    +
    +
    + +

    set ticket attribute of the object.

    +
    Parameters:
    + + +
    $tinteger id of the ticket linked to the info object
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Ticket_Info::setTInfoId (id)
    +
    +
    + +

    set tInfoId attribute of the object.

    +
    Parameters:
    + + +
    $idinteger id of ticket_info object itself
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Ticket_Info::setUser_Id (u)
    +
    +
    + +

    set user_id attribute of the object.

    +
    Parameters:
    + + +
    $uthe user_id.
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Ticket_Info::setUser_Position (u)
    +
    +
    + +

    set user_position attribute of the object.

    +
    Parameters:
    + + +
    $uthe users position
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Ticket_Info::setView_Position (v)
    +
    +
    + +

    set view_position attribute of the object.

    +
    Parameters:
    + + +
    $vthe view position
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    static Ticket_Info::TicketHasInfo (ticket_id) [static]
    +
    +
    + +

    check if a specific ticket has extra info or not.

    +

    Not all tickets have extra info, only tickets made ingame do. This function checks if a specific ticket does have a ticket_info entry linked to it.

    +
    Parameters:
    + + +
    $ticket_idthe id of the ticket that we want to query
    +
    +
    +
    Returns:
    true or false
    + +
    +
    +
    The documentation for this class was generated from the following file:
      +
    • /home/daan/ryzom/ryzomcore/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_info.php
    • +
    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__Log-members.html b/code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__Log-members.html new file mode 100644 index 000000000..15b6f2a07 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__Log-members.html @@ -0,0 +1,136 @@ + + + + + +Ryzom Account Management System: Member List + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + +
    +
    +
    +
    Ticket_Log Member List
    +
    + + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__Log.html b/code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__Log.html new file mode 100644 index 000000000..ef731b55c --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__Log.html @@ -0,0 +1,556 @@ + + + + + +Ryzom Account Management System: Ticket_Log Class Reference + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + +
    + +
    + +

    Class that handles the logging. + More...

    + +

    List of all members.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    +Public Member Functions

     __construct ()
     A constructor.
     set ($values)
     sets the object's attributes.
     load_With_TLogId ($id)
     loads the object's attributes.
    update ()
     update attributes of the object to the DB.
    getTLogId ()
     get tLogId attribute of the object.
    getTimestamp ()
     get timestamp attribute of the object.
    getQuery ()
     get query attribute of the object.
    getAuthor ()
     get author attribute of the object.
    getTicket ()
     get ticket attribute of the object.
    getAction ()
     get the action id out of the query by decoding it.
    getArgument ()
     get the argument out of the query by decoding it.
     getActionTextArray ()
     get the action text(string) array.
     setTLogId ($id)
     set tLogId attribute of the object.
     setTimestamp ($t)
     set timestamp attribute of the object.
     setQuery ($q)
     set query attribute of the object.
     setAuthor ($a)
     set author attribute of the object.
     setTicket ($t)
     set ticket attribute of the object.

    +Static Public Member Functions

    static getLogsOfTicket ($ticket_id)
     return all log entries related to a ticket.
    static createLogEntry ($ticket_id, $author_id, $action, $arg=-1)
     create a new log entry.
    static constr_TLogId ($id)
     return constructed element based on TLogId
    static getAllLogs ($ticket_id)
     return all log entries related to a ticket.

    +Private Attributes

    $tLogId
     The id of the log entry.
    $timestamp
     The timestamp of the log entry.
    $query
     The query (json encoded array containing action id & argument)
    $author
     author of the log
    $ticket
     the id of the ticket related to the log entry
    +

    Detailed Description

    +

    Class that handles the logging.

    +

    The logging will be used when a ticket is created, a reply is added, if someone views a ticket, if someone assigns a ticket to him or if someone forwards a ticket. This class provides functions to get retrieve those logs and also make them.

    +

    -the Action IDs being used are:

    +
      +
    1. User X Created ticket
    2. +
    3. Admin X created ticket for arg
    4. +
    5. Read ticket
    6. +
    7. Added Reply ID: arg to ticket
    8. +
    9. Changed status to arg
    10. +
    11. Changed Priority to arg
    12. +
    13. assigned to the ticket
    14. +
    15. forwarded ticket to support group arg
    16. +
    17. unassigned to the ticket
    18. +
    +
    Author:
    Daan Janssens, mentored by Matthew Lagoe
    +

    Constructor & Destructor Documentation

    + +
    +
    + + + + + + + +
    Ticket_Log::__construct ()
    +
    +
    + +

    A constructor.

    +

    Empty constructor

    + +
    +
    +

    Member Function Documentation

    + +
    +
    + + + + + + + + +
    static Ticket_Log::constr_TLogId (id) [static]
    +
    +
    + +

    return constructed element based on TLogId

    +
    Parameters:
    + + +
    $ticket_logid of the entry that we want to load into our object.
    +
    +
    +
    Returns:
    constructed ticket_log object.
    + +
    +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    static Ticket_Log::createLogEntry (ticket_id,
    author_id,
    action,
    arg = -1 
    ) [static]
    +
    +
    + +

    create a new log entry.

    +

    It will check if the $TICKET_LOGGING global var is true, this var is used to turn logging on and off. In case it's on, the log message will be stored. the action id and argument (which is -1 by default), will be json encoded and stored in the query field in the db.

    +
    Parameters:
    + + + + + +
    $ticket_idthe id of the ticket related to the new log entry
    $author_idthe id of the user that instantiated the logging.
    $actionthe action id (see the list in the class description)
    $argargument for the action (default = -1)
    +
    +
    + +
    +
    + +
    +
    + + + + + + + +
    Ticket_Log::getActionTextArray ()
    +
    +
    + +

    get the action text(string) array.

    +

    this is being read from the language .ini files.

    + +
    +
    + +
    +
    + + + + + + + + +
    static Ticket_Log::getAllLogs (ticket_id) [static]
    +
    +
    + +

    return all log entries related to a ticket.

    +
    Parameters:
    + + +
    $ticket_idthe id of the ticket of which we want all related log entries returned.
    +
    +
    +
    Returns:
    an array of ticket_log objects, here the author is an integer.
    +
    Todo:
    only use one of the 2 comparable functions in the future and make the other depricated.
    + +
    +
    + +
    +
    + + + + + + + + +
    static Ticket_Log::getLogsOfTicket (ticket_id) [static]
    +
    +
    + +

    return all log entries related to a ticket.

    +
    Parameters:
    + + +
    $ticket_idthe id of the ticket of which we want all related log entries returned.
    +
    +
    +
    Returns:
    an array of ticket_log objects, be aware that the author in the ticket_log object is a ticket_user object on its own (so not a simple integer).
    + +
    +
    + +
    +
    + + + + + + + + +
    Ticket_Log::load_With_TLogId (id)
    +
    +
    + +

    loads the object's attributes.

    +

    loads the object's attributes by giving a ticket_log entries ID (TLogId).

    +
    Parameters:
    + + +
    idthe id of the ticket_log entry that should be loaded
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Ticket_Log::set (values)
    +
    +
    + +

    sets the object's attributes.

    +
    Parameters:
    + + +
    $valuesshould be an array.
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Ticket_Log::setAuthor (a)
    +
    +
    + +

    set author attribute of the object.

    +
    Parameters:
    + + +
    $ainteger id of the user who created the log entry
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Ticket_Log::setQuery (q)
    +
    +
    + +

    set query attribute of the object.

    +
    Parameters:
    + + +
    $qthe encoded query
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Ticket_Log::setTicket (t)
    +
    +
    + +

    set ticket attribute of the object.

    +
    Parameters:
    + + +
    $tinteger id of ticket of which the log entry is related to.
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Ticket_Log::setTimestamp (t)
    +
    +
    + +

    set timestamp attribute of the object.

    +
    Parameters:
    + + +
    $ttimestamp of the log entry
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Ticket_Log::setTLogId (id)
    +
    +
    + +

    set tLogId attribute of the object.

    +
    Parameters:
    + + +
    $idinteger id of the log entry
    +
    +
    + +
    +
    +
    The documentation for this class was generated from the following file:
      +
    • /home/daan/ryzom/ryzomcore/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_log.php
    • +
    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__Queue-members.html b/code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__Queue-members.html new file mode 100644 index 000000000..74ccc31df --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__Queue-members.html @@ -0,0 +1,121 @@ + + + + + +Ryzom Account Management System: Member List + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + +
    +
    +
    +
    Ticket_Queue Member List
    +
    + + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__Queue.html b/code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__Queue.html new file mode 100644 index 000000000..2bc513085 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__Queue.html @@ -0,0 +1,270 @@ + + + + + +Ryzom Account Management System: Ticket_Queue Class Reference + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + +
    +
    + +
    +
    Ticket_Queue Class Reference
    +
    +
    + +

    Data class that holds a lot of queries that load specific tickets. + More...

    + +

    List of all members.

    + + + + + + + + + + + + + + + + + + + + + + + + + +

    +Public Member Functions

    loadAllNotAssignedTickets ()
     loads the not yet assigned tickets query into the objects attributes.
    loadAllTickets ()
     loads the 'all' tickets query into the objects attributes.
    loadAllOpenTickets ()
     loads the 'all open' tickets query into the objects attributes.
    loadAllClosedTickets ()
     loads the 'closed' tickets query into the objects attributes.
     loadToDoTickets ($user_id)
     loads the 'todo' tickets query & params into the objects attributes.
     loadAssignedandWaiting ($user_id)
     loads the 'tickets asssigned to a user and waiting on support' query & params into the objects attributes.
     createQueue ($userid, $groupid, $what, $how, $who)
     loads the 'created' query & params into the objects attributes.
    getQuery ()
     get query attribute of the object.
    getParams ()
     get params attribute of the object.

    +Private Attributes

    $query
     The query that loads specific tickets.
    $params
     The parameter array that's being needed by the query.
    +

    Detailed Description

    +

    Data class that holds a lot of queries that load specific tickets.

    +

    These queries are being used by the ticket_queue_handler class. An object of this class holds 2 attributes: the query and the params used for the query.

    +
    Author:
    Daan Janssens, mentored by Matthew Lagoe
    +

    Member Function Documentation

    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Ticket_Queue::createQueue (userid,
    groupid,
    what,
    how,
    who 
    )
    +
    +
    + +

    loads the 'created' query & params into the objects attributes.

    +

    This function creates dynamically a query based on the selected features.

    +
    Parameters:
    + + + + + + +
    $whospecifies if we want to user the user_id or group_id to form the query.
    $user_idthe user's id to whom the tickets should be assigned/not assigned
    $group_idthe group's id to whom the tickets should be forwarded/not forwarded
    $whatspecifies what kind of tickets we want to return: waiting for support, waiting on user, closed
    $howspecifies if the tickets should be or shouldn't be assigned/forwarded to the group/user selected.
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Ticket_Queue::loadAssignedandWaiting (user_id)
    +
    +
    + +

    loads the 'tickets asssigned to a user and waiting on support' query & params into the objects attributes.

    +
    Parameters:
    + + +
    $user_idthe user's id to whom the tickets should be assigned
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Ticket_Queue::loadToDoTickets (user_id)
    +
    +
    + +

    loads the 'todo' tickets query & params into the objects attributes.

    +

    first: find the tickets assigned to the user with status = waiting on support, second find all not assigned tickets that aren't forwarded yet. find all tickets assigned to someone else witht status waiting on support, with timestamp of last reply > 1 day, find all non-assigned tickets forwarded to the support groups to which that user belongs

    +
    Parameters:
    + + +
    $user_idthe user's id to whom the tickets should be assigned
    +
    +
    + +
    +
    +
    The documentation for this class was generated from the following file:
      +
    • /home/daan/ryzom/ryzomcore/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_queue.php
    • +
    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__Queue__Handler-members.html b/code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__Queue__Handler-members.html new file mode 100644 index 000000000..57e41359c --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__Queue__Handler-members.html @@ -0,0 +1,120 @@ + + + + + +Ryzom Account Management System: Member List + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + +
    +
    +
    +
    Ticket_Queue_Handler Member List
    +
    +
    +This is the complete list of members for Ticket_Queue_Handler, including all inherited members. + + + + + + + + + + +
    $paginationTicket_Queue_Handler [private]
    $queueTicket_Queue_Handler [private]
    __construct()Ticket_Queue_Handler
    createQueue($userid, $groupid, $what, $how, $who)Ticket_Queue_Handler
    getNewestTicket()Ticket_Queue_Handler [static]
    getNrOfTickets()Ticket_Queue_Handler [static]
    getNrOfTicketsAssignedWaiting($user_id)Ticket_Queue_Handler [static]
    getNrOfTicketsToDo($user_id)Ticket_Queue_Handler [static]
    getPagination()Ticket_Queue_Handler
    getTickets($input, $user_id)Ticket_Queue_Handler
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__Queue__Handler.html b/code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__Queue__Handler.html new file mode 100644 index 000000000..4f20bf94c --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__Queue__Handler.html @@ -0,0 +1,330 @@ + + + + + +Ryzom Account Management System: Ticket_Queue_Handler Class Reference + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + +
    +
    + +
    +
    Ticket_Queue_Handler Class Reference
    +
    +
    + +

    returns tickets (queues) that are related in some way. + More...

    + +

    List of all members.

    + + + + + + + + + + + + + + + + + + + + + + + + +

    +Public Member Functions

     __construct ()
     A constructor.
     getTickets ($input, $user_id)
     returns the tickets that are related in someway defined by $input.
    getPagination ()
     get pagination attribute of the object.
     createQueue ($userid, $groupid, $what, $how, $who)
     creates the queue.

    +Static Public Member Functions

    static getNrOfTicketsToDo ($user_id)
     get the number of tickets in the todo queue for a specific user.
    static getNrOfTicketsAssignedWaiting ($user_id)
     get the number of tickets assigned to a specific user and waiting for support.
    +static getNrOfTickets ()
     get the total number of tickets.
    +static getNewestTicket ()
     get the ticket object of the latest added ticket.

    +Private Attributes

    $pagination
     Pagination object, this way only a few tickets (related to that pagenumber) will be shown.
     $queue
     The queue object, being used to get the queries and parameters.
    +

    Detailed Description

    +

    returns tickets (queues) that are related in some way.

    +

    This class handles the creation and returning of existing ticket queues. Normally a $_GET['get'] parameter is being used to identify what kind of tickets should be shown. the getTickets() function uses this parameter($input) and uses the ticket_queue class to load the specific query.

    +
    Author:
    Daan Janssens, mentored by Matthew Lagoe
    +

    Constructor & Destructor Documentation

    + +
    +
    + + + + + + + +
    Ticket_Queue_Handler::__construct ()
    +
    +
    + +

    A constructor.

    +

    Instantiates the queue object.

    + +
    +
    +

    Member Function Documentation

    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Ticket_Queue_Handler::createQueue (userid,
    groupid,
    what,
    how,
    who 
    )
    +
    +
    + +

    creates the queue.

    +

    afterwards the getTickets function should be called, else a lot of extra parameters had to be added to the getTickets function..

    + +
    +
    + +
    +
    + + + + + + + + +
    static Ticket_Queue_Handler::getNrOfTicketsAssignedWaiting (user_id) [static]
    +
    +
    + +

    get the number of tickets assigned to a specific user and waiting for support.

    +
    Parameters:
    + + +
    $user_idthe user being queried
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    static Ticket_Queue_Handler::getNrOfTicketsToDo (user_id) [static]
    +
    +
    + +

    get the number of tickets in the todo queue for a specific user.

    +
    Parameters:
    + + +
    $user_idthe user being queried
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + + + + + + + + + + + +
    Ticket_Queue_Handler::getTickets (input,
    user_id 
    )
    +
    +
    + +

    returns the tickets that are related in someway defined by $input.

    +

    The $input parameter should be a string that defines what kind of queue should be loaded. A new pagination object will be instantiated and will load 10 entries, related to the $_GET['pagenum'] variable.

    +
    Parameters:
    + + + +
    $inputidentifier that defines what queue to load.
    $user_idthe id of the user that browses the queues, some queues can be depending on this.
    +
    +
    +
    Returns:
    an array consisting of ticket objects, beware, the author & category of a ticket, are objects on their own (no integers are used this time).
    + +
    +
    +

    Member Data Documentation

    + +
    +
    + + + + +
    Ticket_Queue_Handler::$queue [private]
    +
    +
    + +

    The queue object, being used to get the queries and parameters.

    + +
    +
    +
    The documentation for this class was generated from the following file:
      +
    • /home/daan/ryzom/ryzomcore/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_queue_handler.php
    • +
    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__Reply-members.html b/code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__Reply-members.html new file mode 100644 index 000000000..ed9601dc9 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__Reply-members.html @@ -0,0 +1,136 @@ + + + + + +Ryzom Account Management System: Member List + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + +
    +
    +
    +
    Ticket_Reply Member List
    +
    + + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__Reply.html b/code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__Reply.html new file mode 100644 index 000000000..1d5edbb1d --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__Reply.html @@ -0,0 +1,577 @@ + + + + + +Ryzom Account Management System: Ticket_Reply Class Reference + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + +
    + +
    + +

    handles functions related to replies on tickets. + More...

    + +

    List of all members.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    +Public Member Functions

     __construct ()
     A constructor.
     set ($values)
     sets the object's attributes.
     create ()
     creates a new 'ticket_reply' entry.
     load_With_TReplyId ($id)
     loads the object's attributes.
    update ()
     updates a ticket_reply entry based on the objects attributes.
    getTicket ()
     get ticket attribute of the object.
    getContent ()
     get content attribute of the object.
    getAuthor ()
     get author attribute of the object.
     getTimestamp ()
     get timestamp attribute of the object.
    getTReplyId ()
     get tReplyId attribute of the object.
    getHidden ()
     get hidden attribute of the object.
     setTicket ($t)
     set ticket attribute of the object.
     setContent ($c)
     set content attribute of the object.
     setAuthor ($a)
     set author attribute of the object.
     setTimestamp ($t)
     set timestamp attribute of the object.
     setTReplyId ($i)
     set tReplyId attribute of the object.
     setHidden ($h)
     set hidden attribute of the object.

    +Static Public Member Functions

    static constr_TReplyId ($id)
     return constructed element based on TReplyId.
    static getRepliesOfTicket ($ticket_id, $view_as_admin)
     return all replies on a specific ticket.
    static createReply ($content, $author, $ticket_id, $hidden, $ticket_creator)
     creates a new reply on a ticket.

    +Private Attributes

    $tReplyId
     The id of the reply.
    $ticket
     the ticket id related to the reply
    $content
     the content of the reply
    $author
     The id of the user that made the reply.
    $timestamp
     The timestamp of the reply.
    $hidden
     indicates if reply should be hidden for normal users or not
    +

    Detailed Description

    +

    handles functions related to replies on tickets.

    +
    Author:
    Daan Janssens, mentored by Matthew Lagoe
    +

    Constructor & Destructor Documentation

    + +
    +
    + + + + + + + +
    Ticket_Reply::__construct ()
    +
    +
    + +

    A constructor.

    +

    Empty constructor

    + +
    +
    +

    Member Function Documentation

    + +
    +
    + + + + + + + + +
    static Ticket_Reply::constr_TReplyId (id) [static]
    +
    +
    + +

    return constructed element based on TReplyId.

    +
    Parameters:
    + + +
    $idthe Id the reply we want to load.
    +
    +
    +
    Returns:
    the loaded object.
    + +
    +
    + +
    +
    + + + + + + + +
    Ticket_Reply::create ()
    +
    +
    + +

    creates a new 'ticket_reply' entry.

    +

    this method will use the object's attributes for creating a new 'ticket_reply' entry in the database (the now() function will create the timestamp).

    + +
    +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    static Ticket_Reply::createReply (content,
    author,
    ticket_id,
    hidden,
    ticket_creator 
    ) [static]
    +
    +
    + +

    creates a new reply on a ticket.

    +

    Creates a ticket_content entry and links it with a new created ticket_reply, a log entry will be written about this. In case the ticket creator replies on a ticket, he will set the status by default to 'waiting on support'.

    +
    Parameters:
    + + + + + + +
    $contentthe content of the reply
    $authorthe id of the reply creator.
    $ticket_idthe id of the ticket of which we want the replies.
    $hiddenshould be 0 or 1
    $ticket_creatorthe ticket's starter his id.
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + + + + + + + + + + + +
    static Ticket_Reply::getRepliesOfTicket (ticket_id,
    view_as_admin 
    ) [static]
    +
    +
    + +

    return all replies on a specific ticket.

    +
    Parameters:
    + + + +
    $ticket_idthe id of the ticket of which we want the replies.
    $view_as_adminif the browsing user is an admin/mod it should be 1, this will also show the hidden replies.
    +
    +
    +
    Returns:
    an array with ticket_reply objects (beware the author and content are objects on their own, not integers!)
    + +
    +
    + +
    +
    + + + + + + + +
    Ticket_Reply::getTimestamp ()
    +
    +
    + +

    get timestamp attribute of the object.

    +

    The output format is defined by the Helpers class function, outputTime().

    + +
    +
    + +
    +
    + + + + + + + + +
    Ticket_Reply::load_With_TReplyId (id)
    +
    +
    + +

    loads the object's attributes.

    +

    loads the object's attributes by giving a ticket_reply's id.

    +
    Parameters:
    + + +
    $idthe id of the ticket_reply that should be loaded
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Ticket_Reply::set (values)
    +
    +
    + +

    sets the object's attributes.

    +
    Parameters:
    + + +
    $valuesshould be an array.
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Ticket_Reply::setAuthor (a)
    +
    +
    + +

    set author attribute of the object.

    +
    Parameters:
    + + +
    $ainteger id of the user
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Ticket_Reply::setContent (c)
    +
    +
    + +

    set content attribute of the object.

    +
    Parameters:
    + + +
    $cinteger id of the ticket_content entry
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Ticket_Reply::setHidden (h)
    +
    +
    + +

    set hidden attribute of the object.

    +
    Parameters:
    + + +
    $hshould be 0 or 1
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Ticket_Reply::setTicket (t)
    +
    +
    + +

    set ticket attribute of the object.

    +
    Parameters:
    + + +
    $tinteger id of the ticket
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Ticket_Reply::setTimestamp (t)
    +
    +
    + +

    set timestamp attribute of the object.

    +
    Parameters:
    + + +
    $ttimestamp of the reply
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Ticket_Reply::setTReplyId (i)
    +
    +
    + +

    set tReplyId attribute of the object.

    +
    Parameters:
    + + +
    $iinteger id of the ticket_reply
    +
    +
    + +
    +
    +
    The documentation for this class was generated from the following file:
      +
    • /home/daan/ryzom/ryzomcore/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_reply.php
    • +
    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__User-members.html b/code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__User-members.html new file mode 100644 index 000000000..36714af5c --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__User-members.html @@ -0,0 +1,134 @@ + + + + + +Ryzom Account Management System: Member List + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + +
    +
    +
    +
    Ticket_User Member List
    +
    +
    +This is the complete list of members for Ticket_User, including all inherited members. + + + + + + + + + + + + + + + + + + + + + + + + +
    $externIdTicket_User [private]
    $permissionTicket_User [private]
    $tUserIdTicket_User [private]
    __construct()Ticket_User
    change_permission($user_id, $perm)Ticket_User [static]
    constr_ExternId($id)Ticket_User [static]
    constr_TUserId($id)Ticket_User [static]
    createTicketUser($extern_id, $permission)Ticket_User [static]
    get_email_by_user_id($id)Ticket_User [static]
    get_id_from_email($email)Ticket_User [static]
    get_id_from_username($username)Ticket_User [static]
    get_username_from_id($id)Ticket_User [static]
    getExternId()Ticket_User
    getModsAndAdmins()Ticket_User [static]
    getPermission()Ticket_User
    getTUserId()Ticket_User
    isAdmin($user)Ticket_User [static]
    isMod($user)Ticket_User [static]
    load_With_TUserId($id)Ticket_User
    set($values)Ticket_User
    setExternId($id)Ticket_User
    setPermission($perm)Ticket_User
    setTUserId($id)Ticket_User
    update()Ticket_User
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__User.html b/code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__User.html new file mode 100644 index 000000000..844b7a52c --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__User.html @@ -0,0 +1,634 @@ + + + + + +Ryzom Account Management System: Ticket_User Class Reference + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + +
    + +
    + +

    user entry point in the ticket system. + More...

    + +

    List of all members.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    +Public Member Functions

     __construct ()
     A constructor.
     set ($values)
     sets the object's attributes.
     load_With_TUserId ($id)
     loads the object's attributes.
    update ()
     update the object's attributes to the db.
    getPermission ()
     get permission attribute of the object.
    getExternId ()
     get externId attribute of the object.
    getTUserId ()
     get tUserId attribute of the object.
     setPermission ($perm)
     set permission attribute of the object.
     setExternId ($id)
     set externId attribute of the object.
     setTUserId ($id)
     set tUserId attribute of the object.

    +Static Public Member Functions

    static createTicketUser ($extern_id, $permission)
     create a new ticket user.
    static isMod ($user)
     check if a ticket_user object is a mod or not.
    static isAdmin ($user)
     check if a ticket_user object is an admin or not.
    static constr_TUserId ($id)
     return constructed ticket_user object based on TUserId.
    static getModsAndAdmins ()
     return a list of all mods/admins.
    static constr_ExternId ($id)
     return constructed ticket_user object based on ExternId.
    static change_permission ($user_id, $perm)
     change the permission of a ticket_user.
    static get_email_by_user_id ($id)
     return the email address of a ticket_user.
    static get_username_from_id ($id)
     return the username of a ticket_user.
    static get_id_from_username ($username)
     return the TUserId of a ticket_user by giving a username.
    static get_id_from_email ($email)
     return the ticket_user id from an email address.

    +Private Attributes

    $tUserId
     The id of the user inside the ticket system.
    $permission
     The permission of the user.
    $externId
     The id of the user account in the www (could be drupal,...) that is linked to the ticket_user.
    +

    Detailed Description

    +

    user entry point in the ticket system.

    +

    The ticket_user makes a link between the entire ticket system's lib db and the www user, which is stored in another db (this is the external ID). The externalID could be the ID of a drupal user or wordpress user,.. The ticket_user also stores the permission of that user, this way the permission system is inside the lib itself and can be used in any www version that you like. permission 1 = user, 2 = mod, 3 = admin.

    +
    Author:
    Daan Janssens, mentored by Matthew Lagoe
    +

    Constructor & Destructor Documentation

    + +
    +
    + + + + + + + +
    Ticket_User::__construct ()
    +
    +
    + +

    A constructor.

    +

    Empty constructor

    + +
    +
    +

    Member Function Documentation

    + +
    +
    + + + + + + + + + + + + + + + + + + +
    static Ticket_User::change_permission (user_id,
    perm 
    ) [static]
    +
    +
    + +

    change the permission of a ticket_user.

    +
    Parameters:
    + + + +
    $user_idthe TUserId of the entry.
    $permthe new permission value.
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    static Ticket_User::constr_ExternId (id) [static]
    +
    +
    + +

    return constructed ticket_user object based on ExternId.

    +
    Parameters:
    + + +
    $idthe ExternId of the entry.
    +
    +
    +
    Returns:
    constructed ticket_user object
    + +
    +
    + +
    +
    + + + + + + + + +
    static Ticket_User::constr_TUserId (id) [static]
    +
    +
    + +

    return constructed ticket_user object based on TUserId.

    +
    Parameters:
    + + +
    $idthe TUserId of the entry.
    +
    +
    +
    Returns:
    constructed ticket_user object
    + +
    +
    + +
    +
    + + + + + + + + + + + + + + + + + + +
    static Ticket_User::createTicketUser (extern_id,
    permission 
    ) [static]
    +
    +
    + +

    create a new ticket user.

    +
    Parameters:
    + + + +
    $extern_idthe id of the user account in the www version (drupal,...)
    $permissionthe permission that will be given to the user. 1=user, 2=mod, 3=admin
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    static Ticket_User::get_email_by_user_id (id) [static]
    +
    +
    + +

    return the email address of a ticket_user.

    +
    Parameters:
    + + +
    $idthe TUserId of the entry.
    +
    +
    +
    Returns:
    string containing the email address of that user.
    + +
    +
    + +
    +
    + + + + + + + + +
    static Ticket_User::get_id_from_email (email) [static]
    +
    +
    + +

    return the ticket_user id from an email address.

    +
    Parameters:
    + + +
    $emailthe emailaddress of a user.
    +
    +
    +
    Returns:
    the ticket_user id related to that email address, in case none, return "FALSE".
    + +
    +
    + +
    +
    + + + + + + + + +
    static Ticket_User::get_id_from_username (username) [static]
    +
    +
    + +

    return the TUserId of a ticket_user by giving a username.

    +
    Parameters:
    + + +
    $usernamethe username of a user.
    +
    +
    +
    Returns:
    the TUserId related to that username.
    + +
    +
    + +
    +
    + + + + + + + + +
    static Ticket_User::get_username_from_id (id) [static]
    +
    +
    + +

    return the username of a ticket_user.

    +
    Parameters:
    + + +
    $idthe TUserId of the entry.
    +
    +
    +
    Returns:
    string containing username of that user.
    + +
    +
    + +
    +
    + + + + + + + +
    static Ticket_User::getModsAndAdmins () [static]
    +
    +
    + +

    return a list of all mods/admins.

    +
    Returns:
    an array consisting of ticket_user objects that are mods & admins.
    + +
    +
    + +
    +
    + + + + + + + + +
    static Ticket_User::isAdmin (user) [static]
    +
    +
    + +

    check if a ticket_user object is an admin or not.

    +
    Parameters:
    + + +
    $userthe ticket_user object itself
    +
    +
    +
    Returns:
    true or false
    + +
    +
    + +
    +
    + + + + + + + + +
    static Ticket_User::isMod (user) [static]
    +
    +
    + +

    check if a ticket_user object is a mod or not.

    +
    Parameters:
    + + +
    $userthe ticket_user object itself
    +
    +
    +
    Returns:
    true or false
    + +
    +
    + +
    +
    + + + + + + + + +
    Ticket_User::load_With_TUserId (id)
    +
    +
    + +

    loads the object's attributes.

    +

    loads the object's attributes by giving a TUserId.

    +
    Parameters:
    + + +
    $idthe id of the ticket_user that should be loaded
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Ticket_User::set (values)
    +
    +
    + +

    sets the object's attributes.

    +
    Parameters:
    + + +
    $valuesshould be an array of the form array('TUserId' => id, 'Permission' => perm, 'ExternId' => ext_id).
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Ticket_User::setExternId (id)
    +
    +
    + +

    set externId attribute of the object.

    +
    Parameters:
    + + +
    $idthe external id.
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Ticket_User::setPermission (perm)
    +
    +
    + +

    set permission attribute of the object.

    +
    Parameters:
    + + +
    $perminteger that indicates the permission level. (1= user, 2= mod, 3= admin)
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + +
    Ticket_User::setTUserId (id)
    +
    +
    + +

    set tUserId attribute of the object.

    +
    Parameters:
    + + +
    $idthe ticket_user id
    +
    +
    + +
    +
    +
    The documentation for this class was generated from the following file:
      +
    • /home/daan/ryzom/ryzomcore/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_user.php
    • +
    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/classUsers-members.html b/code/ryzom/tools/server/ryzom_ams_docs/html/classUsers-members.html new file mode 100644 index 000000000..60d0429a6 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/classUsers-members.html @@ -0,0 +1,125 @@ + + + + + +Ryzom Account Management System: Member List + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + +
    +
    +
    +
    Users Member List
    +
    +
    +This is the complete list of members for Users, including all inherited members. + + + + + + + + + + + + + + + +
    check_change_password($values)Users
    check_Register($values)Users
    checkEmail($email)Users
    checkEmailExists($email)Users [protected]
    checkLoginMatch($user, $pass)Users [protected]
    checkPassword($pass)Users
    checkUser($username)Users
    checkUserNameExists($username)Users [protected]
    confirmPassword($pass_result, $pass, $confirmpass)Users [private]
    createPermissions($pvalues)Users [static]
    createUser($values, $user_id)Users [static]
    generateSALT($length=2)Users [static]
    setAmsEmail($user, $mail)Users [protected]
    setAmsPassword($user, $pass)Users [protected]
    validEmail($email)Users
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/classUsers.html b/code/ryzom/tools/server/ryzom_ams_docs/html/classUsers.html new file mode 100644 index 000000000..91c810de7 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/classUsers.html @@ -0,0 +1,639 @@ + + + + + +Ryzom Account Management System: Users Class Reference + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + +
    + +
    + +

    handles basic user registration & management functions (shard related). + More...

    +
    +Inheritance diagram for Users:
    +
    +
    + + +WebUsers + +
    + +

    List of all members.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    +Public Member Functions

     check_Register ($values)
     checks if entered values before registering are valid.
     checkUser ($username)
     checks if entered username is valid.
     checkPassword ($pass)
     checks if the password is valid.
     checkEmail ($email)
     wrapper to check if the email address is valid.
     validEmail ($email)
     check if the emailaddress structure is valid.
     check_change_password ($values)
     check if the changing of a password is valid.

    +Static Public Member Functions

    static generateSALT ($length=2)
     generate a SALT.
    static createUser ($values, $user_id)
     creates a user in the shard.
    static createPermissions ($pvalues)
     creates permissions in the shard db for a user.

    +Protected Member Functions

     checkUserNameExists ($username)
     check if username already exists.
     checkEmailExists ($email)
     check if email already exists.
     checkLoginMatch ($user, $pass)
     check if username and password matches.
     setAmsPassword ($user, $pass)
     sets the shards password.
     setAmsEmail ($user, $mail)
     sets the shards email.

    +Private Member Functions

     confirmPassword ($pass_result, $pass, $confirmpass)
     checks if the confirmPassword matches the original.
    +

    Detailed Description

    +

    handles basic user registration & management functions (shard related).

    +

    The Users class is the basis class of WebUsers, this class provides functions being used by all CMS's and our own www version. The WebUsers class however needs to be reimplemented by using the CMS's it's funcionality. This class handles the writing to the shard db mainly, and in case it's offline: writing to the ams_querycache.

    +
    Author:
    Daan Janssens, mentored by Matthew Lagoe
    +

    Member Function Documentation

    + +
    +
    + + + + + + + + +
    Users::check_change_password (values)
    +
    +
    + +

    check if the changing of a password is valid.

    +

    a mod/admin doesn't has to fill in the previous password when he wants to change the password, however for changing his own password he has to fill it in.

    +
    Parameters:
    + + +
    $valuesan array containing the CurrentPass, ConfirmNewPass, NewPass and adminChangesOthers
    +
    +
    +
    Returns:
    if it is valid "success will be returned, else an array with errors will be returned.
    + +
    +
    + +
    +
    + + + + + + + + +
    Users::check_Register (values)
    +
    +
    + +

    checks if entered values before registering are valid.

    +
    Parameters:
    + + +
    $arraywith Username,Password, ConfirmPass and Email.
    +
    +
    +
    Returns:
    string Info: Returns a string, if input data is valid then "success" is returned, else an array with errors
    + +
    +
    + +
    +
    + + + + + + + + +
    Users::checkEmail (email)
    +
    +
    + +

    wrapper to check if the email address is valid.

    +
    Parameters:
    + + +
    $emailthe email address
    +
    +
    +
    Returns:
    "success", else in case it isn't valid an error will be returned.
    + +
    +
    + +
    +
    + + + + + + + + +
    Users::checkEmailExists (email) [protected]
    +
    +
    + +

    check if email already exists.

    +

    This is the base function, it should be overwritten by the WebUsers class.

    +
    Parameters:
    + + +
    $emailthe email address
    +
    +
    +
    Returns:
    string Info: Returns true or false if the email is in the www db.
    + +

    Reimplemented in WebUsers.

    + +
    +
    + +
    +
    + + + + + + + + + + + + + + + + + + +
    Users::checkLoginMatch (user,
    pass 
    ) [protected]
    +
    +
    + +

    check if username and password matches.

    +

    This is the base function, it should be overwritten by the WebUsers class.

    +
    Parameters:
    + + + +
    $userthe inserted username
    $passthe inserted password
    +
    +
    + +

    Reimplemented in WebUsers.

    + +
    +
    + +
    +
    + + + + + + + + +
    Users::checkPassword (pass)
    +
    +
    + +

    checks if the password is valid.

    +
    Parameters:
    + + +
    $passthe password willing to be used.
    +
    +
    +
    Returns:
    string Info: Returns a string based on if the password is valid, if valid then "success" is returned
    + +
    +
    + +
    +
    + + + + + + + + +
    Users::checkUser (username)
    +
    +
    + +

    checks if entered username is valid.

    +
    Parameters:
    + + +
    $usernamethe username that the user wants to use.
    +
    +
    +
    Returns:
    string Info: Returns a string based on if the username is valid, if valid then "success" is returned
    + +
    +
    + +
    +
    + + + + + + + + +
    Users::checkUserNameExists (username) [protected]
    +
    +
    + +

    check if username already exists.

    +

    This is the base function, it should be overwritten by the WebUsers class.

    +
    Parameters:
    + + +
    $usernamethe username
    +
    +
    +
    Returns:
    string Info: Returns true or false if the user is in the www db.
    + +

    Reimplemented in WebUsers.

    + +
    +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + +
    Users::confirmPassword (pass_result,
    pass,
    confirmpass 
    ) [private]
    +
    +
    + +

    checks if the confirmPassword matches the original.

    +
    Parameters:
    + + + + +
    $pass_resultthe result of the previous password check.
    $passthe original pass.
    $confirmpassthe confirmation password.
    +
    +
    +
    Returns:
    string Info: Verify's $_POST["Password"] is the same as $_POST["ConfirmPass"]
    + +
    +
    + +
    +
    + + + + + + + + +
    static Users::createPermissions (pvalues) [static]
    +
    +
    + +

    creates permissions in the shard db for a user.

    +

    incase the shard is offline it will place it in the ams_querycache.

    +
    Parameters:
    + + +
    $pvalueswith username
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + + + + + + + + + + + +
    static Users::createUser (values,
    user_id 
    ) [static]
    +
    +
    + +

    creates a user in the shard.

    +

    incase the shard is offline it will place it in the ams_querycache.

    +
    Parameters:
    + + +
    $valueswith name,pass and mail
    +
    +
    +
    Returns:
    ok if it's get correctly added to the shard, else return lib offline and put in libDB, if libDB is also offline return liboffline.
    + +
    +
    + +
    +
    + + + + + + + + +
    static Users::generateSALT (length = 2) [static]
    +
    +
    + +

    generate a SALT.

    +
    Parameters:
    + + +
    $length,whichis by default 2
    +
    +
    +
    Returns:
    a random salt of 2 chars
    + +
    +
    + +
    +
    + + + + + + + + + + + + + + + + + + +
    Users::setAmsEmail (user,
    mail 
    ) [protected]
    +
    +
    + +

    sets the shards email.

    +

    in case the shard is offline, the entry will be stored in the ams_querycache.

    +
    Parameters:
    + + + +
    $userthe usersname of the account of which we want to change the emailaddress.
    $mailthe new email address
    +
    +
    +
    Returns:
    ok if it worked, if the lib or shard is offline it will return liboffline or shardoffline.
    + +
    +
    + +
    +
    + + + + + + + + + + + + + + + + + + +
    Users::setAmsPassword (user,
    pass 
    ) [protected]
    +
    +
    + +

    sets the shards password.

    +

    in case the shard is offline, the entry will be stored in the ams_querycache.

    +
    Parameters:
    + + + +
    $userthe usersname of the account of which we want to change the password.
    $passthe new password.
    +
    +
    +
    Returns:
    ok if it worked, if the lib or shard is offline it will return liboffline or shardoffline.
    + +
    +
    + +
    +
    + + + + + + + + +
    Users::validEmail (email)
    +
    +
    + +

    check if the emailaddress structure is valid.

    +
    Parameters:
    + + +
    $emailthe email address
    +
    +
    +
    Returns:
    true or false
    + +
    +
    +
    The documentation for this class was generated from the following file:
      +
    • /home/daan/ryzom/ryzomcore/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/users.php
    • +
    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/classUsers.png b/code/ryzom/tools/server/ryzom_ams_docs/html/classUsers.png new file mode 100644 index 0000000000000000000000000000000000000000..7f67f33b80f777cd60c7ff9a4750fadaa5a22b16 GIT binary patch literal 341 zcmeAS@N?(olHy`uVBq!ia0vp^u0R~X!3-oTJUonnlth3}i0l9V|AEXGZ@!lHADRGU zf$@O@2Ut7r$OE|?B|(0{3_wL7aP?G(5m4-jr;B4q1>@Vfj_VF9@VH1S75-QL&uX^q zbF)ULXsTh}ipvXY6LSK#vq}aA%wyM-7U|i}A9&%tis*_>u9DXsP92Z!61ubF`HK9) z(C@N8MSE+GPh9WavaWn?g~j)b^Md}$n#EqH>`z)n9m<-lu{Jd!2t*WV%zq`QxnzP9 zm*$c^4wlm{UW?asua#S(qfu~J#CO>~U&dc^mn^(y7#_ILYX$e0ON_xem*i3=PrMZG zwlT$?!+YP0iHjLNY4{7)*ZN)g-^cU*)GN(p5Eq!#zY1jgqmJbhr^IVW aeq`TV$**xj!^IouUj|QCKbLh*2~7Y}?TpX> literal 0 HcmV?d00001 diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/classWebUsers-members.html b/code/ryzom/tools/server/ryzom_ams_docs/html/classWebUsers-members.html new file mode 100644 index 000000000..db5537a35 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/classWebUsers-members.html @@ -0,0 +1,151 @@ + + + + + +Ryzom Account Management System: Member List + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + +
    +
    +
    +
    WebUsers Member List
    +
    +
    +This is the complete list of members for WebUsers, including all inherited members. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    $countryWebUsers [private]
    $emailWebUsers [private]
    $firstnameWebUsers [private]
    $genderWebUsers [private]
    $languageWebUsers [private]
    $lastnameWebUsers [private]
    $loginWebUsers [private]
    $receiveMailWebUsers [private]
    $uIdWebUsers [private]
    __construct($UId=0)WebUsers
    check_change_password($values)Users
    check_Register($values)Users
    checkEmail($email)Users
    checkEmailExists($email)WebUsers [protected]
    checkLoginMatch($username, $password)WebUsers
    checkPassword($pass)Users
    checkUser($username)Users
    checkUserNameExists($username)WebUsers [protected]
    createPermissions($pvalues)Users [static]
    createUser($values, $user_id)Users [static]
    createWebuser($name, $pass, $mail)WebUsers [static]
    generateSALT($length=2)Users [static]
    getAllUsersQuery()WebUsers [static]
    getEmail()WebUsers
    getId($username)WebUsers [static]
    getIdFromEmail($email)WebUsers [static]
    getInfo()WebUsers
    getLanguage()WebUsers
    getReceiveMail()WebUsers
    getUId()WebUsers
    getUsername()WebUsers
    getUsers()WebUsers
    isLoggedIn()WebUsers
    set($values)WebUsers
    setAmsEmail($user, $mail)Users [protected]
    setAmsPassword($user, $pass)Users [protected]
    setEmail($user, $mail)WebUsers
    setLanguage($user, $language)WebUsers [static]
    setPassword($user, $pass)WebUsers
    setReceiveMail($user, $receivemail)WebUsers [static]
    validEmail($email)Users
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/classWebUsers.html b/code/ryzom/tools/server/ryzom_ams_docs/html/classWebUsers.html new file mode 100644 index 000000000..d2361cb2c --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/classWebUsers.html @@ -0,0 +1,712 @@ + + + + + +Ryzom Account Management System: WebUsers Class Reference + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + +
    + +
    + +

    handles CMS/WWW related functions regarding user management & registration. + More...

    +
    +Inheritance diagram for WebUsers:
    +
    +
    + + +Users + +
    + +

    List of all members.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    +Public Member Functions

     __construct ($UId=0)
     A constructor.
     set ($values)
     sets the object's attributes.
     checkLoginMatch ($username, $password)
     check if the login username and password match the db.
    getUId ()
     get uId attribute of the object.
     getUsername ()
     get login attribute of the object.
    getEmail ()
     get email attribute of the object.
     getInfo ()
     get basic info of the object.
    getReceiveMail ()
     get receiveMail attribute of the object.
    getLanguage ()
     get language attribute of the object.
     isLoggedIn ()
     check if the user is logged in.
     setPassword ($user, $pass)
     update the password.
     setEmail ($user, $mail)
     update the emailaddress.
     getUsers ()
     return all users.

    +Static Public Member Functions

    static getId ($username)
     returns te id for a given username
    static getIdFromEmail ($email)
     returns te id for a given emailaddress
    static setReceiveMail ($user, $receivemail)
     update the setReceiveMail value in the db.
    static setLanguage ($user, $language)
     update the language value in the db.
    static getAllUsersQuery ()
     return the query that should get all users.
    static createWebuser ($name, $pass, $mail)
     creates a webuser.

    +Protected Member Functions

     checkUserNameExists ($username)
     function that checks if a username exists already or not.
     checkEmailExists ($email)
     function that checks if a email exists already or not.

    +Private Attributes

    $uId
     The user id.
    $login
     The username.
    $email
     The email address.
    $firstname
     The users first name.
    $lastname
     The users last name.
    $gender
     The gender.
    $country
     2 letter word matching the country of the user
     $receiveMail
     configuration regarding if the user wants to receive email notifications or not.
    $language
     Language of the user.
    +

    Detailed Description

    +

    handles CMS/WWW related functions regarding user management & registration.

    +

    inherits from the Users class. The methods of this class have to be rewritten according to the CMS's functionality that you wish to use. The drupal_module has a webusers class of its own in the module itself.

    +
    Author:
    Daan Janssens, mentored by Matthew Lagoe
    +

    Constructor & Destructor Documentation

    + +
    +
    + + + + + + + + +
    WebUsers::__construct (UId = 0)
    +
    +
    + +

    A constructor.

    +

    loads the object with the UID, if none is given it will use 0.

    +
    Parameters:
    + + +
    $UIDthe UID of the user you want to instantiate.
    +
    +
    + +
    +
    +

    Member Function Documentation

    + +
    +
    + + + + + + + + +
    WebUsers::checkEmailExists (email) [protected]
    +
    +
    + +

    function that checks if a email exists already or not.

    +

    This function overrides the function of the base class. $email the email address in question.

    +
    Returns:
    string Info: Returns 0 if the email address is not in the web db, else a positive number is returned.
    + +

    Reimplemented from Users.

    + +
    +
    + +
    +
    + + + + + + + + + + + + + + + + + + +
    WebUsers::checkLoginMatch (username,
    password 
    )
    +
    +
    + +

    check if the login username and password match the db.

    +
    Parameters:
    + + + +
    $usernamethe inserted username
    $passwordthe inserted password (unhashed)
    +
    +
    +
    Returns:
    the logged in user's db row as array if login was a success, else "fail" will be returned.
    + +

    Reimplemented from Users.

    + +
    +
    + +
    +
    + + + + + + + + +
    WebUsers::checkUserNameExists (username) [protected]
    +
    +
    + +

    function that checks if a username exists already or not.

    +

    This function overrides the function of the base class. $username the username in question

    +
    Returns:
    string Info: Returns 0 if the user is not in the web db, else a positive number is returned.
    + +

    Reimplemented from Users.

    + +
    +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + +
    static WebUsers::createWebuser (name,
    pass,
    mail 
    ) [static]
    +
    +
    + +

    creates a webuser.

    +

    it will set the language matching to the language cookie setting and add it to the www/CMS's DB.

    +
    Parameters:
    + + + + +
    $namethe username
    $passthe unhashed password
    $mailthe email address
    +
    +
    + +
    +
    + +
    +
    + + + + + + + +
    static WebUsers::getAllUsersQuery () [static]
    +
    +
    + +

    return the query that should get all users.

    +
    Returns:
    string: the query to receive all users.
    + +
    +
    + +
    +
    + + + + + + + + +
    static WebUsers::getId (username) [static]
    +
    +
    + +

    returns te id for a given username

    +
    Parameters:
    + + +
    $usernamethe username
    +
    +
    +
    Returns:
    the user's id linked to the username
    + +
    +
    + +
    +
    + + + + + + + + +
    static WebUsers::getIdFromEmail (email) [static]
    +
    +
    + +

    returns te id for a given emailaddress

    +
    Parameters:
    + + +
    $emailthe emailaddress
    +
    +
    +
    Returns:
    the user's id linked to the emailaddress
    + +
    +
    + +
    +
    + + + + + + + +
    WebUsers::getInfo ()
    +
    +
    + +

    get basic info of the object.

    +
    Returns:
    returns an array in the form of Array('FirstName' => $this->firstname, 'LastName' => $this->lastname, 'Gender' => $this->gender, 'Country' => $this->country, 'ReceiveMail' => $this->receiveMail)
    + +
    +
    + +
    +
    + + + + + + + +
    WebUsers::getUsername ()
    +
    +
    + +

    get login attribute of the object.

    +

    (username)

    + +
    +
    + +
    +
    + + + + + + + +
    WebUsers::getUsers ()
    +
    +
    + +

    return all users.

    +
    Returns:
    return an array of users
    + +
    +
    + +
    +
    + + + + + + + +
    WebUsers::isLoggedIn ()
    +
    +
    + +

    check if the user is logged in.

    +
    Returns:
    true or false
    + +
    +
    + +
    +
    + + + + + + + + +
    WebUsers::set (values)
    +
    +
    + +

    sets the object's attributes.

    +
    Parameters:
    + + +
    $valuesshould be an array.
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + + + + + + + + + + + +
    WebUsers::setEmail (user,
    mail 
    )
    +
    +
    + +

    update the emailaddress.

    +

    update the emailaddress in the shard + update the emailaddress in the www/CMS version.

    +
    Parameters:
    + + + +
    $userthe username
    $mailthe new emailaddress.
    +
    +
    +
    Returns:
    ok if it worked, if the lib or shard is offline it will return liboffline or shardoffline.
    + +
    +
    + +
    +
    + + + + + + + + + + + + + + + + + + +
    static WebUsers::setLanguage (user,
    language 
    ) [static]
    +
    +
    + +

    update the language value in the db.

    +

    update the language in the www/CMS version.

    +
    Parameters:
    + + + +
    $userthe username
    $languagethe new language value.
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + + + + + + + + + + + +
    WebUsers::setPassword (user,
    pass 
    )
    +
    +
    + +

    update the password.

    +

    update the password in the shard + update the password in the www/CMS version.

    +
    Parameters:
    + + + +
    $userthe username
    $passthe new password.
    +
    +
    +
    Returns:
    ok if it worked, if the lib or shard is offline it will return liboffline or shardoffline.
    + +
    +
    + +
    +
    + + + + + + + + + + + + + + + + + + +
    static WebUsers::setReceiveMail (user,
    receivemail 
    ) [static]
    +
    +
    + +

    update the setReceiveMail value in the db.

    +

    update the receiveMail in the www/CMS version.

    +
    Parameters:
    + + + +
    $userthe username
    $receivemailthe receivemail setting .
    +
    +
    + +
    +
    +

    Member Data Documentation

    + +
    +
    + + + + +
    WebUsers::$receiveMail [private]
    +
    +
    + +

    configuration regarding if the user wants to receive email notifications or not.

    + +
    +
    +
    The documentation for this class was generated from the following file:
      +
    • /home/daan/ryzom/ryzomcore/code/ryzom/tools/server/ryzom_ams/www/html/autoload/webusers.php
    • +
    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/classWebUsers.png b/code/ryzom/tools/server/ryzom_ams_docs/html/classWebUsers.png new file mode 100644 index 0000000000000000000000000000000000000000..3a1e9afe1be4998d876c86badb752b6eb8695a08 GIT binary patch literal 338 zcmeAS@N?(olHy`uVBq!ia0vp^u0R~X!3-oTJUonnlth3}i0l9V|AEXGZ@!lHADRGU zf$@O@2Ut7r$OE|?B|(0{3_wL7aP?G(5m4-*r;B4q1>@Vf2Zfpycw8dqKKyULvz=v9 zgyfNBeeJ#r4=V7RPy875jYs?Ux7`yrIXM2W7GyOF+P=_rn#SJJC2{M5qz-B>z)Di?i0^@x176gb+us4#aT-8+=7lxnWBI4&a2jHxkXoIHF_=t5+@h#S>?85 zf)khKk_n#}SS73ZS=f-LQUDV z&do5L`;hYdD<=XO?w$$`nSUnpOZ3Et$;)C+sI*+^*Sx%k<%Oi`wG}Jcl6%>@Dx^Gj cI-T6WFJ{BPl=n}B7|^o}p00i_>zopr0Q|j@rvLx| literal 0 HcmV?d00001 diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/classes.html b/code/ryzom/tools/server/ryzom_ams_docs/html/classes.html new file mode 100644 index 000000000..da114cfc3 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/classes.html @@ -0,0 +1,139 @@ + + + + + +Ryzom Account Management System: Class Index + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + +
    +
    +
    +
    Class Index
    +
    +
    +
    A | D | F | G | H | I | M | P | Q | S | T | U | W
    + + + + + + + + + + + + + + +
      A  
    +
      H  
    +
      P  
    +
      T  
    +
    Ticket_User   
      U  
    +
    Assigned   Helpers   Pagination   Ticket   
      D  
    +
      I  
    +
      Q  
    +
    Ticket_Category   Users   
    Ticket_Content   
      W  
    +
    DBLayer   In_Support_Group   Querycache   Ticket_Info   
      F  
    +
      M  
    +
      S  
    +
    Ticket_Log   WebUsers   
    Ticket_Queue   
    Forwarded   Mail_Handler   Support_Group   Ticket_Queue_Handler   
      G  
    +
    MyCrypt   Sync   Ticket_Reply   
    Gui_Elements   
    +
    A | D | F | G | H | I | M | P | Q | S | T | U | W
    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/closed.png b/code/ryzom/tools/server/ryzom_ams_docs/html/closed.png new file mode 100644 index 0000000000000000000000000000000000000000..b7d4bd9fef2272c74b94762c9e2496177017775e GIT binary patch literal 126 zcmeAS@N?(olHy`uVBq!ia0vp^oFL4>1|%O$WD@{VuAVNAAr*{o?>h22DDp4|bgj*t z)u^AqcA-V@guRYpb17F<&b?_~8HV>~XqWvB;^$!VVSTy0!eQcJp_yD7TIQA>7dijs YXf6~H5cs^Q6KEiVr>mdKI;Vst0NsWqGynhq literal 0 HcmV?d00001 diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/deprecated.html b/code/ryzom/tools/server/ryzom_ams_docs/html/deprecated.html new file mode 100644 index 000000000..730e884eb --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/deprecated.html @@ -0,0 +1,105 @@ + + + + + +Ryzom Account Management System: Deprecated List + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + +
    +
    +
    +
    Deprecated List
    +
    +
    +
    +
    Member Support_Group ()
    +
    should be removed in the future, because getGroups does the same.
    +
    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/dir_4e66b119222255be04e87a0dd87a3317.html b/code/ryzom/tools/server/ryzom_ams_docs/html/dir_4e66b119222255be04e87a0dd87a3317.html new file mode 100644 index 000000000..8ef1281b4 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/dir_4e66b119222255be04e87a0dd87a3317.html @@ -0,0 +1,126 @@ + + + + + +Ryzom Account Management System: /home/daan/ryzom/ryzomcore/code/ryzom/tools/server/ryzom_ams/ Directory Reference + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + + + + +
    + +
    + + +
    +
    +
    +
    ryzom_ams Directory Reference
    +
    +
    + + + + +

    +Directories

    directory  ams_lib
    directory  www
    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/dir_732e39ca668b77f3e13244f204e87535.html b/code/ryzom/tools/server/ryzom_ams_docs/html/dir_732e39ca668b77f3e13244f204e87535.html new file mode 100644 index 000000000..e2c1421f9 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/dir_732e39ca668b77f3e13244f204e87535.html @@ -0,0 +1,128 @@ + + + + + +Ryzom Account Management System: /home/daan/ryzom/ryzomcore/code/ryzom/tools/server/ryzom_ams/www/html/autoload/ Directory Reference + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + + + + +
    + +
    + + +
    +
    +
    +
    autoload Directory Reference
    +
    +
    + + + +

    +Files

    file  webusers.php
    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/dir_7add0e0eba863b010b52e5fb777019f1.html b/code/ryzom/tools/server/ryzom_ams_docs/html/dir_7add0e0eba863b010b52e5fb777019f1.html new file mode 100644 index 000000000..8f38024c3 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/dir_7add0e0eba863b010b52e5fb777019f1.html @@ -0,0 +1,126 @@ + + + + + +Ryzom Account Management System: /home/daan/ryzom/ryzomcore/code/ryzom/tools/server/ryzom_ams/www/ Directory Reference + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + + + + +
    + +
    + + +
    +
    +
    +
    www Directory Reference
    +
    +
    + + + +

    +Directories

    directory  html
    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/dir_ba1cfc2c2eb4ebe254e0d7aa1e01cfcb.html b/code/ryzom/tools/server/ryzom_ams_docs/html/dir_ba1cfc2c2eb4ebe254e0d7aa1e01cfcb.html new file mode 100644 index 000000000..fc946b559 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/dir_ba1cfc2c2eb4ebe254e0d7aa1e01cfcb.html @@ -0,0 +1,127 @@ + + + + + +Ryzom Account Management System: /home/daan/ryzom/ryzomcore/code/ryzom/tools/server/ryzom_ams/www/html/ Directory Reference + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + + + + +
    + +
    + + +
    +
    +
    +
    html Directory Reference
    +
    +
    + + + +

    +Directories

    directory  autoload
    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/dir_c7daa497b9d1b9c868775e0964568556.html b/code/ryzom/tools/server/ryzom_ams_docs/html/dir_c7daa497b9d1b9c868775e0964568556.html new file mode 100644 index 000000000..19802e8db --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/dir_c7daa497b9d1b9c868775e0964568556.html @@ -0,0 +1,126 @@ + + + + + +Ryzom Account Management System: /home/daan/ryzom/ryzomcore/code/ryzom/tools/server/ryzom_ams/ams_lib/ Directory Reference + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + + + + +
    + +
    + + +
    +
    +
    +
    ams_lib Directory Reference
    +
    +
    + + + +

    +Directories

    directory  autoload
    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/dir_d0847756a6376d36128ff0811ac4d74f.html b/code/ryzom/tools/server/ryzom_ams_docs/html/dir_d0847756a6376d36128ff0811ac4d74f.html new file mode 100644 index 000000000..10394d1da --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/dir_d0847756a6376d36128ff0811ac4d74f.html @@ -0,0 +1,148 @@ + + + + + +Ryzom Account Management System: /home/daan/ryzom/ryzomcore/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ Directory Reference + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + + + + +
    + +
    + + +
    +
    +
    +
    autoload Directory Reference
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + +

    +Files

    file  assigned.php
    file  dblayer.php
    file  forwarded.php
    file  gui_elements.php
    file  helpers.php
    file  in_support_group.php
    file  mail_handler.php
    file  mycrypt.php
    file  pagination.php
    file  querycache.php
    file  support_group.php
    file  sync.php
    file  ticket.php
    file  ticket_category.php
    file  ticket_content.php
    file  ticket_info.php
    file  ticket_log.php
    file  ticket_queue.php
    file  ticket_queue_handler.php
    file  ticket_reply.php
    file  ticket_user.php
    file  users.php
    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/dirs.html b/code/ryzom/tools/server/ryzom_ams_docs/html/dirs.html new file mode 100644 index 000000000..fe6841d17 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/dirs.html @@ -0,0 +1,117 @@ + + + + + +Ryzom Account Management System: Directories + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + +
    +
    +
    +
    Directories
    +
    +
    +
    This directory hierarchy is sorted roughly, but not completely, alphabetically:
    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/doxygen.css b/code/ryzom/tools/server/ryzom_ams_docs/html/doxygen.css new file mode 100644 index 000000000..cee0d06b5 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/doxygen.css @@ -0,0 +1,949 @@ +/* The standard CSS for doxygen */ + +body, table, div, p, dl { + font-family: Lucida Grande, Verdana, Geneva, Arial, sans-serif; + font-size: 13px; + line-height: 1.3; +} + +/* @group Heading Levels */ + +h1 { + font-size: 150%; +} + +.title { + font-size: 150%; + font-weight: bold; + margin: 10px 2px; +} + +h2 { + font-size: 120%; +} + +h3 { + font-size: 100%; +} + +dt { + font-weight: bold; +} + +div.multicol { + -moz-column-gap: 1em; + -webkit-column-gap: 1em; + -moz-column-count: 3; + -webkit-column-count: 3; +} + +p.startli, p.startdd, p.starttd { + margin-top: 2px; +} + +p.endli { + margin-bottom: 0px; +} + +p.enddd { + margin-bottom: 4px; +} + +p.endtd { + margin-bottom: 2px; +} + +/* @end */ + +caption { + font-weight: bold; +} + +span.legend { + font-size: 70%; + text-align: center; +} + +h3.version { + font-size: 90%; + text-align: center; +} + +div.qindex, div.navtab{ + background-color: #EBEFF6; + border: 1px solid #A3B4D7; + text-align: center; +} + +div.qindex, div.navpath { + width: 100%; + line-height: 140%; +} + +div.navtab { + margin-right: 15px; +} + +/* @group Link Styling */ + +a { + color: #3D578C; + font-weight: normal; + text-decoration: none; +} + +.contents a:visited { + color: #4665A2; +} + +a:hover { + text-decoration: underline; +} + +a.qindex { + font-weight: bold; +} + +a.qindexHL { + font-weight: bold; + background-color: #9CAFD4; + color: #ffffff; + border: 1px double #869DCA; +} + +.contents a.qindexHL:visited { + color: #ffffff; +} + +a.el { + font-weight: bold; +} + +a.elRef { +} + +a.code, a.code:visited { + color: #4665A2; +} + +a.codeRef, a.codeRef:visited { + color: #4665A2; +} + +/* @end */ + +dl.el { + margin-left: -1cm; +} + +.fragment { + font-family: monospace, fixed; + font-size: 105%; +} + +pre.fragment { + border: 1px solid #C4CFE5; + background-color: #FBFCFD; + padding: 4px 6px; + margin: 4px 8px 4px 2px; + overflow: auto; + word-wrap: break-word; + font-size: 9pt; + line-height: 125%; +} + +div.ah { + background-color: black; + font-weight: bold; + color: #ffffff; + margin-bottom: 3px; + margin-top: 3px; + padding: 0.2em; + border: solid thin #333; + border-radius: 0.5em; + -webkit-border-radius: .5em; + -moz-border-radius: .5em; + box-shadow: 2px 2px 3px #999; + -webkit-box-shadow: 2px 2px 3px #999; + -moz-box-shadow: rgba(0, 0, 0, 0.15) 2px 2px 2px; + background-image: -webkit-gradient(linear, left top, left bottom, from(#eee), to(#000),color-stop(0.3, #444)); + background-image: -moz-linear-gradient(center top, #eee 0%, #444 40%, #000); +} + +div.groupHeader { + margin-left: 16px; + margin-top: 12px; + font-weight: bold; +} + +div.groupText { + margin-left: 16px; + font-style: italic; +} + +body { + background-color: white; + color: black; + margin: 0; +} + +div.contents { + margin-top: 10px; + margin-left: 8px; + margin-right: 8px; +} + +td.indexkey { + background-color: #EBEFF6; + font-weight: bold; + border: 1px solid #C4CFE5; + margin: 2px 0px 2px 0; + padding: 2px 10px; + white-space: nowrap; + vertical-align: top; +} + +td.indexvalue { + background-color: #EBEFF6; + border: 1px solid #C4CFE5; + padding: 2px 10px; + margin: 2px 0px; +} + +tr.memlist { + background-color: #EEF1F7; +} + +p.formulaDsp { + text-align: center; +} + +img.formulaDsp { + +} + +img.formulaInl { + vertical-align: middle; +} + +div.center { + text-align: center; + margin-top: 0px; + margin-bottom: 0px; + padding: 0px; +} + +div.center img { + border: 0px; +} + +address.footer { + text-align: right; + padding-right: 12px; +} + +img.footer { + border: 0px; + vertical-align: middle; +} + +/* @group Code Colorization */ + +span.keyword { + color: #008000 +} + +span.keywordtype { + color: #604020 +} + +span.keywordflow { + color: #e08000 +} + +span.comment { + color: #800000 +} + +span.preprocessor { + color: #806020 +} + +span.stringliteral { + color: #002080 +} + +span.charliteral { + color: #008080 +} + +span.vhdldigit { + color: #ff00ff +} + +span.vhdlchar { + color: #000000 +} + +span.vhdlkeyword { + color: #700070 +} + +span.vhdllogic { + color: #ff0000 +} + +/* @end */ + +/* +.search { + color: #003399; + font-weight: bold; +} + +form.search { + margin-bottom: 0px; + margin-top: 0px; +} + +input.search { + font-size: 75%; + color: #000080; + font-weight: normal; + background-color: #e8eef2; +} +*/ + +td.tiny { + font-size: 75%; +} + +.dirtab { + padding: 4px; + border-collapse: collapse; + border: 1px solid #A3B4D7; +} + +th.dirtab { + background: #EBEFF6; + font-weight: bold; +} + +hr { + height: 0px; + border: none; + border-top: 1px solid #4A6AAA; +} + +hr.footer { + height: 1px; +} + +/* @group Member Descriptions */ + +table.memberdecls { + border-spacing: 0px; + padding: 0px; +} + +.mdescLeft, .mdescRight, +.memItemLeft, .memItemRight, +.memTemplItemLeft, .memTemplItemRight, .memTemplParams { + background-color: #F9FAFC; + border: none; + margin: 4px; + padding: 1px 0 0 8px; +} + +.mdescLeft, .mdescRight { + padding: 0px 8px 4px 8px; + color: #555; +} + +.memItemLeft, .memItemRight, .memTemplParams { + border-top: 1px solid #C4CFE5; +} + +.memItemLeft, .memTemplItemLeft { + white-space: nowrap; +} + +.memItemRight { + width: 100%; +} + +.memTemplParams { + color: #4665A2; + white-space: nowrap; +} + +/* @end */ + +/* @group Member Details */ + +/* Styles for detailed member documentation */ + +.memtemplate { + font-size: 80%; + color: #4665A2; + font-weight: normal; + margin-left: 9px; +} + +.memnav { + background-color: #EBEFF6; + border: 1px solid #A3B4D7; + text-align: center; + margin: 2px; + margin-right: 15px; + padding: 2px; +} + +.mempage { + width: 100%; +} + +.memitem { + padding: 0; + margin-bottom: 10px; + margin-right: 5px; +} + +.memname { + white-space: nowrap; + font-weight: bold; + margin-left: 6px; +} + +.memproto, dl.reflist dt { + border-top: 1px solid #A8B8D9; + border-left: 1px solid #A8B8D9; + border-right: 1px solid #A8B8D9; + padding: 6px 0px 6px 0px; + color: #253555; + font-weight: bold; + text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.9); + /* opera specific markup */ + box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); + border-top-right-radius: 8px; + border-top-left-radius: 8px; + /* firefox specific markup */ + -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px; + -moz-border-radius-topright: 8px; + -moz-border-radius-topleft: 8px; + /* webkit specific markup */ + -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); + -webkit-border-top-right-radius: 8px; + -webkit-border-top-left-radius: 8px; + background-image:url('nav_f.png'); + background-repeat:repeat-x; + background-color: #E2E8F2; + +} + +.memdoc, dl.reflist dd { + border-bottom: 1px solid #A8B8D9; + border-left: 1px solid #A8B8D9; + border-right: 1px solid #A8B8D9; + padding: 2px 5px; + background-color: #FBFCFD; + border-top-width: 0; + /* opera specific markup */ + border-bottom-left-radius: 8px; + border-bottom-right-radius: 8px; + box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); + /* firefox specific markup */ + -moz-border-radius-bottomleft: 8px; + -moz-border-radius-bottomright: 8px; + -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px; + background-image: -moz-linear-gradient(center top, #FFFFFF 0%, #FFFFFF 60%, #F7F8FB 95%, #EEF1F7); + /* webkit specific markup */ + -webkit-border-bottom-left-radius: 8px; + -webkit-border-bottom-right-radius: 8px; + -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); + background-image: -webkit-gradient(linear,center top,center bottom,from(#FFFFFF), color-stop(0.6,#FFFFFF), color-stop(0.60,#FFFFFF), color-stop(0.95,#F7F8FB), to(#EEF1F7)); +} + +dl.reflist dt { + padding: 5px; +} + +dl.reflist dd { + margin: 0px 0px 10px 0px; + padding: 5px; +} + +.paramkey { + text-align: right; +} + +.paramtype { + white-space: nowrap; +} + +.paramname { + color: #602020; + white-space: nowrap; +} +.paramname em { + font-style: normal; +} + +.params, .retval, .exception, .tparams { + border-spacing: 6px 2px; +} + +.params .paramname, .retval .paramname { + font-weight: bold; + vertical-align: top; +} + +.params .paramtype { + font-style: italic; + vertical-align: top; +} + +.params .paramdir { + font-family: "courier new",courier,monospace; + vertical-align: top; +} + + + + +/* @end */ + +/* @group Directory (tree) */ + +/* for the tree view */ + +.ftvtree { + font-family: sans-serif; + margin: 0px; +} + +/* these are for tree view when used as main index */ + +.directory { + font-size: 9pt; + font-weight: bold; + margin: 5px; +} + +.directory h3 { + margin: 0px; + margin-top: 1em; + font-size: 11pt; +} + +/* +The following two styles can be used to replace the root node title +with an image of your choice. Simply uncomment the next two styles, +specify the name of your image and be sure to set 'height' to the +proper pixel height of your image. +*/ + +/* +.directory h3.swap { + height: 61px; + background-repeat: no-repeat; + background-image: url("yourimage.gif"); +} +.directory h3.swap span { + display: none; +} +*/ + +.directory > h3 { + margin-top: 0; +} + +.directory p { + margin: 0px; + white-space: nowrap; +} + +.directory div { + display: none; + margin: 0px; +} + +.directory img { + vertical-align: -30%; +} + +/* these are for tree view when not used as main index */ + +.directory-alt { + font-size: 100%; + font-weight: bold; +} + +.directory-alt h3 { + margin: 0px; + margin-top: 1em; + font-size: 11pt; +} + +.directory-alt > h3 { + margin-top: 0; +} + +.directory-alt p { + margin: 0px; + white-space: nowrap; +} + +.directory-alt div { + display: none; + margin: 0px; +} + +.directory-alt img { + vertical-align: -30%; +} + +/* @end */ + +div.dynheader { + margin-top: 8px; +} + +address { + font-style: normal; + color: #2A3D61; +} + +table.doxtable { + border-collapse:collapse; +} + +table.doxtable td, table.doxtable th { + border: 1px solid #2D4068; + padding: 3px 7px 2px; +} + +table.doxtable th { + background-color: #374F7F; + color: #FFFFFF; + font-size: 110%; + padding-bottom: 4px; + padding-top: 5px; + text-align:left; +} + +table.fieldtable { + width: 100%; + margin-bottom: 10px; + border: 1px solid #A8B8D9; + border-spacing: 0px; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; + -moz-box-shadow: rgba(0, 0, 0, 0.15) 2px 2px 2px; + -webkit-box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15); + box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15); +} + +.fieldtable td, .fieldtable th { + padding: 3px 7px 2px; +} + +.fieldtable td.fieldtype, .fieldtable td.fieldname { + white-space: nowrap; + border-right: 1px solid #A8B8D9; + border-bottom: 1px solid #A8B8D9; + vertical-align: top; +} + +.fieldtable td.fielddoc { + border-bottom: 1px solid #A8B8D9; + width: 100%; +} + +.fieldtable tr:last-child td { + border-bottom: none; +} + +.fieldtable th { + background-image:url('nav_f.png'); + background-repeat:repeat-x; + background-color: #E2E8F2; + font-size: 90%; + color: #253555; + padding-bottom: 4px; + padding-top: 5px; + text-align:left; + -moz-border-radius-topleft: 4px; + -moz-border-radius-topright: 4px; + -webkit-border-top-left-radius: 4px; + -webkit-border-top-right-radius: 4px; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + border-bottom: 1px solid #A8B8D9; +} + + +.tabsearch { + top: 0px; + left: 10px; + height: 36px; + background-image: url('tab_b.png'); + z-index: 101; + overflow: hidden; + font-size: 13px; +} + +.navpath ul +{ + font-size: 11px; + background-image:url('tab_b.png'); + background-repeat:repeat-x; + height:30px; + line-height:30px; + color:#8AA0CC; + border:solid 1px #C2CDE4; + overflow:hidden; + margin:0px; + padding:0px; +} + +.navpath li +{ + list-style-type:none; + float:left; + padding-left:10px; + padding-right:15px; + background-image:url('bc_s.png'); + background-repeat:no-repeat; + background-position:right; + color:#364D7C; +} + +.navpath li.navelem a +{ + height:32px; + display:block; + text-decoration: none; + outline: none; +} + +.navpath li.navelem a:hover +{ + color:#6884BD; +} + +.navpath li.footer +{ + list-style-type:none; + float:right; + padding-left:10px; + padding-right:15px; + background-image:none; + background-repeat:no-repeat; + background-position:right; + color:#364D7C; + font-size: 8pt; +} + + +div.summary +{ + float: right; + font-size: 8pt; + padding-right: 5px; + width: 50%; + text-align: right; +} + +div.summary a +{ + white-space: nowrap; +} + +div.ingroups +{ + margin-left: 5px; + font-size: 8pt; + padding-left: 5px; + width: 50%; + text-align: left; +} + +div.ingroups a +{ + white-space: nowrap; +} + +div.header +{ + background-image:url('nav_h.png'); + background-repeat:repeat-x; + background-color: #F9FAFC; + margin: 0px; + border-bottom: 1px solid #C4CFE5; +} + +div.headertitle +{ + padding: 5px 5px 5px 7px; +} + +dl +{ + padding: 0 0 0 10px; +} + +dl.note, dl.warning, dl.attention, dl.pre, dl.post, dl.invariant, dl.deprecated, dl.todo, dl.test, dl.bug +{ + border-left:4px solid; + padding: 0 0 0 6px; +} + +dl.note +{ + border-color: #D0C000; +} + +dl.warning, dl.attention +{ + border-color: #FF0000; +} + +dl.pre, dl.post, dl.invariant +{ + border-color: #00D000; +} + +dl.deprecated +{ + border-color: #505050; +} + +dl.todo +{ + border-color: #00C0E0; +} + +dl.test +{ + border-color: #3030E0; +} + +dl.bug +{ + border-color: #C08050; +} + +#projectlogo +{ + text-align: center; + vertical-align: bottom; + border-collapse: separate; +} + +#projectlogo img +{ + border: 0px none; +} + +#projectname +{ + font: 300% Tahoma, Arial,sans-serif; + margin: 0px; + padding: 2px 0px; +} + +#projectbrief +{ + font: 120% Tahoma, Arial,sans-serif; + margin: 0px; + padding: 0px; +} + +#projectnumber +{ + font: 50% Tahoma, Arial,sans-serif; + margin: 0px; + padding: 0px; +} + +#titlearea +{ + padding: 0px; + margin: 0px; + width: 100%; + border-bottom: 1px solid #5373B4; +} + +.image +{ + text-align: center; +} + +.dotgraph +{ + text-align: center; +} + +.mscgraph +{ + text-align: center; +} + +.caption +{ + font-weight: bold; +} + +div.zoom +{ + border: 1px solid #90A5CE; +} + +dl.citelist { + margin-bottom:50px; +} + +dl.citelist dt { + color:#334975; + float:left; + font-weight:bold; + margin-right:10px; + padding:5px; +} + +dl.citelist dd { + margin:2px 0; + padding:5px 0; +} + +@media print +{ + #top { display: none; } + #side-nav { display: none; } + #nav-path { display: none; } + body { overflow:visible; } + h1, h2, h3, h4, h5, h6 { page-break-after: avoid; } + .summary { display: none; } + .memitem { page-break-inside: avoid; } + #doc-content + { + margin-left:0 !important; + height:auto !important; + width:auto !important; + overflow:inherit; + display:inline; + } + pre.fragment + { + overflow: visible; + text-wrap: unrestricted; + white-space: -moz-pre-wrap; /* Moz */ + white-space: -pre-wrap; /* Opera 4-6 */ + white-space: -o-pre-wrap; /* Opera 7 */ + white-space: pre-wrap; /* CSS3 */ + word-wrap: break-word; /* IE 5.5+ */ + } +} + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/doxygen.png b/code/ryzom/tools/server/ryzom_ams_docs/html/doxygen.png new file mode 100644 index 0000000000000000000000000000000000000000..635ed52fce7057ac24df92ec7664088a881fa5d0 GIT binary patch literal 3942 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 A+yDRo literal 0 HcmV?d00001 diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/functions.html b/code/ryzom/tools/server/ryzom_ams_docs/html/functions.html new file mode 100644 index 000000000..db3d518ad --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/functions.html @@ -0,0 +1,349 @@ + + + + + +Ryzom Account Management System: Class Members + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + + + +
    +
    +
    Here is a list of all documented class members with links to the class documentation for each member:
    + +

    - $ -

    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x5f.html b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x5f.html new file mode 100644 index 000000000..1799686a3 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x5f.html @@ -0,0 +1,157 @@ + + + + + +Ryzom Account Management System: Class Members + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + + + +
    +
    +
    Here is a list of all documented class members with links to the class documentation for each member:
    + +

    - _ -

    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x61.html b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x61.html new file mode 100644 index 000000000..17cc9581a --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x61.html @@ -0,0 +1,145 @@ + + + + + +Ryzom Account Management System: Class Members + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + + + +
    +
    +
    Here is a list of all documented class members with links to the class documentation for each member:
    + +

    - a -

    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x63.html b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x63.html new file mode 100644 index 000000000..9593931b6 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x63.html @@ -0,0 +1,252 @@ + + + + + +Ryzom Account Management System: Class Members + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + + + +
    +
    +
    Here is a list of all documented class members with links to the class documentation for each member:
    + +

    - c -

    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x64.html b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x64.html new file mode 100644 index 000000000..5642c6c26 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x64.html @@ -0,0 +1,156 @@ + + + + + +Ryzom Account Management System: Class Members + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + + + +
    +
    +
    Here is a list of all documented class members with links to the class documentation for each member:
    + +

    - d -

    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x65.html b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x65.html new file mode 100644 index 000000000..4613156f9 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x65.html @@ -0,0 +1,150 @@ + + + + + +Ryzom Account Management System: Class Members + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + + + +
    +
    +
    Here is a list of all documented class members with links to the class documentation for each member:
    + +

    - e -

    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x66.html b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x66.html new file mode 100644 index 000000000..901500bf3 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x66.html @@ -0,0 +1,142 @@ + + + + + +Ryzom Account Management System: Class Members + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + + + +
    +
    +
    Here is a list of all documented class members with links to the class documentation for each member:
    + +

    - f -

    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x67.html b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x67.html new file mode 100644 index 000000000..08d04e05a --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x67.html @@ -0,0 +1,456 @@ + + + + + +Ryzom Account Management System: Class Members + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + + + +
    +
    +
    Here is a list of all documented class members with links to the class documentation for each member:
    + +

    - g -

    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x68.html b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x68.html new file mode 100644 index 000000000..97af1051f --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x68.html @@ -0,0 +1,147 @@ + + + + + +Ryzom Account Management System: Class Members + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + + + +
    +
    +
    Here is a list of all documented class members with links to the class documentation for each member:
    + +

    - h -

    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x69.html b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x69.html new file mode 100644 index 000000000..86229aeb2 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x69.html @@ -0,0 +1,156 @@ + + + + + +Ryzom Account Management System: Class Members + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + + + +
    +
    +
    Here is a list of all documented class members with links to the class documentation for each member:
    + +

    - i -

    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x6c.html b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x6c.html new file mode 100644 index 000000000..c61f7d0e2 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x6c.html @@ -0,0 +1,193 @@ + + + + + +Ryzom Account Management System: Class Members + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + + + +
    +
    +
    Here is a list of all documented class members with links to the class documentation for each member:
    + +

    - l -

    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x6d.html b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x6d.html new file mode 100644 index 000000000..609fd9518 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x6d.html @@ -0,0 +1,147 @@ + + + + + +Ryzom Account Management System: Class Members + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + + + +
    +
    +
    Here is a list of all documented class members with links to the class documentation for each member:
    + +

    - m -

    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x6e.html b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x6e.html new file mode 100644 index 000000000..9d3720554 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x6e.html @@ -0,0 +1,141 @@ + + + + + +Ryzom Account Management System: Class Members + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + + + +
    +
    +
    Here is a list of all documented class members with links to the class documentation for each member:
    + +

    - n -

    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x6f.html b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x6f.html new file mode 100644 index 000000000..57b1dd939 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x6f.html @@ -0,0 +1,141 @@ + + + + + +Ryzom Account Management System: Class Members + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + + + +
    +
    +
    Here is a list of all documented class members with links to the class documentation for each member:
    + +

    - o -

    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x73.html b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x73.html new file mode 100644 index 000000000..53fcbd2fb --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x73.html @@ -0,0 +1,341 @@ + + + + + +Ryzom Account Management System: Class Members + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + + + +
    +
    +
    Here is a list of all documented class members with links to the class documentation for each member:
    + +

    - s -

    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x74.html b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x74.html new file mode 100644 index 000000000..10400d8d4 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x74.html @@ -0,0 +1,147 @@ + + + + + +Ryzom Account Management System: Class Members + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + + + +
    +
    +
    Here is a list of all documented class members with links to the class documentation for each member:
    + +

    - t -

    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x75.html b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x75.html new file mode 100644 index 000000000..24784b349 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x75.html @@ -0,0 +1,161 @@ + + + + + +Ryzom Account Management System: Class Members + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + + + +
    +
    +
    Here is a list of all documented class members with links to the class documentation for each member:
    + +

    - u -

    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x76.html b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x76.html new file mode 100644 index 000000000..5d90ab604 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_0x76.html @@ -0,0 +1,141 @@ + + + + + +Ryzom Account Management System: Class Members + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + + + +
    +
    +
    Here is a list of all documented class members with links to the class documentation for each member:
    + +

    - v -

      +
    • validEmail() +: Users +
    • +
    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/functions_func.html b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_func.html new file mode 100644 index 000000000..e8d545941 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_func.html @@ -0,0 +1,156 @@ + + + + + +Ryzom Account Management System: Class Members - Functions + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + + + +
    + + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/functions_func_0x61.html b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_func_0x61.html new file mode 100644 index 000000000..c64997a1a --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_func_0x61.html @@ -0,0 +1,144 @@ + + + + + +Ryzom Account Management System: Class Members - Functions + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + + + +
    +
    +  + +

    - a -

    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/functions_func_0x63.html b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_func_0x63.html new file mode 100644 index 000000000..5d9043e08 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_func_0x63.html @@ -0,0 +1,251 @@ + + + + + +Ryzom Account Management System: Class Members - Functions + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + + + +
    +
    +  + +

    - c -

    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/functions_func_0x64.html b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_func_0x64.html new file mode 100644 index 000000000..371e26ed2 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_func_0x64.html @@ -0,0 +1,155 @@ + + + + + +Ryzom Account Management System: Class Members - Functions + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + + + +
    +
    +  + +

    - d -

    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/functions_func_0x65.html b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_func_0x65.html new file mode 100644 index 000000000..61d8abbd0 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_func_0x65.html @@ -0,0 +1,149 @@ + + + + + +Ryzom Account Management System: Class Members - Functions + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + + + +
    +
    +  + +

    - e -

    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/functions_func_0x66.html b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_func_0x66.html new file mode 100644 index 000000000..b062d7a5c --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_func_0x66.html @@ -0,0 +1,141 @@ + + + + + +Ryzom Account Management System: Class Members - Functions + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + + + +
    +
    +  + +

    - f -

    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/functions_func_0x67.html b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_func_0x67.html new file mode 100644 index 000000000..32369c3f8 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_func_0x67.html @@ -0,0 +1,455 @@ + + + + + +Ryzom Account Management System: Class Members - Functions + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + + + +
    +
    +  + +

    - g -

    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/functions_func_0x68.html b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_func_0x68.html new file mode 100644 index 000000000..c1dbd01ab --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_func_0x68.html @@ -0,0 +1,146 @@ + + + + + +Ryzom Account Management System: Class Members - Functions + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + + + +
    +
    +  + +

    - h -

    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/functions_func_0x69.html b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_func_0x69.html new file mode 100644 index 000000000..7bce51ba9 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_func_0x69.html @@ -0,0 +1,155 @@ + + + + + +Ryzom Account Management System: Class Members - Functions + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + + + +
    +
    +  + +

    - i -

    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/functions_func_0x6c.html b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_func_0x6c.html new file mode 100644 index 000000000..06e8d0f73 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_func_0x6c.html @@ -0,0 +1,192 @@ + + + + + +Ryzom Account Management System: Class Members - Functions + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + + + +
    +
    +  + +

    - l -

    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/functions_func_0x6d.html b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_func_0x6d.html new file mode 100644 index 000000000..8356e2c9c --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_func_0x6d.html @@ -0,0 +1,146 @@ + + + + + +Ryzom Account Management System: Class Members - Functions + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + + + +
    +
    +  + +

    - m -

    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/functions_func_0x6e.html b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_func_0x6e.html new file mode 100644 index 000000000..5e6cdf9ce --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_func_0x6e.html @@ -0,0 +1,140 @@ + + + + + +Ryzom Account Management System: Class Members - Functions + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + + + +
    +
    +  + +

    - n -

    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/functions_func_0x6f.html b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_func_0x6f.html new file mode 100644 index 000000000..8f83d26d3 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_func_0x6f.html @@ -0,0 +1,140 @@ + + + + + +Ryzom Account Management System: Class Members - Functions + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + + + +
    +
    +  + +

    - o -

    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/functions_func_0x73.html b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_func_0x73.html new file mode 100644 index 000000000..df3f5c238 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_func_0x73.html @@ -0,0 +1,340 @@ + + + + + +Ryzom Account Management System: Class Members - Functions + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + + + +
    +
    +  + +

    - s -

    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/functions_func_0x74.html b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_func_0x74.html new file mode 100644 index 000000000..78a3355be --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_func_0x74.html @@ -0,0 +1,146 @@ + + + + + +Ryzom Account Management System: Class Members - Functions + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + + + +
    +
    +  + +

    - t -

    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/functions_func_0x75.html b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_func_0x75.html new file mode 100644 index 000000000..5a25afb03 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_func_0x75.html @@ -0,0 +1,160 @@ + + + + + +Ryzom Account Management System: Class Members - Functions + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + + + +
    +
    +  + +

    - u -

    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/functions_func_0x76.html b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_func_0x76.html new file mode 100644 index 000000000..c2ac69f40 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_func_0x76.html @@ -0,0 +1,140 @@ + + + + + +Ryzom Account Management System: Class Members - Functions + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + + + +
    +
    +  + +

    - v -

      +
    • validEmail() +: Users +
    • +
    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/functions_vars.html b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_vars.html new file mode 100644 index 000000000..03541cd92 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/functions_vars.html @@ -0,0 +1,332 @@ + + + + + +Ryzom Account Management System: Class Members - Variables + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + + + +
    +
    +  + +

    - $ -

    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/hierarchy.html b/code/ryzom/tools/server/ryzom_ams_docs/html/hierarchy.html new file mode 100644 index 000000000..44504268d --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/hierarchy.html @@ -0,0 +1,136 @@ + + + + + +Ryzom Account Management System: Class Hierarchy + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + + +
    +
    +
    +
    Class Hierarchy
    +
    + + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/index.html b/code/ryzom/tools/server/ryzom_ams_docs/html/index.html new file mode 100644 index 000000000..31b1e2b30 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/index.html @@ -0,0 +1,108 @@ + + + + + +Ryzom Account Management System: The Ryzom AMS information pages. + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + +
    +
    +
    +
    The Ryzom AMS information pages.
    +
    +
    +

    +Introduction

    +

    Welcome to the documentation pages of the ryzom account management system library.
    + Doxygen is being used to generate these webpages. They should offer a good reference for anyone who is interested in working with the AMS library.

    +

    +More info?

    +

    if you want more information take a look at the ryzomcore wikipages

    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/installdox b/code/ryzom/tools/server/ryzom_ams_docs/html/installdox new file mode 100755 index 000000000..edf5bbfe3 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/installdox @@ -0,0 +1,112 @@ +#!/usr/bin/perl + +%subst = ( ); +$quiet = 0; + +while ( @ARGV ) { + $_ = shift @ARGV; + if ( s/^-// ) { + if ( /^l(.*)/ ) { + $v = ($1 eq "") ? shift @ARGV : $1; + ($v =~ /\/$/) || ($v .= "/"); + $_ = $v; + if ( /(.+)\@(.+)/ ) { + if ( exists $subst{$1} ) { + $subst{$1} = $2; + } else { + print STDERR "Unknown tag file $1 given with option -l\n"; + &usage(); + } + } else { + print STDERR "Argument $_ is invalid for option -l\n"; + &usage(); + } + } + elsif ( /^q/ ) { + $quiet = 1; + } + elsif ( /^\?|^h/ ) { + &usage(); + } + else { + print STDERR "Illegal option -$_\n"; + &usage(); + } + } + else { + push (@files, $_ ); + } +} + +foreach $sub (keys %subst) +{ + if ( $subst{$sub} eq "" ) + { + print STDERR "No substitute given for tag file `$sub'\n"; + &usage(); + } + elsif ( ! $quiet && $sub ne "_doc" && $sub ne "_cgi" ) + { + print "Substituting $subst{$sub} for each occurrence of tag file $sub\n"; + } +} + +if ( ! @files ) { + if (opendir(D,".")) { + foreach $file ( readdir(D) ) { + $match = ".html"; + next if ( $file =~ /^\.\.?$/ ); + ($file =~ /$match/) && (push @files, $file); + ($file =~ /\.svg/) && (push @files, $file); + ($file =~ "navtree.js") && (push @files, $file); + } + closedir(D); + } +} + +if ( ! @files ) { + print STDERR "Warning: No input files given and none found!\n"; +} + +foreach $f (@files) +{ + if ( ! $quiet ) { + print "Editing: $f...\n"; + } + $oldf = $f; + $f .= ".bak"; + unless (rename $oldf,$f) { + print STDERR "Error: cannot rename file $oldf\n"; + exit 1; + } + if (open(F,"<$f")) { + unless (open(G,">$oldf")) { + print STDERR "Error: opening file $oldf for writing\n"; + exit 1; + } + if ($oldf ne "tree.js") { + while () { + s/doxygen\=\"([^ \"\:\t\>\<]*)\:([^ \"\t\>\<]*)\" (xlink:href|href|src)=\"\2/doxygen\=\"$1:$subst{$1}\" \3=\"$subst{$1}/g; + print G "$_"; + } + } + else { + while () { + s/\"([^ \"\:\t\>\<]*)\:([^ \"\t\>\<]*)\", \"\2/\"$1:$subst{$1}\" ,\"$subst{$1}/g; + print G "$_"; + } + } + } + else { + print STDERR "Warning file $f does not exist\n"; + } + unlink $f; +} + +sub usage { + print STDERR "Usage: installdox [options] [html-file [html-file ...]]\n"; + print STDERR "Options:\n"; + print STDERR " -l tagfile\@linkName tag file + URL or directory \n"; + print STDERR " -q Quiet mode\n\n"; + exit 1; +} diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/jquery.js b/code/ryzom/tools/server/ryzom_ams_docs/html/jquery.js new file mode 100644 index 000000000..90b3a2bc3 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/jquery.js @@ -0,0 +1,64 @@ +/* + * jQuery JavaScript Library v1.3.2 + * http://jquery.com/ + * + * Copyright (c) 2009 John Resig + * Dual licensed under the MIT and GPL licenses. + * http://docs.jquery.com/License + * + * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009) + * Revision: 6246 + */ +(function(){var l=this,g,y=l.jQuery,p=l.$,o=l.jQuery=l.$=function(E,F){return new o.fn.init(E,F)},D=/^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/,f=/^.[^:#\[\.,]*$/;o.fn=o.prototype={init:function(E,H){E=E||document;if(E.nodeType){this[0]=E;this.length=1;this.context=E;return this}if(typeof E==="string"){var G=D.exec(E);if(G&&(G[1]||!H)){if(G[1]){E=o.clean([G[1]],H)}else{var I=document.getElementById(G[3]);if(I&&I.id!=G[3]){return o().find(E)}var F=o(I||[]);F.context=document;F.selector=E;return F}}else{return o(H).find(E)}}else{if(o.isFunction(E)){return o(document).ready(E)}}if(E.selector&&E.context){this.selector=E.selector;this.context=E.context}return this.setArray(o.isArray(E)?E:o.makeArray(E))},selector:"",jquery:"1.3.2",size:function(){return this.length},get:function(E){return E===g?Array.prototype.slice.call(this):this[E]},pushStack:function(F,H,E){var G=o(F);G.prevObject=this;G.context=this.context;if(H==="find"){G.selector=this.selector+(this.selector?" ":"")+E}else{if(H){G.selector=this.selector+"."+H+"("+E+")"}}return G},setArray:function(E){this.length=0;Array.prototype.push.apply(this,E);return this},each:function(F,E){return o.each(this,F,E)},index:function(E){return o.inArray(E&&E.jquery?E[0]:E,this)},attr:function(F,H,G){var E=F;if(typeof F==="string"){if(H===g){return this[0]&&o[G||"attr"](this[0],F)}else{E={};E[F]=H}}return this.each(function(I){for(F in E){o.attr(G?this.style:this,F,o.prop(this,E[F],G,I,F))}})},css:function(E,F){if((E=="width"||E=="height")&&parseFloat(F)<0){F=g}return this.attr(E,F,"curCSS")},text:function(F){if(typeof F!=="object"&&F!=null){return this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(F))}var E="";o.each(F||this,function(){o.each(this.childNodes,function(){if(this.nodeType!=8){E+=this.nodeType!=1?this.nodeValue:o.fn.text([this])}})});return E},wrapAll:function(E){if(this[0]){var F=o(E,this[0].ownerDocument).clone();if(this[0].parentNode){F.insertBefore(this[0])}F.map(function(){var G=this;while(G.firstChild){G=G.firstChild}return G}).append(this)}return this},wrapInner:function(E){return this.each(function(){o(this).contents().wrapAll(E)})},wrap:function(E){return this.each(function(){o(this).wrapAll(E)})},append:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.appendChild(E)}})},prepend:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.insertBefore(E,this.firstChild)}})},before:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this)})},after:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this.nextSibling)})},end:function(){return this.prevObject||o([])},push:[].push,sort:[].sort,splice:[].splice,find:function(E){if(this.length===1){var F=this.pushStack([],"find",E);F.length=0;o.find(E,this[0],F);return F}else{return this.pushStack(o.unique(o.map(this,function(G){return o.find(E,G)})),"find",E)}},clone:function(G){var E=this.map(function(){if(!o.support.noCloneEvent&&!o.isXMLDoc(this)){var I=this.outerHTML;if(!I){var J=this.ownerDocument.createElement("div");J.appendChild(this.cloneNode(true));I=J.innerHTML}return o.clean([I.replace(/ jQuery\d+="(?:\d+|null)"/g,"").replace(/^\s*/,"")])[0]}else{return this.cloneNode(true)}});if(G===true){var H=this.find("*").andSelf(),F=0;E.find("*").andSelf().each(function(){if(this.nodeName!==H[F].nodeName){return}var I=o.data(H[F],"events");for(var K in I){for(var J in I[K]){o.event.add(this,K,I[K][J],I[K][J].data)}}F++})}return E},filter:function(E){return this.pushStack(o.isFunction(E)&&o.grep(this,function(G,F){return E.call(G,F)})||o.multiFilter(E,o.grep(this,function(F){return F.nodeType===1})),"filter",E)},closest:function(E){var G=o.expr.match.POS.test(E)?o(E):null,F=0;return this.map(function(){var H=this;while(H&&H.ownerDocument){if(G?G.index(H)>-1:o(H).is(E)){o.data(H,"closest",F);return H}H=H.parentNode;F++}})},not:function(E){if(typeof E==="string"){if(f.test(E)){return this.pushStack(o.multiFilter(E,this,true),"not",E)}else{E=o.multiFilter(E,this)}}var F=E.length&&E[E.length-1]!==g&&!E.nodeType;return this.filter(function(){return F?o.inArray(this,E)<0:this!=E})},add:function(E){return this.pushStack(o.unique(o.merge(this.get(),typeof E==="string"?o(E):o.makeArray(E))))},is:function(E){return !!E&&o.multiFilter(E,this).length>0},hasClass:function(E){return !!E&&this.is("."+E)},val:function(K){if(K===g){var E=this[0];if(E){if(o.nodeName(E,"option")){return(E.attributes.value||{}).specified?E.value:E.text}if(o.nodeName(E,"select")){var I=E.selectedIndex,L=[],M=E.options,H=E.type=="select-one";if(I<0){return null}for(var F=H?I:0,J=H?I+1:M.length;F=0||o.inArray(this.name,K)>=0)}else{if(o.nodeName(this,"select")){var N=o.makeArray(K);o("option",this).each(function(){this.selected=(o.inArray(this.value,N)>=0||o.inArray(this.text,N)>=0)});if(!N.length){this.selectedIndex=-1}}else{this.value=K}}})},html:function(E){return E===g?(this[0]?this[0].innerHTML.replace(/ jQuery\d+="(?:\d+|null)"/g,""):null):this.empty().append(E)},replaceWith:function(E){return this.after(E).remove()},eq:function(E){return this.slice(E,+E+1)},slice:function(){return this.pushStack(Array.prototype.slice.apply(this,arguments),"slice",Array.prototype.slice.call(arguments).join(","))},map:function(E){return this.pushStack(o.map(this,function(G,F){return E.call(G,F,G)}))},andSelf:function(){return this.add(this.prevObject)},domManip:function(J,M,L){if(this[0]){var I=(this[0].ownerDocument||this[0]).createDocumentFragment(),F=o.clean(J,(this[0].ownerDocument||this[0]),I),H=I.firstChild;if(H){for(var G=0,E=this.length;G1||G>0?I.cloneNode(true):I)}}if(F){o.each(F,z)}}return this;function K(N,O){return M&&o.nodeName(N,"table")&&o.nodeName(O,"tr")?(N.getElementsByTagName("tbody")[0]||N.appendChild(N.ownerDocument.createElement("tbody"))):N}}};o.fn.init.prototype=o.fn;function z(E,F){if(F.src){o.ajax({url:F.src,async:false,dataType:"script"})}else{o.globalEval(F.text||F.textContent||F.innerHTML||"")}if(F.parentNode){F.parentNode.removeChild(F)}}function e(){return +new Date}o.extend=o.fn.extend=function(){var J=arguments[0]||{},H=1,I=arguments.length,E=false,G;if(typeof J==="boolean"){E=J;J=arguments[1]||{};H=2}if(typeof J!=="object"&&!o.isFunction(J)){J={}}if(I==H){J=this;--H}for(;H-1}},swap:function(H,G,I){var E={};for(var F in G){E[F]=H.style[F];H.style[F]=G[F]}I.call(H);for(var F in G){H.style[F]=E[F]}},css:function(H,F,J,E){if(F=="width"||F=="height"){var L,G={position:"absolute",visibility:"hidden",display:"block"},K=F=="width"?["Left","Right"]:["Top","Bottom"];function I(){L=F=="width"?H.offsetWidth:H.offsetHeight;if(E==="border"){return}o.each(K,function(){if(!E){L-=parseFloat(o.curCSS(H,"padding"+this,true))||0}if(E==="margin"){L+=parseFloat(o.curCSS(H,"margin"+this,true))||0}else{L-=parseFloat(o.curCSS(H,"border"+this+"Width",true))||0}})}if(H.offsetWidth!==0){I()}else{o.swap(H,G,I)}return Math.max(0,Math.round(L))}return o.curCSS(H,F,J)},curCSS:function(I,F,G){var L,E=I.style;if(F=="opacity"&&!o.support.opacity){L=o.attr(E,"opacity");return L==""?"1":L}if(F.match(/float/i)){F=w}if(!G&&E&&E[F]){L=E[F]}else{if(q.getComputedStyle){if(F.match(/float/i)){F="float"}F=F.replace(/([A-Z])/g,"-$1").toLowerCase();var M=q.getComputedStyle(I,null);if(M){L=M.getPropertyValue(F)}if(F=="opacity"&&L==""){L="1"}}else{if(I.currentStyle){var J=F.replace(/\-(\w)/g,function(N,O){return O.toUpperCase()});L=I.currentStyle[F]||I.currentStyle[J];if(!/^\d+(px)?$/i.test(L)&&/^\d/.test(L)){var H=E.left,K=I.runtimeStyle.left;I.runtimeStyle.left=I.currentStyle.left;E.left=L||0;L=E.pixelLeft+"px";E.left=H;I.runtimeStyle.left=K}}}}return L},clean:function(F,K,I){K=K||document;if(typeof K.createElement==="undefined"){K=K.ownerDocument||K[0]&&K[0].ownerDocument||document}if(!I&&F.length===1&&typeof F[0]==="string"){var H=/^<(\w+)\s*\/?>$/.exec(F[0]);if(H){return[K.createElement(H[1])]}}var G=[],E=[],L=K.createElement("div");o.each(F,function(P,S){if(typeof S==="number"){S+=""}if(!S){return}if(typeof S==="string"){S=S.replace(/(<(\w+)[^>]*?)\/>/g,function(U,V,T){return T.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i)?U:V+">"});var O=S.replace(/^\s+/,"").substring(0,10).toLowerCase();var Q=!O.indexOf("",""]||!O.indexOf("",""]||O.match(/^<(thead|tbody|tfoot|colg|cap)/)&&[1,"","
    "]||!O.indexOf("
    "]||(!O.indexOf("

    "]||!O.indexOf("
    "]||!o.support.htmlSerialize&&[1,"div
    ","
    "]||[0,"",""];L.innerHTML=Q[1]+S+Q[2];while(Q[0]--){L=L.lastChild}if(!o.support.tbody){var R=/
    ';M={position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"};for(E in M){F.style[E]=M[E]}F.innerHTML=K;L.insertBefore(F,L.firstChild);H=F.firstChild,G=H.firstChild,I=H.nextSibling.firstChild.firstChild;this.doesNotAddBorder=(G.offsetTop!==5);this.doesAddBorderForTableAndCells=(I.offsetTop===5);H.style.overflow="hidden",H.style.position="relative";this.subtractsBorderForOverflowNotVisible=(G.offsetTop===-5);L.style.marginTop="1px";this.doesNotIncludeMarginInBodyOffset=(L.offsetTop===0);L.style.marginTop=J;L.removeChild(F);this.initialized=true},bodyOffset:function(E){o.offset.initialized||o.offset.initialize();var G=E.offsetTop,F=E.offsetLeft;if(o.offset.doesNotIncludeMarginInBodyOffset){G+=parseInt(o.curCSS(E,"marginTop",true),10)||0,F+=parseInt(o.curCSS(E,"marginLeft",true),10)||0}return{top:G,left:F}}};o.fn.extend({position:function(){var I=0,H=0,F;if(this[0]){var G=this.offsetParent(),J=this.offset(),E=/^body|html$/i.test(G[0].tagName)?{top:0,left:0}:G.offset();J.top-=j(this,"marginTop");J.left-=j(this,"marginLeft");E.top+=j(G,"borderTopWidth");E.left+=j(G,"borderLeftWidth");F={top:J.top-E.top,left:J.left-E.left}}return F},offsetParent:function(){var E=this[0].offsetParent||document.body;while(E&&(!/^body|html$/i.test(E.tagName)&&o.css(E,"position")=="static")){E=E.offsetParent}return o(E)}});o.each(["Left","Top"],function(F,E){var G="scroll"+E;o.fn[G]=function(H){if(!this[0]){return null}return H!==g?this.each(function(){this==l||this==document?l.scrollTo(!F?H:o(l).scrollLeft(),F?H:o(l).scrollTop()):this[G]=H}):this[0]==l||this[0]==document?self[F?"pageYOffset":"pageXOffset"]||o.boxModel&&document.documentElement[G]||document.body[G]:this[0][G]}});o.each(["Height","Width"],function(I,G){var E=I?"Left":"Top",H=I?"Right":"Bottom",F=G.toLowerCase();o.fn["inner"+G]=function(){return this[0]?o.css(this[0],F,false,"padding"):null};o.fn["outer"+G]=function(K){return this[0]?o.css(this[0],F,false,K?"margin":"border"):null};var J=G.toLowerCase();o.fn[J]=function(K){return this[0]==l?document.compatMode=="CSS1Compat"&&document.documentElement["client"+G]||document.body["client"+G]:this[0]==document?Math.max(document.documentElement["client"+G],document.body["scroll"+G],document.documentElement["scroll"+G],document.body["offset"+G],document.documentElement["offset"+G]):K===g?(this.length?o.css(this[0],J):null):this.css(J,typeof K==="string"?K:K+"px")}})})(); + +/* + * jQuery hashchange event - v1.3 - 7/21/2010 + * http://benalman.com/projects/jquery-hashchange-plugin/ + * + * Copyright (c) 2010 "Cowboy" Ben Alman + * Dual licensed under the MIT and GPL licenses. + * http://benalman.com/about/license/ + */ +(function($,e,b){var c="hashchange",h=document,f,g=$.event.special,i=h.documentMode,d="on"+c in e&&(i===b||i>7);function a(j){j=j||location.href;return"#"+j.replace(/^[^#]*#?(.*)$/,"$1")}$.fn[c]=function(j){return j?this.bind(c,j):this.trigger(c)};$.fn[c].delay=50;g[c]=$.extend(g[c],{setup:function(){if(d){return false}$(f.start)},teardown:function(){if(d){return false}$(f.stop)}});f=(function(){var j={},p,m=a(),k=function(q){return q},l=k,o=k;j.start=function(){p||n()};j.stop=function(){p&&clearTimeout(p);p=b};function n(){var r=a(),q=o(m);if(r!==m){l(m=r,q);$(e).trigger(c)}else{if(q!==m){location.href=location.href.replace(/#.*/,"")+q}}p=setTimeout(n,$.fn[c].delay)}$.browser.msie&&!d&&(function(){var q,r;j.start=function(){if(!q){r=$.fn[c].src;r=r&&r+a();q=$(' + + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_24.html b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_24.html new file mode 100644 index 000000000..476d13496 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_24.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_24.js b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_24.js new file mode 100644 index 000000000..afa19a40e --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_24.js @@ -0,0 +1,68 @@ +var searchData= +[ + ['_24amountofrows',['$amountOfRows',['../classPagination.html#a0cd85db34fde63f50175dc68951b2965',1,'Pagination']]], + ['_24author',['$author',['../classTicket.html#a800d58b1adb950b389ed8170a8bf95ea',1,'Ticket\$author()'],['../classTicket__Log.html#ad1458fe1e2a7831a7b53ea61adb21db1',1,'Ticket_Log\$author()'],['../classTicket__Reply.html#ac8fd9d68eece69dd9103bb8bc6d6e12e',1,'Ticket_Reply\$author()']]], + ['_24client_5fversion',['$client_version',['../classTicket__Info.html#a8b0f292dc30fe169fbfb160710ab9a5d',1,'Ticket_Info']]], + ['_24config',['$config',['../classMyCrypt.html#a9701fd447bb31495494d7580f8fbad31',1,'MyCrypt']]], + ['_24connect_5fstate',['$connect_state',['../classTicket__Info.html#a192ebb3925ab30beb6fa90c6c881abaa',1,'Ticket_Info']]], + ['_24content',['$content',['../classTicket__Content.html#a0b76d58db5325853374f459b56535040',1,'Ticket_Content\$content()'],['../classTicket__Reply.html#a8f40940bb3b2cbcc6e2f60570a435e33',1,'Ticket_Reply\$content()']]], + ['_24country',['$country',['../classWebUsers.html#a0aeb933ed72691c038d40ffc57ec1504',1,'WebUsers']]], + ['_24cpu_5fid',['$cpu_id',['../classTicket__Info.html#a007fb1bced444de7ea2e18558f8f4877',1,'Ticket_Info']]], + ['_24cpu_5fmask',['$cpu_mask',['../classTicket__Info.html#af93d1a84a429aaabfd84f1d244fc44b8',1,'Ticket_Info']]], + ['_24current',['$current',['../classPagination.html#ae7664ad0a9768c43b1c3920271e7f07e',1,'Pagination']]], + ['_24db',['$db',['../classMail__Handler.html#a586665d45069c9b7ffcc9759872b30f5',1,'Mail_Handler\$db()'],['../classQuerycache.html#a781742cc162f5714e15c7ef7dc30ebb2',1,'Querycache\$db()']]], + ['_24element_5farray',['$element_array',['../classPagination.html#a0691015765a20fb52a5b95509051c52a',1,'Pagination']]], + ['_24email',['$email',['../classWebUsers.html#a532667fc1fa71ee38f3b8a9a0bbb3991',1,'WebUsers']]], + ['_24externid',['$externId',['../classTicket__User.html#abee3b416d7f7db5bf23ed39a095c36c6',1,'Ticket_User']]], + ['_24firstname',['$firstname',['../classWebUsers.html#a39d55355d537ad77e08f2c196b1b39b5',1,'WebUsers']]], + ['_24gender',['$gender',['../classWebUsers.html#a101472ee52f1e37416b472fdd151a6b4',1,'WebUsers']]], + ['_24group',['$group',['../classForwarded.html#a66cff5a347920873ffe30f715065d340',1,'Forwarded\$group()'],['../classIn__Support__Group.html#a4baa32d40511a8e53a942346c997424c',1,'In_Support_Group\$group()']]], + ['_24groupemail',['$groupEmail',['../classSupport__Group.html#a4255fbb39299728472147bafc838eacd',1,'Support_Group']]], + ['_24hidden',['$hidden',['../classTicket__Reply.html#a319fbe3e5d64f295800b270a1ed21260',1,'Ticket_Reply']]], + ['_24ht',['$ht',['../classTicket__Info.html#adcb070ed9b106f52207fe66b228d7594',1,'Ticket_Info']]], + ['_24imap_5fmailserver',['$iMAP_MailServer',['../classSupport__Group.html#a15d68505e0f11fb84d9c1c90340e391b',1,'Support_Group']]], + ['_24imap_5fpassword',['$iMAP_Password',['../classSupport__Group.html#a78c5c6a76d106eb4264417336bccd431',1,'Support_Group']]], + ['_24imap_5fusername',['$iMAP_Username',['../classSupport__Group.html#aed9c9e853a744e8148262a263879d5e7',1,'Support_Group']]], + ['_24language',['$language',['../classWebUsers.html#af7e97cee72066f85737453d81c931550',1,'WebUsers']]], + ['_24last',['$last',['../classPagination.html#a761c473a0e483c48b20ee5b784135883',1,'Pagination']]], + ['_24lastname',['$lastname',['../classWebUsers.html#ac0122258b652e5629269ec98db168a44',1,'WebUsers']]], + ['_24local_5faddress',['$local_address',['../classTicket__Info.html#a31c74f0ade3e12188fba83f5eb8814c7',1,'Ticket_Info']]], + ['_24login',['$login',['../classWebUsers.html#a7b61ca04b134c0b5452fa2e61f4b36bd',1,'WebUsers']]], + ['_24memory',['$memory',['../classTicket__Info.html#a28200cd05abfb56f1f4464eeb83bbd85',1,'Ticket_Info']]], + ['_24name',['$name',['../classSupport__Group.html#a7e3e953217f5ff39f6e5e4713f9d1897',1,'Support_Group\$name()'],['../classTicket__Category.html#a81ea14ddb8794f34800aad4e9d0baf91',1,'Ticket_Category\$name()']]], + ['_24nel3d',['$nel3d',['../classTicket__Info.html#a44a096a9876a075b728ee5f0f63e13d5',1,'Ticket_Info']]], + ['_24os',['$os',['../classTicket__Info.html#af1e68104013e10fec558878888aca10d',1,'Ticket_Info']]], + ['_24pagination',['$pagination',['../classTicket__Queue__Handler.html#acadf7665fa564ba8c6dbaf047d3a220b',1,'Ticket_Queue_Handler']]], + ['_24params',['$params',['../classTicket__Queue.html#ad87a813bbfd9ce28b5f7d763f094e655',1,'Ticket_Queue']]], + ['_24patch_5fversion',['$patch_version',['../classTicket__Info.html#a0b167808c021482f66ace1107780bd39',1,'Ticket_Info']]], + ['_24pdo',['$PDO',['../classDBLayer.html#a0019ba4102cd0e107fbe91bd5cc43425',1,'DBLayer']]], + ['_24permission',['$permission',['../classTicket__User.html#a2a3f7ae998f3926ab666167c9f63bcfc',1,'Ticket_User']]], + ['_24priority',['$priority',['../classTicket.html#a0ccc2e11515a6a15e46a0fcba94d0d79',1,'Ticket']]], + ['_24processor',['$processor',['../classTicket__Info.html#acd56a987584494704425a36bbfbf0fc5',1,'Ticket_Info']]], + ['_24query',['$query',['../classQuerycache.html#a6be0975d09b713fdacc2e32330d30c74',1,'Querycache\$query()'],['../classTicket__Log.html#a7b8691897e69a12536fff2c3f3c29ae8',1,'Ticket_Log\$query()'],['../classTicket__Queue.html#a83d238bbb14b4ca80ff563b8d1992355',1,'Ticket_Queue\$query()']]], + ['_24queue',['$queue',['../classTicket.html#a5ad011635a520888002255901cb7a2a5',1,'Ticket\$queue()'],['../classTicket__Queue__Handler.html#ae9d10b4bfc09d7cd09a2609f3317686f',1,'Ticket_Queue_Handler\$queue()']]], + ['_24receivemail',['$receiveMail',['../classWebUsers.html#a7186a146bb276d51181b9e62ad0ed8c5',1,'WebUsers']]], + ['_24server_5ftick',['$server_tick',['../classTicket__Info.html#a7627ce03ac1afd90cf86b94ab48fbd9c',1,'Ticket_Info']]], + ['_24sgroupid',['$sGroupId',['../classSupport__Group.html#a26af3960be749dfdbd9d677391d7f3c3',1,'Support_Group']]], + ['_24shardid',['$shardid',['../classTicket__Info.html#a44cd7539051540e36402b7d368e67fd3',1,'Ticket_Info']]], + ['_24sid',['$SID',['../classQuerycache.html#a30393f88b13bfd390c76cc94454b123e',1,'Querycache']]], + ['_24status',['$status',['../classTicket.html#a4e6917333bc2f16a9c12a44b1d12335e',1,'Ticket']]], + ['_24tag',['$tag',['../classSupport__Group.html#aa93b8392d651b40fd208bd65f71d55cc',1,'Support_Group']]], + ['_24tcategoryid',['$tCategoryId',['../classTicket__Category.html#a2928ce0f2387b21b42bc6ede4a3f0e6a',1,'Ticket_Category']]], + ['_24tcontentid',['$tContentId',['../classTicket__Content.html#a0865e27754d94bf8e036ae0539e84c33',1,'Ticket_Content']]], + ['_24ticket',['$ticket',['../classAssigned.html#a4e729931e02c5ea56933dd18d5cdfc25',1,'Assigned\$ticket()'],['../classForwarded.html#ad5345ede5ff6a41143a382ad0102958e',1,'Forwarded\$ticket()'],['../classTicket__Info.html#a6a88dbc8a703a1c493d33f17360e9d79',1,'Ticket_Info\$ticket()'],['../classTicket__Log.html#ae7c23a6f7c0c9f083a3e01f3bcedf2b3',1,'Ticket_Log\$ticket()'],['../classTicket__Reply.html#ab09b437216a7b0641a6da2b455218a89',1,'Ticket_Reply\$ticket()']]], + ['_24ticket_5fcategory',['$ticket_category',['../classTicket.html#abe4fc08f405ef045048cebaf16a3604d',1,'Ticket']]], + ['_24tid',['$tId',['../classTicket.html#a8d2c0138844cb7225dcd6b7403d9a001',1,'Ticket']]], + ['_24timestamp',['$timestamp',['../classTicket.html#ac52a47a12cfbe87a9384a600eade0440',1,'Ticket\$timestamp()'],['../classTicket__Log.html#a89151c1a9a3cd99c14cceec07105db15',1,'Ticket_Log\$timestamp()'],['../classTicket__Reply.html#a747f6ed39e4343974a97b58d2439d1d9',1,'Ticket_Reply\$timestamp()']]], + ['_24tinfoid',['$tInfoId',['../classTicket__Info.html#ab324ecd00efe8324a0891fabff6161ae',1,'Ticket_Info']]], + ['_24title',['$title',['../classTicket.html#a49401c28cce1972efbe9208266e71dd3',1,'Ticket']]], + ['_24tlogid',['$tLogId',['../classTicket__Log.html#af6d2b7abef8e6f4ff14fefd02a5cbe11',1,'Ticket_Log']]], + ['_24treplyid',['$tReplyId',['../classTicket__Reply.html#a3147d06712c721703250e3435447467f',1,'Ticket_Reply']]], + ['_24tuserid',['$tUserId',['../classTicket__User.html#ab017c0eab7b6083b3de58c8388251b50',1,'Ticket_User']]], + ['_24type',['$type',['../classQuerycache.html#a299788dde0e69545f3e0199132f013d2',1,'Querycache']]], + ['_24uid',['$uId',['../classWebUsers.html#a4c118152530d9b7a1aab240392b504b8',1,'WebUsers']]], + ['_24user',['$user',['../classAssigned.html#a9d9ca239af74fc2671568461854310da',1,'Assigned\$user()'],['../classIn__Support__Group.html#a50e2869129d0542a657de987e21e8517',1,'In_Support_Group\$user()']]], + ['_24user_5fid',['$user_id',['../classTicket__Info.html#a3007474eed1000a0bb99058d1d27b506',1,'Ticket_Info']]], + ['_24user_5fposition',['$user_position',['../classTicket__Info.html#a11602c42f574f902fb1e43a064c2ef9b',1,'Ticket_Info']]], + ['_24view_5fposition',['$view_position',['../classTicket__Info.html#ad45f4c7aa17b628789c2c2c81bfaba52',1,'Ticket_Info']]] +]; diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_5f.html b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_5f.html new file mode 100644 index 000000000..1f27755ab --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_5f.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_5f.js b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_5f.js new file mode 100644 index 000000000..a91cf6dd4 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_5f.js @@ -0,0 +1,4 @@ +var searchData= +[ + ['_5f_5fconstruct',['__construct',['../classAssigned.html#a34db106389637f5136b822a48e363043',1,'Assigned\__construct()'],['../classDBLayer.html#ad1d7f86c8854cb42b51849de74fe2ad6',1,'DBLayer\__construct()'],['../classForwarded.html#a9650a496b9c85e29d543374185d7d602',1,'Forwarded\__construct()'],['../classIn__Support__Group.html#af4b764c40f52108c6ea90534d38c8bae',1,'In_Support_Group\__construct()'],['../classMyCrypt.html#a35146c564f37f0b0274dfa0c05d3ba13',1,'MyCrypt\__construct()'],['../classPagination.html#adf554474cd78134da39228c2fb973d9e',1,'Pagination\__construct()'],['../classQuerycache.html#a7ddcdec027091f50d8009adfc6b44ec4',1,'Querycache\__construct()'],['../classSupport__Group.html#acf48f29ae284344ca7513afd0678c434',1,'Support_Group\__construct()'],['../classTicket.html#a83aa76a51e7a7263f0d919500321daec',1,'Ticket\__construct()'],['../classTicket__Category.html#a6dfdb4328151d710388fd9171043ad85',1,'Ticket_Category\__construct()'],['../classTicket__Content.html#ae540c72e04d1722c1bf52339449761db',1,'Ticket_Content\__construct()'],['../classTicket__Info.html#af8021f1bfe6e51fa02aed2af129d7fbe',1,'Ticket_Info\__construct()'],['../classTicket__Log.html#a3f9b0887d0f4552a120f8df9e7765c0d',1,'Ticket_Log\__construct()'],['../classTicket__Queue__Handler.html#a0831f1e09fce035802be012a71bc6e88',1,'Ticket_Queue_Handler\__construct()'],['../classTicket__Reply.html#a1e4c273b8cee38d54452823a70875485',1,'Ticket_Reply\__construct()'],['../classTicket__User.html#a7581ea4400e1a8f0824598a1e0e038b3',1,'Ticket_User\__construct()'],['../classWebUsers.html#a3402f45e3a0f54f9756d1054afef0716',1,'WebUsers\__construct()']]] +]; diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_61.html b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_61.html new file mode 100644 index 000000000..a3164d553 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_61.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_61.js b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_61.js new file mode 100644 index 000000000..407ba6790 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_61.js @@ -0,0 +1,6 @@ +var searchData= +[ + ['addusertosupportgroup',['addUserToSupportGroup',['../classSupport__Group.html#adaa2d30acacac56032605b34e246b253',1,'Support_Group']]], + ['assigned',['Assigned',['../classAssigned.html',1,'']]], + ['assignticket',['assignTicket',['../classAssigned.html#ac493092d0fcd0e3b2b9145cc8cd03d8e',1,'Assigned\assignTicket()'],['../classTicket.html#ab2653e3c80e3582c38c6845e6add6fac',1,'Ticket\assignTicket()']]] +]; diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_63.html b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_63.html new file mode 100644 index 000000000..56b5ad1e9 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_63.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_63.js b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_63.js new file mode 100644 index 000000000..e896babc9 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_63.js @@ -0,0 +1,37 @@ +var searchData= +[ + ['change_5fpermission',['change_permission',['../classTicket__User.html#aa3123d101bc451c3d2911263e10a4464',1,'Ticket_User']]], + ['check_5fchange_5fpassword',['check_change_password',['../classUsers.html#ad6da3241c4a12f4e81729ab830737e3c',1,'Users']]], + ['check_5fif_5fgame_5fclient',['check_if_game_client',['../classHelpers.html#a828e3e0cf73e98beb9ace8f5277e5f29',1,'Helpers']]], + ['check_5flogin_5fingame',['check_login_ingame',['../classHelpers.html#a0d0ad4ad22288b9aa225b7d71534fc33',1,'Helpers']]], + ['check_5fmethods',['check_methods',['../classMyCrypt.html#a20dcd7883eb21d0f6f62175891c9e51c',1,'MyCrypt']]], + ['check_5fregister',['check_Register',['../classUsers.html#a6f124a6e3b8d117604e707601a6b4876',1,'Users']]], + ['checkemail',['checkEmail',['../classUsers.html#a1ccd65e0ad68710b6176b381fe062a18',1,'Users']]], + ['checkemailexists',['checkEmailExists',['../classUsers.html#ab48b703be38135a0478aed6674a30fb0',1,'Users\checkEmailExists()'],['../classWebUsers.html#a2874604f0ac49c770bbbffef342466ff',1,'WebUsers\checkEmailExists()']]], + ['checkloginmatch',['checkLoginMatch',['../classUsers.html#a230235f569309159fb4b2b03956ebbd5',1,'Users\checkLoginMatch()'],['../classWebUsers.html#a488f20463bfac01871810fa5f7461e10',1,'WebUsers\checkLoginMatch()']]], + ['checkpassword',['checkPassword',['../classUsers.html#a7c61019e954d60fa38fe0c20554fa53d',1,'Users']]], + ['checkuser',['checkUser',['../classUsers.html#ac078497a378d4bedb8bab22dcc362b7c',1,'Users']]], + ['checkusernameexists',['checkUserNameExists',['../classUsers.html#a809c817e5f9d61f80b6c07aaf3882e2d',1,'Users\checkUserNameExists()'],['../classWebUsers.html#a717ecd295ccfcf9dfa9a9d6d84b97d3b',1,'WebUsers\checkUserNameExists()']]], + ['confirmpassword',['confirmPassword',['../classUsers.html#a7bbf5cef9e09e9dc49d7811d3012da86',1,'Users']]], + ['constr_5fexternid',['constr_ExternId',['../classTicket__User.html#ab61b472135a3b4cc4c24481de6bd90cc',1,'Ticket_User']]], + ['constr_5fsgroupid',['constr_SGroupId',['../classSupport__Group.html#a0522ca36e298ba573c2b6acf0a71f535',1,'Support_Group']]], + ['constr_5ftcategoryid',['constr_TCategoryId',['../classTicket__Category.html#a9069b59fd0dfc8e92e01c8a3c19c6f35',1,'Ticket_Category']]], + ['constr_5ftcontentid',['constr_TContentId',['../classTicket__Content.html#aadfcf86b2b9d7b1d802382786dca339d',1,'Ticket_Content']]], + ['constr_5ftlogid',['constr_TLogId',['../classTicket__Log.html#a6a67397d349e8f08bef67f4c7a330c44',1,'Ticket_Log']]], + ['constr_5ftreplyid',['constr_TReplyId',['../classTicket__Reply.html#aaf594222ac722101e691a612f0b3f26e',1,'Ticket_Reply']]], + ['constr_5ftuserid',['constr_TUserId',['../classTicket__User.html#a43c21d6bdccb68a79677eb41291b3082',1,'Ticket_User']]], + ['create',['create',['../classAssigned.html#a1b0669bd661f60f9984d9c623102f4c0',1,'Assigned\create()'],['../classForwarded.html#abf271dbbb38d1be6df0a2e0fd0345c0c',1,'Forwarded\create()'],['../classIn__Support__Group.html#a19eb31ee240ca0d78f70363dae4555b9',1,'In_Support_Group\create()'],['../classSupport__Group.html#a229aeddc62ba4a03d4c84479eae97248',1,'Support_Group\create()'],['../classTicket.html#a16156b509b1da77e551c64809e0187a4',1,'Ticket\create()'],['../classTicket__Content.html#ac7d5c5f92d1d5c38b04842fd5b7acbbb',1,'Ticket_Content\create()'],['../classTicket__Info.html#a55f6b3b365cdcbf9b6476c9aa296a759',1,'Ticket_Info\create()'],['../classTicket__Reply.html#a20f54f6ea88a5cee51516a1237dd12ef',1,'Ticket_Reply\create()']]], + ['create_5ffolders',['create_folders',['../classHelpers.html#a121768ce7285cdfd011d461ec359ac18',1,'Helpers']]], + ['create_5fticket',['create_Ticket',['../classTicket.html#a3892155f3a6dd43880e2d8fab128b59c',1,'Ticket']]], + ['create_5fticket_5finfo',['create_Ticket_Info',['../classTicket__Info.html#a5b2ece9189baf4f6dd010baee340e2ac',1,'Ticket_Info']]], + ['createlogentry',['createLogEntry',['../classTicket__Log.html#a6f51a7e188cb3579be9d28ea003c78f1',1,'Ticket_Log']]], + ['createpermissions',['createPermissions',['../classUsers.html#a5879a0f4b8da3e91af153cd19ca6e548',1,'Users']]], + ['createqueue',['createQueue',['../classTicket__Queue.html#a7b9b007668bc769c605e22075630b6dd',1,'Ticket_Queue\createQueue()'],['../classTicket__Queue__Handler.html#acd3f4a706276cd12a5f105b098bcc274',1,'Ticket_Queue_Handler\createQueue()']]], + ['createreply',['createReply',['../classTicket.html#acd9596d84d5fd0b68226269d6eff9cbb',1,'Ticket\createReply()'],['../classTicket__Reply.html#ab86fae04d57eb33689793e132b95014b',1,'Ticket_Reply\createReply()']]], + ['createsupportgroup',['createSupportGroup',['../classSupport__Group.html#a9b627241029d1821c7978309f77948f5',1,'Support_Group']]], + ['createticketcategory',['createTicketCategory',['../classTicket__Category.html#a9229f3d3e6479058d094aedd7ecaeaef',1,'Ticket_Category']]], + ['createticketuser',['createTicketUser',['../classTicket__User.html#a531f0c78a90cb284e81bc1a9651d808d',1,'Ticket_User']]], + ['createuser',['createUser',['../classUsers.html#a110c60194f014cf45b93bde09b40a565',1,'Users']]], + ['createwebuser',['createWebuser',['../classWebUsers.html#abb1b5f937d609c7bbf02710efdc49115',1,'WebUsers']]], + ['cron',['cron',['../classMail__Handler.html#a228e3c16f470e35a68a6c639bfb08803',1,'Mail_Handler']]] +]; diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_64.html b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_64.html new file mode 100644 index 000000000..b53ff083e --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_64.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_64.js b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_64.js new file mode 100644 index 000000000..c20a27a6f --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_64.js @@ -0,0 +1,9 @@ +var searchData= +[ + ['dblayer',['DBLayer',['../classDBLayer.html',1,'']]], + ['decode_5futf8',['decode_utf8',['../classMail__Handler.html#a0941972d8f81caba60261e743d4d4567',1,'Mail_Handler']]], + ['decrypt',['decrypt',['../classMyCrypt.html#a8833cd07b177eaac011d154ad46991c4',1,'MyCrypt']]], + ['delete',['delete',['../classAssigned.html#adf0e3ee5ea2c0536d4d59408868136c8',1,'Assigned\delete()'],['../classForwarded.html#aa4aef6d29496ca0a802bf2398d7e6192',1,'Forwarded\delete()'],['../classIn__Support__Group.html#a14b353623a7af41c0e801d79c7c5f9c0',1,'In_Support_Group\delete()'],['../classSupport__Group.html#ad31e17d6eeaf862da0589dd010a0f794',1,'Support_Group\delete()']]], + ['deletesupportgroup',['deleteSupportGroup',['../classSupport__Group.html#a0aa6d0164c2e5a0fddcbf062c4f2ae7f',1,'Support_Group']]], + ['deleteuserofsupportgroup',['deleteUserOfSupportGroup',['../classSupport__Group.html#a8c7ee9fb4786198c4c53e599d77f5352',1,'Support_Group']]] +]; diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_65.html b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_65.html new file mode 100644 index 000000000..66cc83487 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_65.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_65.js b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_65.js new file mode 100644 index 000000000..de729509e --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_65.js @@ -0,0 +1,7 @@ +var searchData= +[ + ['encrypt',['encrypt',['../classMyCrypt.html#a72a803d99bc6687a64d0d80043a7516c',1,'MyCrypt']]], + ['execute',['execute',['../classDBLayer.html#ad3ebb9fd7b55f2f597bb20e12602d6f4',1,'DBLayer']]], + ['executereturnid',['executeReturnId',['../classDBLayer.html#a82c63e5aa7205b3f54a4766427ab51f9',1,'DBLayer']]], + ['executewithoutparams',['executeWithoutParams',['../classDBLayer.html#a6831a10286b2f8795bb27dcea4b6edfe',1,'DBLayer']]] +]; diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_66.html b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_66.html new file mode 100644 index 000000000..3d1f8b35e --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_66.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_66.js b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_66.js new file mode 100644 index 000000000..bfb311a59 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_66.js @@ -0,0 +1,5 @@ +var searchData= +[ + ['forwarded',['Forwarded',['../classForwarded.html',1,'']]], + ['forwardticket',['forwardTicket',['../classForwarded.html#a6e65be335e132dc4a2b06abc4815d730',1,'Forwarded\forwardTicket()'],['../classTicket.html#ada6fa74d4a6b5ce9d9e55b8f7398beb9',1,'Ticket\forwardTicket()']]] +]; diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_67.html b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_67.html new file mode 100644 index 000000000..41a459ae7 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_67.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_67.js b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_67.js new file mode 100644 index 000000000..f3bdc1ae2 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_67.js @@ -0,0 +1,105 @@ +var searchData= +[ + ['generatesalt',['generateSALT',['../classUsers.html#ad9b1ccad9f285f5e3554003b17884482',1,'Users']]], + ['get_5femail_5fby_5fuser_5fid',['get_email_by_user_id',['../classTicket__User.html#ac9233453c39a9193993baa30711f1856',1,'Ticket_User']]], + ['get_5fid_5ffrom_5femail',['get_id_from_email',['../classTicket__User.html#ad74a6cb7ac2f021ff70cf8bcb43b1cba',1,'Ticket_User']]], + ['get_5fid_5ffrom_5fusername',['get_id_from_username',['../classTicket__User.html#a7e8ec955186676aff3f3b7757841962c',1,'Ticket_User']]], + ['get_5fmime_5ftype',['get_mime_type',['../classMail__Handler.html#ae4fbc4bc50da356e611e50b9ef77a52e',1,'Mail_Handler']]], + ['get_5fticket_5fid_5ffrom_5fsubject',['get_ticket_id_from_subject',['../classMail__Handler.html#a642b3996a7b826cdf84f057cc6813458',1,'Mail_Handler']]], + ['get_5fusername_5ffrom_5fid',['get_username_from_id',['../classTicket__User.html#a550d9a436e4820854d7fd3d998a2cb7c',1,'Ticket_User']]], + ['getaction',['getAction',['../classTicket__Log.html#ae74bffd2a4d675a9d693a63d893d9bd3',1,'Ticket_Log']]], + ['getactiontextarray',['getActionTextArray',['../classTicket__Log.html#a0a4484014b124129e99929358be47e10',1,'Ticket_Log']]], + ['getallcategories',['getAllCategories',['../classTicket__Category.html#ad0ae199cc88bb083768f37515e0a143b',1,'Ticket_Category']]], + ['getalllogs',['getAllLogs',['../classTicket__Log.html#a7111b8cf64ff9c9a81572cd68718564b',1,'Ticket_Log']]], + ['getallsupportgroups',['getAllSupportGroups',['../classSupport__Group.html#a3b78af4839ac313f8f056e0cd86c7d4f',1,'Support_Group']]], + ['getallusersofsupportgroup',['getAllUsersOfSupportGroup',['../classSupport__Group.html#ad18e4bb9f7988fe92631e2ec2f53ab95',1,'Support_Group']]], + ['getallusersquery',['getAllUsersQuery',['../classWebUsers.html#ab7901cc22a26dcff53de5257579bb7ea',1,'WebUsers']]], + ['getamountofrows',['getAmountOfRows',['../classPagination.html#a9afee51d409525ebef762b4b7adfebde',1,'Pagination']]], + ['getargument',['getArgument',['../classTicket__Log.html#ac40db2b8ff140a476c9345c119b64f53',1,'Ticket_Log']]], + ['getassigned',['getAssigned',['../classTicket.html#a60fa0babd57b4fd86a2c1a466f2f0d67',1,'Ticket']]], + ['getauthor',['getAuthor',['../classTicket.html#aa93ff96cd8be6ef2b9b27554ec344e53',1,'Ticket\getAuthor()'],['../classTicket__Log.html#ad5995445284657a1041be811e29d9c7d',1,'Ticket_Log\getAuthor()'],['../classTicket__Reply.html#a291a3e0eeff0564433c47eccc0ec7d04',1,'Ticket_Reply\getAuthor()']]], + ['getcategoryname',['getCategoryName',['../classTicket.html#ab151edff1be8fe3f2e80227d49043493',1,'Ticket']]], + ['getclient_5fversion',['getClient_Version',['../classTicket__Info.html#a0cd57bdd61693a1a4e871470991f3b32',1,'Ticket_Info']]], + ['getconnect_5fstate',['getConnect_State',['../classTicket__Info.html#a7e4012fe943044af9bc544015812a81e',1,'Ticket_Info']]], + ['getcontent',['getContent',['../classTicket__Content.html#ac18a21f04565b77901a25091b470a065',1,'Ticket_Content\getContent()'],['../classTicket__Reply.html#a5097d916b201ae089d7ab206fa9debec',1,'Ticket_Reply\getContent()']]], + ['getcpu_5fmask',['getCPU_Mask',['../classTicket__Info.html#a0954d53be4c289c7adbbb93e1f136b8e',1,'Ticket_Info']]], + ['getcpuid',['getCPUId',['../classTicket__Info.html#aa98fbe419f73988551505510cf941ce5',1,'Ticket_Info']]], + ['getcurrent',['getCurrent',['../classPagination.html#afd1f1ab1a73dc5b4154d6bda5b42abfc',1,'Pagination']]], + ['getdb',['getDb',['../classQuerycache.html#ab7d8e4258fc22338afe76c5ca0487588',1,'Querycache']]], + ['getelements',['getElements',['../classPagination.html#a76ed587e80929eeba79fcbac4944179a',1,'Pagination']]], + ['getemail',['getEmail',['../classWebUsers.html#a8c782c9dfcacbebb2deca789930849f3',1,'WebUsers']]], + ['getentireticket',['getEntireTicket',['../classTicket.html#aab16857c573bbc275dbc47b5a193ef7a',1,'Ticket']]], + ['getexternid',['getExternId',['../classTicket__User.html#a45675149ecaa6bbabd8673823437b1ea',1,'Ticket_User']]], + ['getforwardedgroupid',['getForwardedGroupId',['../classTicket.html#a0b95696da230b5a73d066e0e319eec3a',1,'Ticket']]], + ['getforwardedgroupname',['getForwardedGroupName',['../classTicket.html#a33f1ce4b9efbd4a86af3253ceaab8bf6',1,'Ticket']]], + ['getgroup',['getGroup',['../classForwarded.html#a6269c9b2300e371234ed45b622125be5',1,'Forwarded\getGroup()'],['../classIn__Support__Group.html#a9682d3369161a216dd24695f5348d0e0',1,'In_Support_Group\getGroup()'],['../classSupport__Group.html#a3015fdd2bc168dc716606f7726edaa7b',1,'Support_Group\getGroup()']]], + ['getgroupemail',['getGroupEmail',['../classSupport__Group.html#a7d241e89ecac8a3758d6ebef98785d55',1,'Support_Group']]], + ['getgroups',['getGroups',['../classSupport__Group.html#a7dcfecaf50ff3eb16fb715cfacc75987',1,'Support_Group']]], + ['gethidden',['getHidden',['../classTicket__Reply.html#a8d30cdde15a466114bf3ea42a5fba693',1,'Ticket_Reply']]], + ['getht',['getHT',['../classTicket__Info.html#ad331265bf90315c82e6879bf4636179f',1,'Ticket_Info']]], + ['getid',['getId',['../classWebUsers.html#adfdf81197e5dd57e151e2faaa0f83480',1,'WebUsers']]], + ['getidfromemail',['getIdFromEmail',['../classWebUsers.html#a3d0ec30b9d7320e0a892a0e8f07ce243',1,'WebUsers']]], + ['getimap_5fmailserver',['getIMAP_MailServer',['../classSupport__Group.html#a5e9b8df4b4da25de13c0b025ad231ae5',1,'Support_Group']]], + ['getimap_5fpassword',['getIMAP_Password',['../classSupport__Group.html#a9cbd297595e51767e965f9e37dba6536',1,'Support_Group']]], + ['getimap_5fusername',['getIMAP_Username',['../classSupport__Group.html#a15a30346429128d56d499e2afa185ca5',1,'Support_Group']]], + ['getinfo',['getInfo',['../classWebUsers.html#a323844dc355f89ee9c81254cb15f8bd4',1,'WebUsers']]], + ['getlanguage',['getLanguage',['../classWebUsers.html#ac2f2d5c0335ecdd177f9db54150bcb2b',1,'WebUsers']]], + ['getlast',['getLast',['../classPagination.html#af4f4279390b6ef62039fe64797db54f5',1,'Pagination']]], + ['getlatestreply',['getLatestReply',['../classTicket.html#a36ca4b3d45286b8eca1fae7678c1c62c',1,'Ticket']]], + ['getlinks',['getLinks',['../classPagination.html#abf3dd22d43688359aea77b3086f4a9ec',1,'Pagination']]], + ['getlocal_5faddress',['getLocal_Address',['../classTicket__Info.html#a69279ebba47904cca0a9045a1a0c4200',1,'Ticket_Info']]], + ['getlogsofticket',['getLogsOfTicket',['../classTicket__Log.html#ae5acfd872f759c4d06caf30bde61aab6',1,'Ticket_Log']]], + ['getmemory',['getMemory',['../classTicket__Info.html#a324025a0efb29aa53df9d9dd5c407397',1,'Ticket_Info']]], + ['getmodsandadmins',['getModsAndAdmins',['../classTicket__User.html#af988917dc55b56b71764e0bb79075e97',1,'Ticket_User']]], + ['getname',['getName',['../classSupport__Group.html#acc94d47e83f484aba760719077a3ce0c',1,'Support_Group\getName()'],['../classTicket__Category.html#a04cf7fa795b9d23b5b0f2664b4bdcc1c',1,'Ticket_Category\getName()']]], + ['getnel3d',['getNel3D',['../classTicket__Info.html#ae00e5a2d95f040bbb9ef23e45517e056',1,'Ticket_Info']]], + ['getnewestticket',['getNewestTicket',['../classTicket__Queue__Handler.html#a6a130b06a4cbbc10e5a19431456ae080',1,'Ticket_Queue_Handler']]], + ['getnroftickets',['getNrOfTickets',['../classTicket__Queue__Handler.html#acbee619eecfb9a81769f4389fa48ff51',1,'Ticket_Queue_Handler']]], + ['getnrofticketsassignedwaiting',['getNrOfTicketsAssignedWaiting',['../classTicket__Queue__Handler.html#aaa4b015079a5b37ede9ec03f63f203e4',1,'Ticket_Queue_Handler']]], + ['getnrofticketstodo',['getNrOfTicketsToDo',['../classTicket__Queue__Handler.html#a1623e3f03e28508e76fc148b936bdfda',1,'Ticket_Queue_Handler']]], + ['getos',['getOS',['../classTicket__Info.html#a733b4a44369971889dcac14408235b9c',1,'Ticket_Info']]], + ['getpagination',['getPagination',['../classTicket__Queue__Handler.html#a50a09229d462401177d1cd532853d358',1,'Ticket_Queue_Handler']]], + ['getparams',['getParams',['../classTicket__Queue.html#a0e2af91baf1bf2de6d564c2e6616e64c',1,'Ticket_Queue']]], + ['getpatch_5fversion',['getPatch_Version',['../classTicket__Info.html#ac81ff5e867efc8d198d833cf5418f047',1,'Ticket_Info']]], + ['getpermission',['getPermission',['../classTicket__User.html#a3f28fd031679b16d4a80653754f179eb',1,'Ticket_User']]], + ['getpriority',['getPriority',['../classTicket.html#a50f01c32532cb06451c2d3151e641287',1,'Ticket']]], + ['getpriorityarray',['getPriorityArray',['../classTicket.html#ab309a3fa2ac23bd6847bbf48cfc271d2',1,'Ticket']]], + ['getprioritytext',['getPriorityText',['../classTicket.html#ab7c6c081ea47c3fe8e2d06ec84d01595',1,'Ticket']]], + ['getprocessor',['getProcessor',['../classTicket__Info.html#aeae8a9bbf14e5c204242d82b1da7fca8',1,'Ticket_Info']]], + ['getquery',['getQuery',['../classQuerycache.html#a75b6539cc93eed689f4c34cf3140684f',1,'Querycache\getQuery()'],['../classTicket__Log.html#a53c652f4677d734ea8a4dcab512cd536',1,'Ticket_Log\getQuery()'],['../classTicket__Queue.html#a7c6eef6c98cebf9aec82a83027afef13',1,'Ticket_Queue\getQuery()']]], + ['getqueue',['getQueue',['../classTicket.html#aa658a033e7c11f65f6e30e8d8c23d5c7',1,'Ticket']]], + ['getreceivemail',['getReceiveMail',['../classWebUsers.html#a46aa8f355529e73c680e2e85f6fb8d75',1,'WebUsers']]], + ['getrepliesofticket',['getRepliesOfTicket',['../classTicket__Reply.html#ab579ada18583a68ac5b1b819e267ba7d',1,'Ticket_Reply']]], + ['getserver_5ftick',['getServer_Tick',['../classTicket__Info.html#af97b266fb8ac44892151b4ab71b96325',1,'Ticket_Info']]], + ['getsgroupid',['getSGroupId',['../classSupport__Group.html#a76e436109e6907bf69d2614323ede0b7',1,'Support_Group']]], + ['getsgroupofticket',['getSGroupOfTicket',['../classForwarded.html#a98de9698c91fbc552eba480f04c05db3',1,'Forwarded']]], + ['getshardid',['getShardId',['../classTicket__Info.html#a72e6978e622978fc526620df10c89b34',1,'Ticket_Info']]], + ['getsid',['getSID',['../classQuerycache.html#ad090d4d9a422540e8f93d1480d811c57',1,'Querycache']]], + ['getstatus',['getStatus',['../classTicket.html#af09548afeefddb6c2c258eb33a4cb5ca',1,'Ticket']]], + ['getstatusarray',['getStatusArray',['../classTicket.html#a58b91ffd0eed86af12ef8f418edb4199',1,'Ticket']]], + ['getstatustext',['getStatusText',['../classTicket.html#a620f54bbc80d257dafec2d4c33019d1a',1,'Ticket']]], + ['gettag',['getTag',['../classSupport__Group.html#ac0b76c1838dc23be75cab548625e9cd6',1,'Support_Group']]], + ['gettcategoryid',['getTCategoryId',['../classTicket__Category.html#ac1b758198fb7edcebf962b317167022e',1,'Ticket_Category']]], + ['gettcontentid',['getTContentId',['../classTicket__Content.html#ab233080cda7a46b84cd7993ad0f50d2c',1,'Ticket_Content']]], + ['getticket',['getTicket',['../classAssigned.html#ac42feb1177792e8d37b3b92ad33065fb',1,'Assigned\getTicket()'],['../classForwarded.html#a842cd8941a2a80fe72eeb6086e88d9fa',1,'Forwarded\getTicket()'],['../classTicket__Info.html#a5f227ee6c93f65fb250e7dbed55585bd',1,'Ticket_Info\getTicket()'],['../classTicket__Log.html#ac6767e71178317c003e4b0b9e7aa2430',1,'Ticket_Log\getTicket()'],['../classTicket__Reply.html#a95edaa9ec8b0b0c44d2ffc08f9f489d0',1,'Ticket_Reply\getTicket()']]], + ['getticket_5fcategory',['getTicket_Category',['../classTicket.html#a152f10aff0c29e859fda6a94ed41de41',1,'Ticket']]], + ['gettickets',['getTickets',['../classTicket__Queue__Handler.html#a989ef733df2ecd57cbdf89364985d98a',1,'Ticket_Queue_Handler']]], + ['getticketsof',['getTicketsOf',['../classTicket.html#ad7f9ba34b522a1fce4a0c7e9b7849b34',1,'Ticket']]], + ['gettid',['getTId',['../classTicket.html#af899c5b8fce8abbc88691e37b02e47ef',1,'Ticket']]], + ['gettimestamp',['getTimestamp',['../classTicket.html#abe472e82afc5c7f7d289736ed8d81484',1,'Ticket\getTimestamp()'],['../classTicket__Log.html#ad75b52a665afe333ea5cb505bfb1bb16',1,'Ticket_Log\getTimestamp()'],['../classTicket__Reply.html#a2336fde89bfa1dcb11d2d5825d611f12',1,'Ticket_Reply\getTimestamp()']]], + ['gettinfoid',['getTInfoId',['../classTicket__Info.html#a5ca59186f94c97b71dae5e52c98bbfea',1,'Ticket_Info']]], + ['gettitle',['getTitle',['../classTicket.html#a488ce26f6e9a69de4d63883492b9d5b8',1,'Ticket']]], + ['gettlogid',['getTLogId',['../classTicket__Log.html#aeebe7f20e361f7cb7d48cfe40d32190c',1,'Ticket_Log']]], + ['gettreplyid',['getTReplyId',['../classTicket__Reply.html#a7a14d87b977fde3193837195a43e673e',1,'Ticket_Reply']]], + ['gettuserid',['getTUserId',['../classTicket__User.html#a4df262f17c5e543842e5f49db1970280',1,'Ticket_User']]], + ['gettype',['getType',['../classQuerycache.html#a3e833722f16d96f44c0d887f4e833bcf',1,'Querycache']]], + ['getuid',['getUId',['../classWebUsers.html#a38654984bdbf75178cd0277807d94bfe',1,'WebUsers']]], + ['getuser',['getUser',['../classAssigned.html#a3b04e63f83c1b064018159f6f6ef68f7',1,'Assigned\getUser()'],['../classIn__Support__Group.html#add8c8620030ac6d125bc7f5787d7e501',1,'In_Support_Group\getUser()']]], + ['getuser_5fid',['getUser_Id',['../classTicket__Info.html#a9c96394e5906ac48818eb9ce62836043',1,'Ticket_Info']]], + ['getuser_5fposition',['getUser_Position',['../classTicket__Info.html#a9330a31fc877932f5250ce83ea9b0568',1,'Ticket_Info']]], + ['getuserassignedtoticket',['getUserAssignedToTicket',['../classAssigned.html#a2a745eac7f9c3ea9ce80d38c1fdc11a7',1,'Assigned']]], + ['getusername',['getUsername',['../classWebUsers.html#a6f9bc7fdf77969e857dcfd5c1f8fd9bc',1,'WebUsers']]], + ['getusers',['getUsers',['../classWebUsers.html#a9c773c1cc57aaf3429b11fd127b01a70',1,'WebUsers']]], + ['getview_5fposition',['getView_Position',['../classTicket__Info.html#a72e470ecde4faa28cff2748e7a51a98f',1,'Ticket_Info']]], + ['gui_5felements',['Gui_Elements',['../classGui__Elements.html',1,'']]] +]; diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_68.html b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_68.html new file mode 100644 index 000000000..6df909782 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_68.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_68.js b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_68.js new file mode 100644 index 000000000..6ed1ebfbd --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_68.js @@ -0,0 +1,7 @@ +var searchData= +[ + ['handle_5flanguage',['handle_language',['../classHelpers.html#a42edc35c0057da2769d5b5ede1da0001',1,'Helpers']]], + ['hashiv',['hashIV',['../classMyCrypt.html#a338bfce76aec4f14c73af16e7c0b6fe6',1,'MyCrypt']]], + ['hasinfo',['hasInfo',['../classTicket.html#ab5b11fd2cf66eac9f6b148795c021dee',1,'Ticket']]], + ['helpers',['Helpers',['../classHelpers.html',1,'']]] +]; diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_69.html b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_69.html new file mode 100644 index 000000000..1a00b554d --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_69.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_69.js b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_69.js new file mode 100644 index 000000000..48b7bf27f --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_69.js @@ -0,0 +1,10 @@ +var searchData= +[ + ['in_5fsupport_5fgroup',['In_Support_Group',['../classIn__Support__Group.html',1,'']]], + ['incoming_5fmail_5fhandler',['incoming_mail_handler',['../classMail__Handler.html#a4cee2c20a8c05b71647ef4762879f0a4',1,'Mail_Handler']]], + ['isadmin',['isAdmin',['../classTicket__User.html#a30a17880b9ad06c7d5c3e7a8f2fb5d9c',1,'Ticket_User']]], + ['isassigned',['isAssigned',['../classAssigned.html#a41debee6bc7d4974204cdafd51e33c88',1,'Assigned']]], + ['isforwarded',['isForwarded',['../classForwarded.html#a4d0bd0223c5655e8232a17e53a03cc80',1,'Forwarded']]], + ['isloggedin',['isLoggedIn',['../classWebUsers.html#a46665dd9dba9bd764837aad1bdec3774',1,'WebUsers']]], + ['ismod',['isMod',['../classTicket__User.html#ab0badcae4b662e589f2eab4124a2adf7',1,'Ticket_User']]] +]; diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_6c.html b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_6c.html new file mode 100644 index 000000000..f6383cc22 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_6c.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_6c.js b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_6c.js new file mode 100644 index 000000000..dcf4465c6 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_6c.js @@ -0,0 +1,21 @@ +var searchData= +[ + ['load',['load',['../classAssigned.html#aa3f2ea4abe6e71c19373de8df7481cfb',1,'Assigned\load()'],['../classForwarded.html#a9ef9ba4dff566a32fb08fe1bb3e36740',1,'Forwarded\load()']]], + ['load_5fwith_5fsgroupid',['load_With_SGroupId',['../classSupport__Group.html#aa7dcaff3a34942cbfa74fdda31e37620',1,'Support_Group']]], + ['load_5fwith_5fsid',['load_With_SID',['../classQuerycache.html#a579edcc576b076f884884cf509436768',1,'Querycache']]], + ['load_5fwith_5ftcategoryid',['load_With_TCategoryId',['../classTicket__Category.html#a66f5c7cb775b14bfa5ffc9467a63970f',1,'Ticket_Category']]], + ['load_5fwith_5ftcontentid',['load_With_TContentId',['../classTicket__Content.html#a3fe4c1b84b3751c8d8afe82eeb46155d',1,'Ticket_Content']]], + ['load_5fwith_5fticket',['load_With_Ticket',['../classTicket__Info.html#a745aad9aceb76cc81222a458e8eba99d',1,'Ticket_Info']]], + ['load_5fwith_5ftid',['load_With_TId',['../classTicket.html#a015b73c7de955bbf6991f1345d91eecf',1,'Ticket']]], + ['load_5fwith_5ftinfoid',['load_With_TInfoId',['../classTicket__Info.html#a7d2ea8863661e2e492b2a31c3b619139',1,'Ticket_Info']]], + ['load_5fwith_5ftlogid',['load_With_TLogId',['../classTicket__Log.html#a56d7a9f19d4913489aef19c279c0e076',1,'Ticket_Log']]], + ['load_5fwith_5ftreplyid',['load_With_TReplyId',['../classTicket__Reply.html#a262662361c75e7d12b148058f340ecf4',1,'Ticket_Reply']]], + ['load_5fwith_5ftuserid',['load_With_TUserId',['../classTicket__User.html#a982f1e0aa98f3abb6451772ceac74708',1,'Ticket_User']]], + ['loadallclosedtickets',['loadAllClosedTickets',['../classTicket__Queue.html#a328adadd5c9481a87ff712b2830a8519',1,'Ticket_Queue']]], + ['loadallnotassignedtickets',['loadAllNotAssignedTickets',['../classTicket__Queue.html#a5760df002c7ad36b92479754835058e9',1,'Ticket_Queue']]], + ['loadallopentickets',['loadAllOpenTickets',['../classTicket__Queue.html#a93e3b5126f01a9b14dce1d89bacc73b6',1,'Ticket_Queue']]], + ['loadalltickets',['loadAllTickets',['../classTicket__Queue.html#a0053185e73f37195adf22051cc7d87f8',1,'Ticket_Queue']]], + ['loadassignedandwaiting',['loadAssignedandWaiting',['../classTicket__Queue.html#a309b4c6c5423eecd6c8f57af7f60aee2',1,'Ticket_Queue']]], + ['loadtemplate',['loadTemplate',['../classHelpers.html#ad70beebe7bd2f0909dc882f6d381cbb7',1,'Helpers']]], + ['loadtodotickets',['loadToDoTickets',['../classTicket__Queue.html#ac35ffa886f20979b41a70b446bf7e6b2',1,'Ticket_Queue']]] +]; diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_6d.html b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_6d.html new file mode 100644 index 000000000..2e27d4d64 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_6d.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_6d.js b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_6d.js new file mode 100644 index 000000000..81a33e7be --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_6d.js @@ -0,0 +1,8 @@ +var searchData= +[ + ['mail_5ffork',['mail_fork',['../classMail__Handler.html#a285306bacd7e6774c5737c91acdf8ba4',1,'Mail_Handler']]], + ['mail_5fhandler',['Mail_Handler',['../classMail__Handler.html',1,'']]], + ['make_5ftable',['make_table',['../classGui__Elements.html#a1636b9683d6e0649e95fd7c09d4bf8b7',1,'Gui_Elements']]], + ['make_5ftable_5fwith_5fkey_5fis_5fid',['make_table_with_key_is_id',['../classGui__Elements.html#a4ffe8dfe08c927ae43ea70c742a811f1',1,'Gui_Elements']]], + ['mycrypt',['MyCrypt',['../classMyCrypt.html',1,'']]] +]; diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_6e.html b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_6e.html new file mode 100644 index 000000000..1f92ee5b6 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_6e.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_6e.js b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_6e.js new file mode 100644 index 000000000..76ad6866c --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_6e.js @@ -0,0 +1,4 @@ +var searchData= +[ + ['new_5fmessage_5fid',['new_message_id',['../classMail__Handler.html#aec330ff71f834079a89cb80ba9cd079c',1,'Mail_Handler']]] +]; diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_6f.html b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_6f.html new file mode 100644 index 000000000..61827e82e --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_6f.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_6f.js b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_6f.js new file mode 100644 index 000000000..de6319927 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_6f.js @@ -0,0 +1,4 @@ +var searchData= +[ + ['outputtime',['outputTime',['../classHelpers.html#a61b01d13dc043d49115ee7f5c169ac73',1,'Helpers']]] +]; diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_70.html b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_70.html new file mode 100644 index 000000000..0340151b6 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_70.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_70.js b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_70.js new file mode 100644 index 000000000..157d7c6b6 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_70.js @@ -0,0 +1,4 @@ +var searchData= +[ + ['pagination',['Pagination',['../classPagination.html',1,'']]] +]; diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_71.html b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_71.html new file mode 100644 index 000000000..b4dc1e6ee --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_71.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_71.js b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_71.js new file mode 100644 index 000000000..e2ca060ac --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_71.js @@ -0,0 +1,4 @@ +var searchData= +[ + ['querycache',['Querycache',['../classQuerycache.html',1,'']]] +]; diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_73.html b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_73.html new file mode 100644 index 000000000..1ec8f174d --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_73.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_73.js b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_73.js new file mode 100644 index 000000000..e7f086be1 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_73.js @@ -0,0 +1,65 @@ +var searchData= +[ + ['send_5fmail',['send_mail',['../classMail__Handler.html#a39c146922e58ad8456fb73f979649263',1,'Mail_Handler']]], + ['send_5fticketing_5fmail',['send_ticketing_mail',['../classMail__Handler.html#ab94d83ed9e6875da971c416c1752cbef',1,'Mail_Handler']]], + ['set',['set',['../classAssigned.html#ad4d2eececfa3d1ad7b72c8594f1eec33',1,'Assigned\set()'],['../classForwarded.html#ab2dfe7ba68ff867277ce008027561b62',1,'Forwarded\set()'],['../classIn__Support__Group.html#af0c47b359920019e0195ad31519437ea',1,'In_Support_Group\set()'],['../classQuerycache.html#a0c711df493c72eb461241855f9f34c5f',1,'Querycache\set()'],['../classSupport__Group.html#aabac23ba7e6eb67c3d7df596d651ef4b',1,'Support_Group\set()'],['../classTicket.html#a1e87f173b2cba0916092865c842c1075',1,'Ticket\set()'],['../classTicket__Info.html#af89b82bb0e3a885141a83f19bdacad2f',1,'Ticket_Info\set()'],['../classTicket__Log.html#a5faab1d6b0e6aeca20b51dd80112e565',1,'Ticket_Log\set()'],['../classTicket__Reply.html#adeb42b039232a828740377f23f7c8971',1,'Ticket_Reply\set()'],['../classTicket__User.html#af038656737c0518eab287ef4098d37e7',1,'Ticket_User\set()'],['../classWebUsers.html#a69330e33d34bc1f667644b5417999c8d',1,'WebUsers\set()']]], + ['setamsemail',['setAmsEmail',['../classUsers.html#aa28b8a5cb27f4330acf692f02a83d39f',1,'Users']]], + ['setamspassword',['setAmsPassword',['../classUsers.html#a6373c269ad6747ca429fa128e7eaf20b',1,'Users']]], + ['setauthor',['setAuthor',['../classTicket.html#aa906eb0d2cd7afa3ff8ba0afbf3537c7',1,'Ticket\setAuthor()'],['../classTicket__Log.html#abbcf255fe8cb6f84c325c2175fa77c69',1,'Ticket_Log\setAuthor()'],['../classTicket__Reply.html#ad7cf8e84af7401e5a415413a024b548d',1,'Ticket_Reply\setAuthor()']]], + ['setclient_5fversion',['setClient_Version',['../classTicket__Info.html#ab74158b0e29307c9df5e0118fc5ecf1a',1,'Ticket_Info']]], + ['setconnect_5fstate',['setConnect_State',['../classTicket__Info.html#af4f6bb975f3f102c78e11d92778a3dec',1,'Ticket_Info']]], + ['setcontent',['setContent',['../classTicket__Content.html#ab98a55ea6a3ca4a703803f3f36b162cb',1,'Ticket_Content\setContent()'],['../classTicket__Reply.html#a33b259de2020850920dffb5b7074c2ac',1,'Ticket_Reply\setContent()']]], + ['setcpu_5fmask',['setCPU_Mask',['../classTicket__Info.html#a4abad809d5da7e1e2c56e5114db5f8ed',1,'Ticket_Info']]], + ['setcpuid',['setCPUId',['../classTicket__Info.html#a071770d06778a2a5cf7c4c504bb230e6',1,'Ticket_Info']]], + ['setdb',['setDb',['../classQuerycache.html#a26331885b240e1ac6cd3b8409f6787d2',1,'Querycache']]], + ['setemail',['setEmail',['../classWebUsers.html#a687c7b5c20d9d2e10ac807e396eeaeca',1,'WebUsers']]], + ['setexternid',['setExternId',['../classTicket__User.html#af62595ffa430126baa9cb965174486f0',1,'Ticket_User']]], + ['setgroup',['setGroup',['../classForwarded.html#ae79dfb98cc8785d28ef4acff4ea3ddf2',1,'Forwarded\setGroup()'],['../classIn__Support__Group.html#adf6c62710753773bb90123e4d0f1c7b0',1,'In_Support_Group\setGroup()']]], + ['setgroupemail',['setGroupEmail',['../classSupport__Group.html#aed4561e1e14e810adc990de873227169',1,'Support_Group']]], + ['sethidden',['setHidden',['../classTicket__Reply.html#aa1296a31aa315f39e6133880ba06bf0b',1,'Ticket_Reply']]], + ['setht',['setHT',['../classTicket__Info.html#aeeed22b3cee00d2362f34e7078cc592a',1,'Ticket_Info']]], + ['setimap_5fmailserver',['setIMAP_MailServer',['../classSupport__Group.html#a078c0ce4be642b530567131ffc2b065a',1,'Support_Group']]], + ['setimap_5fpassword',['setIMAP_Password',['../classSupport__Group.html#a9a5ce866df0f4cc2cabee22b4e3936a7',1,'Support_Group']]], + ['setimap_5fusername',['setIMAP_Username',['../classSupport__Group.html#ac0d79d7e6aa23ad743aafce8e772db4e',1,'Support_Group']]], + ['setlanguage',['setLanguage',['../classWebUsers.html#a44c8a46add7a525e8902a4e2a3a9f9d1',1,'WebUsers']]], + ['setlocal_5faddress',['setLocal_Address',['../classTicket__Info.html#ad05594084b8e941f0114cd4b55b9a365',1,'Ticket_Info']]], + ['setmemory',['setMemory',['../classTicket__Info.html#aef05e49c066d9eb9b110d228c3cf9585',1,'Ticket_Info']]], + ['setname',['setName',['../classSupport__Group.html#adc21361d8243caad9c2e50fbb6a96140',1,'Support_Group\setName()'],['../classTicket__Category.html#afa5aabc3304855425c3efd65de740012',1,'Ticket_Category\setName()']]], + ['setnel3d',['setNel3D',['../classTicket__Info.html#a849f9d8f9f9f99fb51721b015b31b62a',1,'Ticket_Info']]], + ['setos',['setOS',['../classTicket__Info.html#af7ada3491b01382ae95aa3615a6cb294',1,'Ticket_Info']]], + ['setpassword',['setPassword',['../classWebUsers.html#abc9803620918174c45ebf4b1ee8b7b97',1,'WebUsers']]], + ['setpatch_5fversion',['setPatch_Version',['../classTicket__Info.html#a334a1a473d78c250e1987e8413f6da95',1,'Ticket_Info']]], + ['setpermission',['setPermission',['../classTicket__User.html#a3a35f8730a62f8bc527fda697a520df6',1,'Ticket_User']]], + ['setpriority',['setPriority',['../classTicket.html#a57bb563835bee337cbd87c77e94344c5',1,'Ticket']]], + ['setprocessor',['setProcessor',['../classTicket__Info.html#a51c321cf9227a8224c148d956cfc83bb',1,'Ticket_Info']]], + ['setquery',['setQuery',['../classQuerycache.html#a6e2e369125270695466baf423ae62ad2',1,'Querycache\setQuery()'],['../classTicket__Log.html#a8bdfb3350931c321e4f0b5e95e9edc72',1,'Ticket_Log\setQuery()']]], + ['setqueue',['setQueue',['../classTicket.html#a01c96663844da94d9e1bfa087cf2bce0',1,'Ticket']]], + ['setreceivemail',['setReceiveMail',['../classWebUsers.html#a5655d4bf05243a4e0ad2ffdc77ed02e2',1,'WebUsers']]], + ['setserver_5ftick',['setServer_Tick',['../classTicket__Info.html#a080e6773bbe65eca6c98c2c500ad277d',1,'Ticket_Info']]], + ['setsgroupid',['setSGroupId',['../classSupport__Group.html#a8ce0276b5ac26f1fdd18f9708ea0c84b',1,'Support_Group']]], + ['setshardid',['setShardId',['../classTicket__Info.html#a33610e4ba47f681782d5c6008196dc89',1,'Ticket_Info']]], + ['setsid',['setSID',['../classQuerycache.html#a7a4b7276270a9df57f7a5cc920fe5edc',1,'Querycache']]], + ['setstatus',['setStatus',['../classTicket.html#a3728c830f27453c477146d86adb92436',1,'Ticket']]], + ['settag',['setTag',['../classSupport__Group.html#ae67c636c5b11382fede819b00f3df40d',1,'Support_Group']]], + ['settcategoryid',['setTCategoryId',['../classTicket__Category.html#ab9c3df7c426fcc8d3de87b3b115e3aaa',1,'Ticket_Category']]], + ['settcontentid',['setTContentId',['../classTicket__Content.html#a8cb93fa5405ea177b5806211f0cc8c46',1,'Ticket_Content']]], + ['setticket',['setTicket',['../classAssigned.html#a80db44acb118dae58653443eb103402f',1,'Assigned\setTicket()'],['../classForwarded.html#a8885583370e3d29c42bdd9332f7e0398',1,'Forwarded\setTicket()'],['../classTicket__Info.html#aaa4df08bf37e477fb9631334f8310892',1,'Ticket_Info\setTicket()'],['../classTicket__Log.html#a428d22c1acf7713b66c6257de486885c',1,'Ticket_Log\setTicket()'],['../classTicket__Reply.html#ac3b44a5ab04a7234691403eec4c6a9b0',1,'Ticket_Reply\setTicket()']]], + ['setticket_5fcategory',['setTicket_Category',['../classTicket.html#aa53611afbc62ea031f9f2865a0d77721',1,'Ticket']]], + ['settid',['setTId',['../classTicket.html#ae0612846bbef3918b8ce95fe7f0d677e',1,'Ticket']]], + ['settimestamp',['setTimestamp',['../classTicket.html#a8496c014022175eb420071e61bfb3b6b',1,'Ticket\setTimestamp()'],['../classTicket__Log.html#aba55ce726ac8f07ad8e8fc394c88d2a0',1,'Ticket_Log\setTimestamp()'],['../classTicket__Reply.html#a86484ee918ae0134baa300329ffac4a7',1,'Ticket_Reply\setTimestamp()']]], + ['settinfoid',['setTInfoId',['../classTicket__Info.html#a1cc32cbfd19a31495aac2984ba267acf',1,'Ticket_Info']]], + ['settitle',['setTitle',['../classTicket.html#a873552a6011a56a75af7988e87e6f731',1,'Ticket']]], + ['settlogid',['setTLogId',['../classTicket__Log.html#a1176599841ab35b84321449d6d5cfb8d',1,'Ticket_Log']]], + ['settreplyid',['setTReplyId',['../classTicket__Reply.html#a2735ba1d359a5d095133c45c577a83b6',1,'Ticket_Reply']]], + ['settuserid',['setTUserId',['../classTicket__User.html#a3dd334c95ac15ccbc49c8971fab95b35',1,'Ticket_User']]], + ['settype',['setType',['../classQuerycache.html#ac4a9fb03e63f009d57045c1c23adf550',1,'Querycache']]], + ['setuser',['setUser',['../classAssigned.html#a5a9a262eec26f7b7607d269c36dac0a4',1,'Assigned\setUser()'],['../classIn__Support__Group.html#a83b004d30957217bf8fe570df50c4601',1,'In_Support_Group\setUser()']]], + ['setuser_5fid',['setUser_Id',['../classTicket__Info.html#a71a1a8a3907db742bb8c6900f575272a',1,'Ticket_Info']]], + ['setuser_5fposition',['setUser_Position',['../classTicket__Info.html#a973eeb99a4bd7074c8a30c223b7e1da1',1,'Ticket_Info']]], + ['setview_5fposition',['setView_Position',['../classTicket__Info.html#a077e1c9e3e8d53b9fd3a7b897b3c1805',1,'Ticket_Info']]], + ['support_5fgroup',['Support_Group',['../classSupport__Group.html',1,'']]], + ['supportgroup_5fentrynotexists',['supportGroup_EntryNotExists',['../classSupport__Group.html#ac87e9bcdb36a48a1c76037a7f647708d',1,'Support_Group']]], + ['supportgroup_5fexists',['supportGroup_Exists',['../classSupport__Group.html#ad57709a8f4323b92a9417d6f8f5aa4d4',1,'Support_Group']]], + ['sync',['Sync',['../classSync.html',1,'']]], + ['syncdata',['syncdata',['../classSync.html#a98bd0a08014a652bde33ae9e42004ed3',1,'Sync']]] +]; diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_74.html b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_74.html new file mode 100644 index 000000000..fdc6589d0 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_74.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_74.js b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_74.js new file mode 100644 index 000000000..b821273b7 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_74.js @@ -0,0 +1,15 @@ +var searchData= +[ + ['ticket',['Ticket',['../classTicket.html',1,'']]], + ['ticket_5fcategory',['Ticket_Category',['../classTicket__Category.html',1,'']]], + ['ticket_5fcontent',['Ticket_Content',['../classTicket__Content.html',1,'']]], + ['ticket_5finfo',['Ticket_Info',['../classTicket__Info.html',1,'']]], + ['ticket_5flog',['Ticket_Log',['../classTicket__Log.html',1,'']]], + ['ticket_5fqueue',['Ticket_Queue',['../classTicket__Queue.html',1,'']]], + ['ticket_5fqueue_5fhandler',['Ticket_Queue_Handler',['../classTicket__Queue__Handler.html',1,'']]], + ['ticket_5freply',['Ticket_Reply',['../classTicket__Reply.html',1,'']]], + ['ticket_5fuser',['Ticket_User',['../classTicket__User.html',1,'']]], + ['ticketexists',['ticketExists',['../classTicket.html#a669c205f88c73c7857a5a2beb9a159ae',1,'Ticket']]], + ['tickethasinfo',['TicketHasInfo',['../classTicket__Info.html#a9bf51b7fe6c31ad6f114bea0f510302d',1,'Ticket_Info']]], + ['time_5felapsed_5fstring',['time_elapsed_string',['../classGui__Elements.html#afb74abd6dc4abcae4e035e23eb22619a',1,'Gui_Elements']]] +]; diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_75.html b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_75.html new file mode 100644 index 000000000..ab8455ed5 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_75.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_75.js b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_75.js new file mode 100644 index 000000000..d3177b30c --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_75.js @@ -0,0 +1,9 @@ +var searchData= +[ + ['unassignticket',['unAssignTicket',['../classAssigned.html#ab4713000e4216c858bf62783a96e6770',1,'Assigned\unAssignTicket()'],['../classTicket.html#a344f4b5e5ae5f1312c0647c6779bfc5c',1,'Ticket\unAssignTicket()']]], + ['update',['update',['../classQuerycache.html#a5840a78a238aeccc749f9e76fd9bfcde',1,'Querycache\update()'],['../classSupport__Group.html#ab454ffc861833bb6aa3a4cea3ae5c785',1,'Support_Group\update()'],['../classTicket.html#a4f834700b722501738b5c20c1cb1e719',1,'Ticket\update()'],['../classTicket__Category.html#a9120c893d7a2db005dec5439f3868ff6',1,'Ticket_Category\update()'],['../classTicket__Content.html#abafe7398dea17511c92764d8f910e401',1,'Ticket_Content\update()'],['../classTicket__Log.html#a55abe9e6ffb80530c0df403a45c724f7',1,'Ticket_Log\update()'],['../classTicket__Reply.html#a4286b6ece613a13eb1eec7f259f36c8f',1,'Ticket_Reply\update()'],['../classTicket__User.html#a68c6a834f65de5fbdddaa9818cf4842d',1,'Ticket_User\update()']]], + ['updateticketstatus',['updateTicketStatus',['../classTicket.html#a10b96b8e6426201809f09cd35b251b28',1,'Ticket']]], + ['updateticketstatusandpriority',['updateTicketStatusAndPriority',['../classTicket.html#a8b58a3d21d48f7295de71be30e740af8',1,'Ticket']]], + ['userexistsinsgroup',['userExistsInSGroup',['../classIn__Support__Group.html#a5c50054a5ee9171859a329f9e3ef57bb',1,'In_Support_Group']]], + ['users',['Users',['../classUsers.html',1,'']]] +]; diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_76.html b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_76.html new file mode 100644 index 000000000..0ff5edd34 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_76.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_76.js b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_76.js new file mode 100644 index 000000000..db7c7fe32 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_76.js @@ -0,0 +1,4 @@ +var searchData= +[ + ['validemail',['validEmail',['../classUsers.html#a090fa5f2ca4565c03ffd21eae1393ab0',1,'Users']]] +]; diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_77.html b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_77.html new file mode 100644 index 000000000..73323d316 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_77.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_77.js b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_77.js new file mode 100644 index 000000000..0e579ee3d --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/all_77.js @@ -0,0 +1,4 @@ +var searchData= +[ + ['webusers',['WebUsers',['../classWebUsers.html',1,'']]] +]; diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_61.html b/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_61.html new file mode 100644 index 000000000..85e5d72a3 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_61.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_61.js b/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_61.js new file mode 100644 index 000000000..39d4914a1 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_61.js @@ -0,0 +1,4 @@ +var searchData= +[ + ['assigned',['Assigned',['../classAssigned.html',1,'']]] +]; diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_64.html b/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_64.html new file mode 100644 index 000000000..590270830 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_64.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_64.js b/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_64.js new file mode 100644 index 000000000..db9b214c1 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_64.js @@ -0,0 +1,4 @@ +var searchData= +[ + ['dblayer',['DBLayer',['../classDBLayer.html',1,'']]] +]; diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_66.html b/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_66.html new file mode 100644 index 000000000..941988c65 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_66.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_66.js b/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_66.js new file mode 100644 index 000000000..518614d90 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_66.js @@ -0,0 +1,4 @@ +var searchData= +[ + ['forwarded',['Forwarded',['../classForwarded.html',1,'']]] +]; diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_67.html b/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_67.html new file mode 100644 index 000000000..78b514df0 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_67.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_67.js b/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_67.js new file mode 100644 index 000000000..e85658e41 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_67.js @@ -0,0 +1,4 @@ +var searchData= +[ + ['gui_5felements',['Gui_Elements',['../classGui__Elements.html',1,'']]] +]; diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_68.html b/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_68.html new file mode 100644 index 000000000..475eeb738 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_68.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_68.js b/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_68.js new file mode 100644 index 000000000..9e86cb7a3 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_68.js @@ -0,0 +1,4 @@ +var searchData= +[ + ['helpers',['Helpers',['../classHelpers.html',1,'']]] +]; diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_69.html b/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_69.html new file mode 100644 index 000000000..961dbea27 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_69.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_69.js b/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_69.js new file mode 100644 index 000000000..49e6133b0 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_69.js @@ -0,0 +1,4 @@ +var searchData= +[ + ['in_5fsupport_5fgroup',['In_Support_Group',['../classIn__Support__Group.html',1,'']]] +]; diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_6d.html b/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_6d.html new file mode 100644 index 000000000..abe6f0d1d --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_6d.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_6d.js b/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_6d.js new file mode 100644 index 000000000..ae1e0f417 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_6d.js @@ -0,0 +1,5 @@ +var searchData= +[ + ['mail_5fhandler',['Mail_Handler',['../classMail__Handler.html',1,'']]], + ['mycrypt',['MyCrypt',['../classMyCrypt.html',1,'']]] +]; diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_70.html b/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_70.html new file mode 100644 index 000000000..e4b520874 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_70.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_70.js b/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_70.js new file mode 100644 index 000000000..157d7c6b6 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_70.js @@ -0,0 +1,4 @@ +var searchData= +[ + ['pagination',['Pagination',['../classPagination.html',1,'']]] +]; diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_71.html b/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_71.html new file mode 100644 index 000000000..80a4fbb83 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_71.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_71.js b/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_71.js new file mode 100644 index 000000000..e2ca060ac --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_71.js @@ -0,0 +1,4 @@ +var searchData= +[ + ['querycache',['Querycache',['../classQuerycache.html',1,'']]] +]; diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_73.html b/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_73.html new file mode 100644 index 000000000..a1bf0b912 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_73.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_73.js b/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_73.js new file mode 100644 index 000000000..8941e45df --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_73.js @@ -0,0 +1,5 @@ +var searchData= +[ + ['support_5fgroup',['Support_Group',['../classSupport__Group.html',1,'']]], + ['sync',['Sync',['../classSync.html',1,'']]] +]; diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_74.html b/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_74.html new file mode 100644 index 000000000..f7f27cea7 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_74.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_74.js b/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_74.js new file mode 100644 index 000000000..90ec7727d --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_74.js @@ -0,0 +1,12 @@ +var searchData= +[ + ['ticket',['Ticket',['../classTicket.html',1,'']]], + ['ticket_5fcategory',['Ticket_Category',['../classTicket__Category.html',1,'']]], + ['ticket_5fcontent',['Ticket_Content',['../classTicket__Content.html',1,'']]], + ['ticket_5finfo',['Ticket_Info',['../classTicket__Info.html',1,'']]], + ['ticket_5flog',['Ticket_Log',['../classTicket__Log.html',1,'']]], + ['ticket_5fqueue',['Ticket_Queue',['../classTicket__Queue.html',1,'']]], + ['ticket_5fqueue_5fhandler',['Ticket_Queue_Handler',['../classTicket__Queue__Handler.html',1,'']]], + ['ticket_5freply',['Ticket_Reply',['../classTicket__Reply.html',1,'']]], + ['ticket_5fuser',['Ticket_User',['../classTicket__User.html',1,'']]] +]; diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_75.html b/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_75.html new file mode 100644 index 000000000..807d7426e --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_75.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_75.js b/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_75.js new file mode 100644 index 000000000..432c5d270 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_75.js @@ -0,0 +1,4 @@ +var searchData= +[ + ['users',['Users',['../classUsers.html',1,'']]] +]; diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_77.html b/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_77.html new file mode 100644 index 000000000..e3967e972 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_77.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_77.js b/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_77.js new file mode 100644 index 000000000..0e579ee3d --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/classes_77.js @@ -0,0 +1,4 @@ +var searchData= +[ + ['webusers',['WebUsers',['../classWebUsers.html',1,'']]] +]; diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/close.png b/code/ryzom/tools/server/ryzom_ams_docs/html/search/close.png new file mode 100644 index 0000000000000000000000000000000000000000..9342d3dfeea7b7c4ee610987e717804b5a42ceb9 GIT binary patch literal 273 zcmV+s0q*{ZP)4(RlMby96)VwnbG{ zbe&}^BDn7x>$<{ck4zAK-=nT;=hHG)kmplIF${xqm8db3oX6wT3bvp`TE@m0cg;b) zBuSL}5?N7O(iZLdAlz@)b)Rd~DnSsSX&P5qC`XwuFwcAYLC+d2>+1(8on;wpt8QIC X2MT$R4iQDd00000NkvXXu0mjfia~GN literal 0 HcmV?d00001 diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_5f.html b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_5f.html new file mode 100644 index 000000000..cb54e927d --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_5f.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_5f.js b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_5f.js new file mode 100644 index 000000000..a91cf6dd4 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_5f.js @@ -0,0 +1,4 @@ +var searchData= +[ + ['_5f_5fconstruct',['__construct',['../classAssigned.html#a34db106389637f5136b822a48e363043',1,'Assigned\__construct()'],['../classDBLayer.html#ad1d7f86c8854cb42b51849de74fe2ad6',1,'DBLayer\__construct()'],['../classForwarded.html#a9650a496b9c85e29d543374185d7d602',1,'Forwarded\__construct()'],['../classIn__Support__Group.html#af4b764c40f52108c6ea90534d38c8bae',1,'In_Support_Group\__construct()'],['../classMyCrypt.html#a35146c564f37f0b0274dfa0c05d3ba13',1,'MyCrypt\__construct()'],['../classPagination.html#adf554474cd78134da39228c2fb973d9e',1,'Pagination\__construct()'],['../classQuerycache.html#a7ddcdec027091f50d8009adfc6b44ec4',1,'Querycache\__construct()'],['../classSupport__Group.html#acf48f29ae284344ca7513afd0678c434',1,'Support_Group\__construct()'],['../classTicket.html#a83aa76a51e7a7263f0d919500321daec',1,'Ticket\__construct()'],['../classTicket__Category.html#a6dfdb4328151d710388fd9171043ad85',1,'Ticket_Category\__construct()'],['../classTicket__Content.html#ae540c72e04d1722c1bf52339449761db',1,'Ticket_Content\__construct()'],['../classTicket__Info.html#af8021f1bfe6e51fa02aed2af129d7fbe',1,'Ticket_Info\__construct()'],['../classTicket__Log.html#a3f9b0887d0f4552a120f8df9e7765c0d',1,'Ticket_Log\__construct()'],['../classTicket__Queue__Handler.html#a0831f1e09fce035802be012a71bc6e88',1,'Ticket_Queue_Handler\__construct()'],['../classTicket__Reply.html#a1e4c273b8cee38d54452823a70875485',1,'Ticket_Reply\__construct()'],['../classTicket__User.html#a7581ea4400e1a8f0824598a1e0e038b3',1,'Ticket_User\__construct()'],['../classWebUsers.html#a3402f45e3a0f54f9756d1054afef0716',1,'WebUsers\__construct()']]] +]; diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_61.html b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_61.html new file mode 100644 index 000000000..7f395337e --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_61.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_61.js b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_61.js new file mode 100644 index 000000000..253a4ef58 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_61.js @@ -0,0 +1,5 @@ +var searchData= +[ + ['addusertosupportgroup',['addUserToSupportGroup',['../classSupport__Group.html#adaa2d30acacac56032605b34e246b253',1,'Support_Group']]], + ['assignticket',['assignTicket',['../classAssigned.html#ac493092d0fcd0e3b2b9145cc8cd03d8e',1,'Assigned\assignTicket()'],['../classTicket.html#ab2653e3c80e3582c38c6845e6add6fac',1,'Ticket\assignTicket()']]] +]; diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_63.html b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_63.html new file mode 100644 index 000000000..9ebe11d69 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_63.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_63.js b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_63.js new file mode 100644 index 000000000..e896babc9 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_63.js @@ -0,0 +1,37 @@ +var searchData= +[ + ['change_5fpermission',['change_permission',['../classTicket__User.html#aa3123d101bc451c3d2911263e10a4464',1,'Ticket_User']]], + ['check_5fchange_5fpassword',['check_change_password',['../classUsers.html#ad6da3241c4a12f4e81729ab830737e3c',1,'Users']]], + ['check_5fif_5fgame_5fclient',['check_if_game_client',['../classHelpers.html#a828e3e0cf73e98beb9ace8f5277e5f29',1,'Helpers']]], + ['check_5flogin_5fingame',['check_login_ingame',['../classHelpers.html#a0d0ad4ad22288b9aa225b7d71534fc33',1,'Helpers']]], + ['check_5fmethods',['check_methods',['../classMyCrypt.html#a20dcd7883eb21d0f6f62175891c9e51c',1,'MyCrypt']]], + ['check_5fregister',['check_Register',['../classUsers.html#a6f124a6e3b8d117604e707601a6b4876',1,'Users']]], + ['checkemail',['checkEmail',['../classUsers.html#a1ccd65e0ad68710b6176b381fe062a18',1,'Users']]], + ['checkemailexists',['checkEmailExists',['../classUsers.html#ab48b703be38135a0478aed6674a30fb0',1,'Users\checkEmailExists()'],['../classWebUsers.html#a2874604f0ac49c770bbbffef342466ff',1,'WebUsers\checkEmailExists()']]], + ['checkloginmatch',['checkLoginMatch',['../classUsers.html#a230235f569309159fb4b2b03956ebbd5',1,'Users\checkLoginMatch()'],['../classWebUsers.html#a488f20463bfac01871810fa5f7461e10',1,'WebUsers\checkLoginMatch()']]], + ['checkpassword',['checkPassword',['../classUsers.html#a7c61019e954d60fa38fe0c20554fa53d',1,'Users']]], + ['checkuser',['checkUser',['../classUsers.html#ac078497a378d4bedb8bab22dcc362b7c',1,'Users']]], + ['checkusernameexists',['checkUserNameExists',['../classUsers.html#a809c817e5f9d61f80b6c07aaf3882e2d',1,'Users\checkUserNameExists()'],['../classWebUsers.html#a717ecd295ccfcf9dfa9a9d6d84b97d3b',1,'WebUsers\checkUserNameExists()']]], + ['confirmpassword',['confirmPassword',['../classUsers.html#a7bbf5cef9e09e9dc49d7811d3012da86',1,'Users']]], + ['constr_5fexternid',['constr_ExternId',['../classTicket__User.html#ab61b472135a3b4cc4c24481de6bd90cc',1,'Ticket_User']]], + ['constr_5fsgroupid',['constr_SGroupId',['../classSupport__Group.html#a0522ca36e298ba573c2b6acf0a71f535',1,'Support_Group']]], + ['constr_5ftcategoryid',['constr_TCategoryId',['../classTicket__Category.html#a9069b59fd0dfc8e92e01c8a3c19c6f35',1,'Ticket_Category']]], + ['constr_5ftcontentid',['constr_TContentId',['../classTicket__Content.html#aadfcf86b2b9d7b1d802382786dca339d',1,'Ticket_Content']]], + ['constr_5ftlogid',['constr_TLogId',['../classTicket__Log.html#a6a67397d349e8f08bef67f4c7a330c44',1,'Ticket_Log']]], + ['constr_5ftreplyid',['constr_TReplyId',['../classTicket__Reply.html#aaf594222ac722101e691a612f0b3f26e',1,'Ticket_Reply']]], + ['constr_5ftuserid',['constr_TUserId',['../classTicket__User.html#a43c21d6bdccb68a79677eb41291b3082',1,'Ticket_User']]], + ['create',['create',['../classAssigned.html#a1b0669bd661f60f9984d9c623102f4c0',1,'Assigned\create()'],['../classForwarded.html#abf271dbbb38d1be6df0a2e0fd0345c0c',1,'Forwarded\create()'],['../classIn__Support__Group.html#a19eb31ee240ca0d78f70363dae4555b9',1,'In_Support_Group\create()'],['../classSupport__Group.html#a229aeddc62ba4a03d4c84479eae97248',1,'Support_Group\create()'],['../classTicket.html#a16156b509b1da77e551c64809e0187a4',1,'Ticket\create()'],['../classTicket__Content.html#ac7d5c5f92d1d5c38b04842fd5b7acbbb',1,'Ticket_Content\create()'],['../classTicket__Info.html#a55f6b3b365cdcbf9b6476c9aa296a759',1,'Ticket_Info\create()'],['../classTicket__Reply.html#a20f54f6ea88a5cee51516a1237dd12ef',1,'Ticket_Reply\create()']]], + ['create_5ffolders',['create_folders',['../classHelpers.html#a121768ce7285cdfd011d461ec359ac18',1,'Helpers']]], + ['create_5fticket',['create_Ticket',['../classTicket.html#a3892155f3a6dd43880e2d8fab128b59c',1,'Ticket']]], + ['create_5fticket_5finfo',['create_Ticket_Info',['../classTicket__Info.html#a5b2ece9189baf4f6dd010baee340e2ac',1,'Ticket_Info']]], + ['createlogentry',['createLogEntry',['../classTicket__Log.html#a6f51a7e188cb3579be9d28ea003c78f1',1,'Ticket_Log']]], + ['createpermissions',['createPermissions',['../classUsers.html#a5879a0f4b8da3e91af153cd19ca6e548',1,'Users']]], + ['createqueue',['createQueue',['../classTicket__Queue.html#a7b9b007668bc769c605e22075630b6dd',1,'Ticket_Queue\createQueue()'],['../classTicket__Queue__Handler.html#acd3f4a706276cd12a5f105b098bcc274',1,'Ticket_Queue_Handler\createQueue()']]], + ['createreply',['createReply',['../classTicket.html#acd9596d84d5fd0b68226269d6eff9cbb',1,'Ticket\createReply()'],['../classTicket__Reply.html#ab86fae04d57eb33689793e132b95014b',1,'Ticket_Reply\createReply()']]], + ['createsupportgroup',['createSupportGroup',['../classSupport__Group.html#a9b627241029d1821c7978309f77948f5',1,'Support_Group']]], + ['createticketcategory',['createTicketCategory',['../classTicket__Category.html#a9229f3d3e6479058d094aedd7ecaeaef',1,'Ticket_Category']]], + ['createticketuser',['createTicketUser',['../classTicket__User.html#a531f0c78a90cb284e81bc1a9651d808d',1,'Ticket_User']]], + ['createuser',['createUser',['../classUsers.html#a110c60194f014cf45b93bde09b40a565',1,'Users']]], + ['createwebuser',['createWebuser',['../classWebUsers.html#abb1b5f937d609c7bbf02710efdc49115',1,'WebUsers']]], + ['cron',['cron',['../classMail__Handler.html#a228e3c16f470e35a68a6c639bfb08803',1,'Mail_Handler']]] +]; diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_64.html b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_64.html new file mode 100644 index 000000000..d8b63943c --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_64.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_64.js b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_64.js new file mode 100644 index 000000000..def175b9c --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_64.js @@ -0,0 +1,8 @@ +var searchData= +[ + ['decode_5futf8',['decode_utf8',['../classMail__Handler.html#a0941972d8f81caba60261e743d4d4567',1,'Mail_Handler']]], + ['decrypt',['decrypt',['../classMyCrypt.html#a8833cd07b177eaac011d154ad46991c4',1,'MyCrypt']]], + ['delete',['delete',['../classAssigned.html#adf0e3ee5ea2c0536d4d59408868136c8',1,'Assigned\delete()'],['../classForwarded.html#aa4aef6d29496ca0a802bf2398d7e6192',1,'Forwarded\delete()'],['../classIn__Support__Group.html#a14b353623a7af41c0e801d79c7c5f9c0',1,'In_Support_Group\delete()'],['../classSupport__Group.html#ad31e17d6eeaf862da0589dd010a0f794',1,'Support_Group\delete()']]], + ['deletesupportgroup',['deleteSupportGroup',['../classSupport__Group.html#a0aa6d0164c2e5a0fddcbf062c4f2ae7f',1,'Support_Group']]], + ['deleteuserofsupportgroup',['deleteUserOfSupportGroup',['../classSupport__Group.html#a8c7ee9fb4786198c4c53e599d77f5352',1,'Support_Group']]] +]; diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_65.html b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_65.html new file mode 100644 index 000000000..a77debae0 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_65.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_65.js b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_65.js new file mode 100644 index 000000000..de729509e --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_65.js @@ -0,0 +1,7 @@ +var searchData= +[ + ['encrypt',['encrypt',['../classMyCrypt.html#a72a803d99bc6687a64d0d80043a7516c',1,'MyCrypt']]], + ['execute',['execute',['../classDBLayer.html#ad3ebb9fd7b55f2f597bb20e12602d6f4',1,'DBLayer']]], + ['executereturnid',['executeReturnId',['../classDBLayer.html#a82c63e5aa7205b3f54a4766427ab51f9',1,'DBLayer']]], + ['executewithoutparams',['executeWithoutParams',['../classDBLayer.html#a6831a10286b2f8795bb27dcea4b6edfe',1,'DBLayer']]] +]; diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_66.html b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_66.html new file mode 100644 index 000000000..319a5316a --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_66.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_66.js b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_66.js new file mode 100644 index 000000000..8348b6375 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_66.js @@ -0,0 +1,4 @@ +var searchData= +[ + ['forwardticket',['forwardTicket',['../classForwarded.html#a6e65be335e132dc4a2b06abc4815d730',1,'Forwarded\forwardTicket()'],['../classTicket.html#ada6fa74d4a6b5ce9d9e55b8f7398beb9',1,'Ticket\forwardTicket()']]] +]; diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_67.html b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_67.html new file mode 100644 index 000000000..d0ab42a3f --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_67.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_67.js b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_67.js new file mode 100644 index 000000000..c2380db1b --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_67.js @@ -0,0 +1,104 @@ +var searchData= +[ + ['generatesalt',['generateSALT',['../classUsers.html#ad9b1ccad9f285f5e3554003b17884482',1,'Users']]], + ['get_5femail_5fby_5fuser_5fid',['get_email_by_user_id',['../classTicket__User.html#ac9233453c39a9193993baa30711f1856',1,'Ticket_User']]], + ['get_5fid_5ffrom_5femail',['get_id_from_email',['../classTicket__User.html#ad74a6cb7ac2f021ff70cf8bcb43b1cba',1,'Ticket_User']]], + ['get_5fid_5ffrom_5fusername',['get_id_from_username',['../classTicket__User.html#a7e8ec955186676aff3f3b7757841962c',1,'Ticket_User']]], + ['get_5fmime_5ftype',['get_mime_type',['../classMail__Handler.html#ae4fbc4bc50da356e611e50b9ef77a52e',1,'Mail_Handler']]], + ['get_5fticket_5fid_5ffrom_5fsubject',['get_ticket_id_from_subject',['../classMail__Handler.html#a642b3996a7b826cdf84f057cc6813458',1,'Mail_Handler']]], + ['get_5fusername_5ffrom_5fid',['get_username_from_id',['../classTicket__User.html#a550d9a436e4820854d7fd3d998a2cb7c',1,'Ticket_User']]], + ['getaction',['getAction',['../classTicket__Log.html#ae74bffd2a4d675a9d693a63d893d9bd3',1,'Ticket_Log']]], + ['getactiontextarray',['getActionTextArray',['../classTicket__Log.html#a0a4484014b124129e99929358be47e10',1,'Ticket_Log']]], + ['getallcategories',['getAllCategories',['../classTicket__Category.html#ad0ae199cc88bb083768f37515e0a143b',1,'Ticket_Category']]], + ['getalllogs',['getAllLogs',['../classTicket__Log.html#a7111b8cf64ff9c9a81572cd68718564b',1,'Ticket_Log']]], + ['getallsupportgroups',['getAllSupportGroups',['../classSupport__Group.html#a3b78af4839ac313f8f056e0cd86c7d4f',1,'Support_Group']]], + ['getallusersofsupportgroup',['getAllUsersOfSupportGroup',['../classSupport__Group.html#ad18e4bb9f7988fe92631e2ec2f53ab95',1,'Support_Group']]], + ['getallusersquery',['getAllUsersQuery',['../classWebUsers.html#ab7901cc22a26dcff53de5257579bb7ea',1,'WebUsers']]], + ['getamountofrows',['getAmountOfRows',['../classPagination.html#a9afee51d409525ebef762b4b7adfebde',1,'Pagination']]], + ['getargument',['getArgument',['../classTicket__Log.html#ac40db2b8ff140a476c9345c119b64f53',1,'Ticket_Log']]], + ['getassigned',['getAssigned',['../classTicket.html#a60fa0babd57b4fd86a2c1a466f2f0d67',1,'Ticket']]], + ['getauthor',['getAuthor',['../classTicket.html#aa93ff96cd8be6ef2b9b27554ec344e53',1,'Ticket\getAuthor()'],['../classTicket__Log.html#ad5995445284657a1041be811e29d9c7d',1,'Ticket_Log\getAuthor()'],['../classTicket__Reply.html#a291a3e0eeff0564433c47eccc0ec7d04',1,'Ticket_Reply\getAuthor()']]], + ['getcategoryname',['getCategoryName',['../classTicket.html#ab151edff1be8fe3f2e80227d49043493',1,'Ticket']]], + ['getclient_5fversion',['getClient_Version',['../classTicket__Info.html#a0cd57bdd61693a1a4e871470991f3b32',1,'Ticket_Info']]], + ['getconnect_5fstate',['getConnect_State',['../classTicket__Info.html#a7e4012fe943044af9bc544015812a81e',1,'Ticket_Info']]], + ['getcontent',['getContent',['../classTicket__Content.html#ac18a21f04565b77901a25091b470a065',1,'Ticket_Content\getContent()'],['../classTicket__Reply.html#a5097d916b201ae089d7ab206fa9debec',1,'Ticket_Reply\getContent()']]], + ['getcpu_5fmask',['getCPU_Mask',['../classTicket__Info.html#a0954d53be4c289c7adbbb93e1f136b8e',1,'Ticket_Info']]], + ['getcpuid',['getCPUId',['../classTicket__Info.html#aa98fbe419f73988551505510cf941ce5',1,'Ticket_Info']]], + ['getcurrent',['getCurrent',['../classPagination.html#afd1f1ab1a73dc5b4154d6bda5b42abfc',1,'Pagination']]], + ['getdb',['getDb',['../classQuerycache.html#ab7d8e4258fc22338afe76c5ca0487588',1,'Querycache']]], + ['getelements',['getElements',['../classPagination.html#a76ed587e80929eeba79fcbac4944179a',1,'Pagination']]], + ['getemail',['getEmail',['../classWebUsers.html#a8c782c9dfcacbebb2deca789930849f3',1,'WebUsers']]], + ['getentireticket',['getEntireTicket',['../classTicket.html#aab16857c573bbc275dbc47b5a193ef7a',1,'Ticket']]], + ['getexternid',['getExternId',['../classTicket__User.html#a45675149ecaa6bbabd8673823437b1ea',1,'Ticket_User']]], + ['getforwardedgroupid',['getForwardedGroupId',['../classTicket.html#a0b95696da230b5a73d066e0e319eec3a',1,'Ticket']]], + ['getforwardedgroupname',['getForwardedGroupName',['../classTicket.html#a33f1ce4b9efbd4a86af3253ceaab8bf6',1,'Ticket']]], + ['getgroup',['getGroup',['../classForwarded.html#a6269c9b2300e371234ed45b622125be5',1,'Forwarded\getGroup()'],['../classIn__Support__Group.html#a9682d3369161a216dd24695f5348d0e0',1,'In_Support_Group\getGroup()'],['../classSupport__Group.html#a3015fdd2bc168dc716606f7726edaa7b',1,'Support_Group\getGroup()']]], + ['getgroupemail',['getGroupEmail',['../classSupport__Group.html#a7d241e89ecac8a3758d6ebef98785d55',1,'Support_Group']]], + ['getgroups',['getGroups',['../classSupport__Group.html#a7dcfecaf50ff3eb16fb715cfacc75987',1,'Support_Group']]], + ['gethidden',['getHidden',['../classTicket__Reply.html#a8d30cdde15a466114bf3ea42a5fba693',1,'Ticket_Reply']]], + ['getht',['getHT',['../classTicket__Info.html#ad331265bf90315c82e6879bf4636179f',1,'Ticket_Info']]], + ['getid',['getId',['../classWebUsers.html#adfdf81197e5dd57e151e2faaa0f83480',1,'WebUsers']]], + ['getidfromemail',['getIdFromEmail',['../classWebUsers.html#a3d0ec30b9d7320e0a892a0e8f07ce243',1,'WebUsers']]], + ['getimap_5fmailserver',['getIMAP_MailServer',['../classSupport__Group.html#a5e9b8df4b4da25de13c0b025ad231ae5',1,'Support_Group']]], + ['getimap_5fpassword',['getIMAP_Password',['../classSupport__Group.html#a9cbd297595e51767e965f9e37dba6536',1,'Support_Group']]], + ['getimap_5fusername',['getIMAP_Username',['../classSupport__Group.html#a15a30346429128d56d499e2afa185ca5',1,'Support_Group']]], + ['getinfo',['getInfo',['../classWebUsers.html#a323844dc355f89ee9c81254cb15f8bd4',1,'WebUsers']]], + ['getlanguage',['getLanguage',['../classWebUsers.html#ac2f2d5c0335ecdd177f9db54150bcb2b',1,'WebUsers']]], + ['getlast',['getLast',['../classPagination.html#af4f4279390b6ef62039fe64797db54f5',1,'Pagination']]], + ['getlatestreply',['getLatestReply',['../classTicket.html#a36ca4b3d45286b8eca1fae7678c1c62c',1,'Ticket']]], + ['getlinks',['getLinks',['../classPagination.html#abf3dd22d43688359aea77b3086f4a9ec',1,'Pagination']]], + ['getlocal_5faddress',['getLocal_Address',['../classTicket__Info.html#a69279ebba47904cca0a9045a1a0c4200',1,'Ticket_Info']]], + ['getlogsofticket',['getLogsOfTicket',['../classTicket__Log.html#ae5acfd872f759c4d06caf30bde61aab6',1,'Ticket_Log']]], + ['getmemory',['getMemory',['../classTicket__Info.html#a324025a0efb29aa53df9d9dd5c407397',1,'Ticket_Info']]], + ['getmodsandadmins',['getModsAndAdmins',['../classTicket__User.html#af988917dc55b56b71764e0bb79075e97',1,'Ticket_User']]], + ['getname',['getName',['../classSupport__Group.html#acc94d47e83f484aba760719077a3ce0c',1,'Support_Group\getName()'],['../classTicket__Category.html#a04cf7fa795b9d23b5b0f2664b4bdcc1c',1,'Ticket_Category\getName()']]], + ['getnel3d',['getNel3D',['../classTicket__Info.html#ae00e5a2d95f040bbb9ef23e45517e056',1,'Ticket_Info']]], + ['getnewestticket',['getNewestTicket',['../classTicket__Queue__Handler.html#a6a130b06a4cbbc10e5a19431456ae080',1,'Ticket_Queue_Handler']]], + ['getnroftickets',['getNrOfTickets',['../classTicket__Queue__Handler.html#acbee619eecfb9a81769f4389fa48ff51',1,'Ticket_Queue_Handler']]], + ['getnrofticketsassignedwaiting',['getNrOfTicketsAssignedWaiting',['../classTicket__Queue__Handler.html#aaa4b015079a5b37ede9ec03f63f203e4',1,'Ticket_Queue_Handler']]], + ['getnrofticketstodo',['getNrOfTicketsToDo',['../classTicket__Queue__Handler.html#a1623e3f03e28508e76fc148b936bdfda',1,'Ticket_Queue_Handler']]], + ['getos',['getOS',['../classTicket__Info.html#a733b4a44369971889dcac14408235b9c',1,'Ticket_Info']]], + ['getpagination',['getPagination',['../classTicket__Queue__Handler.html#a50a09229d462401177d1cd532853d358',1,'Ticket_Queue_Handler']]], + ['getparams',['getParams',['../classTicket__Queue.html#a0e2af91baf1bf2de6d564c2e6616e64c',1,'Ticket_Queue']]], + ['getpatch_5fversion',['getPatch_Version',['../classTicket__Info.html#ac81ff5e867efc8d198d833cf5418f047',1,'Ticket_Info']]], + ['getpermission',['getPermission',['../classTicket__User.html#a3f28fd031679b16d4a80653754f179eb',1,'Ticket_User']]], + ['getpriority',['getPriority',['../classTicket.html#a50f01c32532cb06451c2d3151e641287',1,'Ticket']]], + ['getpriorityarray',['getPriorityArray',['../classTicket.html#ab309a3fa2ac23bd6847bbf48cfc271d2',1,'Ticket']]], + ['getprioritytext',['getPriorityText',['../classTicket.html#ab7c6c081ea47c3fe8e2d06ec84d01595',1,'Ticket']]], + ['getprocessor',['getProcessor',['../classTicket__Info.html#aeae8a9bbf14e5c204242d82b1da7fca8',1,'Ticket_Info']]], + ['getquery',['getQuery',['../classQuerycache.html#a75b6539cc93eed689f4c34cf3140684f',1,'Querycache\getQuery()'],['../classTicket__Log.html#a53c652f4677d734ea8a4dcab512cd536',1,'Ticket_Log\getQuery()'],['../classTicket__Queue.html#a7c6eef6c98cebf9aec82a83027afef13',1,'Ticket_Queue\getQuery()']]], + ['getqueue',['getQueue',['../classTicket.html#aa658a033e7c11f65f6e30e8d8c23d5c7',1,'Ticket']]], + ['getreceivemail',['getReceiveMail',['../classWebUsers.html#a46aa8f355529e73c680e2e85f6fb8d75',1,'WebUsers']]], + ['getrepliesofticket',['getRepliesOfTicket',['../classTicket__Reply.html#ab579ada18583a68ac5b1b819e267ba7d',1,'Ticket_Reply']]], + ['getserver_5ftick',['getServer_Tick',['../classTicket__Info.html#af97b266fb8ac44892151b4ab71b96325',1,'Ticket_Info']]], + ['getsgroupid',['getSGroupId',['../classSupport__Group.html#a76e436109e6907bf69d2614323ede0b7',1,'Support_Group']]], + ['getsgroupofticket',['getSGroupOfTicket',['../classForwarded.html#a98de9698c91fbc552eba480f04c05db3',1,'Forwarded']]], + ['getshardid',['getShardId',['../classTicket__Info.html#a72e6978e622978fc526620df10c89b34',1,'Ticket_Info']]], + ['getsid',['getSID',['../classQuerycache.html#ad090d4d9a422540e8f93d1480d811c57',1,'Querycache']]], + ['getstatus',['getStatus',['../classTicket.html#af09548afeefddb6c2c258eb33a4cb5ca',1,'Ticket']]], + ['getstatusarray',['getStatusArray',['../classTicket.html#a58b91ffd0eed86af12ef8f418edb4199',1,'Ticket']]], + ['getstatustext',['getStatusText',['../classTicket.html#a620f54bbc80d257dafec2d4c33019d1a',1,'Ticket']]], + ['gettag',['getTag',['../classSupport__Group.html#ac0b76c1838dc23be75cab548625e9cd6',1,'Support_Group']]], + ['gettcategoryid',['getTCategoryId',['../classTicket__Category.html#ac1b758198fb7edcebf962b317167022e',1,'Ticket_Category']]], + ['gettcontentid',['getTContentId',['../classTicket__Content.html#ab233080cda7a46b84cd7993ad0f50d2c',1,'Ticket_Content']]], + ['getticket',['getTicket',['../classAssigned.html#ac42feb1177792e8d37b3b92ad33065fb',1,'Assigned\getTicket()'],['../classForwarded.html#a842cd8941a2a80fe72eeb6086e88d9fa',1,'Forwarded\getTicket()'],['../classTicket__Info.html#a5f227ee6c93f65fb250e7dbed55585bd',1,'Ticket_Info\getTicket()'],['../classTicket__Log.html#ac6767e71178317c003e4b0b9e7aa2430',1,'Ticket_Log\getTicket()'],['../classTicket__Reply.html#a95edaa9ec8b0b0c44d2ffc08f9f489d0',1,'Ticket_Reply\getTicket()']]], + ['getticket_5fcategory',['getTicket_Category',['../classTicket.html#a152f10aff0c29e859fda6a94ed41de41',1,'Ticket']]], + ['gettickets',['getTickets',['../classTicket__Queue__Handler.html#a989ef733df2ecd57cbdf89364985d98a',1,'Ticket_Queue_Handler']]], + ['getticketsof',['getTicketsOf',['../classTicket.html#ad7f9ba34b522a1fce4a0c7e9b7849b34',1,'Ticket']]], + ['gettid',['getTId',['../classTicket.html#af899c5b8fce8abbc88691e37b02e47ef',1,'Ticket']]], + ['gettimestamp',['getTimestamp',['../classTicket.html#abe472e82afc5c7f7d289736ed8d81484',1,'Ticket\getTimestamp()'],['../classTicket__Log.html#ad75b52a665afe333ea5cb505bfb1bb16',1,'Ticket_Log\getTimestamp()'],['../classTicket__Reply.html#a2336fde89bfa1dcb11d2d5825d611f12',1,'Ticket_Reply\getTimestamp()']]], + ['gettinfoid',['getTInfoId',['../classTicket__Info.html#a5ca59186f94c97b71dae5e52c98bbfea',1,'Ticket_Info']]], + ['gettitle',['getTitle',['../classTicket.html#a488ce26f6e9a69de4d63883492b9d5b8',1,'Ticket']]], + ['gettlogid',['getTLogId',['../classTicket__Log.html#aeebe7f20e361f7cb7d48cfe40d32190c',1,'Ticket_Log']]], + ['gettreplyid',['getTReplyId',['../classTicket__Reply.html#a7a14d87b977fde3193837195a43e673e',1,'Ticket_Reply']]], + ['gettuserid',['getTUserId',['../classTicket__User.html#a4df262f17c5e543842e5f49db1970280',1,'Ticket_User']]], + ['gettype',['getType',['../classQuerycache.html#a3e833722f16d96f44c0d887f4e833bcf',1,'Querycache']]], + ['getuid',['getUId',['../classWebUsers.html#a38654984bdbf75178cd0277807d94bfe',1,'WebUsers']]], + ['getuser',['getUser',['../classAssigned.html#a3b04e63f83c1b064018159f6f6ef68f7',1,'Assigned\getUser()'],['../classIn__Support__Group.html#add8c8620030ac6d125bc7f5787d7e501',1,'In_Support_Group\getUser()']]], + ['getuser_5fid',['getUser_Id',['../classTicket__Info.html#a9c96394e5906ac48818eb9ce62836043',1,'Ticket_Info']]], + ['getuser_5fposition',['getUser_Position',['../classTicket__Info.html#a9330a31fc877932f5250ce83ea9b0568',1,'Ticket_Info']]], + ['getuserassignedtoticket',['getUserAssignedToTicket',['../classAssigned.html#a2a745eac7f9c3ea9ce80d38c1fdc11a7',1,'Assigned']]], + ['getusername',['getUsername',['../classWebUsers.html#a6f9bc7fdf77969e857dcfd5c1f8fd9bc',1,'WebUsers']]], + ['getusers',['getUsers',['../classWebUsers.html#a9c773c1cc57aaf3429b11fd127b01a70',1,'WebUsers']]], + ['getview_5fposition',['getView_Position',['../classTicket__Info.html#a72e470ecde4faa28cff2748e7a51a98f',1,'Ticket_Info']]] +]; diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_68.html b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_68.html new file mode 100644 index 000000000..66b85be31 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_68.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_68.js b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_68.js new file mode 100644 index 000000000..940a80caf --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_68.js @@ -0,0 +1,6 @@ +var searchData= +[ + ['handle_5flanguage',['handle_language',['../classHelpers.html#a42edc35c0057da2769d5b5ede1da0001',1,'Helpers']]], + ['hashiv',['hashIV',['../classMyCrypt.html#a338bfce76aec4f14c73af16e7c0b6fe6',1,'MyCrypt']]], + ['hasinfo',['hasInfo',['../classTicket.html#ab5b11fd2cf66eac9f6b148795c021dee',1,'Ticket']]] +]; diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_69.html b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_69.html new file mode 100644 index 000000000..e2041976c --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_69.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_69.js b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_69.js new file mode 100644 index 000000000..5c2eef8a9 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_69.js @@ -0,0 +1,9 @@ +var searchData= +[ + ['incoming_5fmail_5fhandler',['incoming_mail_handler',['../classMail__Handler.html#a4cee2c20a8c05b71647ef4762879f0a4',1,'Mail_Handler']]], + ['isadmin',['isAdmin',['../classTicket__User.html#a30a17880b9ad06c7d5c3e7a8f2fb5d9c',1,'Ticket_User']]], + ['isassigned',['isAssigned',['../classAssigned.html#a41debee6bc7d4974204cdafd51e33c88',1,'Assigned']]], + ['isforwarded',['isForwarded',['../classForwarded.html#a4d0bd0223c5655e8232a17e53a03cc80',1,'Forwarded']]], + ['isloggedin',['isLoggedIn',['../classWebUsers.html#a46665dd9dba9bd764837aad1bdec3774',1,'WebUsers']]], + ['ismod',['isMod',['../classTicket__User.html#ab0badcae4b662e589f2eab4124a2adf7',1,'Ticket_User']]] +]; diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_6c.html b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_6c.html new file mode 100644 index 000000000..da371cfab --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_6c.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_6c.js b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_6c.js new file mode 100644 index 000000000..dcf4465c6 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_6c.js @@ -0,0 +1,21 @@ +var searchData= +[ + ['load',['load',['../classAssigned.html#aa3f2ea4abe6e71c19373de8df7481cfb',1,'Assigned\load()'],['../classForwarded.html#a9ef9ba4dff566a32fb08fe1bb3e36740',1,'Forwarded\load()']]], + ['load_5fwith_5fsgroupid',['load_With_SGroupId',['../classSupport__Group.html#aa7dcaff3a34942cbfa74fdda31e37620',1,'Support_Group']]], + ['load_5fwith_5fsid',['load_With_SID',['../classQuerycache.html#a579edcc576b076f884884cf509436768',1,'Querycache']]], + ['load_5fwith_5ftcategoryid',['load_With_TCategoryId',['../classTicket__Category.html#a66f5c7cb775b14bfa5ffc9467a63970f',1,'Ticket_Category']]], + ['load_5fwith_5ftcontentid',['load_With_TContentId',['../classTicket__Content.html#a3fe4c1b84b3751c8d8afe82eeb46155d',1,'Ticket_Content']]], + ['load_5fwith_5fticket',['load_With_Ticket',['../classTicket__Info.html#a745aad9aceb76cc81222a458e8eba99d',1,'Ticket_Info']]], + ['load_5fwith_5ftid',['load_With_TId',['../classTicket.html#a015b73c7de955bbf6991f1345d91eecf',1,'Ticket']]], + ['load_5fwith_5ftinfoid',['load_With_TInfoId',['../classTicket__Info.html#a7d2ea8863661e2e492b2a31c3b619139',1,'Ticket_Info']]], + ['load_5fwith_5ftlogid',['load_With_TLogId',['../classTicket__Log.html#a56d7a9f19d4913489aef19c279c0e076',1,'Ticket_Log']]], + ['load_5fwith_5ftreplyid',['load_With_TReplyId',['../classTicket__Reply.html#a262662361c75e7d12b148058f340ecf4',1,'Ticket_Reply']]], + ['load_5fwith_5ftuserid',['load_With_TUserId',['../classTicket__User.html#a982f1e0aa98f3abb6451772ceac74708',1,'Ticket_User']]], + ['loadallclosedtickets',['loadAllClosedTickets',['../classTicket__Queue.html#a328adadd5c9481a87ff712b2830a8519',1,'Ticket_Queue']]], + ['loadallnotassignedtickets',['loadAllNotAssignedTickets',['../classTicket__Queue.html#a5760df002c7ad36b92479754835058e9',1,'Ticket_Queue']]], + ['loadallopentickets',['loadAllOpenTickets',['../classTicket__Queue.html#a93e3b5126f01a9b14dce1d89bacc73b6',1,'Ticket_Queue']]], + ['loadalltickets',['loadAllTickets',['../classTicket__Queue.html#a0053185e73f37195adf22051cc7d87f8',1,'Ticket_Queue']]], + ['loadassignedandwaiting',['loadAssignedandWaiting',['../classTicket__Queue.html#a309b4c6c5423eecd6c8f57af7f60aee2',1,'Ticket_Queue']]], + ['loadtemplate',['loadTemplate',['../classHelpers.html#ad70beebe7bd2f0909dc882f6d381cbb7',1,'Helpers']]], + ['loadtodotickets',['loadToDoTickets',['../classTicket__Queue.html#ac35ffa886f20979b41a70b446bf7e6b2',1,'Ticket_Queue']]] +]; diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_6d.html b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_6d.html new file mode 100644 index 000000000..d01ac5376 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_6d.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_6d.js b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_6d.js new file mode 100644 index 000000000..b08253ebc --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_6d.js @@ -0,0 +1,6 @@ +var searchData= +[ + ['mail_5ffork',['mail_fork',['../classMail__Handler.html#a285306bacd7e6774c5737c91acdf8ba4',1,'Mail_Handler']]], + ['make_5ftable',['make_table',['../classGui__Elements.html#a1636b9683d6e0649e95fd7c09d4bf8b7',1,'Gui_Elements']]], + ['make_5ftable_5fwith_5fkey_5fis_5fid',['make_table_with_key_is_id',['../classGui__Elements.html#a4ffe8dfe08c927ae43ea70c742a811f1',1,'Gui_Elements']]] +]; diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_6e.html b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_6e.html new file mode 100644 index 000000000..d734dd07e --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_6e.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_6e.js b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_6e.js new file mode 100644 index 000000000..76ad6866c --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_6e.js @@ -0,0 +1,4 @@ +var searchData= +[ + ['new_5fmessage_5fid',['new_message_id',['../classMail__Handler.html#aec330ff71f834079a89cb80ba9cd079c',1,'Mail_Handler']]] +]; diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_6f.html b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_6f.html new file mode 100644 index 000000000..222f0f836 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_6f.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_6f.js b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_6f.js new file mode 100644 index 000000000..de6319927 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_6f.js @@ -0,0 +1,4 @@ +var searchData= +[ + ['outputtime',['outputTime',['../classHelpers.html#a61b01d13dc043d49115ee7f5c169ac73',1,'Helpers']]] +]; diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_73.html b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_73.html new file mode 100644 index 000000000..774d577f5 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_73.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_73.js b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_73.js new file mode 100644 index 000000000..3e6dd8070 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_73.js @@ -0,0 +1,63 @@ +var searchData= +[ + ['send_5fmail',['send_mail',['../classMail__Handler.html#a39c146922e58ad8456fb73f979649263',1,'Mail_Handler']]], + ['send_5fticketing_5fmail',['send_ticketing_mail',['../classMail__Handler.html#ab94d83ed9e6875da971c416c1752cbef',1,'Mail_Handler']]], + ['set',['set',['../classAssigned.html#ad4d2eececfa3d1ad7b72c8594f1eec33',1,'Assigned\set()'],['../classForwarded.html#ab2dfe7ba68ff867277ce008027561b62',1,'Forwarded\set()'],['../classIn__Support__Group.html#af0c47b359920019e0195ad31519437ea',1,'In_Support_Group\set()'],['../classQuerycache.html#a0c711df493c72eb461241855f9f34c5f',1,'Querycache\set()'],['../classSupport__Group.html#aabac23ba7e6eb67c3d7df596d651ef4b',1,'Support_Group\set()'],['../classTicket.html#a1e87f173b2cba0916092865c842c1075',1,'Ticket\set()'],['../classTicket__Info.html#af89b82bb0e3a885141a83f19bdacad2f',1,'Ticket_Info\set()'],['../classTicket__Log.html#a5faab1d6b0e6aeca20b51dd80112e565',1,'Ticket_Log\set()'],['../classTicket__Reply.html#adeb42b039232a828740377f23f7c8971',1,'Ticket_Reply\set()'],['../classTicket__User.html#af038656737c0518eab287ef4098d37e7',1,'Ticket_User\set()'],['../classWebUsers.html#a69330e33d34bc1f667644b5417999c8d',1,'WebUsers\set()']]], + ['setamsemail',['setAmsEmail',['../classUsers.html#aa28b8a5cb27f4330acf692f02a83d39f',1,'Users']]], + ['setamspassword',['setAmsPassword',['../classUsers.html#a6373c269ad6747ca429fa128e7eaf20b',1,'Users']]], + ['setauthor',['setAuthor',['../classTicket.html#aa906eb0d2cd7afa3ff8ba0afbf3537c7',1,'Ticket\setAuthor()'],['../classTicket__Log.html#abbcf255fe8cb6f84c325c2175fa77c69',1,'Ticket_Log\setAuthor()'],['../classTicket__Reply.html#ad7cf8e84af7401e5a415413a024b548d',1,'Ticket_Reply\setAuthor()']]], + ['setclient_5fversion',['setClient_Version',['../classTicket__Info.html#ab74158b0e29307c9df5e0118fc5ecf1a',1,'Ticket_Info']]], + ['setconnect_5fstate',['setConnect_State',['../classTicket__Info.html#af4f6bb975f3f102c78e11d92778a3dec',1,'Ticket_Info']]], + ['setcontent',['setContent',['../classTicket__Content.html#ab98a55ea6a3ca4a703803f3f36b162cb',1,'Ticket_Content\setContent()'],['../classTicket__Reply.html#a33b259de2020850920dffb5b7074c2ac',1,'Ticket_Reply\setContent()']]], + ['setcpu_5fmask',['setCPU_Mask',['../classTicket__Info.html#a4abad809d5da7e1e2c56e5114db5f8ed',1,'Ticket_Info']]], + ['setcpuid',['setCPUId',['../classTicket__Info.html#a071770d06778a2a5cf7c4c504bb230e6',1,'Ticket_Info']]], + ['setdb',['setDb',['../classQuerycache.html#a26331885b240e1ac6cd3b8409f6787d2',1,'Querycache']]], + ['setemail',['setEmail',['../classWebUsers.html#a687c7b5c20d9d2e10ac807e396eeaeca',1,'WebUsers']]], + ['setexternid',['setExternId',['../classTicket__User.html#af62595ffa430126baa9cb965174486f0',1,'Ticket_User']]], + ['setgroup',['setGroup',['../classForwarded.html#ae79dfb98cc8785d28ef4acff4ea3ddf2',1,'Forwarded\setGroup()'],['../classIn__Support__Group.html#adf6c62710753773bb90123e4d0f1c7b0',1,'In_Support_Group\setGroup()']]], + ['setgroupemail',['setGroupEmail',['../classSupport__Group.html#aed4561e1e14e810adc990de873227169',1,'Support_Group']]], + ['sethidden',['setHidden',['../classTicket__Reply.html#aa1296a31aa315f39e6133880ba06bf0b',1,'Ticket_Reply']]], + ['setht',['setHT',['../classTicket__Info.html#aeeed22b3cee00d2362f34e7078cc592a',1,'Ticket_Info']]], + ['setimap_5fmailserver',['setIMAP_MailServer',['../classSupport__Group.html#a078c0ce4be642b530567131ffc2b065a',1,'Support_Group']]], + ['setimap_5fpassword',['setIMAP_Password',['../classSupport__Group.html#a9a5ce866df0f4cc2cabee22b4e3936a7',1,'Support_Group']]], + ['setimap_5fusername',['setIMAP_Username',['../classSupport__Group.html#ac0d79d7e6aa23ad743aafce8e772db4e',1,'Support_Group']]], + ['setlanguage',['setLanguage',['../classWebUsers.html#a44c8a46add7a525e8902a4e2a3a9f9d1',1,'WebUsers']]], + ['setlocal_5faddress',['setLocal_Address',['../classTicket__Info.html#ad05594084b8e941f0114cd4b55b9a365',1,'Ticket_Info']]], + ['setmemory',['setMemory',['../classTicket__Info.html#aef05e49c066d9eb9b110d228c3cf9585',1,'Ticket_Info']]], + ['setname',['setName',['../classSupport__Group.html#adc21361d8243caad9c2e50fbb6a96140',1,'Support_Group\setName()'],['../classTicket__Category.html#afa5aabc3304855425c3efd65de740012',1,'Ticket_Category\setName()']]], + ['setnel3d',['setNel3D',['../classTicket__Info.html#a849f9d8f9f9f99fb51721b015b31b62a',1,'Ticket_Info']]], + ['setos',['setOS',['../classTicket__Info.html#af7ada3491b01382ae95aa3615a6cb294',1,'Ticket_Info']]], + ['setpassword',['setPassword',['../classWebUsers.html#abc9803620918174c45ebf4b1ee8b7b97',1,'WebUsers']]], + ['setpatch_5fversion',['setPatch_Version',['../classTicket__Info.html#a334a1a473d78c250e1987e8413f6da95',1,'Ticket_Info']]], + ['setpermission',['setPermission',['../classTicket__User.html#a3a35f8730a62f8bc527fda697a520df6',1,'Ticket_User']]], + ['setpriority',['setPriority',['../classTicket.html#a57bb563835bee337cbd87c77e94344c5',1,'Ticket']]], + ['setprocessor',['setProcessor',['../classTicket__Info.html#a51c321cf9227a8224c148d956cfc83bb',1,'Ticket_Info']]], + ['setquery',['setQuery',['../classQuerycache.html#a6e2e369125270695466baf423ae62ad2',1,'Querycache\setQuery()'],['../classTicket__Log.html#a8bdfb3350931c321e4f0b5e95e9edc72',1,'Ticket_Log\setQuery()']]], + ['setqueue',['setQueue',['../classTicket.html#a01c96663844da94d9e1bfa087cf2bce0',1,'Ticket']]], + ['setreceivemail',['setReceiveMail',['../classWebUsers.html#a5655d4bf05243a4e0ad2ffdc77ed02e2',1,'WebUsers']]], + ['setserver_5ftick',['setServer_Tick',['../classTicket__Info.html#a080e6773bbe65eca6c98c2c500ad277d',1,'Ticket_Info']]], + ['setsgroupid',['setSGroupId',['../classSupport__Group.html#a8ce0276b5ac26f1fdd18f9708ea0c84b',1,'Support_Group']]], + ['setshardid',['setShardId',['../classTicket__Info.html#a33610e4ba47f681782d5c6008196dc89',1,'Ticket_Info']]], + ['setsid',['setSID',['../classQuerycache.html#a7a4b7276270a9df57f7a5cc920fe5edc',1,'Querycache']]], + ['setstatus',['setStatus',['../classTicket.html#a3728c830f27453c477146d86adb92436',1,'Ticket']]], + ['settag',['setTag',['../classSupport__Group.html#ae67c636c5b11382fede819b00f3df40d',1,'Support_Group']]], + ['settcategoryid',['setTCategoryId',['../classTicket__Category.html#ab9c3df7c426fcc8d3de87b3b115e3aaa',1,'Ticket_Category']]], + ['settcontentid',['setTContentId',['../classTicket__Content.html#a8cb93fa5405ea177b5806211f0cc8c46',1,'Ticket_Content']]], + ['setticket',['setTicket',['../classAssigned.html#a80db44acb118dae58653443eb103402f',1,'Assigned\setTicket()'],['../classForwarded.html#a8885583370e3d29c42bdd9332f7e0398',1,'Forwarded\setTicket()'],['../classTicket__Info.html#aaa4df08bf37e477fb9631334f8310892',1,'Ticket_Info\setTicket()'],['../classTicket__Log.html#a428d22c1acf7713b66c6257de486885c',1,'Ticket_Log\setTicket()'],['../classTicket__Reply.html#ac3b44a5ab04a7234691403eec4c6a9b0',1,'Ticket_Reply\setTicket()']]], + ['setticket_5fcategory',['setTicket_Category',['../classTicket.html#aa53611afbc62ea031f9f2865a0d77721',1,'Ticket']]], + ['settid',['setTId',['../classTicket.html#ae0612846bbef3918b8ce95fe7f0d677e',1,'Ticket']]], + ['settimestamp',['setTimestamp',['../classTicket.html#a8496c014022175eb420071e61bfb3b6b',1,'Ticket\setTimestamp()'],['../classTicket__Log.html#aba55ce726ac8f07ad8e8fc394c88d2a0',1,'Ticket_Log\setTimestamp()'],['../classTicket__Reply.html#a86484ee918ae0134baa300329ffac4a7',1,'Ticket_Reply\setTimestamp()']]], + ['settinfoid',['setTInfoId',['../classTicket__Info.html#a1cc32cbfd19a31495aac2984ba267acf',1,'Ticket_Info']]], + ['settitle',['setTitle',['../classTicket.html#a873552a6011a56a75af7988e87e6f731',1,'Ticket']]], + ['settlogid',['setTLogId',['../classTicket__Log.html#a1176599841ab35b84321449d6d5cfb8d',1,'Ticket_Log']]], + ['settreplyid',['setTReplyId',['../classTicket__Reply.html#a2735ba1d359a5d095133c45c577a83b6',1,'Ticket_Reply']]], + ['settuserid',['setTUserId',['../classTicket__User.html#a3dd334c95ac15ccbc49c8971fab95b35',1,'Ticket_User']]], + ['settype',['setType',['../classQuerycache.html#ac4a9fb03e63f009d57045c1c23adf550',1,'Querycache']]], + ['setuser',['setUser',['../classAssigned.html#a5a9a262eec26f7b7607d269c36dac0a4',1,'Assigned\setUser()'],['../classIn__Support__Group.html#a83b004d30957217bf8fe570df50c4601',1,'In_Support_Group\setUser()']]], + ['setuser_5fid',['setUser_Id',['../classTicket__Info.html#a71a1a8a3907db742bb8c6900f575272a',1,'Ticket_Info']]], + ['setuser_5fposition',['setUser_Position',['../classTicket__Info.html#a973eeb99a4bd7074c8a30c223b7e1da1',1,'Ticket_Info']]], + ['setview_5fposition',['setView_Position',['../classTicket__Info.html#a077e1c9e3e8d53b9fd3a7b897b3c1805',1,'Ticket_Info']]], + ['supportgroup_5fentrynotexists',['supportGroup_EntryNotExists',['../classSupport__Group.html#ac87e9bcdb36a48a1c76037a7f647708d',1,'Support_Group']]], + ['supportgroup_5fexists',['supportGroup_Exists',['../classSupport__Group.html#ad57709a8f4323b92a9417d6f8f5aa4d4',1,'Support_Group']]], + ['syncdata',['syncdata',['../classSync.html#a98bd0a08014a652bde33ae9e42004ed3',1,'Sync']]] +]; diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_74.html b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_74.html new file mode 100644 index 000000000..e3c96c339 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_74.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_74.js b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_74.js new file mode 100644 index 000000000..9dc11b114 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_74.js @@ -0,0 +1,6 @@ +var searchData= +[ + ['ticketexists',['ticketExists',['../classTicket.html#a669c205f88c73c7857a5a2beb9a159ae',1,'Ticket']]], + ['tickethasinfo',['TicketHasInfo',['../classTicket__Info.html#a9bf51b7fe6c31ad6f114bea0f510302d',1,'Ticket_Info']]], + ['time_5felapsed_5fstring',['time_elapsed_string',['../classGui__Elements.html#afb74abd6dc4abcae4e035e23eb22619a',1,'Gui_Elements']]] +]; diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_75.html b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_75.html new file mode 100644 index 000000000..2d61754d1 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_75.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_75.js b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_75.js new file mode 100644 index 000000000..a4ddcbb30 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_75.js @@ -0,0 +1,8 @@ +var searchData= +[ + ['unassignticket',['unAssignTicket',['../classAssigned.html#ab4713000e4216c858bf62783a96e6770',1,'Assigned\unAssignTicket()'],['../classTicket.html#a344f4b5e5ae5f1312c0647c6779bfc5c',1,'Ticket\unAssignTicket()']]], + ['update',['update',['../classQuerycache.html#a5840a78a238aeccc749f9e76fd9bfcde',1,'Querycache\update()'],['../classSupport__Group.html#ab454ffc861833bb6aa3a4cea3ae5c785',1,'Support_Group\update()'],['../classTicket.html#a4f834700b722501738b5c20c1cb1e719',1,'Ticket\update()'],['../classTicket__Category.html#a9120c893d7a2db005dec5439f3868ff6',1,'Ticket_Category\update()'],['../classTicket__Content.html#abafe7398dea17511c92764d8f910e401',1,'Ticket_Content\update()'],['../classTicket__Log.html#a55abe9e6ffb80530c0df403a45c724f7',1,'Ticket_Log\update()'],['../classTicket__Reply.html#a4286b6ece613a13eb1eec7f259f36c8f',1,'Ticket_Reply\update()'],['../classTicket__User.html#a68c6a834f65de5fbdddaa9818cf4842d',1,'Ticket_User\update()']]], + ['updateticketstatus',['updateTicketStatus',['../classTicket.html#a10b96b8e6426201809f09cd35b251b28',1,'Ticket']]], + ['updateticketstatusandpriority',['updateTicketStatusAndPriority',['../classTicket.html#a8b58a3d21d48f7295de71be30e740af8',1,'Ticket']]], + ['userexistsinsgroup',['userExistsInSGroup',['../classIn__Support__Group.html#a5c50054a5ee9171859a329f9e3ef57bb',1,'In_Support_Group']]] +]; diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_76.html b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_76.html new file mode 100644 index 000000000..2ec05695c --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_76.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_76.js b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_76.js new file mode 100644 index 000000000..db7c7fe32 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_76.js @@ -0,0 +1,4 @@ +var searchData= +[ + ['validemail',['validEmail',['../classUsers.html#a090fa5f2ca4565c03ffd21eae1393ab0',1,'Users']]] +]; diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/mag_sel.png b/code/ryzom/tools/server/ryzom_ams_docs/html/search/mag_sel.png new file mode 100644 index 0000000000000000000000000000000000000000..81f6040a2092402b4d98f9ffa8855d12a0d4ca17 GIT binary patch literal 563 zcmV-30?hr1P)zxx&tqG15pu7)IiiXFflOc2k;dXd>%13GZAy? zRz!q0=|E6a6vV)&ZBS~G9oe0kbqyw1*gvY`{Pop2oKq#FlzgXt@Xh-7fxh>}`Fxg> z$%N%{$!4=5nM{(;=c!aG1Ofr^Do{u%Ih{^&Fc@H2)+a-?TBXrw5DW&z%Nb6mQ!L9O zl}b@6mB?f=tX3;#vl)}ggh(Vpyh(IK z(Mb0D{l{U$FsRjP;!{($+bsaaVi8T#1c0V#qEIOCYa9@UVLV`f__E81L;?WEaRA;Y zUH;rZ;vb;mk7JX|$=i3O~&If0O@oZfLg8gfIjW=dcBsz;gI=!{-r4# z4%6v$&~;q^j7Fo67yJ(NJWuX+I~I!tj^nW3?}^9bq|<3^+vapS5sgM^x7!cs(+mMT z&y%j};&~po+YO)3hoUH4E*E;e9>?R6SS&`X)p`njycAVcg{rEb41T{~Hk(bl-7eSb zmFxA2uIqo#@R?lKm50ND`~6Nfn|-b1|L6O98vt3Tx@gKz#isxO002ovPDHLkV1kyW B_l^Jn literal 0 HcmV?d00001 diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/nomatches.html b/code/ryzom/tools/server/ryzom_ams_docs/html/search/nomatches.html new file mode 100644 index 000000000..b1ded27e9 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/nomatches.html @@ -0,0 +1,12 @@ + + + + + + + +
    +
    No Matches
    +
    + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/search.css b/code/ryzom/tools/server/ryzom_ams_docs/html/search/search.css new file mode 100644 index 000000000..d18c1da8c --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/search.css @@ -0,0 +1,238 @@ +/*---------------- Search Box */ + +#FSearchBox { + float: left; +} + +#MSearchBox { + white-space : nowrap; + position: absolute; + float: none; + display: inline; + margin-top: 8px; + right: 0px; + width: 170px; + z-index: 102; + background-color: white; +} + +#MSearchBox .left +{ + display:block; + position:absolute; + left:10px; + width:20px; + height:19px; + background:url('search_l.png') no-repeat; + background-position:right; +} + +#MSearchSelect { + display:block; + position:absolute; + width:20px; + height:19px; +} + +.left #MSearchSelect { + left:4px; +} + +.right #MSearchSelect { + right:5px; +} + +#MSearchField { + display:block; + position:absolute; + height:19px; + background:url('search_m.png') repeat-x; + border:none; + width:116px; + margin-left:20px; + padding-left:4px; + color: #909090; + outline: none; + font: 9pt Arial, Verdana, sans-serif; +} + +#FSearchBox #MSearchField { + margin-left:15px; +} + +#MSearchBox .right { + display:block; + position:absolute; + right:10px; + top:0px; + width:20px; + height:19px; + background:url('search_r.png') no-repeat; + background-position:left; +} + +#MSearchClose { + display: none; + position: absolute; + top: 4px; + background : none; + border: none; + margin: 0px 4px 0px 0px; + padding: 0px 0px; + outline: none; +} + +.left #MSearchClose { + left: 6px; +} + +.right #MSearchClose { + right: 2px; +} + +.MSearchBoxActive #MSearchField { + color: #000000; +} + +/*---------------- Search filter selection */ + +#MSearchSelectWindow { + display: none; + position: absolute; + left: 0; top: 0; + border: 1px solid #90A5CE; + background-color: #F9FAFC; + z-index: 1; + padding-top: 4px; + padding-bottom: 4px; + -moz-border-radius: 4px; + -webkit-border-top-left-radius: 4px; + -webkit-border-top-right-radius: 4px; + -webkit-border-bottom-left-radius: 4px; + -webkit-border-bottom-right-radius: 4px; + -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); +} + +.SelectItem { + font: 8pt Arial, Verdana, sans-serif; + padding-left: 2px; + padding-right: 12px; + border: 0px; +} + +span.SelectionMark { + margin-right: 4px; + font-family: monospace; + outline-style: none; + text-decoration: none; +} + +a.SelectItem { + display: block; + outline-style: none; + color: #000000; + text-decoration: none; + padding-left: 6px; + padding-right: 12px; +} + +a.SelectItem:focus, +a.SelectItem:active { + color: #000000; + outline-style: none; + text-decoration: none; +} + +a.SelectItem:hover { + color: #FFFFFF; + background-color: #3D578C; + outline-style: none; + text-decoration: none; + cursor: pointer; + display: block; +} + +/*---------------- Search results window */ + +iframe#MSearchResults { + width: 60ex; + height: 15em; +} + +#MSearchResultsWindow { + display: none; + position: absolute; + left: 0; top: 0; + border: 1px solid #000; + background-color: #EEF1F7; +} + +/* ----------------------------------- */ + + +#SRIndex { + clear:both; + padding-bottom: 15px; +} + +.SREntry { + font-size: 10pt; + padding-left: 1ex; +} + +.SRPage .SREntry { + font-size: 8pt; + padding: 1px 5px; +} + +body.SRPage { + margin: 5px 2px; +} + +.SRChildren { + padding-left: 3ex; padding-bottom: .5em +} + +.SRPage .SRChildren { + display: none; +} + +.SRSymbol { + font-weight: bold; + color: #425E97; + font-family: Arial, Verdana, sans-serif; + text-decoration: none; + outline: none; +} + +a.SRScope { + display: block; + color: #425E97; + font-family: Arial, Verdana, sans-serif; + text-decoration: none; + outline: none; +} + +a.SRSymbol:focus, a.SRSymbol:active, +a.SRScope:focus, a.SRScope:active { + text-decoration: underline; +} + +span.SRScope { + padding-left: 4px; +} + +.SRPage .SRStatus { + padding: 2px 5px; + font-size: 8pt; + font-style: italic; +} + +.SRResult { + display: none; +} + +DIV.searchresults { + margin-left: 10px; + margin-right: 10px; +} diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/search.js b/code/ryzom/tools/server/ryzom_ams_docs/html/search/search.js new file mode 100644 index 000000000..ce8abb55b --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/search.js @@ -0,0 +1,801 @@ +// Search script generated by doxygen +// Copyright (C) 2009 by Dimitri van Heesch. + +// The code in this file is loosly based on main.js, part of Natural Docs, +// which is Copyright (C) 2003-2008 Greg Valure +// Natural Docs is licensed under the GPL. + +var indexSectionsWithContent = +{ + 0: "0000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000010101111111001111110111110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + 1: "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100101111000100110111010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + 2: "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010101111111001111000111100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + 3: "0000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" +}; + +var indexSectionNames = +{ + 0: "all", + 1: "classes", + 2: "functions", + 3: "variables" +}; + +function convertToId(search) +{ + var result = ''; + for (i=0;i do a search + { + this.Search(); + } + } + + this.OnSearchSelectKey = function(evt) + { + var e = (evt) ? evt : window.event; // for IE + if (e.keyCode==40 && this.searchIndex0) // Up + { + this.searchIndex--; + this.OnSelectItem(this.searchIndex); + } + else if (e.keyCode==13 || e.keyCode==27) + { + this.OnSelectItem(this.searchIndex); + this.CloseSelectionWindow(); + this.DOMSearchField().focus(); + } + return false; + } + + // --------- Actions + + // Closes the results window. + this.CloseResultsWindow = function() + { + this.DOMPopupSearchResultsWindow().style.display = 'none'; + this.DOMSearchClose().style.display = 'none'; + this.Activate(false); + } + + this.CloseSelectionWindow = function() + { + this.DOMSearchSelectWindow().style.display = 'none'; + } + + // Performs a search. + this.Search = function() + { + this.keyTimeout = 0; + + // strip leading whitespace + var searchValue = this.DOMSearchField().value.replace(/^ +/, ""); + + var code = searchValue.toLowerCase().charCodeAt(0); + var hexCode; + if (code<16) + { + hexCode="0"+code.toString(16); + } + else + { + hexCode=code.toString(16); + } + + var resultsPage; + var resultsPageWithSearch; + var hasResultsPage; + + if (indexSectionsWithContent[this.searchIndex].charAt(code) == '1') + { + resultsPage = this.resultsPath + '/' + indexSectionNames[this.searchIndex] + '_' + hexCode + '.html'; + resultsPageWithSearch = resultsPage+'?'+escape(searchValue); + hasResultsPage = true; + } + else // nothing available for this search term + { + resultsPage = this.resultsPath + '/nomatches.html'; + resultsPageWithSearch = resultsPage; + hasResultsPage = false; + } + + window.frames.MSearchResults.location = resultsPageWithSearch; + var domPopupSearchResultsWindow = this.DOMPopupSearchResultsWindow(); + + if (domPopupSearchResultsWindow.style.display!='block') + { + var domSearchBox = this.DOMSearchBox(); + this.DOMSearchClose().style.display = 'inline'; + if (this.insideFrame) + { + var domPopupSearchResults = this.DOMPopupSearchResults(); + domPopupSearchResultsWindow.style.position = 'relative'; + domPopupSearchResultsWindow.style.display = 'block'; + var width = document.body.clientWidth - 8; // the -8 is for IE :-( + domPopupSearchResultsWindow.style.width = width + 'px'; + domPopupSearchResults.style.width = width + 'px'; + } + else + { + var domPopupSearchResults = this.DOMPopupSearchResults(); + var left = getXPos(domSearchBox) + 150; // domSearchBox.offsetWidth; + var top = getYPos(domSearchBox) + 20; // domSearchBox.offsetHeight + 1; + domPopupSearchResultsWindow.style.display = 'block'; + left -= domPopupSearchResults.offsetWidth; + domPopupSearchResultsWindow.style.top = top + 'px'; + domPopupSearchResultsWindow.style.left = left + 'px'; + } + } + + this.lastSearchValue = searchValue; + this.lastResultsPage = resultsPage; + } + + // -------- Activation Functions + + // Activates or deactivates the search panel, resetting things to + // their default values if necessary. + this.Activate = function(isActive) + { + if (isActive || // open it + this.DOMPopupSearchResultsWindow().style.display == 'block' + ) + { + this.DOMSearchBox().className = 'MSearchBoxActive'; + + var searchField = this.DOMSearchField(); + + if (searchField.value == this.searchLabel) // clear "Search" term upon entry + { + searchField.value = ''; + this.searchActive = true; + } + } + else if (!isActive) // directly remove the panel + { + this.DOMSearchBox().className = 'MSearchBoxInactive'; + this.DOMSearchField().value = this.searchLabel; + this.searchActive = false; + this.lastSearchValue = '' + this.lastResultsPage = ''; + } + } +} + +// ----------------------------------------------------------------------- + +// The class that handles everything on the search results page. +function SearchResults(name) +{ + // The number of matches from the last run of . + this.lastMatchCount = 0; + this.lastKey = 0; + this.repeatOn = false; + + // Toggles the visibility of the passed element ID. + this.FindChildElement = function(id) + { + var parentElement = document.getElementById(id); + var element = parentElement.firstChild; + + while (element && element!=parentElement) + { + if (element.nodeName == 'DIV' && element.className == 'SRChildren') + { + return element; + } + + if (element.nodeName == 'DIV' && element.hasChildNodes()) + { + element = element.firstChild; + } + else if (element.nextSibling) + { + element = element.nextSibling; + } + else + { + do + { + element = element.parentNode; + } + while (element && element!=parentElement && !element.nextSibling); + + if (element && element!=parentElement) + { + element = element.nextSibling; + } + } + } + } + + this.Toggle = function(id) + { + var element = this.FindChildElement(id); + if (element) + { + if (element.style.display == 'block') + { + element.style.display = 'none'; + } + else + { + element.style.display = 'block'; + } + } + } + + // Searches for the passed string. If there is no parameter, + // it takes it from the URL query. + // + // Always returns true, since other documents may try to call it + // and that may or may not be possible. + this.Search = function(search) + { + if (!search) // get search word from URL + { + search = window.location.search; + search = search.substring(1); // Remove the leading '?' + search = unescape(search); + } + + search = search.replace(/^ +/, ""); // strip leading spaces + search = search.replace(/ +$/, ""); // strip trailing spaces + search = search.toLowerCase(); + search = convertToId(search); + + var resultRows = document.getElementsByTagName("div"); + var matches = 0; + + var i = 0; + while (i < resultRows.length) + { + var row = resultRows.item(i); + if (row.className == "SRResult") + { + var rowMatchName = row.id.toLowerCase(); + rowMatchName = rowMatchName.replace(/^sr\d*_/, ''); // strip 'sr123_' + + if (search.length<=rowMatchName.length && + rowMatchName.substr(0, search.length)==search) + { + row.style.display = 'block'; + matches++; + } + else + { + row.style.display = 'none'; + } + } + i++; + } + document.getElementById("Searching").style.display='none'; + if (matches == 0) // no results + { + document.getElementById("NoMatches").style.display='block'; + } + else // at least one result + { + document.getElementById("NoMatches").style.display='none'; + } + this.lastMatchCount = matches; + return true; + } + + // return the first item with index index or higher that is visible + this.NavNext = function(index) + { + var focusItem; + while (1) + { + var focusName = 'Item'+index; + focusItem = document.getElementById(focusName); + if (focusItem && focusItem.parentNode.parentNode.style.display=='block') + { + break; + } + else if (!focusItem) // last element + { + break; + } + focusItem=null; + index++; + } + return focusItem; + } + + this.NavPrev = function(index) + { + var focusItem; + while (1) + { + var focusName = 'Item'+index; + focusItem = document.getElementById(focusName); + if (focusItem && focusItem.parentNode.parentNode.style.display=='block') + { + break; + } + else if (!focusItem) // last element + { + break; + } + focusItem=null; + index--; + } + return focusItem; + } + + this.ProcessKeys = function(e) + { + if (e.type == "keydown") + { + this.repeatOn = false; + this.lastKey = e.keyCode; + } + else if (e.type == "keypress") + { + if (!this.repeatOn) + { + if (this.lastKey) this.repeatOn = true; + return false; // ignore first keypress after keydown + } + } + else if (e.type == "keyup") + { + this.lastKey = 0; + this.repeatOn = false; + } + return this.lastKey!=0; + } + + this.Nav = function(evt,itemIndex) + { + var e = (evt) ? evt : window.event; // for IE + if (e.keyCode==13) return true; + if (!this.ProcessKeys(e)) return false; + + if (this.lastKey==38) // Up + { + var newIndex = itemIndex-1; + var focusItem = this.NavPrev(newIndex); + if (focusItem) + { + var child = this.FindChildElement(focusItem.parentNode.parentNode.id); + if (child && child.style.display == 'block') // children visible + { + var n=0; + var tmpElem; + while (1) // search for last child + { + tmpElem = document.getElementById('Item'+newIndex+'_c'+n); + if (tmpElem) + { + focusItem = tmpElem; + } + else // found it! + { + break; + } + n++; + } + } + } + if (focusItem) + { + focusItem.focus(); + } + else // return focus to search field + { + parent.document.getElementById("MSearchField").focus(); + } + } + else if (this.lastKey==40) // Down + { + var newIndex = itemIndex+1; + var focusItem; + var item = document.getElementById('Item'+itemIndex); + var elem = this.FindChildElement(item.parentNode.parentNode.id); + if (elem && elem.style.display == 'block') // children visible + { + focusItem = document.getElementById('Item'+itemIndex+'_c0'); + } + if (!focusItem) focusItem = this.NavNext(newIndex); + if (focusItem) focusItem.focus(); + } + else if (this.lastKey==39) // Right + { + var item = document.getElementById('Item'+itemIndex); + var elem = this.FindChildElement(item.parentNode.parentNode.id); + if (elem) elem.style.display = 'block'; + } + else if (this.lastKey==37) // Left + { + var item = document.getElementById('Item'+itemIndex); + var elem = this.FindChildElement(item.parentNode.parentNode.id); + if (elem) elem.style.display = 'none'; + } + else if (this.lastKey==27) // Escape + { + parent.searchBox.CloseResultsWindow(); + parent.document.getElementById("MSearchField").focus(); + } + else if (this.lastKey==13) // Enter + { + return true; + } + return false; + } + + this.NavChild = function(evt,itemIndex,childIndex) + { + var e = (evt) ? evt : window.event; // for IE + if (e.keyCode==13) return true; + if (!this.ProcessKeys(e)) return false; + + if (this.lastKey==38) // Up + { + if (childIndex>0) + { + var newIndex = childIndex-1; + document.getElementById('Item'+itemIndex+'_c'+newIndex).focus(); + } + else // already at first child, jump to parent + { + document.getElementById('Item'+itemIndex).focus(); + } + } + else if (this.lastKey==40) // Down + { + var newIndex = childIndex+1; + var elem = document.getElementById('Item'+itemIndex+'_c'+newIndex); + if (!elem) // last child, jump to parent next parent + { + elem = this.NavNext(itemIndex+1); + } + if (elem) + { + elem.focus(); + } + } + else if (this.lastKey==27) // Escape + { + parent.searchBox.CloseResultsWindow(); + parent.document.getElementById("MSearchField").focus(); + } + else if (this.lastKey==13) // Enter + { + return true; + } + return false; + } +} + +function setKeyActions(elem,action) +{ + elem.setAttribute('onkeydown',action); + elem.setAttribute('onkeypress',action); + elem.setAttribute('onkeyup',action); +} + +function setClassAttr(elem,attr) +{ + elem.setAttribute('class',attr); + elem.setAttribute('className',attr); +} + +function createResults() +{ + var results = document.getElementById("SRResults"); + for (var e=0; ek7RCwB~R6VQOP#AvB$vH7i{6H{96zot$7cZT<7246EF5Np6N}+$IbiG6W zg#87A+NFaX+=_^xM1#gCtshC=E{%9^uQX_%?YwXvo{#q&MnpJ8uh(O?ZRc&~_1%^SsPxG@rfElJg-?U zm!Cz-IOn(qJP3kDp-^~qt+FGbl=5jNli^Wj_xIBG{Rc0en{!oFvyoNC7{V~T8}b>| z=jL2WIReZzX(YN(_9fV;BBD$VXQIxNasAL8ATvEu822WQ%mvv4FO#qs` BFGc_W literal 0 HcmV?d00001 diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/search_r.png b/code/ryzom/tools/server/ryzom_ams_docs/html/search/search_r.png new file mode 100644 index 0000000000000000000000000000000000000000..97ee8b439687084201b79c6f776a41f495c6392a GIT binary patch literal 612 zcmV-q0-ODbP)PbXFRCwB?)W514K@j&X?z2*SxFI6-@HT2E2K=9X9%Pb zEK*!TBw&g(DMC;|A)uGlRkOS9vd-?zNs%bR4d$w+ox_iFnE8fvIvv7^5<(>Te12Li z7C)9srCzmK{ZcNM{YIl9j{DePFgOWiS%xG@5CnnnJa4nvY<^glbz7^|-ZY!dUkAwd z{gaTC@_>b5h~;ug#R0wRL0>o5!hxm*s0VW?8dr}O#zXTRTnrQm_Z7z1Mrnx>&p zD4qifUjzLvbVVWi?l?rUzwt^sdb~d!f_LEhsRVIXZtQ=qSxuxqm zEX#tf>$?M_Y1-LSDT)HqG?`%-%ZpY!#{N!rcNIiL;G7F0`l?)mNGTD9;f9F5Up3Kg zw}a<-JylhG&;=!>B+fZaCX+?C+kHYrP%c?X2!Zu_olK|GcS4A70HEy;vn)I0>0kLH z`jc(WIaaHc7!HS@f*^R^Znx8W=_jIl2oWJoQ*h1^$FX!>*PqR1J8k|fw}w_y}TpE>7m8DqDO<3z`OzXt$ccSejbEZCg@0000 + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/search/variables_24.js b/code/ryzom/tools/server/ryzom_ams_docs/html/search/variables_24.js new file mode 100644 index 000000000..afa19a40e --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/search/variables_24.js @@ -0,0 +1,68 @@ +var searchData= +[ + ['_24amountofrows',['$amountOfRows',['../classPagination.html#a0cd85db34fde63f50175dc68951b2965',1,'Pagination']]], + ['_24author',['$author',['../classTicket.html#a800d58b1adb950b389ed8170a8bf95ea',1,'Ticket\$author()'],['../classTicket__Log.html#ad1458fe1e2a7831a7b53ea61adb21db1',1,'Ticket_Log\$author()'],['../classTicket__Reply.html#ac8fd9d68eece69dd9103bb8bc6d6e12e',1,'Ticket_Reply\$author()']]], + ['_24client_5fversion',['$client_version',['../classTicket__Info.html#a8b0f292dc30fe169fbfb160710ab9a5d',1,'Ticket_Info']]], + ['_24config',['$config',['../classMyCrypt.html#a9701fd447bb31495494d7580f8fbad31',1,'MyCrypt']]], + ['_24connect_5fstate',['$connect_state',['../classTicket__Info.html#a192ebb3925ab30beb6fa90c6c881abaa',1,'Ticket_Info']]], + ['_24content',['$content',['../classTicket__Content.html#a0b76d58db5325853374f459b56535040',1,'Ticket_Content\$content()'],['../classTicket__Reply.html#a8f40940bb3b2cbcc6e2f60570a435e33',1,'Ticket_Reply\$content()']]], + ['_24country',['$country',['../classWebUsers.html#a0aeb933ed72691c038d40ffc57ec1504',1,'WebUsers']]], + ['_24cpu_5fid',['$cpu_id',['../classTicket__Info.html#a007fb1bced444de7ea2e18558f8f4877',1,'Ticket_Info']]], + ['_24cpu_5fmask',['$cpu_mask',['../classTicket__Info.html#af93d1a84a429aaabfd84f1d244fc44b8',1,'Ticket_Info']]], + ['_24current',['$current',['../classPagination.html#ae7664ad0a9768c43b1c3920271e7f07e',1,'Pagination']]], + ['_24db',['$db',['../classMail__Handler.html#a586665d45069c9b7ffcc9759872b30f5',1,'Mail_Handler\$db()'],['../classQuerycache.html#a781742cc162f5714e15c7ef7dc30ebb2',1,'Querycache\$db()']]], + ['_24element_5farray',['$element_array',['../classPagination.html#a0691015765a20fb52a5b95509051c52a',1,'Pagination']]], + ['_24email',['$email',['../classWebUsers.html#a532667fc1fa71ee38f3b8a9a0bbb3991',1,'WebUsers']]], + ['_24externid',['$externId',['../classTicket__User.html#abee3b416d7f7db5bf23ed39a095c36c6',1,'Ticket_User']]], + ['_24firstname',['$firstname',['../classWebUsers.html#a39d55355d537ad77e08f2c196b1b39b5',1,'WebUsers']]], + ['_24gender',['$gender',['../classWebUsers.html#a101472ee52f1e37416b472fdd151a6b4',1,'WebUsers']]], + ['_24group',['$group',['../classForwarded.html#a66cff5a347920873ffe30f715065d340',1,'Forwarded\$group()'],['../classIn__Support__Group.html#a4baa32d40511a8e53a942346c997424c',1,'In_Support_Group\$group()']]], + ['_24groupemail',['$groupEmail',['../classSupport__Group.html#a4255fbb39299728472147bafc838eacd',1,'Support_Group']]], + ['_24hidden',['$hidden',['../classTicket__Reply.html#a319fbe3e5d64f295800b270a1ed21260',1,'Ticket_Reply']]], + ['_24ht',['$ht',['../classTicket__Info.html#adcb070ed9b106f52207fe66b228d7594',1,'Ticket_Info']]], + ['_24imap_5fmailserver',['$iMAP_MailServer',['../classSupport__Group.html#a15d68505e0f11fb84d9c1c90340e391b',1,'Support_Group']]], + ['_24imap_5fpassword',['$iMAP_Password',['../classSupport__Group.html#a78c5c6a76d106eb4264417336bccd431',1,'Support_Group']]], + ['_24imap_5fusername',['$iMAP_Username',['../classSupport__Group.html#aed9c9e853a744e8148262a263879d5e7',1,'Support_Group']]], + ['_24language',['$language',['../classWebUsers.html#af7e97cee72066f85737453d81c931550',1,'WebUsers']]], + ['_24last',['$last',['../classPagination.html#a761c473a0e483c48b20ee5b784135883',1,'Pagination']]], + ['_24lastname',['$lastname',['../classWebUsers.html#ac0122258b652e5629269ec98db168a44',1,'WebUsers']]], + ['_24local_5faddress',['$local_address',['../classTicket__Info.html#a31c74f0ade3e12188fba83f5eb8814c7',1,'Ticket_Info']]], + ['_24login',['$login',['../classWebUsers.html#a7b61ca04b134c0b5452fa2e61f4b36bd',1,'WebUsers']]], + ['_24memory',['$memory',['../classTicket__Info.html#a28200cd05abfb56f1f4464eeb83bbd85',1,'Ticket_Info']]], + ['_24name',['$name',['../classSupport__Group.html#a7e3e953217f5ff39f6e5e4713f9d1897',1,'Support_Group\$name()'],['../classTicket__Category.html#a81ea14ddb8794f34800aad4e9d0baf91',1,'Ticket_Category\$name()']]], + ['_24nel3d',['$nel3d',['../classTicket__Info.html#a44a096a9876a075b728ee5f0f63e13d5',1,'Ticket_Info']]], + ['_24os',['$os',['../classTicket__Info.html#af1e68104013e10fec558878888aca10d',1,'Ticket_Info']]], + ['_24pagination',['$pagination',['../classTicket__Queue__Handler.html#acadf7665fa564ba8c6dbaf047d3a220b',1,'Ticket_Queue_Handler']]], + ['_24params',['$params',['../classTicket__Queue.html#ad87a813bbfd9ce28b5f7d763f094e655',1,'Ticket_Queue']]], + ['_24patch_5fversion',['$patch_version',['../classTicket__Info.html#a0b167808c021482f66ace1107780bd39',1,'Ticket_Info']]], + ['_24pdo',['$PDO',['../classDBLayer.html#a0019ba4102cd0e107fbe91bd5cc43425',1,'DBLayer']]], + ['_24permission',['$permission',['../classTicket__User.html#a2a3f7ae998f3926ab666167c9f63bcfc',1,'Ticket_User']]], + ['_24priority',['$priority',['../classTicket.html#a0ccc2e11515a6a15e46a0fcba94d0d79',1,'Ticket']]], + ['_24processor',['$processor',['../classTicket__Info.html#acd56a987584494704425a36bbfbf0fc5',1,'Ticket_Info']]], + ['_24query',['$query',['../classQuerycache.html#a6be0975d09b713fdacc2e32330d30c74',1,'Querycache\$query()'],['../classTicket__Log.html#a7b8691897e69a12536fff2c3f3c29ae8',1,'Ticket_Log\$query()'],['../classTicket__Queue.html#a83d238bbb14b4ca80ff563b8d1992355',1,'Ticket_Queue\$query()']]], + ['_24queue',['$queue',['../classTicket.html#a5ad011635a520888002255901cb7a2a5',1,'Ticket\$queue()'],['../classTicket__Queue__Handler.html#ae9d10b4bfc09d7cd09a2609f3317686f',1,'Ticket_Queue_Handler\$queue()']]], + ['_24receivemail',['$receiveMail',['../classWebUsers.html#a7186a146bb276d51181b9e62ad0ed8c5',1,'WebUsers']]], + ['_24server_5ftick',['$server_tick',['../classTicket__Info.html#a7627ce03ac1afd90cf86b94ab48fbd9c',1,'Ticket_Info']]], + ['_24sgroupid',['$sGroupId',['../classSupport__Group.html#a26af3960be749dfdbd9d677391d7f3c3',1,'Support_Group']]], + ['_24shardid',['$shardid',['../classTicket__Info.html#a44cd7539051540e36402b7d368e67fd3',1,'Ticket_Info']]], + ['_24sid',['$SID',['../classQuerycache.html#a30393f88b13bfd390c76cc94454b123e',1,'Querycache']]], + ['_24status',['$status',['../classTicket.html#a4e6917333bc2f16a9c12a44b1d12335e',1,'Ticket']]], + ['_24tag',['$tag',['../classSupport__Group.html#aa93b8392d651b40fd208bd65f71d55cc',1,'Support_Group']]], + ['_24tcategoryid',['$tCategoryId',['../classTicket__Category.html#a2928ce0f2387b21b42bc6ede4a3f0e6a',1,'Ticket_Category']]], + ['_24tcontentid',['$tContentId',['../classTicket__Content.html#a0865e27754d94bf8e036ae0539e84c33',1,'Ticket_Content']]], + ['_24ticket',['$ticket',['../classAssigned.html#a4e729931e02c5ea56933dd18d5cdfc25',1,'Assigned\$ticket()'],['../classForwarded.html#ad5345ede5ff6a41143a382ad0102958e',1,'Forwarded\$ticket()'],['../classTicket__Info.html#a6a88dbc8a703a1c493d33f17360e9d79',1,'Ticket_Info\$ticket()'],['../classTicket__Log.html#ae7c23a6f7c0c9f083a3e01f3bcedf2b3',1,'Ticket_Log\$ticket()'],['../classTicket__Reply.html#ab09b437216a7b0641a6da2b455218a89',1,'Ticket_Reply\$ticket()']]], + ['_24ticket_5fcategory',['$ticket_category',['../classTicket.html#abe4fc08f405ef045048cebaf16a3604d',1,'Ticket']]], + ['_24tid',['$tId',['../classTicket.html#a8d2c0138844cb7225dcd6b7403d9a001',1,'Ticket']]], + ['_24timestamp',['$timestamp',['../classTicket.html#ac52a47a12cfbe87a9384a600eade0440',1,'Ticket\$timestamp()'],['../classTicket__Log.html#a89151c1a9a3cd99c14cceec07105db15',1,'Ticket_Log\$timestamp()'],['../classTicket__Reply.html#a747f6ed39e4343974a97b58d2439d1d9',1,'Ticket_Reply\$timestamp()']]], + ['_24tinfoid',['$tInfoId',['../classTicket__Info.html#ab324ecd00efe8324a0891fabff6161ae',1,'Ticket_Info']]], + ['_24title',['$title',['../classTicket.html#a49401c28cce1972efbe9208266e71dd3',1,'Ticket']]], + ['_24tlogid',['$tLogId',['../classTicket__Log.html#af6d2b7abef8e6f4ff14fefd02a5cbe11',1,'Ticket_Log']]], + ['_24treplyid',['$tReplyId',['../classTicket__Reply.html#a3147d06712c721703250e3435447467f',1,'Ticket_Reply']]], + ['_24tuserid',['$tUserId',['../classTicket__User.html#ab017c0eab7b6083b3de58c8388251b50',1,'Ticket_User']]], + ['_24type',['$type',['../classQuerycache.html#a299788dde0e69545f3e0199132f013d2',1,'Querycache']]], + ['_24uid',['$uId',['../classWebUsers.html#a4c118152530d9b7a1aab240392b504b8',1,'WebUsers']]], + ['_24user',['$user',['../classAssigned.html#a9d9ca239af74fc2671568461854310da',1,'Assigned\$user()'],['../classIn__Support__Group.html#a50e2869129d0542a657de987e21e8517',1,'In_Support_Group\$user()']]], + ['_24user_5fid',['$user_id',['../classTicket__Info.html#a3007474eed1000a0bb99058d1d27b506',1,'Ticket_Info']]], + ['_24user_5fposition',['$user_position',['../classTicket__Info.html#a11602c42f574f902fb1e43a064c2ef9b',1,'Ticket_Info']]], + ['_24view_5fposition',['$view_position',['../classTicket__Info.html#ad45f4c7aa17b628789c2c2c81bfaba52',1,'Ticket_Info']]] +]; diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/tab_a.png b/code/ryzom/tools/server/ryzom_ams_docs/html/tab_a.png new file mode 100644 index 0000000000000000000000000000000000000000..2d99ef23fed78c7683f0b5aa803d937060d288c4 GIT binary patch literal 140 zcmeAS@N?(olHy`uVBq!ia0vp^j6kfy!2~3aiye;!Qo)`sjv*C{Z|CmjY;X`^DSv)) z;hc^cTF;t%XWXdwWP5+kt?jQ5uhqKtjd^EY`^^-S;M%tFAj_l)EwVTK)E@1LSD0{e q?a6($SGQTzz1#QBzr0NMKf^0WCX-0bi?u-G89ZJ6T-G@yGywp8?ljB* literal 0 HcmV?d00001 diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/tab_b.png b/code/ryzom/tools/server/ryzom_ams_docs/html/tab_b.png new file mode 100644 index 0000000000000000000000000000000000000000..b2c3d2be3c7e518fbca6bb30f571882e72fc506d GIT binary patch literal 178 zcmeAS@N?(olHy`uVBq!ia0vp^j6kfy!2~3aiye;!Qk9-Ajv*C{Z|~mbJ)|JfaM8Xd zIP7xAmLwau9@iXhZTrl-TjWj9jM#?{xt`6uU{<)jb9Suc^QnbhJ(o{ib8=j9u0_mE8M7kgF7f<7W7IEf=8(L_qx|g0H;V7iPxm&Q@G7p8W2Kx&iT|YUM=ITC zY<0Qbr;u&AtXD{o@41wH=7&d8=2Z_{M9Tsa=g*t*@A3H$UOlxZk7?f6RUWpx>Fc_L s#LQ{edY3MpIXkMeV^&YV=9fR%8Jv|Kya=#u06K}m)78&qol`;+0RKEt)&Kwi literal 0 HcmV?d00001 diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/tab_s.png b/code/ryzom/tools/server/ryzom_ams_docs/html/tab_s.png new file mode 100644 index 0000000000000000000000000000000000000000..978943ac807718de0e69e5a585a8f0a1e5999285 GIT binary patch literal 189 zcmeAS@N?(olHy`uVBq!ia0vp^j6kfy!2~3aiye;!QZ1e?jv*C{Z|}b5Yzkm-c<7z3 zq^cq0=~}Z;b(!Zvb5Z%sTRFKGlz1=qOFg;myyu?$r`wZb^irPsN1a)6)TwB0r+)wb zPL25;=adu89?fTK`qDR>$D*)b_WOmdKI;Vst02j(hg8%>k literal 0 HcmV?d00001 diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/tabs.css b/code/ryzom/tools/server/ryzom_ams_docs/html/tabs.css new file mode 100644 index 000000000..21920562a --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/tabs.css @@ -0,0 +1,59 @@ +.tabs, .tabs2, .tabs3 { + background-image: url('tab_b.png'); + width: 100%; + z-index: 101; + font-size: 13px; +} + +.tabs2 { + font-size: 10px; +} +.tabs3 { + font-size: 9px; +} + +.tablist { + margin: 0; + padding: 0; + display: table; +} + +.tablist li { + float: left; + display: table-cell; + background-image: url('tab_b.png'); + line-height: 36px; + list-style: none; +} + +.tablist a { + display: block; + padding: 0 20px; + font-weight: bold; + background-image:url('tab_s.png'); + background-repeat:no-repeat; + background-position:right; + color: #283A5D; + text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.9); + text-decoration: none; + outline: none; +} + +.tabs3 .tablist a { + padding: 0 10px; +} + +.tablist a:hover { + background-image: url('tab_h.png'); + background-repeat:repeat-x; + color: #fff; + text-shadow: 0px 1px 1px rgba(0, 0, 0, 1.0); + text-decoration: none; +} + +.tablist li.current a { + background-image: url('tab_a.png'); + background-repeat:repeat-x; + color: #fff; + text-shadow: 0px 1px 1px rgba(0, 0, 0, 1.0); +} diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/todo.html b/code/ryzom/tools/server/ryzom_ams_docs/html/todo.html new file mode 100644 index 000000000..4816bbe36 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/todo.html @@ -0,0 +1,113 @@ + + + + + +Ryzom Account Management System: Todo List + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + +
    +
    Ryzom Account Management System + +
    + +
    +
    + + + + +
    +
    +
    +
    Todo List
    +
    +
    +
    +
    Member Helpers ()
    +
    for the drupal module it might be possible that drupal_mkdir needs to be used instead of mkdir, also this should be in the install.php instead.
    +
    Member Mail_Handler (&$structure)
    +
    take care of the HTML part of incoming emails.
    +
    Class Querycache
    +
    make sure that the querycache class is being used by the sync class and also for inserting the queries themselfs into it. Atm this class isn't used yet if I remember correctly
    +
    Member Ticket ($ticket_id, $newStatus, $newPriority, $author)
    +
    break this function up into a updateStatus (already exists) and updatePriority function and perhaps write a wrapper function for the combo.
    +
    Member Ticket_Log ($ticket_id)
    +
    only use one of the 2 comparable functions in the future and make the other depricated.
    +
    +
    + + + + +
    + +
    + + + + + + + From 2ee6843cd916300c3ac0e158ac45ee97c1ce5beb Mon Sep 17 00:00:00 2001 From: kaetemi Date: Fri, 13 Sep 2013 17:08:56 +0200 Subject: [PATCH 227/313] Fixes for d3d --- .../3d/driver/direct3d/driver_direct3d_pixel_program.cpp | 2 +- code/nel/src/3d/stereo_debugger.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/code/nel/src/3d/driver/direct3d/driver_direct3d_pixel_program.cpp b/code/nel/src/3d/driver/direct3d/driver_direct3d_pixel_program.cpp index 01d3c4844..1519a9554 100644 --- a/code/nel/src/3d/driver/direct3d/driver_direct3d_pixel_program.cpp +++ b/code/nel/src/3d/driver/direct3d/driver_direct3d_pixel_program.cpp @@ -57,7 +57,7 @@ CPixelProgramDrvInfosD3D::~CPixelProgramDrvInfosD3D() bool CDriverD3D::supportPixelProgram (CPixelProgram::TProfile profile) const { H_AUTO_D3D(CDriverD3D_supportPixelProgram_profile) - return ((profile & 0xFFFF0000) == 0xD3D00000) + return ((profile & 0xFFFF0000) == 0xD9020000) && (_PixelProgramVersion >= (uint16)(profile & 0x0000FFFF)); } diff --git a/code/nel/src/3d/stereo_debugger.cpp b/code/nel/src/3d/stereo_debugger.cpp index ccb2a6e96..74d8806f0 100644 --- a/code/nel/src/3d/stereo_debugger.cpp +++ b/code/nel/src/3d/stereo_debugger.cpp @@ -118,7 +118,6 @@ const char *a_ps_2_0 = "mov r0.yzw, r2\n" "cmp r0.x, -r0, r1, r2\n" "mov oC0, r0\n"; -; class CStereoDebuggerFactory : public IStereoDeviceFactory { @@ -155,7 +154,8 @@ CStereoDebugger::~CStereoDebugger() void CStereoDebugger::setDriver(NL3D::UDriver *driver) { nlassert(!m_PixelProgram); - + + m_Driver = driver; NL3D::IDriver *drvInternal = (static_cast(driver))->getDriver(); if (drvInternal->supportBloomEffect() && drvInternal->supportNonPowerOfTwoTextures()) @@ -179,6 +179,8 @@ void CStereoDebugger::setDriver(NL3D::UDriver *driver) } if (!drvInternal->compilePixelProgram(m_PixelProgram)) { + nlwarning("No supported pixel program for stereo debugger"); + delete m_PixelProgram; m_PixelProgram = NULL; } @@ -186,8 +188,6 @@ void CStereoDebugger::setDriver(NL3D::UDriver *driver) if (m_PixelProgram) { - m_Driver = driver; - initTextures(); m_Mat = m_Driver->createMaterial(); From 2f384d9789c822ea90891283260cfb991c4fbbdd Mon Sep 17 00:00:00 2001 From: kaetemi Date: Fri, 13 Sep 2013 17:09:05 +0200 Subject: [PATCH 228/313] Update veget implementation --- code/nel/include/nel/3d/vegetable_manager.h | 2 +- code/nel/src/3d/vegetable_manager.cpp | 49 +++++++++++---------- 2 files changed, 27 insertions(+), 24 deletions(-) diff --git a/code/nel/include/nel/3d/vegetable_manager.h b/code/nel/include/nel/3d/vegetable_manager.h index 8b3026794..ee21af3f3 100644 --- a/code/nel/include/nel/3d/vegetable_manager.h +++ b/code/nel/include/nel/3d/vegetable_manager.h @@ -344,7 +344,7 @@ private: /// setup the vertexProgram constants. - void setupVertexProgramConstants(IDriver *driver); + void setupVertexProgramConstants(IDriver *driver, bool fogEnabled); /** swap the RdrPass type (hard or soft) of the rdrPass of an instance group. diff --git a/code/nel/src/3d/vegetable_manager.cpp b/code/nel/src/3d/vegetable_manager.cpp index 2fcc25144..f2c8457f8 100644 --- a/code/nel/src/3d/vegetable_manager.cpp +++ b/code/nel/src/3d/vegetable_manager.cpp @@ -1866,7 +1866,7 @@ public: // *************************************************************************** -void CVegetableManager::setupVertexProgramConstants(IDriver *driver) +void CVegetableManager::setupVertexProgramConstants(IDriver *driver, bool fogEnabled) { nlassert(_ActiveVertexProgram); @@ -1874,37 +1874,40 @@ void CVegetableManager::setupVertexProgramConstants(IDriver *driver) // Standard // setup VertexProgram constants. // c[0..3] take the ModelViewProjection Matrix. After setupModelMatrix(); - driver->setConstantMatrix(0, IDriver::ModelViewProjection, IDriver::Identity); + driver->setUniformMatrix(IDriver::VertexProgram, _ActiveVertexProgram->getUniformIndex(CGPUProgramIndex::ModelViewProjection), IDriver::ModelViewProjection, IDriver::Identity); // c[6] take the Fog vector. After setupModelMatrix(); - driver->setConstantFog(6); + if (fogEnabled) + { + driver->setUniformFog(IDriver::VertexProgram, _ActiveVertexProgram->getUniformIndex(CGPUProgramIndex::Fog)); + } // c[8] take useful constants. - driver->setConstant(8, 0, 1, 0.5f, 2); + driver->setUniform4f(IDriver::VertexProgram, _ActiveVertexProgram->idx().ProgramConstants0, 0, 1, 0.5f, 2); // c[9] take normalized directional light - driver->setConstant(9, _DirectionalLight); + driver->setUniform3f(IDriver::VertexProgram, _ActiveVertexProgram->idx().DirectionalLight, _DirectionalLight); // c[10] take pos of camera - driver->setConstant(10, _ViewCenter); + driver->setUniform3f(IDriver::VertexProgram, _ActiveVertexProgram->idx().ViewCenter, _ViewCenter); // c[11] take factor for Blend formula - driver->setConstant(11, -1.f/NL3D_VEGETABLE_BLOCK_BLEND_TRANSITION_DIST, 0, 0, 0); + driver->setUniform1f(IDriver::VertexProgram, _ActiveVertexProgram->idx().NegInvTransDist, -1.f/NL3D_VEGETABLE_BLOCK_BLEND_TRANSITION_DIST); // Bend. // c[16]= quaternion axis. w==1, and z must be 0 - driver->setConstant( 16, _AngleAxis.x, _AngleAxis.y, _AngleAxis.z, 1); + driver->setUniform4f(IDriver::VertexProgram, _ActiveVertexProgram->idx().AngleAxis, _AngleAxis, 1); // c[17]= {timeAnim, WindPower, WindPower*(1-WindBendMin)/2, 0)} - driver->setConstant( 17, (float)_WindAnimTime, _WindPower, _WindPower*(1-_WindBendMin)/2, 0 ); + driver->setUniform3f(IDriver::VertexProgram, _ActiveVertexProgram->idx().Wind, (float)_WindAnimTime, _WindPower, _WindPower * (1 - _WindBendMin) / 2); // c[18]= High order Taylor cos coefficient: { -1/2, 1/24, -1/720, 1/40320 } - driver->setConstant( 18, -1/2.f, 1/24.f, -1/720.f, 1/40320.f ); + driver->setUniform4f(IDriver::VertexProgram, _ActiveVertexProgram->idx().CosCoeff0, -1/2.f, 1/24.f, -1/720.f, 1/40320.f ); // c[19]= Low order Taylor cos coefficient: { 1, -1/2, 1/24, -1/720 } - driver->setConstant( 19, 1, -1/2.f, 1/24.f, -1/720.f ); + driver->setUniform4f(IDriver::VertexProgram, _ActiveVertexProgram->idx().CosCoeff1, 1, -1/2.f, 1/24.f, -1/720.f ); // c[20]= Low order Taylor sin coefficient: { 1, -1/6, 1/120, -1/5040 } - driver->setConstant( 20, 1, -1/6.f, 1/120.f, -1/5040.f ); + driver->setUniform4f(IDriver::VertexProgram, _ActiveVertexProgram->idx().CosCoeff2, 1, -1/6.f, 1/120.f, -1/5040.f ); // c[21]= Special constant vector for quatToMatrix: { 0, 1, -1, 0 } - driver->setConstant( 21, 0.f, 1.f, -1.f, 0.f); + driver->setUniform4f(IDriver::VertexProgram, _ActiveVertexProgram->idx().QuatConstants, 0.f, 1.f, -1.f, 0.f); // c[22]= {0.5f, Pi, 2*Pi, 1/(2*Pi)} - driver->setConstant( 22, 0.5f, (float)Pi, (float)(2*Pi), (float)(1/(2*Pi)) ); + driver->setUniform4f(IDriver::VertexProgram, _ActiveVertexProgram->idx().PiConstants, 0.5f, (float)Pi, (float)(2*Pi), (float)(1/(2*Pi))); // c[23]= {NL3D_VEGETABLE_VP_LUT_SIZE, 0, 0, 0}. NL3D_VEGETABLE_VP_LUT_SIZE==64. - driver->setConstant( 23, NL3D_VEGETABLE_VP_LUT_SIZE, 0.f, 0.f, 0.f ); + driver->setUniform1f(IDriver::VertexProgram, _ActiveVertexProgram->idx().LUTSize, NL3D_VEGETABLE_VP_LUT_SIZE); // Fill constant. Start at 32. @@ -1912,7 +1915,7 @@ void CVegetableManager::setupVertexProgramConstants(IDriver *driver) { CVector2f cur= _WindTable[i]; CVector2f delta= _WindDeltaTable[i]; - driver->setConstant( 32+i, cur.x, cur.y, delta.x, delta.y ); + driver->setUniform4f(IDriver::VertexProgram, _ActiveVertexProgram->idx().LUT[i], cur.x, cur.y, delta.x, delta.y); } } @@ -2079,9 +2082,9 @@ void CVegetableManager::render(const CVector &viewCenter, const CVector &front bool uprogst = driver->isUniformProgramState(); bool progstateset[NL3D_VEGETABLE_NRDRPASS]; - for (sint rdrPass=0; rdrPass < NL3D_VEGETABLE_NRDRPASS; rdrPass++) + for (sint rdrPass = 0; rdrPass < NL3D_VEGETABLE_NRDRPASS; ++rdrPass) { - progstateset[rdrPass] = !uprogst; + progstateset[rdrPass] = false; } /* @@ -2117,9 +2120,9 @@ void CVegetableManager::render(const CVector &viewCenter, const CVector &front nlverify(driver->activeVertexProgram(_ActiveVertexProgram)); // Set VP constants - if (!progstateset[uprogst ? 0 : rdrPass]) + if (!progstateset[uprogst ? rdrPass : 0]) { - setupVertexProgramConstants(driver); + setupVertexProgramConstants(driver, uprogst ? fogged : true); } // Activate the unique material. @@ -2393,12 +2396,12 @@ void CVegetableManager::setupRenderStateForBlendLayerModel(IDriver *driver) nlverify(driver->activeVertexProgram(_ActiveVertexProgram)); // setup VP constants. - setupVertexProgramConstants(driver); + setupVertexProgramConstants(driver, fogged); - if (fogged) + /*if (fogged) // duplicate { driver->setConstantFog(6); - } + }*/ // Activate the unique material (correclty setuped for AlphaBlend in render()). driver->setupMaterial(_VegetableMaterial); From 7d854d54d184592bea1ab617ca1e38385c2f3d1c Mon Sep 17 00:00:00 2001 From: kaetemi Date: Fri, 13 Sep 2013 17:11:42 +0200 Subject: [PATCH 229/313] Modify calls in bloom effect --- code/nel/src/3d/bloom_effect.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/code/nel/src/3d/bloom_effect.cpp b/code/nel/src/3d/bloom_effect.cpp index 23d5c1e18..25439ee46 100644 --- a/code/nel/src/3d/bloom_effect.cpp +++ b/code/nel/src/3d/bloom_effect.cpp @@ -446,8 +446,8 @@ void CBloomEffect::applyBlur() // initialize vertex program drvInternal->activeVertexProgram(&TextureOffsetVertexProgram); - drvInternal->setConstant(8, 255.f, 255.f, 255.f, 255.f); - drvInternal->setConstant(9, 0.0f, 0.f, 0.f, 1.f); + drvInternal->setUniform4f(IDriver::VertexProgram, 8, 255.f, 255.f, 255.f, 255.f); + drvInternal->setUniform4f(IDriver::VertexProgram, 9, 0.0f, 0.f, 0.f, 1.f); // initialize blur material UMaterial displayBlurMat; @@ -552,8 +552,8 @@ void CBloomEffect::doBlur(bool horizontalBlur) // initialize vertex program drvInternal->activeVertexProgram(&TextureOffsetVertexProgram); - drvInternal->setConstant(8, 255.f, 255.f, 255.f, 255.f); - drvInternal->setConstant(9, 0.0f, 0.f, 0.f, 1.f); + drvInternal->setUniform4f(IDriver::VertexProgram, 8, 255.f, 255.f, 255.f, 255.f); + drvInternal->setUniform4f(IDriver::VertexProgram, 9, 0.0f, 0.f, 0.f, 1.f); // set several decal constants in order to obtain in the render target texture a mix of color // of a texel and its neighbored texels on the axe of the pass. @@ -572,10 +572,10 @@ void CBloomEffect::doBlur(bool horizontalBlur) decalR = 1.f; decal2R = 2.f; } - drvInternal->setConstant(10, (decalR/(float)_BlurWidth)*blurVec.x, (decalR/(float)_BlurHeight)*blurVec.y, 0.f, 0.f); - drvInternal->setConstant(11, (decal2R/(float)_BlurWidth)*blurVec.x, (decal2R/(float)_BlurHeight)*blurVec.y, 0.f, 0.f); - drvInternal->setConstant(12, (decalL/(float)_BlurWidth)*blurVec.x, (decalL/(float)_BlurHeight)*blurVec.y, 0.f, 0.f); - drvInternal->setConstant(13, (decal2L/(float)_BlurWidth)*blurVec.x, (decal2L/(float)_BlurHeight)*blurVec.y, 0.f, 0.f); + drvInternal->setUniform2f(IDriver::VertexProgram, 10, (decalR/(float)_BlurWidth)*blurVec.x, (decalR/(float)_BlurHeight)*blurVec.y); + drvInternal->setUniform2f(IDriver::VertexProgram, 11, (decal2R/(float)_BlurWidth)*blurVec.x, (decal2R/(float)_BlurHeight)*blurVec.y); + drvInternal->setUniform2f(IDriver::VertexProgram, 12, (decalL/(float)_BlurWidth)*blurVec.x, (decalL/(float)_BlurHeight)*blurVec.y); + drvInternal->setUniform2f(IDriver::VertexProgram, 13, (decal2L/(float)_BlurWidth)*blurVec.x, (decal2L/(float)_BlurHeight)*blurVec.y); // initialize material textures CMaterial * matObject = _BlurMat.getObjectPtr(); From ce7357ffa4e9be916795716a16341f63aca2dcbc Mon Sep 17 00:00:00 2001 From: kaetemi Date: Fri, 13 Sep 2013 17:17:11 +0200 Subject: [PATCH 230/313] Do not use old interface here --- code/nel/src/3d/driver/opengl/driver_opengl.cpp | 6 ++++-- code/nel/src/3d/vegetable_manager.cpp | 2 +- code/ryzom/client/src/decal.cpp | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/code/nel/src/3d/driver/opengl/driver_opengl.cpp b/code/nel/src/3d/driver/opengl/driver_opengl.cpp index 3eff7eebf..946e69ee0 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl.cpp +++ b/code/nel/src/3d/driver/opengl/driver_opengl.cpp @@ -1415,11 +1415,13 @@ void CDriverGL::setupFog(float start, float end, CRGBA color) // last constant is used to store fog information (fog must be rescaled to [0, 1], because of a driver bug) if (start != end) { - setConstant(_EVSNumConstant, 1.f / (start - end), - end / (start - end), 0, 0); + float datas[] = { 1.f / (start - end), - end / (start - end), 0, 0 }; + nglSetInvariantEXT(_EVSConstantHandle + _EVSNumConstant, GL_FLOAT, datas); } else { - setConstant(_EVSNumConstant, 0.f, 0, 0, 0); + float datas[] = { 0.f, 0, 0, 0 }; + nglSetInvariantEXT(_EVSConstantHandle + _EVSNumConstant, GL_FLOAT, datas); } } } diff --git a/code/nel/src/3d/vegetable_manager.cpp b/code/nel/src/3d/vegetable_manager.cpp index f2c8457f8..ba5f721bb 100644 --- a/code/nel/src/3d/vegetable_manager.cpp +++ b/code/nel/src/3d/vegetable_manager.cpp @@ -2400,7 +2400,7 @@ void CVegetableManager::setupRenderStateForBlendLayerModel(IDriver *driver) /*if (fogged) // duplicate { - driver->setConstantFog(6); + driver->setCon/stantFog(6); }*/ // Activate the unique material (correclty setuped for AlphaBlend in render()). diff --git a/code/ryzom/client/src/decal.cpp b/code/ryzom/client/src/decal.cpp index 5649b9b57..a1cca247d 100644 --- a/code/ryzom/client/src/decal.cpp +++ b/code/ryzom/client/src/decal.cpp @@ -621,7 +621,7 @@ void CDecalRenderList::renderAllDecals() if (!forceNoVertexProgram && drvInternal->compileVertexProgram(&DecalAttenuationVertexProgram)) { drvInternal->activeVertexProgram(&DecalAttenuationVertexProgram); - //drvInternal->setConstantMatrix(0, NL3D::IDriver::ModelViewProjection, NL3D::IDriver::Identity); + //drvInternal->setCons/tantMatrix(0, NL3D::IDriver::ModelViewProjection, NL3D::IDriver::Identity); drvInternal->setUniform4f(IDriver::VertexProgram, DecalAttenuationVertexProgram.idx().DistScaleBias, _DistScale, _DistBias, 0.f, 1.f); useVertexProgram = true; } From f7e80187ad08097719fb732c869707dc6b2468af Mon Sep 17 00:00:00 2001 From: kaetemi Date: Fri, 13 Sep 2013 18:25:52 +0200 Subject: [PATCH 231/313] Adjust order for meshvp --- code/nel/src/3d/meshvp_per_pixel_light.cpp | 12 ++++++------ code/nel/src/3d/meshvp_wind_tree.cpp | 18 +++++++++++------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/code/nel/src/3d/meshvp_per_pixel_light.cpp b/code/nel/src/3d/meshvp_per_pixel_light.cpp index e4f43efc5..3e2828328 100644 --- a/code/nel/src/3d/meshvp_per_pixel_light.cpp +++ b/code/nel/src/3d/meshvp_per_pixel_light.cpp @@ -363,7 +363,7 @@ void CMeshVPPerPixelLight::initInstance(CMeshBaseInstance *mbi) { // init the vertexProgram code. static bool vpCreated= false; - if(!vpCreated) + if (!vpCreated) { vpCreated= true; @@ -437,6 +437,8 @@ bool CMeshVPPerPixelLight::begin(IDriver *drv, return false; } // + enable(true, drv); // must enable the vertex program before the vb is activated + // CRenderTrav *renderTrav= &scene->getRenderTrav(); /// Setup for gouraud lighting renderTrav->beginVPLightSetup(VPLightConstantStart, @@ -482,9 +484,7 @@ bool CMeshVPPerPixelLight::begin(IDriver *drv, // c[0..3] take the ModelViewProjection Matrix. After setupModelMatrix(); drv->setConstantMatrix(0, IDriver::ModelViewProjection, IDriver::Identity); - // - enable(true, drv); // must enable the vertex program before the vb is activated - // + return true; } @@ -538,6 +538,8 @@ bool CMeshVPPerPixelLight::setupForMaterial(const CMaterial &mat, ) { bool enabled = (mat.getShader() == CMaterial::PerPixelLighting || mat.getShader() == CMaterial::PerPixelLightingNoSpec); + bool change = (enabled != _Enabled); + enable(enabled, drv); // enable disable the vertex program (for material that don't have the right shader) if (enabled) { CRenderTrav *renderTrav= &scene->getRenderTrav(); @@ -547,8 +549,6 @@ bool CMeshVPPerPixelLight::setupForMaterial(const CMaterial &mat, renderTrav->getStrongestLightColors(pplDiffuse, pplSpecular); drv->setPerPixelLightingLight(pplDiffuse, pplSpecular, mat.getShininess()); } - bool change = (enabled != _Enabled); - enable(enabled, drv); // enable disable the vertex program (for material that don't have the right shader) return change; } //================================================================================= diff --git a/code/nel/src/3d/meshvp_wind_tree.cpp b/code/nel/src/3d/meshvp_wind_tree.cpp index bf1bc0c4e..477814f6f 100644 --- a/code/nel/src/3d/meshvp_wind_tree.cpp +++ b/code/nel/src/3d/meshvp_wind_tree.cpp @@ -291,30 +291,34 @@ bool CMeshVPWindTree::begin(IDriver *driver, CScene *scene, CMeshBaseInstance *m if (!(driver->supportVertexProgram() && !driver->isVertexProgramEmulated())) return false; - // precompute mesh - setupPerMesh(driver, scene); - - // Setup instance constants - setupPerInstanceConstants(driver, scene, mbi, invertedModelMat); - // Activate the good VertexProgram //=============== + // Get how many pointLights are setuped now. nlassert(scene != NULL); CRenderTrav *renderTrav= &scene->getRenderTrav(); sint numPls= renderTrav->getNumVPLights()-1; clamp(numPls, 0, CRenderTrav::MaxVPLight-1); + // Enable normalize only if requested by user. Because lighting don't manage correct "scale lighting" uint idVP= (SpecularLighting?2:0) + (driver->isForceNormalize()?1:0) ; // correct VP id for correct unmber of pls. idVP= numPls*4 + idVP; - // activate VP. driver->activeVertexProgram(_VertexProgram[idVP]); + // precompute mesh + setupPerMesh(driver, scene); + + // Setup instance constants + setupPerInstanceConstants(driver, scene, mbi, invertedModelMat); + + + + return true; } From 482c13fd1a02a6c42c4a592d9122454a5813501f Mon Sep 17 00:00:00 2001 From: kaetemi Date: Fri, 13 Sep 2013 19:03:05 +0200 Subject: [PATCH 232/313] Add container for lighted vertex program --- code/nel/include/nel/3d/render_trav.h | 41 +++++++++++++++++- code/nel/src/3d/meshvp_per_pixel_light.cpp | 4 +- code/nel/src/3d/meshvp_wind_tree.cpp | 2 +- code/nel/src/3d/render_trav.cpp | 50 +++++++++++++++++++++- 4 files changed, 91 insertions(+), 6 deletions(-) diff --git a/code/nel/include/nel/3d/render_trav.h b/code/nel/include/nel/3d/render_trav.h index 6e8d04b32..76931d68f 100644 --- a/code/nel/include/nel/3d/render_trav.h +++ b/code/nel/include/nel/3d/render_trav.h @@ -27,6 +27,7 @@ #include "nel/3d/mesh_block_manager.h" #include "nel/3d/shadow_map_manager.h" #include "nel/3d/u_scene.h" +#include "nel/3d/vertex_program.h" #include @@ -68,6 +69,41 @@ class CWaterModel; #define NL3D_SHADOW_MESH_SKIN_MANAGER_MAXVERTICES 3000 #define NL3D_SHADOW_MESH_SKIN_MANAGER_NUMVB 8 +/// Container for lighted vertex program. +class CVertexProgramLighted : CVertexProgram +{ +public: + static const uint MaxLight = 4; + static const uint MaxPointLight = (MaxLight - 1); + struct CIdxLighted + { + uint Ambient; + uint Diffuse[MaxLight]; + uint Specular[MaxLight]; + uint DirOrPos[MaxLight]; // light 0, directional sun; light 1,2,3, omni point light + uint EyePosition; + uint DiffuseAlpha; + }; + struct CFeaturesLighted + { + /// Number of point lights that this program is generated for, varies from 0 to 3. + uint NumActivePointLights; + bool SupportSpecular; + bool Normalize; + /// Start of constants to use for lighting with assembly shaders. + uint CtStartNeLVP; + }; + CVertexProgramLighted() { } + virtual ~CVertexProgramLighted() { } + virtual void buildInfo(); + const CIdxLighted &idxLighted() const { return m_IdxLighted; } + const CFeaturesLighted &featuresLighted() const { return m_FeaturesLighted; } + +private: + CIdxLighted m_IdxLighted; + CFeaturesLighted m_FeaturesLighted; + +}; // *************************************************************************** @@ -224,7 +260,7 @@ public: // @{ // Max VP Light setup Infos. - enum {MaxVPLight= 4}; + enum {MaxVPLight = CVertexProgramLighted::MaxLight}; /** reset the lighting setup in the driver (all lights are disabled). * called at beginning of traverse(). Must be called by any model (before and after rendering) @@ -299,7 +335,8 @@ public: * \param numActivePoinLights tells how many point light from 0 to 3 this VP must handle. NB: the Sun directionnal is not option * NB: nlassert(numActiveLights<=MaxVPLight-1). */ - static std::string getLightVPFragment(uint numActivePointLights, uint ctStart, bool supportSpecular, bool normalize); + static std::string getLightVPFragmentNeLVP(uint numActivePointLights, uint ctStart, bool supportSpecular, bool normalize); + // TODO_VP_GLSL /** This returns a reference to a driver light, by its index * \see getStrongestLightIndex diff --git a/code/nel/src/3d/meshvp_per_pixel_light.cpp b/code/nel/src/3d/meshvp_per_pixel_light.cpp index 3e2828328..809dd16c8 100644 --- a/code/nel/src/3d/meshvp_per_pixel_light.cpp +++ b/code/nel/src/3d/meshvp_per_pixel_light.cpp @@ -393,10 +393,10 @@ void CMeshVPPerPixelLight::initInstance(CMeshBaseInstance *mbi) for (uint vp = 0; vp < NumVp; ++vp) { // \todo yoyo TODO_OPTIM Manage different number of pointLights - // NB: never call getLightVPFragment() with normalize, because already done by PerPixel fragment before. + // NB: never call getLightVPFragmentNeLVP() with normalize, because already done by PerPixel fragment before. std::string vpCode = std::string(vpName[vp]) + std::string("# ***************") // temp for debug - + CRenderTrav::getLightVPFragment(CRenderTrav::MaxVPLight-1, VPLightConstantStart, (vp & 2) != 0, false) + + CRenderTrav::getLightVPFragmentNeLVP(CRenderTrav::MaxVPLight-1, VPLightConstantStart, (vp & 2) != 0, false) + std::string("# ***************") // temp for debug + std::string(PPLightingVPCodeEnd); #ifdef NL_DEBUG diff --git a/code/nel/src/3d/meshvp_wind_tree.cpp b/code/nel/src/3d/meshvp_wind_tree.cpp index 477814f6f..85a5550e8 100644 --- a/code/nel/src/3d/meshvp_wind_tree.cpp +++ b/code/nel/src/3d/meshvp_wind_tree.cpp @@ -155,7 +155,7 @@ void CMeshVPWindTree::initInstance(CMeshBaseInstance *mbi) // combine fragments vpCode= string(WindTreeVPCodeWave) - + CRenderTrav::getLightVPFragment(numPls, VPLightConstantStart, specular, normalize) + + CRenderTrav::getLightVPFragmentNeLVP(numPls, VPLightConstantStart, specular, normalize) + WindTreeVPCodeEnd; _VertexProgram[i] = new CVertexProgram(vpCode.c_str()); // TODO_VP_GLSL diff --git a/code/nel/src/3d/render_trav.cpp b/code/nel/src/3d/render_trav.cpp index 5cf6fd20e..eba42e724 100644 --- a/code/nel/src/3d/render_trav.cpp +++ b/code/nel/src/3d/render_trav.cpp @@ -1168,8 +1168,56 @@ static void strReplaceAll(string &strInOut, const string &tokenSrc, const string } } +void CVertexProgramLighted::buildInfo() +{ + if (m_FeaturesLighted.CtStartNeLVP != ~0) + { + // Fixed uniform locations + m_IdxLighted.Ambient = 0; + for (uint i = 0; i < MaxLight; ++i) + { + m_IdxLighted.Diffuse[i] = 1 + i; + } + if (m_FeaturesLighted.SupportSpecular) + { + for (uint i = 0; i < MaxLight; ++i) + { + m_IdxLighted.Specular[i] = 5 + i; + } + m_IdxLighted.DirOrPos[0] = 9; + for (uint i = 1; i < MaxLight; ++i) + { + m_IdxLighted.DirOrPos[i] = (12 - 1) + i; + } + m_IdxLighted.DiffuseAlpha = 10; + m_IdxLighted.EyePosition = 11; + } + else + { + for (uint i = 0; i < MaxLight; ++i) + { + m_IdxLighted.Specular[i] = ~0; + } + for (uint i = 0; i < MaxLight; ++i) + { + m_IdxLighted.DirOrPos[i] = 5 + i; + } + m_IdxLighted.DiffuseAlpha = 9; + m_IdxLighted.EyePosition = ~0; + } + } + else + { + // Named uniform locations + // TODO_VP_GLSL + // m_IdxLighted.Ambient = getUniformIndex("ambient"); + // etc + } +} + +// generates the lighting part of a vertex program, nelvp profile // *************************************************************************** -std::string CRenderTrav::getLightVPFragment(uint numActivePointLights, uint ctStart, bool supportSpecular, bool normalize) +std::string CRenderTrav::getLightVPFragmentNeLVP(uint numActivePointLights, uint ctStart, bool supportSpecular, bool normalize) { string ret; From ba945f30a89645785d848ed9448dd2a5cdb6bae9 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Fri, 13 Sep 2013 20:00:20 +0200 Subject: [PATCH 233/313] Use lighted vertex program container for per pixel light program --- .../include/nel/3d/meshvp_per_pixel_light.h | 5 +- code/nel/include/nel/3d/meshvp_wind_tree.h | 2 +- code/nel/include/nel/3d/render_trav.h | 4 +- code/nel/src/3d/meshvp_per_pixel_light.cpp | 123 ++++++++++++------ code/nel/src/3d/render_trav.cpp | 3 +- 5 files changed, 94 insertions(+), 43 deletions(-) diff --git a/code/nel/include/nel/3d/meshvp_per_pixel_light.h b/code/nel/include/nel/3d/meshvp_per_pixel_light.h index 5df1bfc16..d81143eac 100644 --- a/code/nel/include/nel/3d/meshvp_per_pixel_light.h +++ b/code/nel/include/nel/3d/meshvp_per_pixel_light.h @@ -27,6 +27,7 @@ namespace NL3D { +class CVertexProgramPerPixelLight; /** * This vertex program is used to perform perpixel lighting with meshs. Its outputs are : @@ -49,6 +50,8 @@ namespace NL3D { class CMeshVPPerPixelLight : public IMeshVertexProgram { public: + friend class CVertexProgramPerPixelLight; + /// true if want Specular Lighting. bool SpecularLighting; public: @@ -84,7 +87,7 @@ private: bool _IsPointLight; // enum { NumVp = 8}; - static NLMISC::CSmartPtr _VertexProgram[NumVp]; + static NLMISC::CSmartPtr _VertexProgram[NumVp]; }; } // NL3D diff --git a/code/nel/include/nel/3d/meshvp_wind_tree.h b/code/nel/include/nel/3d/meshvp_wind_tree.h index 4e5d2875a..a19f6b43d 100644 --- a/code/nel/include/nel/3d/meshvp_wind_tree.h +++ b/code/nel/include/nel/3d/meshvp_wind_tree.h @@ -112,7 +112,7 @@ private: /** The 16 versions: Specular or not (0 or 2), + normalize normal or not (0 or 1). * All multiplied by 4, because support from 0 to 3 pointLights activated. (0.., 4.., 8.., 12..) */ - static NLMISC::CSmartPtr _VertexProgram[NumVp]; + static NLMISC::CSmartPtr _VertexProgram[NumVp]; // WindTree Time for this mesh param setup. Stored in mesh because same for all instances. float _CurrentTime[HrcDepth]; diff --git a/code/nel/include/nel/3d/render_trav.h b/code/nel/include/nel/3d/render_trav.h index 76931d68f..fe37b5930 100644 --- a/code/nel/include/nel/3d/render_trav.h +++ b/code/nel/include/nel/3d/render_trav.h @@ -70,7 +70,7 @@ class CWaterModel; #define NL3D_SHADOW_MESH_SKIN_MANAGER_NUMVB 8 /// Container for lighted vertex program. -class CVertexProgramLighted : CVertexProgram +class CVertexProgramLighted : public CVertexProgram { public: static const uint MaxLight = 4; @@ -99,7 +99,7 @@ public: const CIdxLighted &idxLighted() const { return m_IdxLighted; } const CFeaturesLighted &featuresLighted() const { return m_FeaturesLighted; } -private: +protected: CIdxLighted m_IdxLighted; CFeaturesLighted m_FeaturesLighted; diff --git a/code/nel/src/3d/meshvp_per_pixel_light.cpp b/code/nel/src/3d/meshvp_per_pixel_light.cpp index 809dd16c8..a7a04f265 100644 --- a/code/nel/src/3d/meshvp_per_pixel_light.cpp +++ b/code/nel/src/3d/meshvp_per_pixel_light.cpp @@ -32,14 +32,13 @@ namespace NL3D { -NLMISC::CSmartPtr CMeshVPPerPixelLight::_VertexProgram[NumVp]; + +NLMISC::CSmartPtr CMeshVPPerPixelLight::_VertexProgram[NumVp]; // *************************************************************************** // Light VP fragment constants start at 24 static const uint VPLightConstantStart = 24; - - // *************************************************************************** // *************************************************************************** @@ -355,18 +354,33 @@ static const char* PPLightingVPCodeTest = "; ***************************************************************/ - - - -//================================================================================= -void CMeshVPPerPixelLight::initInstance(CMeshBaseInstance *mbi) +class CVertexProgramPerPixelLight : public CVertexProgramLighted { - // init the vertexProgram code. - static bool vpCreated= false; - if (!vpCreated) +public: + class CIdx { - vpCreated= true; + }; + CVertexProgramPerPixelLight(uint vp); + virtual ~CVertexProgramPerPixelLight() { }; + virtual void buildInfo(); + const CIdx &idx() const { return m_Idx; } + +private: + CIdx m_Idx; + +}; + +CVertexProgramPerPixelLight::CVertexProgramPerPixelLight(uint vp) +{ + // lighted settings + m_FeaturesLighted.SupportSpecular = (vp & 2) != 0; + m_FeaturesLighted.NumActivePointLights = MaxLight - 1; + m_FeaturesLighted.Normalize = false; + m_FeaturesLighted.CtStartNeLVP = VPLightConstantStart; + + // nelvp + { // Gives each vp name // Bit 0 : 1 when it is a directionnal light // Bit 1 : 1 when specular is needed @@ -389,34 +403,67 @@ void CMeshVPPerPixelLight::initInstance(CMeshBaseInstance *mbi) }; uint numvp = sizeof(vpName) / sizeof(const char *); - nlassert(NumVp == numvp); // make sure that it is in sync with header..todo : compile time assert :) + nlassert(CMeshVPPerPixelLight::NumVp == numvp); // make sure that it is in sync with header..todo : compile time assert :) + + // \todo yoyo TODO_OPTIM Manage different number of pointLights + // NB: never call getLightVPFragmentNeLVP() with normalize, because already done by PerPixel fragment before. + std::string vpCode = std::string(vpName[vp]) + + std::string("# ***************") // temp for debug + + CRenderTrav::getLightVPFragmentNeLVP( + m_FeaturesLighted.NumActivePointLights, + m_FeaturesLighted.CtStartNeLVP, + m_FeaturesLighted.SupportSpecular, + m_FeaturesLighted.Normalize) + + std::string("# ***************") // temp for debug + + std::string(PPLightingVPCodeEnd); + #ifdef NL_DEBUG + /** For test : parse those programs before they are used. + * As a matter of fact some program will works with the NV_VERTEX_PROGRAM extension, + * but won't with EXT_vertex_shader, because there are some limitations (can't read a temp + * register that hasn't been written before..) + */ + CVPParser vpParser; + CVPParser::TProgram result; + std::string parseOutput; + if (!vpParser.parse(vpCode.c_str(), result, parseOutput)) + { + nlwarning(parseOutput.c_str()); + nlassert(0); + } + #endif + + CSource *source = new CSource(); + source->DisplayName = NLMISC::toString("nelvp/MeshVPPerPixel/%i", vp); + source->Profile = CVertexProgram::nelvp; + source->setSource(vpCode); + addSource(source); + } + + // glsl + { + // TODO_VP_GLSL + } +} + +void CVertexProgramPerPixelLight::buildInfo() +{ + CVertexProgramLighted::buildInfo(); +} + + +//================================================================================= +void CMeshVPPerPixelLight::initInstance(CMeshBaseInstance *mbi) +{ + // init the vertexProgram code. + static bool vpCreated= false; + if (!vpCreated) + { + vpCreated = true; + for (uint vp = 0; vp < NumVp; ++vp) { - // \todo yoyo TODO_OPTIM Manage different number of pointLights - // NB: never call getLightVPFragmentNeLVP() with normalize, because already done by PerPixel fragment before. - std::string vpCode = std::string(vpName[vp]) - + std::string("# ***************") // temp for debug - + CRenderTrav::getLightVPFragmentNeLVP(CRenderTrav::MaxVPLight-1, VPLightConstantStart, (vp & 2) != 0, false) - + std::string("# ***************") // temp for debug - + std::string(PPLightingVPCodeEnd); - #ifdef NL_DEBUG - /** For test : parse those programs before they are used. - * As a matter of fact some program will works with the NV_VERTEX_PROGRAM extension, - * but won't with EXT_vertex_shader, because there are some limitations (can't read a temp - * register that hasn't been written before..) - */ - CVPParser vpParser; - CVPParser::TProgram result; - std::string parseOutput; - if (!vpParser.parse(vpCode.c_str(), result, parseOutput)) - { - nlwarning(parseOutput.c_str()); - nlassert(0); - } - #endif - _VertexProgram[vp] = new CVertexProgram(vpCode.c_str()); + _VertexProgram[vp] = new CVertexProgramPerPixelLight(vp); } - } } @@ -521,7 +568,7 @@ void CMeshVPPerPixelLight::enable(bool enabled, IDriver *drv) | (SpecularLighting ? 2 : 0) | (_IsPointLight ? 1 : 0); // - drv->activeVertexProgram(_VertexProgram[idVP]); + drv->activeVertexProgram((CVertexProgramPerPixelLight *)_VertexProgram[idVP]); } else { diff --git a/code/nel/src/3d/render_trav.cpp b/code/nel/src/3d/render_trav.cpp index eba42e724..5735e71e1 100644 --- a/code/nel/src/3d/render_trav.cpp +++ b/code/nel/src/3d/render_trav.cpp @@ -1170,7 +1170,8 @@ static void strReplaceAll(string &strInOut, const string &tokenSrc, const string void CVertexProgramLighted::buildInfo() { - if (m_FeaturesLighted.CtStartNeLVP != ~0) + CVertexProgram::buildInfo(); + if (profile() == nelvp) { // Fixed uniform locations m_IdxLighted.Ambient = 0; From a0fbb152ee48182df9318cf27d7ab060f36789dd Mon Sep 17 00:00:00 2001 From: kaetemi Date: Fri, 13 Sep 2013 20:45:06 +0200 Subject: [PATCH 234/313] Updated wind tree program container --- code/nel/include/nel/3d/meshvp_wind_tree.h | 4 +- code/nel/src/3d/meshvp_wind_tree.cpp | 96 +++++++++++++++++----- 2 files changed, 78 insertions(+), 22 deletions(-) diff --git a/code/nel/include/nel/3d/meshvp_wind_tree.h b/code/nel/include/nel/3d/meshvp_wind_tree.h index a19f6b43d..b33ac7587 100644 --- a/code/nel/include/nel/3d/meshvp_wind_tree.h +++ b/code/nel/include/nel/3d/meshvp_wind_tree.h @@ -24,6 +24,7 @@ namespace NL3D { +class CVertexProgramWindTree; // *************************************************************************** /** @@ -35,6 +36,7 @@ namespace NL3D { class CMeshVPWindTree : public IMeshVertexProgram { public: + friend class CVertexProgramWindTree; enum {HrcDepth= 3}; @@ -112,7 +114,7 @@ private: /** The 16 versions: Specular or not (0 or 2), + normalize normal or not (0 or 1). * All multiplied by 4, because support from 0 to 3 pointLights activated. (0.., 4.., 8.., 12..) */ - static NLMISC::CSmartPtr _VertexProgram[NumVp]; + static NLMISC::CSmartPtr _VertexProgram[NumVp]; // WindTree Time for this mesh param setup. Stored in mesh because same for all instances. float _CurrentTime[HrcDepth]; diff --git a/code/nel/src/3d/meshvp_wind_tree.cpp b/code/nel/src/3d/meshvp_wind_tree.cpp index 85a5550e8..ca7964ac1 100644 --- a/code/nel/src/3d/meshvp_wind_tree.cpp +++ b/code/nel/src/3d/meshvp_wind_tree.cpp @@ -35,11 +35,11 @@ namespace NL3D // *************************************************************************** // Light VP fragment constants start at 24 -static const uint VPLightConstantStart= 24; +static const uint VPLightConstantStart = 24; // *************************************************************************** -NLMISC::CSmartPtr CMeshVPWindTree::_VertexProgram[CMeshVPWindTree::NumVp]; +NLMISC::CSmartPtr CMeshVPWindTree::_VertexProgram[CMeshVPWindTree::NumVp]; static const char* WindTreeVPCodeWave= "!!VP1.0 \n\ @@ -79,6 +79,59 @@ static const char* WindTreeVPCodeEnd= END \n\ "; + +class CVertexProgramWindTree : public CVertexProgramLighted +{ +public: + class CIdx + { + + }; + CVertexProgramWindTree(uint numPls, bool specular, bool normalize); + virtual ~CVertexProgramWindTree() { }; + virtual void buildInfo(); + const CIdx &idx() const { return m_Idx; } + + bool PerMeshSetup; + +private: + CIdx m_Idx; + +}; + +CVertexProgramWindTree::CVertexProgramWindTree(uint numPls, bool specular, bool normalize) +{ + // lighted settings + m_FeaturesLighted.SupportSpecular = specular; + m_FeaturesLighted.NumActivePointLights = numPls; + m_FeaturesLighted.Normalize = normalize; + m_FeaturesLighted.CtStartNeLVP = VPLightConstantStart; + + // constants cache + PerMeshSetup = false; + + // nelvp + { + std::string vpCode = std::string(WindTreeVPCodeWave) + + CRenderTrav::getLightVPFragmentNeLVP(numPls, VPLightConstantStart, specular, normalize) + + WindTreeVPCodeEnd; + + CSource *source = new CSource(); + source->DisplayName = NLMISC::toString("nelvp/MeshVPWindTree/%i/%s/%s", numPls, specular ? "spec" : "nospec", normalize ? "normalize" : "nonormalize"); + source->Profile = CVertexProgram::nelvp; + source->setSource(vpCode); + addSource(source); + } + + // TODO_VP_GLSL +} + +void CVertexProgramWindTree::buildInfo() +{ + CVertexProgramLighted::buildInfo(); +} + + // *************************************************************************** float CMeshVPWindTree::speedCos(float angle) { @@ -142,9 +195,6 @@ void CMeshVPWindTree::initInstance(CMeshBaseInstance *mbi) // All vpcode and begin() written for HrcDepth==3 nlassert(HrcDepth==3); - // combine fragments. - string vpCode; - // For all possible VP. for(uint i=0;i 0. */ - _LastMBRIdVP= 0; + _LastMBRIdVP = 0; // activate VP. driver->activeVertexProgram(_VertexProgram[_LastMBRIdVP]); + + // precompute mesh + setupPerMesh(driver, scene); + _VertexProgram[_LastMBRIdVP]->PerMeshSetup = true; } // *************************************************************************** void CMeshVPWindTree::beginMBRInstance(IDriver *driver, CScene *scene, CMeshBaseInstance *mbi, const NLMISC::CMatrix &invertedModelMat) { - // setup first constants for this instance - setupPerInstanceConstants(driver, scene, mbi, invertedModelMat); - // Get how many pointLights are setuped now. nlassert(scene != NULL); CRenderTrav *renderTrav= &scene->getRenderTrav(); @@ -403,16 +447,26 @@ void CMeshVPWindTree::beginMBRInstance(IDriver *driver, CScene *scene, CMeshBase clamp(numPls, 0, CRenderTrav::MaxVPLight-1); // Enable normalize only if requested by user. Because lighting don't manage correct "scale lighting" - uint idVP= (SpecularLighting?2:0) + (driver->isForceNormalize()?1:0) ; + uint idVP = (SpecularLighting?2:0) + (driver->isForceNormalize()?1:0) ; // correct VP id for correct number of pls. - idVP= numPls*4 + idVP; + idVP = numPls*4 + idVP; // re-activate VP if idVP different from last setup - if( idVP!=_LastMBRIdVP ) + if(idVP != _LastMBRIdVP) { _LastMBRIdVP= idVP; driver->activeVertexProgram(_VertexProgram[_LastMBRIdVP]); + + if (!_VertexProgram[_LastMBRIdVP]->PerMeshSetup) + { + // precompute mesh + setupPerMesh(driver, scene); + _VertexProgram[_LastMBRIdVP]->PerMeshSetup = true; + } } + + // setup first constants for this instance + setupPerInstanceConstants(driver, scene, mbi, invertedModelMat); } // *************************************************************************** From edec14807fe0715379926cba0533da239f153758 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Fri, 13 Sep 2013 21:31:47 +0200 Subject: [PATCH 235/313] Use named uniform indices for lighted vertex program --- code/nel/include/nel/3d/driver.h | 1 + .../include/nel/3d/meshvp_per_pixel_light.h | 2 + code/nel/include/nel/3d/meshvp_wind_tree.h | 2 + code/nel/include/nel/3d/render_trav.h | 8 +- .../src/3d/driver/direct3d/driver_direct3d.h | 1 + .../direct3d/driver_direct3d_uniform.cpp | 5 + code/nel/src/3d/driver/opengl/driver_opengl.h | 1 + .../driver/opengl/driver_opengl_uniform.cpp | 5 + code/nel/src/3d/meshvp_per_pixel_light.cpp | 6 +- code/nel/src/3d/meshvp_wind_tree.cpp | 8 +- code/nel/src/3d/render_trav.cpp | 97 ++++++++++--------- 11 files changed, 81 insertions(+), 55 deletions(-) diff --git a/code/nel/include/nel/3d/driver.h b/code/nel/include/nel/3d/driver.h index a97606a97..38c0f6c32 100644 --- a/code/nel/include/nel/3d/driver.h +++ b/code/nel/include/nel/3d/driver.h @@ -1191,6 +1191,7 @@ public: virtual void setUniform4ui(TProgram program, uint index, uint32 ui0, uint32 ui1, uint32 ui2, uint32 ui3) = 0; virtual void setUniform3f(TProgram program, uint index, const NLMISC::CVector& v) = 0; virtual void setUniform4f(TProgram program, uint index, const NLMISC::CVector& v, float f3) = 0; + virtual void setUniform4f(TProgram program, uint index, const NLMISC::CRGBAF& rgba) = 0; virtual void setUniform4x4f(TProgram program, uint index, const NLMISC::CMatrix& m) = 0; virtual void setUniform4fv(TProgram program, uint index, size_t num, const float *src) = 0; virtual void setUniform4iv(TProgram program, uint index, size_t num, const sint32 *src) = 0; diff --git a/code/nel/include/nel/3d/meshvp_per_pixel_light.h b/code/nel/include/nel/3d/meshvp_per_pixel_light.h index d81143eac..a234feddd 100644 --- a/code/nel/include/nel/3d/meshvp_per_pixel_light.h +++ b/code/nel/include/nel/3d/meshvp_per_pixel_light.h @@ -88,6 +88,8 @@ private: // enum { NumVp = 8}; static NLMISC::CSmartPtr _VertexProgram[NumVp]; + + NLMISC::CRefPtr _ActiveVertexProgram; }; } // NL3D diff --git a/code/nel/include/nel/3d/meshvp_wind_tree.h b/code/nel/include/nel/3d/meshvp_wind_tree.h index b33ac7587..2e353da05 100644 --- a/code/nel/include/nel/3d/meshvp_wind_tree.h +++ b/code/nel/include/nel/3d/meshvp_wind_tree.h @@ -116,6 +116,8 @@ private: */ static NLMISC::CSmartPtr _VertexProgram[NumVp]; + NLMISC::CRefPtr _ActiveVertexProgram; + // WindTree Time for this mesh param setup. Stored in mesh because same for all instances. float _CurrentTime[HrcDepth]; double _LastSceneTime; diff --git a/code/nel/include/nel/3d/render_trav.h b/code/nel/include/nel/3d/render_trav.h index fe37b5930..61d62e94c 100644 --- a/code/nel/include/nel/3d/render_trav.h +++ b/code/nel/include/nel/3d/render_trav.h @@ -289,7 +289,7 @@ public: * \param supportSpecular asitsounds. PointLights and dirLight are localViewer * \param invObjectWM the inverse of object matrix: lights are mul by this. Vp compute in object space. */ - void beginVPLightSetup(uint ctStart, bool supportSpecular, const CMatrix &invObjectWM); + void beginVPLightSetup(CVertexProgramLighted *program, const CMatrix &invObjectWM); /** change the driver VP LightSetup constants which depends on material. * \param excludeStrongest This remove the strongest light from the setup. The typical use is to have it computed by using perpixel lighting. @@ -418,12 +418,14 @@ private: mutable uint _StrongestLightIndex; mutable bool _StrongestLightTouched; + // Current vp setuped with beginVPLightSetup() + NLMISC::CRefPtr _VPCurrent; // Current ctStart setuped with beginVPLightSetup() - uint _VPCurrentCtStart; + //uint _VPCurrentCtStart; // Current num of VP lights enabled. uint _VPNumLights; // Current support of specular - bool _VPSupportSpecular; + //bool _VPSupportSpecular; // Sum of all ambiant of all lights + ambiantGlobal. NLMISC::CRGBAF _VPFinalAmbient; // Diffuse/Spec comp of all light / 255. diff --git a/code/nel/src/3d/driver/direct3d/driver_direct3d.h b/code/nel/src/3d/driver/direct3d/driver_direct3d.h index 80b39f786..d21bef92f 100644 --- a/code/nel/src/3d/driver/direct3d/driver_direct3d.h +++ b/code/nel/src/3d/driver/direct3d/driver_direct3d.h @@ -1200,6 +1200,7 @@ public: virtual void setUniform4ui(TProgram program, uint index, uint32 ui0, uint32 ui1, uint32 ui2, uint32 ui3); virtual void setUniform3f(TProgram program, uint index, const NLMISC::CVector& v); virtual void setUniform4f(TProgram program, uint index, const NLMISC::CVector& v, float f3); + virtual void setUniform4f(TProgram program, uint index, const NLMISC::CRGBAF& rgba); virtual void setUniform4x4f(TProgram program, uint index, const NLMISC::CMatrix& m); virtual void setUniform4fv(TProgram program, uint index, size_t num, const float *src); virtual void setUniform4iv(TProgram program, uint index, size_t num, const sint32 *src); diff --git a/code/nel/src/3d/driver/direct3d/driver_direct3d_uniform.cpp b/code/nel/src/3d/driver/direct3d/driver_direct3d_uniform.cpp index a77a86549..e44780e89 100644 --- a/code/nel/src/3d/driver/direct3d/driver_direct3d_uniform.cpp +++ b/code/nel/src/3d/driver/direct3d/driver_direct3d_uniform.cpp @@ -138,6 +138,11 @@ void CDriverD3D::setUniform4f(TProgram program, uint index, const NLMISC::CVecto CDriverD3D::setUniform4f(program, index, v.x, v.y, v.z, f3); } +void CDriverD3D::setUniform4f(TProgram program, uint index, const NLMISC::CRGBAF& rgba) +{ + CDriverD3D::setUniform4fv(program, index, 1, &rgba.R); +} + void CDriverD3D::setUniform4x4f(TProgram program, uint index, const NLMISC::CMatrix& m) { H_AUTO_D3D(CDriverD3D_setUniform4x4f); diff --git a/code/nel/src/3d/driver/opengl/driver_opengl.h b/code/nel/src/3d/driver/opengl/driver_opengl.h index e20d7d4fa..afab8209c 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl.h +++ b/code/nel/src/3d/driver/opengl/driver_opengl.h @@ -1406,6 +1406,7 @@ private: virtual void setUniform4ui(TProgram program, uint index, uint32 ui0, uint32 ui1, uint32 ui2, uint32 ui3); virtual void setUniform3f(TProgram program, uint index, const NLMISC::CVector& v); virtual void setUniform4f(TProgram program, uint index, const NLMISC::CVector& v, float f3); + virtual void setUniform4f(TProgram program, uint index, const NLMISC::CRGBAF& rgba); virtual void setUniform4x4f(TProgram program, uint index, const NLMISC::CMatrix& m); virtual void setUniform4fv(TProgram program, uint index, size_t num, const float *src); virtual void setUniform4iv(TProgram program, uint index, size_t num, const sint32 *src); diff --git a/code/nel/src/3d/driver/opengl/driver_opengl_uniform.cpp b/code/nel/src/3d/driver/opengl/driver_opengl_uniform.cpp index 658b6739a..deee62379 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl_uniform.cpp +++ b/code/nel/src/3d/driver/opengl/driver_opengl_uniform.cpp @@ -169,6 +169,11 @@ void CDriverGL::setUniform4f(TProgram program, uint index, const NLMISC::CVector CDriverGL::setUniform4f(program, index, v.x, v.y, v.z, f3); } +void CDriverGL::setUniform4f(TProgram program, uint index, const NLMISC::CRGBAF& rgba) +{ + CDriverGL::setUniform4fv(program, index, 1, &rgba.R); +} + void CDriverGL::setUniform4x4f(TProgram program, uint index, const NLMISC::CMatrix& m) { H_AUTO_OGL(CDriverGL_setUniform4x4f); diff --git a/code/nel/src/3d/meshvp_per_pixel_light.cpp b/code/nel/src/3d/meshvp_per_pixel_light.cpp index a7a04f265..cf2d35f39 100644 --- a/code/nel/src/3d/meshvp_per_pixel_light.cpp +++ b/code/nel/src/3d/meshvp_per_pixel_light.cpp @@ -488,9 +488,7 @@ bool CMeshVPPerPixelLight::begin(IDriver *drv, // CRenderTrav *renderTrav= &scene->getRenderTrav(); /// Setup for gouraud lighting - renderTrav->beginVPLightSetup(VPLightConstantStart, - SpecularLighting, - invertedModelMat); + renderTrav->beginVPLightSetup(_ActiveVertexProgram, invertedModelMat); // sint strongestLightIndex = renderTrav->getStrongestLightIndex(); if (strongestLightIndex == -1) return false; // if no strongest light, disable this vertex program @@ -569,10 +567,12 @@ void CMeshVPPerPixelLight::enable(bool enabled, IDriver *drv) | (_IsPointLight ? 1 : 0); // drv->activeVertexProgram((CVertexProgramPerPixelLight *)_VertexProgram[idVP]); + _ActiveVertexProgram = _VertexProgram[idVP]; } else { drv->activeVertexProgram(NULL); + _ActiveVertexProgram = NULL; } _Enabled = enabled; } diff --git a/code/nel/src/3d/meshvp_wind_tree.cpp b/code/nel/src/3d/meshvp_wind_tree.cpp index ca7964ac1..17e847c3e 100644 --- a/code/nel/src/3d/meshvp_wind_tree.cpp +++ b/code/nel/src/3d/meshvp_wind_tree.cpp @@ -354,6 +354,7 @@ bool CMeshVPWindTree::begin(IDriver *driver, CScene *scene, CMeshBaseInstance *m idVP= numPls*4 + idVP; // activate VP. driver->activeVertexProgram(_VertexProgram[idVP]); + _ActiveVertexProgram = _VertexProgram[idVP]; // precompute mesh @@ -373,6 +374,7 @@ void CMeshVPWindTree::end(IDriver *driver) { // Disable the VertexProgram driver->activeVertexProgram(NULL); + _ActiveVertexProgram = NULL; } // *************************************************************************** @@ -398,7 +400,8 @@ void CMeshVPWindTree::setupLighting(CScene *scene, CMeshBaseInstance *mbi, const nlassert(scene != NULL); CRenderTrav *renderTrav= &scene->getRenderTrav(); // setup cte for lighting - renderTrav->beginVPLightSetup(VPLightConstantStart, SpecularLighting, invertedModelMat); + CVertexProgramWindTree *program = _ActiveVertexProgram; + renderTrav->beginVPLightSetup(program, invertedModelMat); } @@ -431,6 +434,7 @@ void CMeshVPWindTree::beginMBRMesh(IDriver *driver, CScene *scene) // activate VP. driver->activeVertexProgram(_VertexProgram[_LastMBRIdVP]); + _ActiveVertexProgram = _VertexProgram[_LastMBRIdVP]; // precompute mesh setupPerMesh(driver, scene); @@ -456,6 +460,7 @@ void CMeshVPWindTree::beginMBRInstance(IDriver *driver, CScene *scene, CMeshBase { _LastMBRIdVP= idVP; driver->activeVertexProgram(_VertexProgram[_LastMBRIdVP]); + _ActiveVertexProgram = _VertexProgram[_LastMBRIdVP]; if (!_VertexProgram[_LastMBRIdVP]->PerMeshSetup) { @@ -474,6 +479,7 @@ void CMeshVPWindTree::endMBRMesh(IDriver *driver) { // Disable the VertexProgram driver->activeVertexProgram(NULL); + _ActiveVertexProgram = NULL; } // *************************************************************************** diff --git a/code/nel/src/3d/render_trav.cpp b/code/nel/src/3d/render_trav.cpp index 5735e71e1..4b43d18e9 100644 --- a/code/nel/src/3d/render_trav.cpp +++ b/code/nel/src/3d/render_trav.cpp @@ -762,13 +762,15 @@ void CRenderTrav::changeLightSetup(CLightContribution *lightContribution, bool // *************************************************************************** -void CRenderTrav::beginVPLightSetup(uint ctStart, bool supportSpecular, const CMatrix &invObjectWM) +void CRenderTrav::beginVPLightSetup(CVertexProgramLighted *program, const CMatrix &invObjectWM) { uint i; nlassert(MaxVPLight==4); _VPNumLights= min(_NumLightEnabled, (uint)MaxVPLight); - _VPCurrentCtStart= ctStart; - _VPSupportSpecular= supportSpecular; + // _VPCurrentCtStart= ctStart; + // _VPSupportSpecular= supportSpecular; + _VPCurrent = program; + bool supportSpecular = program->featuresLighted().SupportSpecular; // Prepare Colors (to be multiplied by material) //================ @@ -786,8 +788,11 @@ void CRenderTrav::beginVPLightSetup(uint ctStart, bool supportSpecular, const C // reset other to 0. for(; isetConstant(_VPCurrentCtStart+1+i, 0.f, 0.f, 0.f, 0.f); + _VPLightDiffuse[i] = CRGBA::Black; + if (program->idxLighted().Diffuse[i] != ~0) + { + Driver->setUniform4f(IDriver::VertexProgram, program->idxLighted().Diffuse[i], 0.f, 0.f, 0.f, 0.f); + } } // Specular. _VPCurrentCtStart+5 to 8 (only if supportSpecular) if(supportSpecular) @@ -800,7 +805,10 @@ void CRenderTrav::beginVPLightSetup(uint ctStart, bool supportSpecular, const C for(; isetConstant(_VPCurrentCtStart+5+i, 0.f, 0.f, 0.f, 0.f); + if (program->idxLighted().Specular[i] != ~0) + { + Driver->setUniform4f(IDriver::VertexProgram, program->idxLighted().Specular[i], 0.f, 0.f, 0.f, 0.f); + } } } @@ -816,40 +824,24 @@ void CRenderTrav::beginVPLightSetup(uint ctStart, bool supportSpecular, const C lightDir= invObjectWM.mulVector(_DriverLight[0].getDirection()); lightDir.normalize(); lightDir= -lightDir; - if(supportSpecular) - { - // Setup lightDir. - Driver->setConstant(_VPCurrentCtStart+9, lightDir); - } - else - { - // Setup lightDir. NB: no specular color! - Driver->setConstant(_VPCurrentCtStart+5, lightDir); - } + Driver->setUniform3f(IDriver::VertexProgram, program->idxLighted().DirOrPos[0], lightDir); // The sun is the same for every instance. // Setup PointLights //================ uint startPLPos; - if(supportSpecular) + if (supportSpecular) { // Setup eye in objectSpace for localViewer - Driver->setConstant(_VPCurrentCtStart+11, eye); - // Start at 12. - startPLPos= 12; - } - else - { - // Start at 6. - startPLPos= 6; + Driver->setUniform3f(IDriver::VertexProgram, program->idxLighted().EyePosition, eye); } // For all pointLight enabled (other are black: don't matter) for(i=1; i<_VPNumLights; i++) { // Setup position of light. CVector lightPos; - lightPos= invObjectWM * _DriverLight[i].getPosition(); - Driver->setConstant(_VPCurrentCtStart+startPLPos+(i-1), lightPos); + lightPos = invObjectWM * _DriverLight[i].getPosition(); + Driver->setUniform3f(IDriver::VertexProgram, program->idxLighted().DirOrPos[i], lightPos); } @@ -860,6 +852,9 @@ void CRenderTrav::beginVPLightSetup(uint ctStart, bool supportSpecular, const C // *************************************************************************** void CRenderTrav::changeVPLightSetupMaterial(const CMaterial &mat, bool excludeStrongest) { + CVertexProgramLighted *program = _VPCurrent; + nlassert(program); + // Must test if at least done one time. if(!_VPMaterialCacheDirty) { @@ -869,7 +864,7 @@ void CRenderTrav::changeVPLightSetupMaterial(const CMaterial &mat, bool exclude _VPMaterialCacheDiffuse == mat.getDiffuse().getPacked() ) { // Same Diffuse part, test if same specular if necessary - if( !_VPSupportSpecular || + if( !program->featuresLighted().SupportSpecular || ( _VPMaterialCacheSpecular == mat.getSpecular().getPacked() && _VPMaterialCacheShininess == mat.getShininess() ) ) { @@ -899,7 +894,7 @@ void CRenderTrav::changeVPLightSetupMaterial(const CMaterial &mat, bool exclude // setup Ambient + Emissive color= _VPFinalAmbient * mat.getAmbient(); color+= mat.getEmissive(); - Driver->setConstant(_VPCurrentCtStart+0, 1, &color.R); + Driver->setUniform4f(IDriver::VertexProgram, program->idxLighted().Ambient, color); // is the strongest light is not excluded, its index should have been setup to _VPNumLights @@ -908,7 +903,7 @@ void CRenderTrav::changeVPLightSetupMaterial(const CMaterial &mat, bool exclude for(i = 0; i < strongestLightIndex; ++i) { color= _VPLightDiffuse[i] * matDiff; - Driver->setConstant(_VPCurrentCtStart+1+i, 1, &color.R); + Driver->setUniform4f(IDriver::VertexProgram, program->idxLighted().Diffuse[i], color); } @@ -917,24 +912,24 @@ void CRenderTrav::changeVPLightSetupMaterial(const CMaterial &mat, bool exclude color= _VPLightDiffuse[i] * matDiff; _StrongestLightDiffuse.set((uint8) (255.f * color.R), (uint8) (255.f * color.G), (uint8) (255.f * color.B), (uint8) (255.f * color.A)); // setup strongest light to black for the gouraud part - Driver->setConstant(_VPCurrentCtStart + 1 + i, 0.f, 0.f, 0.f, 0.f); + Driver->setUniform4f(IDriver::VertexProgram, program->idxLighted().Diffuse[i], 0.f, 0.f, 0.f, 0.f); ++i; // setup other lights for(; i < _VPNumLights; i++) { color= _VPLightDiffuse[i] * matDiff; - Driver->setConstant(_VPCurrentCtStart + 1 + i, 1, &color.R); + Driver->setUniform4f(IDriver::VertexProgram, program->idxLighted().Diffuse[i], color); } } // setup Specular - if(_VPSupportSpecular) + if (program->featuresLighted().SupportSpecular) { for(i = 0; i < strongestLightIndex; ++i) { color= _VPLightSpecular[i] * matSpec; color.A= specExp; - Driver->setConstant(_VPCurrentCtStart+5+i, 1, &color.R); + Driver->setUniform4f(IDriver::VertexProgram, program->idxLighted().Specular[i], color); } if (i != _VPNumLights) @@ -943,14 +938,14 @@ void CRenderTrav::changeVPLightSetupMaterial(const CMaterial &mat, bool exclude _StrongestLightSpecular.set((uint8) (255.f * color.R), (uint8) (255.f * color.G), (uint8) (255.f * color.B), (uint8) (255.f * color.A)); // setup strongest light to black (for gouraud part) - Driver->setConstant(_VPCurrentCtStart + 5 + i, 0.f, 0.f, 0.f, 0.f); + Driver->setUniform4f(IDriver::VertexProgram, program->idxLighted().Specular[i], 0.f, 0.f, 0.f, 0.f); ++i; // setup other lights for(; i < _VPNumLights; i++) { color= _VPLightSpecular[i] * matSpec; color.A= specExp; - Driver->setConstant(_VPCurrentCtStart + 5 + i, 1, &color.R); + Driver->setUniform4f(IDriver::VertexProgram, program->idxLighted().Specular[i], color); } } } @@ -959,10 +954,7 @@ void CRenderTrav::changeVPLightSetupMaterial(const CMaterial &mat, bool exclude static float alphaCte[4]= {0,0,1,0}; alphaCte[3]= matDiff.A; // setup at good place - if(_VPSupportSpecular) - Driver->setConstant(_VPCurrentCtStart+10, 1, alphaCte); - else - Driver->setConstant(_VPCurrentCtStart+9, 1, alphaCte); + Driver->setUniform4fv(IDriver::VertexProgram, program->idxLighted().DiffuseAlpha, 1, alphaCte); } // *************************************************************************** @@ -1174,24 +1166,24 @@ void CVertexProgramLighted::buildInfo() if (profile() == nelvp) { // Fixed uniform locations - m_IdxLighted.Ambient = 0; + m_IdxLighted.Ambient = m_FeaturesLighted.CtStartNeLVP + 0; for (uint i = 0; i < MaxLight; ++i) { - m_IdxLighted.Diffuse[i] = 1 + i; + m_IdxLighted.Diffuse[i] = m_FeaturesLighted.CtStartNeLVP + 1 + i; } if (m_FeaturesLighted.SupportSpecular) { for (uint i = 0; i < MaxLight; ++i) { - m_IdxLighted.Specular[i] = 5 + i; + m_IdxLighted.Specular[i] = m_FeaturesLighted.CtStartNeLVP + 5 + i; } m_IdxLighted.DirOrPos[0] = 9; for (uint i = 1; i < MaxLight; ++i) { - m_IdxLighted.DirOrPos[i] = (12 - 1) + i; + m_IdxLighted.DirOrPos[i] = m_FeaturesLighted.CtStartNeLVP + (12 - 1) + i; } - m_IdxLighted.DiffuseAlpha = 10; - m_IdxLighted.EyePosition = 11; + m_IdxLighted.DiffuseAlpha = m_FeaturesLighted.CtStartNeLVP + 10; + m_IdxLighted.EyePosition = m_FeaturesLighted.CtStartNeLVP + 11; } else { @@ -1201,9 +1193,9 @@ void CVertexProgramLighted::buildInfo() } for (uint i = 0; i < MaxLight; ++i) { - m_IdxLighted.DirOrPos[i] = 5 + i; + m_IdxLighted.DirOrPos[i] = m_FeaturesLighted.CtStartNeLVP + 5 + i; } - m_IdxLighted.DiffuseAlpha = 9; + m_IdxLighted.DiffuseAlpha = m_FeaturesLighted.CtStartNeLVP + 9; m_IdxLighted.EyePosition = ~0; } } @@ -1214,6 +1206,15 @@ void CVertexProgramLighted::buildInfo() // m_IdxLighted.Ambient = getUniformIndex("ambient"); // etc } + + nlassert(m_IdxLighted.Diffuse[0] != ~0); + if (m_FeaturesLighted.SupportSpecular) + { + nlassert(m_IdxLighted.Specular[0] != ~0); + nlassert(m_IdxLighted.EyePosition != ~0); + } + nlassert(m_IdxLighted.DirOrPos[0] != ~0); + nlassert(m_IdxLighted.DiffuseAlpha != ~0); } // generates the lighting part of a vertex program, nelvp profile From 7176cbc0af66d2309bb514c3c8f7f551c6231146 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Fri, 13 Sep 2013 21:47:42 +0200 Subject: [PATCH 236/313] Use named indices for per pixel light vertex program --- code/nel/src/3d/meshvp_per_pixel_light.cpp | 41 ++++++++++++++++++---- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/code/nel/src/3d/meshvp_per_pixel_light.cpp b/code/nel/src/3d/meshvp_per_pixel_light.cpp index cf2d35f39..b607aa7d5 100644 --- a/code/nel/src/3d/meshvp_per_pixel_light.cpp +++ b/code/nel/src/3d/meshvp_per_pixel_light.cpp @@ -357,9 +357,12 @@ static const char* PPLightingVPCodeTest = class CVertexProgramPerPixelLight : public CVertexProgramLighted { public: - class CIdx + struct CIdx { - + /// Position or direction of strongest light + uint StrongestLight; + /// Viewer position + uint ViewerPos; }; CVertexProgramPerPixelLight(uint vp); virtual ~CVertexProgramPerPixelLight() { }; @@ -436,6 +439,7 @@ CVertexProgramPerPixelLight::CVertexProgramPerPixelLight(uint vp) source->DisplayName = NLMISC::toString("nelvp/MeshVPPerPixel/%i", vp); source->Profile = CVertexProgram::nelvp; source->setSource(vpCode); + source->ParamIndices["modelViewProjection"] = 0; addSource(source); } @@ -448,6 +452,27 @@ CVertexProgramPerPixelLight::CVertexProgramPerPixelLight(uint vp) void CVertexProgramPerPixelLight::buildInfo() { CVertexProgramLighted::buildInfo(); + if (profile() == nelvp) + { + m_Idx.StrongestLight = 4; + if (m_FeaturesLighted.SupportSpecular) + { + m_Idx.ViewerPos = 5; + } + else + { + m_Idx.ViewerPos = ~0; + } + } + else + { + // TODO_VP_GLSL + } + nlassert(m_Idx.StrongestLight != ~0); + if (m_FeaturesLighted.SupportSpecular) + { + nlassert(m_Idx.ViewerPos != ~0); + } } @@ -485,10 +510,12 @@ bool CMeshVPPerPixelLight::begin(IDriver *drv, } // enable(true, drv); // must enable the vertex program before the vb is activated + CVertexProgramPerPixelLight *program = _ActiveVertexProgram; + nlassert(program); // CRenderTrav *renderTrav= &scene->getRenderTrav(); /// Setup for gouraud lighting - renderTrav->beginVPLightSetup(_ActiveVertexProgram, invertedModelMat); + renderTrav->beginVPLightSetup(program, invertedModelMat); // sint strongestLightIndex = renderTrav->getStrongestLightIndex(); if (strongestLightIndex == -1) return false; // if no strongest light, disable this vertex program @@ -502,7 +529,7 @@ bool CMeshVPPerPixelLight::begin(IDriver *drv, { // put light direction in object space NLMISC::CVector lPos = invertedModelMat.mulVector(strongestLight.getDirection()); - drv->setConstant(4, lPos); + drv->setUniform3f(IDriver::VertexProgram, program->idx().StrongestLight, lPos); _IsPointLight = false; } break; @@ -510,7 +537,7 @@ bool CMeshVPPerPixelLight::begin(IDriver *drv, { // put light in object space NLMISC::CVector lPos = invertedModelMat * strongestLight.getPosition(); - drv->setConstant(4, lPos); + drv->setUniform3f(IDriver::VertexProgram, program->idx().StrongestLight, lPos); _IsPointLight = true; } break; @@ -524,11 +551,11 @@ bool CMeshVPPerPixelLight::begin(IDriver *drv, { // viewer pos in object space NLMISC::CVector vPos = invertedModelMat * viewerPos; - drv->setConstant(5, vPos); + drv->setUniform3f(IDriver::VertexProgram, program->idx().ViewerPos, vPos); } // c[0..3] take the ModelViewProjection Matrix. After setupModelMatrix(); - drv->setConstantMatrix(0, IDriver::ModelViewProjection, IDriver::Identity); + drv->setUniformMatrix(IDriver::VertexProgram, program->getUniformIndex(CGPUProgramIndex::ModelViewProjection), IDriver::ModelViewProjection, IDriver::Identity); return true; } From 881949a5d7f3034e063eb75b9fc26b8d8426088c Mon Sep 17 00:00:00 2001 From: kaetemi Date: Fri, 13 Sep 2013 22:20:52 +0200 Subject: [PATCH 237/313] Use named indices with wind tree program --- code/nel/src/3d/meshvp_wind_tree.cpp | 78 +++++++++++++++++++++------- 1 file changed, 59 insertions(+), 19 deletions(-) diff --git a/code/nel/src/3d/meshvp_wind_tree.cpp b/code/nel/src/3d/meshvp_wind_tree.cpp index 17e847c3e..174adfda8 100644 --- a/code/nel/src/3d/meshvp_wind_tree.cpp +++ b/code/nel/src/3d/meshvp_wind_tree.cpp @@ -83,9 +83,12 @@ static const char* WindTreeVPCodeEnd= class CVertexProgramWindTree : public CVertexProgramLighted { public: - class CIdx + struct CIdx { - + uint ProgramConstants[3]; + uint WindLevel1; + uint WindLevel2[4]; + uint WindLevel3[4]; }; CVertexProgramWindTree(uint numPls, bool specular, bool normalize); virtual ~CVertexProgramWindTree() { }; @@ -120,6 +123,8 @@ CVertexProgramWindTree::CVertexProgramWindTree(uint numPls, bool specular, bool source->DisplayName = NLMISC::toString("nelvp/MeshVPWindTree/%i/%s/%s", numPls, specular ? "spec" : "nospec", normalize ? "normalize" : "nonormalize"); source->Profile = CVertexProgram::nelvp; source->setSource(vpCode); + source->ParamIndices["modelViewProjection"] = 0; + source->ParamIndices["fog"] = 6; addSource(source); } @@ -129,6 +134,25 @@ CVertexProgramWindTree::CVertexProgramWindTree(uint numPls, bool specular, bool void CVertexProgramWindTree::buildInfo() { CVertexProgramLighted::buildInfo(); + if (profile() == nelvp) + { + m_Idx.ProgramConstants[0] = 8; + m_Idx.ProgramConstants[1] = 9; + m_Idx.ProgramConstants[2] = 10; + m_Idx.WindLevel1 = 15; + m_Idx.WindLevel2[0] = 16; + m_Idx.WindLevel2[1] = 17; + m_Idx.WindLevel2[2] = 18; + m_Idx.WindLevel2[3] = 19; + m_Idx.WindLevel3[0] = 20; + m_Idx.WindLevel3[1] = 21; + m_Idx.WindLevel3[2] = 22; + m_Idx.WindLevel3[3] = 23; + } + else + { + // TODO_VP_GLSL + } } @@ -250,21 +274,27 @@ inline void CMeshVPWindTree::setupPerMesh(IDriver *driver, CScene *scene) } } + CVertexProgramWindTree *program = _ActiveVertexProgram; + nlassert(program); + // Setup common constants for each instances. // c[8] take useful constants. - static float ct8[4]= {0, 1, 0.5f, 2}; - driver->setConstant(8, 1, ct8); + driver->setUniform4f(IDriver::VertexProgram, program->idx().ProgramConstants[0], + 0, 1, 0.5f, 2); // c[9] take other useful constants. - static float ct9[4]= {3.f, 0.f, -1.f, -2.f}; - driver->setConstant(9, 1, ct9); + driver->setUniform4f(IDriver::VertexProgram, program->idx().ProgramConstants[1], + 3.f, 0.f, -1.f, -2.f); // c[10] take Number of phase (4) for level2 and 3. -0.01 to avoid int value == 4. - static float ct10[4]= {4-0.01f, 0, 0, 0}; - driver->setConstant(10, 1, ct10); + driver->setUniform4f(IDriver::VertexProgram, program->idx().ProgramConstants[2], + 4-0.01f, 0, 0, 0); } // *************************************************************************** inline void CMeshVPWindTree::setupPerInstanceConstants(IDriver *driver, CScene *scene, CMeshBaseInstance *mbi, const NLMISC::CMatrix &invertedModelMat) { + CVertexProgramWindTree *program = _ActiveVertexProgram; + nlassert(program); + // get instance info float instancePhase= mbi->_VPWindTreePhase; @@ -285,16 +315,18 @@ inline void CMeshVPWindTree::setupPerInstanceConstants(IDriver *driver, CScene setupLighting(scene, mbi, invertedModelMat); // c[0..3] take the ModelViewProjection Matrix. After setupModelMatrix(); - driver->setConstantMatrix(0, IDriver::ModelViewProjection, IDriver::Identity); + driver->setUniformMatrix(IDriver::VertexProgram, program->getUniformIndex(CGPUProgramIndex::ModelViewProjection), + IDriver::ModelViewProjection, IDriver::Identity); // c[4..7] take the ModelView Matrix. After setupModelMatrix();00 - driver->setConstantFog(6); + driver->setUniformFog(IDriver::VertexProgram, program->getUniformIndex(CGPUProgramIndex::Fog)); // c[15] take Wind of level 0. float f; f= _CurrentTime[0] + instancePhase; f= speedCos(f) + Bias[0]; - driver->setConstant(15, maxDeltaPosOS[0]*f ); + driver->setUniform3f(IDriver::VertexProgram, program->idx().WindLevel1, + maxDeltaPosOS[0]*f ); // c[16-19] take Wind of level 1. @@ -302,16 +334,20 @@ inline void CMeshVPWindTree::setupPerInstanceConstants(IDriver *driver, CScene float instTime1= _CurrentTime[1] + instancePhase; // phase 0. f= speedCos( instTime1+0 ) + Bias[1]; - driver->setConstant(16+0, maxDeltaPosOS[1]*f); + driver->setUniform3f(IDriver::VertexProgram, program->idx().WindLevel2[0], + maxDeltaPosOS[1]*f); // phase 1. f= speedCos( instTime1+0.25f ) + Bias[1]; - driver->setConstant(16+1, maxDeltaPosOS[1]*f); + driver->setUniform3f(IDriver::VertexProgram, program->idx().WindLevel2[1], + maxDeltaPosOS[1]*f); // phase 2. f= speedCos( instTime1+0.50f ) + Bias[1]; - driver->setConstant(16+2, maxDeltaPosOS[1]*f); + driver->setUniform3f(IDriver::VertexProgram, program->idx().WindLevel2[2], + maxDeltaPosOS[1]*f); // phase 3. f= speedCos( instTime1+0.75f ) + Bias[1]; - driver->setConstant(16+3, maxDeltaPosOS[1]*f); + driver->setUniform3f(IDriver::VertexProgram, program->idx().WindLevel2[3], + maxDeltaPosOS[1]*f); // c[20, 23] take Wind of level 2. @@ -319,16 +355,20 @@ inline void CMeshVPWindTree::setupPerInstanceConstants(IDriver *driver, CScene float instTime2= _CurrentTime[2] + instancePhase; // phase 0. f= speedCos( instTime2+0 ) + Bias[2]; - driver->setConstant(20+0, maxDeltaPosOS[2]*f); + driver->setUniform3f(IDriver::VertexProgram, program->idx().WindLevel3[0], + maxDeltaPosOS[2]*f); // phase 1. f= speedCos( instTime2+0.25f ) + Bias[2]; - driver->setConstant(20+1, maxDeltaPosOS[2]*f); + driver->setUniform3f(IDriver::VertexProgram, program->idx().WindLevel3[1], + maxDeltaPosOS[2]*f); // phase 2. f= speedCos( instTime2+0.50f ) + Bias[2]; - driver->setConstant(20+2, maxDeltaPosOS[2]*f); + driver->setUniform3f(IDriver::VertexProgram, program->idx().WindLevel3[2], + maxDeltaPosOS[2]*f); // phase 3. f= speedCos( instTime2+0.75f ) + Bias[2]; - driver->setConstant(20+3, maxDeltaPosOS[2]*f); + driver->setUniform3f(IDriver::VertexProgram, program->idx().WindLevel3[3], + maxDeltaPosOS[2]*f); } // *************************************************************************** From 291c5d60120f8979b78fa8b18f3267340b7f5f8b Mon Sep 17 00:00:00 2001 From: kaetemi Date: Fri, 13 Sep 2013 23:02:35 +0200 Subject: [PATCH 238/313] Cleanup --- code/nel/src/3d/bloom_effect.cpp | 13 ++++++++----- code/nel/src/3d/water_env_map.cpp | 13 +++++++++---- code/ryzom/client/src/decal.cpp | 25 +++++++++++++++---------- 3 files changed, 32 insertions(+), 19 deletions(-) diff --git a/code/nel/src/3d/bloom_effect.cpp b/code/nel/src/3d/bloom_effect.cpp index 25439ee46..7809aba2c 100644 --- a/code/nel/src/3d/bloom_effect.cpp +++ b/code/nel/src/3d/bloom_effect.cpp @@ -55,15 +55,18 @@ static const char *TextureOffset = END \n"; -static CVertexProgram TextureOffsetVertexProgram(TextureOffset); - -// TODO_VP_GLSL +static NLMISC::CSmartPtr TextureOffsetVertexProgram; //----------------------------------------------------------------------------------------------------------- CBloomEffect::CBloomEffect() { + if (!TextureOffsetVertexProgram) + { + TextureOffsetVertexProgram = new CVertexProgram(TextureOffset); + } + _Driver = NULL; _Scene = NULL; _SquareBloom = true; @@ -445,7 +448,7 @@ void CBloomEffect::applyBlur() } // initialize vertex program - drvInternal->activeVertexProgram(&TextureOffsetVertexProgram); + drvInternal->activeVertexProgram(TextureOffsetVertexProgram); drvInternal->setUniform4f(IDriver::VertexProgram, 8, 255.f, 255.f, 255.f, 255.f); drvInternal->setUniform4f(IDriver::VertexProgram, 9, 0.0f, 0.f, 0.f, 1.f); @@ -551,7 +554,7 @@ void CBloomEffect::doBlur(bool horizontalBlur) } // initialize vertex program - drvInternal->activeVertexProgram(&TextureOffsetVertexProgram); + drvInternal->activeVertexProgram(TextureOffsetVertexProgram); drvInternal->setUniform4f(IDriver::VertexProgram, 8, 255.f, 255.f, 255.f, 255.f); drvInternal->setUniform4f(IDriver::VertexProgram, 9, 0.0f, 0.f, 0.f, 1.f); diff --git a/code/nel/src/3d/water_env_map.cpp b/code/nel/src/3d/water_env_map.cpp index e75fd172a..6bc6beda5 100644 --- a/code/nel/src/3d/water_env_map.cpp +++ b/code/nel/src/3d/water_env_map.cpp @@ -274,13 +274,18 @@ private: }; -static CVertexProgramTestMeshVP testMeshVP; +static NLMISC::CSmartPtr testMeshVP; // ******************************************************************************* void CWaterEnvMap::renderTestMesh(IDriver &driver) { + if (!testMeshVP) + { + testMeshVP = new CVertexProgramTestMeshVP(); + } + doInit(); CMaterial testMat; testMat.setLighting(false); @@ -294,12 +299,12 @@ void CWaterEnvMap::renderTestMesh(IDriver &driver) testMat.setZWrite(false); testMat.setZFunc(CMaterial::always); // tmp : test cubemap - driver.activeVertexProgram(&testMeshVP); + driver.activeVertexProgram(testMeshVP); driver.activeVertexBuffer(_TestVB); driver.activeIndexBuffer(_TestIB); _MaterialPassThruZTest.setTexture(0, _EnvCubic); - driver.setUniformMatrix(IDriver::VertexProgram, testMeshVP.getUniformIndex(CGPUProgramIndex::ModelViewProjection), IDriver::ModelViewProjection, IDriver::Identity); - driver.setUniform2f(IDriver::VertexProgram, testMeshVP.idx().ProgramConstant0, 2.f, 1.f); + driver.setUniformMatrix(IDriver::VertexProgram, testMeshVP->getUniformIndex(CGPUProgramIndex::ModelViewProjection), IDriver::ModelViewProjection, IDriver::Identity); + driver.setUniform2f(IDriver::VertexProgram, testMeshVP->idx().ProgramConstant0, 2.f, 1.f); //driver.renderTriangles(testMat, 0, TEST_VB_NUM_TRIS); driver.renderTriangles(_MaterialPassThruZTest, 0, TEST_VB_NUM_TRIS); driver.activeVertexProgram(NULL); diff --git a/code/ryzom/client/src/decal.cpp b/code/ryzom/client/src/decal.cpp index a1cca247d..073c1ef0d 100644 --- a/code/ryzom/client/src/decal.cpp +++ b/code/ryzom/client/src/decal.cpp @@ -142,7 +142,7 @@ private: CIdx m_Idx; }; -static CVertexProgramDecalAttenuation DecalAttenuationVertexProgram; +static NLMISC::CSmartPtr DecalAttenuationVertexProgram; typedef CShadowPolyReceiver::CRGBAVertex CRGBAVertex; @@ -150,6 +150,10 @@ typedef CShadowPolyReceiver::CRGBAVertex CRGBAVertex; // **************************************************************************** CDecal::CDecal() { + if (!DecalAttenuationVertexProgram) + { + DecalAttenuationVertexProgram = new CVertexProgramDecalAttenuation(); + } _ShadowMap = new CShadowMap(&(((CSceneUser *) Scene)->getScene().getRenderTrav().getShadowMapManager())); _Material.initUnlit(); _Diffuse = CRGBA::White; @@ -361,6 +365,7 @@ void CDecal::renderTriCache(NL3D::IDriver &drv, NL3D::CShadowPolyReceiver &/* drv.setupModelMatrix(modelMat); if (useVertexProgram) { + CVertexProgramDecalAttenuation *program = DecalAttenuationVertexProgram; { CVertexBufferReadWrite vba; _VB.setNumVertices((uint32)_TriCache.size()); @@ -368,16 +373,16 @@ void CDecal::renderTriCache(NL3D::IDriver &drv, NL3D::CShadowPolyReceiver &/* memcpy(vba.getVertexCoordPointer(), &_TriCache[0], sizeof(CRGBAVertex) * _TriCache.size()); } drv.activeVertexBuffer(_VB); - drv.setUniformMatrix(IDriver::VertexProgram, DecalAttenuationVertexProgram.getUniformIndex(CGPUProgramIndex::ModelViewProjection), NL3D::IDriver::ModelViewProjection, NL3D::IDriver::Identity); - drv.setUniform4f(IDriver::VertexProgram, DecalAttenuationVertexProgram.idx().WorldToUV0, _WorldToUVMatrix[0][0], _WorldToUVMatrix[1][0], _WorldToUVMatrix[2][0], _WorldToUVMatrix[3][0]); - drv.setUniform4f(IDriver::VertexProgram, DecalAttenuationVertexProgram.idx().WorldToUV1, _WorldToUVMatrix[0][1], _WorldToUVMatrix[1][1], _WorldToUVMatrix[2][1], _WorldToUVMatrix[3][1]); - drv.setUniform4f(IDriver::VertexProgram, DecalAttenuationVertexProgram.idx().Diffuse, _Diffuse.R * (1.f / 255.f), _Diffuse.G * (1.f / 255.f), _Diffuse.B * (1.f / 255.f), 1.f); + drv.setUniformMatrix(IDriver::VertexProgram, program->getUniformIndex(CGPUProgramIndex::ModelViewProjection), NL3D::IDriver::ModelViewProjection, NL3D::IDriver::Identity); + drv.setUniform4f(IDriver::VertexProgram, program->idx().WorldToUV0, _WorldToUVMatrix[0][0], _WorldToUVMatrix[1][0], _WorldToUVMatrix[2][0], _WorldToUVMatrix[3][0]); + drv.setUniform4f(IDriver::VertexProgram, program->idx().WorldToUV1, _WorldToUVMatrix[0][1], _WorldToUVMatrix[1][1], _WorldToUVMatrix[2][1], _WorldToUVMatrix[3][1]); + drv.setUniform4f(IDriver::VertexProgram, program->idx().Diffuse, _Diffuse.R * (1.f / 255.f), _Diffuse.G * (1.f / 255.f), _Diffuse.B * (1.f / 255.f), 1.f); const NLMISC::CVector &camPos = MainCam.getMatrix().getPos(); - drv.setUniform4f(IDriver::VertexProgram, DecalAttenuationVertexProgram.idx().RefCamDist, camPos.x - _RefPosition.x, camPos.y - _RefPosition.y, camPos.z - _RefPosition.z, 1.f); + drv.setUniform4f(IDriver::VertexProgram, program->idx().RefCamDist, camPos.x - _RefPosition.x, camPos.y - _RefPosition.y, camPos.z - _RefPosition.z, 1.f); // bottom & top blend float bottomBlendScale = 1.f / favoid0(_BottomBlendZMax - _BottomBlendZMin); float topBlendScale = 1.f / favoid0(_TopBlendZMin - _TopBlendZMax); - drv.setUniform4f(IDriver::VertexProgram, DecalAttenuationVertexProgram.idx().BlendScale, bottomBlendScale, bottomBlendScale * (_RefPosition.z - _BottomBlendZMin), + drv.setUniform4f(IDriver::VertexProgram, program->idx().BlendScale, bottomBlendScale, bottomBlendScale * (_RefPosition.z - _BottomBlendZMin), topBlendScale, topBlendScale * (_RefPosition.z - _TopBlendZMax)); // static volatile bool wantSimpleMat = false; @@ -618,11 +623,11 @@ void CDecalRenderList::renderAllDecals() NL3D::IDriver *drvInternal = ((CDriverUser *) Driver)->getDriver(); // static volatile bool forceNoVertexProgram = false; - if (!forceNoVertexProgram && drvInternal->compileVertexProgram(&DecalAttenuationVertexProgram)) + if (!forceNoVertexProgram && drvInternal->compileVertexProgram(DecalAttenuationVertexProgram)) { - drvInternal->activeVertexProgram(&DecalAttenuationVertexProgram); + drvInternal->activeVertexProgram(DecalAttenuationVertexProgram); //drvInternal->setCons/tantMatrix(0, NL3D::IDriver::ModelViewProjection, NL3D::IDriver::Identity); - drvInternal->setUniform4f(IDriver::VertexProgram, DecalAttenuationVertexProgram.idx().DistScaleBias, _DistScale, _DistBias, 0.f, 1.f); + drvInternal->setUniform4f(IDriver::VertexProgram, DecalAttenuationVertexProgram->idx().DistScaleBias, _DistScale, _DistBias, 0.f, 1.f); useVertexProgram = true; } else From 4f193231a100dad2c0a534b2e17a7a06cff0f695 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Fri, 13 Sep 2013 23:23:19 +0200 Subject: [PATCH 239/313] Fix compile errors caused by missing includes --- code/nel/include/nel/misc/fast_floor.h | 1 + code/nel/src/misc/p_thread.cpp | 3 +++ 2 files changed, 4 insertions(+) diff --git a/code/nel/include/nel/misc/fast_floor.h b/code/nel/include/nel/misc/fast_floor.h index f58e475e5..6cb8e209c 100644 --- a/code/nel/include/nel/misc/fast_floor.h +++ b/code/nel/include/nel/misc/fast_floor.h @@ -19,6 +19,7 @@ #include "types_nl.h" #include +#include namespace NLMISC { diff --git a/code/nel/src/misc/p_thread.cpp b/code/nel/src/misc/p_thread.cpp index e4deab4ca..a24029b3c 100644 --- a/code/nel/src/misc/p_thread.cpp +++ b/code/nel/src/misc/p_thread.cpp @@ -17,6 +17,9 @@ #include "stdmisc.h" +#include +#include + #ifdef NL_OS_UNIX #include "nel/misc/p_thread.h" From 08f37643b7093642aab1b369c23db0ce8728067a Mon Sep 17 00:00:00 2001 From: kaetemi Date: Sat, 14 Sep 2013 00:36:06 +0200 Subject: [PATCH 240/313] Cleanup --- code/CMakeModules/FindFreeType.cmake | 2 + code/nel/include/nel/3d/driver.h | 4 -- code/nel/src/3d/driver/opengl/driver_opengl.h | 6 +-- .../driver/opengl/driver_opengl_material.cpp | 8 ++-- .../driver/opengl/driver_opengl_uniform.cpp | 40 +++++++++++++------ code/nel/src/3d/vegetable_manager.cpp | 4 +- 6 files changed, 36 insertions(+), 28 deletions(-) diff --git a/code/CMakeModules/FindFreeType.cmake b/code/CMakeModules/FindFreeType.cmake index e15f55793..68a3ccdbd 100644 --- a/code/CMakeModules/FindFreeType.cmake +++ b/code/CMakeModules/FindFreeType.cmake @@ -52,6 +52,7 @@ FIND_LIBRARY(FREETYPE_LIBRARY_RELEASE /opt/csw/lib /opt/lib /usr/freeware/lib64 + /usr/lib/x86_64-linux-gnu ) FIND_LIBRARY(FREETYPE_LIBRARY_DEBUG @@ -67,6 +68,7 @@ FIND_LIBRARY(FREETYPE_LIBRARY_DEBUG /opt/csw/lib /opt/lib /usr/freeware/lib64 + /usr/lib/x86_64-linux-gnu ) IF(FREETYPE_INCLUDE_DIRS) diff --git a/code/nel/include/nel/3d/driver.h b/code/nel/include/nel/3d/driver.h index 38c0f6c32..c9c55f864 100644 --- a/code/nel/include/nel/3d/driver.h +++ b/code/nel/include/nel/3d/driver.h @@ -1200,10 +1200,6 @@ public: virtual void setUniformMatrix(TProgram program, uint index, TMatrix matrix, TTransform transform) = 0; virtual void setUniformFog(TProgram program, uint index) = 0; // Set feature parameters - virtual bool setUniformDriver(TProgram program) = 0; // set all driver-specific features params (based on program->features->DriverFlags) (called automatically when rendering with cmaterial and using a user program) - virtual bool setUniformMaterial(TProgram program, CMaterial &material) = 0; // set all material-specific feature params (based on program->features->MaterialFlags) (called automatically when rendering with cmaterial and using a user program) - virtual void setUniformParams(TProgram program, CGPUProgramParams ¶ms) = 0; // set all user-provided params from the storage - // Return true if uniforms are kept as program state and switched together with programs, false if uniforms are driver state and stay accross program switches. virtual bool isUniformProgramState() = 0; // @} diff --git a/code/nel/src/3d/driver/opengl/driver_opengl.h b/code/nel/src/3d/driver/opengl/driver_opengl.h index afab8209c..154506250 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl.h +++ b/code/nel/src/3d/driver/opengl/driver_opengl.h @@ -1392,6 +1392,8 @@ private: /// \name Program parameters // @{ // Set parameters + inline void setUniform4fInl(TProgram program, uint index, float f0, float f1, float f2, float f3); + inline void setUniform4fvInl(TProgram program, uint index, size_t num, const float *src); virtual void setUniform1f(TProgram program, uint index, float f0); virtual void setUniform2f(TProgram program, uint index, float f0, float f1); virtual void setUniform3f(TProgram program, uint index, float f0, float f1, float f2); @@ -1415,10 +1417,6 @@ private: virtual void setUniformMatrix(TProgram program, uint index, TMatrix matrix, TTransform transform); virtual void setUniformFog(TProgram program, uint index); // Set feature parameters - virtual bool setUniformDriver(TProgram program); // set all driver-specific features params (based on program->features->DriverFlags) - virtual bool setUniformMaterial(TProgram program, CMaterial &material); // set all material-specific feature params (based on program->features->MaterialFlags) - bool setUniformMaterialInternal(TProgram program, CMaterial &material); // set all material-specific feature params (based on program->features->MaterialFlags) - virtual void setUniformParams(TProgram program, CGPUProgramParams ¶ms); // set all user-provided params from the storage virtual bool isUniformProgramState() { return false; } // @} diff --git a/code/nel/src/3d/driver/opengl/driver_opengl_material.cpp b/code/nel/src/3d/driver/opengl/driver_opengl_material.cpp index 67afe7868..61a685428 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl_material.cpp +++ b/code/nel/src/3d/driver/opengl/driver_opengl_material.cpp @@ -362,17 +362,17 @@ bool CDriverGL::setupMaterial(CMaterial& mat) // 2b. User supplied pixel shader overrides material //================================== - if (_VertexProgramEnabled) + /*if (_VertexProgramEnabled) { if (!setUniformDriver(VertexProgram)) return false; if (!setUniformMaterialInternal(VertexProgram, mat)) return false; - } + }*/ if (_PixelProgramEnabled) { matShader = CMaterial::Program; - if (!setUniformDriver(PixelProgram)) return false; - if (!setUniformMaterialInternal(PixelProgram, mat)) return false; + // if (!setUniformDriver(PixelProgram)) return false; + // if (!setUniformMaterialInternal(PixelProgram, mat)) return false; if (!_LastSetuppedPP) return false; } else diff --git a/code/nel/src/3d/driver/opengl/driver_opengl_uniform.cpp b/code/nel/src/3d/driver/opengl/driver_opengl_uniform.cpp index deee62379..b3ba1ba54 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl_uniform.cpp +++ b/code/nel/src/3d/driver/opengl/driver_opengl_uniform.cpp @@ -31,7 +31,7 @@ namespace NLDRIVERGL { #endif #endif -inline void CDriverGL::setUniform4f(TProgram program, uint index, float f0, float f1, float f2, float f3) +inline void CDriverGL::setUniform4fInl(TProgram program, uint index, float f0, float f1, float f2, float f3) { H_AUTO_OGL(CDriverGL_setUniform4f); @@ -64,7 +64,7 @@ inline void CDriverGL::setUniform4f(TProgram program, uint index, float f0, floa #endif } -inline void CDriverGL::setUniform4fv(TProgram program, uint index, size_t num, const float *src) +inline void CDriverGL::setUniform4fvInl(TProgram program, uint index, size_t num, const float *src) { H_AUTO_OGL(CDriverGL_setUniform4fv); @@ -106,17 +106,22 @@ inline void CDriverGL::setUniform4fv(TProgram program, uint index, size_t num, c void CDriverGL::setUniform1f(TProgram program, uint index, float f0) { - CDriverGL::setUniform4f(program, index, f0, 0.f, 0.f, 0.f); + CDriverGL::setUniform4fInl(program, index, f0, 0.f, 0.f, 0.f); } void CDriverGL::setUniform2f(TProgram program, uint index, float f0, float f1) { - CDriverGL::setUniform4f(program, index, f0, f1, 0.f, 0.f); + CDriverGL::setUniform4fInl(program, index, f0, f1, 0.f, 0.f); } void CDriverGL::setUniform3f(TProgram program, uint index, float f0, float f1, float f2) { - CDriverGL::setUniform4f(program, index, f0, f1, f2, 0.0f); + CDriverGL::setUniform4fInl(program, index, f0, f1, f2, 0.0f); +} + +void CDriverGL::setUniform4f(TProgram program, uint index, float f0, float f1, float f2, float f3) +{ + CDriverGL::setUniform4fInl(program, index, f0, f1, f2, f3); } void CDriverGL::setUniform1i(TProgram program, uint index, sint32 i0) @@ -161,17 +166,17 @@ void CDriverGL::setUniform4ui(TProgram program, uint index, uint32 ui0, uint32 u void CDriverGL::setUniform3f(TProgram program, uint index, const NLMISC::CVector& v) { - CDriverGL::setUniform4f(program, index, v.x, v.y, v.z, 0.f); + CDriverGL::setUniform4fInl(program, index, v.x, v.y, v.z, 0.f); } void CDriverGL::setUniform4f(TProgram program, uint index, const NLMISC::CVector& v, float f3) { - CDriverGL::setUniform4f(program, index, v.x, v.y, v.z, f3); + CDriverGL::setUniform4fInl(program, index, v.x, v.y, v.z, f3); } void CDriverGL::setUniform4f(TProgram program, uint index, const NLMISC::CRGBAF& rgba) { - CDriverGL::setUniform4fv(program, index, 1, &rgba.R); + CDriverGL::setUniform4fvInl(program, index, 1, &rgba.R); } void CDriverGL::setUniform4x4f(TProgram program, uint index, const NLMISC::CMatrix& m) @@ -183,7 +188,12 @@ void CDriverGL::setUniform4x4f(TProgram program, uint index, const NLMISC::CMatr mat.transpose(); const float *md = mat.get(); - CDriverGL::setUniform4fv(program, index, 4, md); + CDriverGL::setUniform4fvInl(program, index, 4, md); +} + +void CDriverGL::setUniform4fv(TProgram program, uint index, size_t num, const float *src) +{ + CDriverGL::setUniform4fvInl(program, index, num, src); } void CDriverGL::setUniform4iv(TProgram program, uint index, size_t num, const sint32 *src) @@ -283,7 +293,7 @@ void CDriverGL::setUniformMatrix(NL3D::IDriver::TProgram program, uint index, NL mat.transpose(); const float *md = mat.get(); - CDriverGL::setUniform4fv(program, index, 4, md); + CDriverGL::setUniform4fvInl(program, index, 4, md); } #endif } @@ -293,9 +303,11 @@ void CDriverGL::setUniformFog(NL3D::IDriver::TProgram program, uint index) H_AUTO_OGL(CDriverGL_setUniformFog) const float *values = _ModelViewMatrix.get(); - CDriverGL::setUniform4f(program, index, -values[2], -values[6], -values[10], -values[14]); + CDriverGL::setUniform4fInl(program, index, -values[2], -values[6], -values[10], -values[14]); } +/* + bool CDriverGL::setUniformDriver(TProgram program) { IGPUProgram *prog = NULL; @@ -476,9 +488,9 @@ void CDriverGL::setUniformParams(TProgram program, CGPUProgramParams ¶ms) if (index == ~0) { const std::string &name = params.getNameByOffset(offset); - nlassert(!name.empty() /* missing both parameter name and index, code error */); + nlassert(!name.empty() /* missing both parameter name and index, code error /); uint index = prog->getUniformIndex(name.c_str()); - nlassert(index != ~0 /* invalid parameter name */); + nlassert(index != ~0 /* invalid parameter name /); params.map(index, name); } @@ -488,6 +500,8 @@ void CDriverGL::setUniformParams(TProgram program, CGPUProgramParams ¶ms) } } +*/ + #ifdef NL_STATIC } // NLDRIVERGL/ES #endif diff --git a/code/nel/src/3d/vegetable_manager.cpp b/code/nel/src/3d/vegetable_manager.cpp index ba5f721bb..d548413a8 100644 --- a/code/nel/src/3d/vegetable_manager.cpp +++ b/code/nel/src/3d/vegetable_manager.cpp @@ -126,9 +126,7 @@ CVegetableManager::~CVegetableManager() // delete All VP for(sint i=0; i Date: Sat, 14 Sep 2013 00:39:03 +0200 Subject: [PATCH 241/313] Added more documentations, this time also the inc/func files are documented and changed the doxygen config file with the help of Botanic --- .hgignore | 1 + .../ryzom_ams/ams_lib/autoload/dblayer.php | 2 +- .../ams_lib/autoload/mail_handler.php | 6 +- .../ams_lib/autoload/support_group.php | 2 +- .../ryzom_ams/ams_lib/autoload/ticket_log.php | 2 +- .../ams_lib/autoload/ticket_queue.php | 4 +- .../ryzom_ams/ams_lib/autoload/users.php | 7 +- .../ryzom_ams/ams_lib/cron/mail_cron.php | 5 + .../ryzom_ams/ams_lib/cron/sync_cron.php | 5 + .../server/ryzom_ams/ams_lib/libinclude.php | 7 +- .../ryzommanage/autoload/webusers.php | 6 +- .../drupal_module/ryzommanage/config.php | 18 +- .../ryzommanage/ryzommanage.module | 12 +- code/ryzom/tools/server/ryzom_ams/todo.txt | 4 +- .../tools/server/ryzom_ams/www/config.php | 27 +- .../ryzom_ams/www/html/autoload/webusers.php | 6 +- .../tools/server/ryzom_ams/www/html/error.php | 79 - .../ryzom_ams/www/html/func/add_sgroup.php | 15 +- .../ryzom_ams/www/html/func/add_user.php | 15 +- .../www/html/func/add_user_to_sgroup.php | 17 +- .../ryzom_ams/www/html/func/change_info.php | 16 +- .../ryzom_ams/www/html/func/change_mail.php | 14 +- .../www/html/func/change_password.php | 10 +- .../www/html/func/change_receivemail.php | 9 +- .../ryzom_ams/www/html/func/create_ticket.php | 11 +- .../server/ryzom_ams/www/html/func/login.php | 8 +- .../www/html/func/modify_email_of_sgroup.php | 9 +- .../www/html/func/reply_on_ticket.php | 15 +- .../www/html/inc/change_permission.php | 11 +- .../ryzom_ams/www/html/inc/createticket.php | 8 +- .../ryzom_ams/www/html/inc/dashboard.php | 9 +- .../server/ryzom_ams/www/html/inc/error.php | 6 +- .../server/ryzom_ams/www/html/inc/login.php | 9 +- .../server/ryzom_ams/www/html/inc/logout.php | 6 +- .../ryzom_ams/www/html/inc/register.php | 5 +- .../ryzom_ams/www/html/inc/settings.php | 7 +- .../ryzom_ams/www/html/inc/sgroup_list.php | 8 +- .../ryzom_ams/www/html/inc/show_queue.php | 18 +- .../ryzom_ams/www/html/inc/show_reply.php | 8 +- .../ryzom_ams/www/html/inc/show_sgroup.php | 9 +- .../ryzom_ams/www/html/inc/show_ticket.php | 9 +- .../www/html/inc/show_ticket_info.php | 7 +- .../www/html/inc/show_ticket_log.php | 13 +- .../ryzom_ams/www/html/inc/show_user.php | 9 +- .../server/ryzom_ams/www/html/inc/syncing.php | 10 +- .../ryzom_ams/www/html/inc/userlist.php | 6 +- .../tools/server/ryzom_ams/www/html/index.php | 13 + .../server/ryzom_ams/www/html/sql/install.php | 8 + .../server/ryzom_ams_docs/doxygen/Doxyfile | 2489 ++++++++++------- .../server/ryzom_ams_docs/doxygen/logo.png | Bin 0 -> 19642 bytes .../ryzom_ams_docs/html/add__sgroup_8php.html | 138 + .../ryzom_ams_docs/html/add__user_8php.html | 156 ++ .../html/add__user__to__sgroup_8php.html | 138 + .../server/ryzom_ams_docs/html/annotated.html | 24 +- ...ements-members.html => assigned_8php.html} | 36 +- .../html/change__info_8php.html | 138 + .../html/change__mail_8php.html | 138 + .../html/change__password_8php.html | 138 + .../html/change__permission_8php.html | 138 + .../html/change__receivemail_8php.html | 138 + .../html/classAssigned-members.html | 125 - .../ryzom_ams_docs/html/classAssigned.html | 197 +- .../ryzom_ams_docs/html/classDBLayer.html | 77 +- .../html/classForwarded-members.html | 124 - .../ryzom_ams_docs/html/classForwarded.html | 189 +- .../html/classGui__Elements.html | 48 +- .../html/classHelpers-members.html | 116 - .../ryzom_ams_docs/html/classHelpers.html | 72 +- .../html/classIn__Support__Group-members.html | 121 - .../html/classIn__Support__Group.html | 165 +- .../html/classMail__Handler-members.html | 121 - .../html/classMail__Handler.html | 158 +- .../ryzom_ams_docs/html/classMyCrypt.html | 83 +- .../html/classPagination-members.html | 120 - .../ryzom_ams_docs/html/classPagination.html | 151 +- .../html/classQuerycache-members.html | 126 - .../ryzom_ams_docs/html/classQuerycache.html | 274 +- .../html/classSupport__Group-members.html | 148 - .../html/classSupport__Group.html | 533 +++- .../server/ryzom_ams_docs/html/classSync.html | 30 +- .../html/classTicket-members.html | 159 -- .../ryzom_ams_docs/html/classTicket.html | 721 +++-- .../html/classTicket__Category-members.html | 122 - .../html/classTicket__Category.html | 188 +- .../html/classTicket__Content-members.html | 121 - .../html/classTicket__Content.html | 180 +- .../html/classTicket__Info-members.html | 171 -- .../html/classTicket__Info.html | 1013 +++++-- .../html/classTicket__Log-members.html | 136 - .../ryzom_ams_docs/html/classTicket__Log.html | 413 ++- .../html/classTicket__Queue-members.html | 121 - .../html/classTicket__Queue.html | 229 +- .../classTicket__Queue__Handler-members.html | 120 - .../html/classTicket__Queue__Handler.html | 163 +- .../html/classTicket__Reply-members.html | 136 - .../html/classTicket__Reply.html | 393 ++- .../html/classTicket__User-members.html | 134 - .../html/classTicket__User.html | 311 +- .../html/classUsers-members.html | 125 - .../ryzom_ams_docs/html/classUsers.html | 156 +- .../server/ryzom_ams_docs/html/classUsers.png | Bin 341 -> 423 bytes .../html/classWebUsers-members.html | 151 - .../ryzom_ams_docs/html/classWebUsers.html | 1053 ++++++- .../ryzom_ams_docs/html/classWebUsers.png | Bin 338 -> 428 bytes .../server/ryzom_ams_docs/html/classes.html | 22 +- .../html/create__ticket_8php.html | 138 + .../html/createticket_8php.html | 138 + .../ryzom_ams_docs/html/dashboard_8php.html | 138 + .../ryzom_ams_docs/html/dblayer_8php.html | 117 + .../ryzom_ams_docs/html/deprecated.html | 14 +- .../dir_d0847756a6376d36128ff0811ac4d74f.html | 148 - ...2ryzommanage_2autoload_2webusers_8php.html | 117 + ...pal__module_2ryzommanage_2config_8php.html | 820 ++++++ ...odule_2ryzommanage_2inc_2logout_8php.html} | 63 +- ...dule_2ryzommanage_2inc_2settings_8php.html | 150 + ...e_2ryzommanage_2inc_2show__user_8php.html} | 63 +- .../ryzom_ams_docs/html/error_8php.html | 138 + .../server/ryzom_ams_docs/html/files.html | 175 ++ .../ryzom_ams_docs/html/forwarded_8php.html | 117 + .../ryzom_ams_docs/html/func_2login_8php.html | 138 + .../server/ryzom_ams_docs/html/functions.html | 184 +- .../ryzom_ams_docs/html/functions_0x5f.html | 56 +- .../ryzom_ams_docs/html/functions_0x61.html | 28 +- .../ryzom_ams_docs/html/functions_0x63.html | 114 +- .../ryzom_ams_docs/html/functions_0x64.html | 38 +- .../ryzom_ams_docs/html/functions_0x65.html | 30 +- .../ryzom_ams_docs/html/functions_0x66.html | 26 +- .../ryzom_ams_docs/html/functions_0x67.html | 257 +- .../ryzom_ams_docs/html/functions_0x68.html | 28 +- .../ryzom_ams_docs/html/functions_0x69.html | 34 +- .../ryzom_ams_docs/html/functions_0x6c.html | 60 +- .../ryzom_ams_docs/html/functions_0x6d.html | 28 +- .../ryzom_ams_docs/html/functions_0x6e.html | 24 +- .../ryzom_ams_docs/html/functions_0x6f.html | 24 +- .../ryzom_ams_docs/html/functions_0x73.html | 189 +- .../ryzom_ams_docs/html/functions_0x74.html | 28 +- .../ryzom_ams_docs/html/functions_0x75.html | 48 +- .../ryzom_ams_docs/html/functions_0x76.html | 24 +- .../ryzom_ams_docs/html/functions_func.html | 54 +- .../html/functions_func_0x61.html | 26 +- .../html/functions_func_0x63.html | 112 +- .../html/functions_func_0x64.html | 36 +- .../html/functions_func_0x65.html | 28 +- .../html/functions_func_0x66.html | 24 +- .../html/functions_func_0x67.html | 255 +- .../html/functions_func_0x68.html | 26 +- .../html/functions_func_0x69.html | 32 +- .../html/functions_func_0x6c.html | 58 +- .../html/functions_func_0x6d.html | 26 +- .../html/functions_func_0x6e.html | 22 +- .../html/functions_func_0x6f.html | 22 +- .../html/functions_func_0x73.html | 187 +- .../html/functions_func_0x74.html | 26 +- .../html/functions_func_0x75.html | 46 +- .../html/functions_func_0x76.html | 22 +- .../ryzom_ams_docs/html/functions_vars.html | 182 +- .../server/ryzom_ams_docs/html/globals.html | 345 +++ .../ryzom_ams_docs/html/globals_func.html | 269 ++ .../ryzom_ams_docs/html/globals_vars.html | 192 ++ .../html/gui__elements_8php.html | 117 + .../ryzom_ams_docs/html/helpers_8php.html | 117 + .../server/ryzom_ams_docs/html/hierarchy.html | 19 +- .../html/in__support__group_8php.html | 117 + .../ryzom_ams_docs/html/inc_2login_8php.html | 138 + .../server/ryzom_ams_docs/html/index.html | 12 +- ...classSync-members.html => index_8php.html} | 26 +- .../html/{dirs.html => info_8php.html} | 38 +- .../ryzom_ams_docs/html/install_8php.html | 130 + ...7a0dd87a3317.html => libinclude_8php.html} | 68 +- .../tools/server/ryzom_ams_docs/html/logo.png | Bin 0 -> 19642 bytes .../ryzom_ams_docs/html/mail__cron_8php.html | 134 + .../html/mail__handler_8php.html | 117 + .../html/modify__email__of__sgroup_8php.html | 138 + .../ryzom_ams_docs/html/mycrypt_8php.html | 117 + .../server/ryzom_ams_docs/html/pages.html | 12 +- .../ryzom_ams_docs/html/pagination_8php.html | 117 + .../ryzom_ams_docs/html/querycache_8php.html | 117 + ...e0d7aa1e01cfcb.html => register_8php.html} | 68 +- .../html/reply__on__ticket_8php.html | 138 + .../ryzom_ams_docs/html/search/all_24.js | 153 +- .../ryzom_ams_docs/html/search/all_5f.js | 3 +- .../ryzom_ams_docs/html/search/all_61.js | 11 +- .../ryzom_ams_docs/html/search/all_63.js | 82 +- .../ryzom_ams_docs/html/search/all_64.js | 13 +- .../ryzom_ams_docs/html/search/all_65.js | 10 +- .../ryzom_ams_docs/html/search/all_66.js | 3 +- .../ryzom_ams_docs/html/search/all_67.js | 207 +- .../ryzom_ams_docs/html/search/all_68.js | 9 +- .../ryzom_ams_docs/html/search/all_69.js | 16 +- .../ryzom_ams_docs/html/search/all_6c.js | 43 +- .../ryzom_ams_docs/html/search/all_6d.js | 13 +- .../ryzom_ams_docs/html/search/all_6e.js | 2 +- .../ryzom_ams_docs/html/search/all_6f.js | 2 +- .../ryzom_ams_docs/html/search/all_70.js | 3 +- .../ryzom_ams_docs/html/search/all_71.js | 3 +- .../ryzom_ams_docs/html/search/all_72.html | 25 + .../ryzom_ams_docs/html/search/all_72.js | 7 + .../ryzom_ams_docs/html/search/all_73.js | 145 +- .../ryzom_ams_docs/html/search/all_74.js | 15 +- .../ryzom_ams_docs/html/search/all_75.js | 15 +- .../ryzom_ams_docs/html/search/all_76.js | 2 +- .../ryzom_ams_docs/html/search/all_77.js | 5 +- .../ryzom_ams_docs/html/search/files_61.html | 25 + .../ryzom_ams_docs/html/search/files_61.js | 7 + .../ryzom_ams_docs/html/search/files_63.html | 25 + .../ryzom_ams_docs/html/search/files_63.js | 12 + .../ryzom_ams_docs/html/search/files_64.html | 25 + .../ryzom_ams_docs/html/search/files_64.js | 5 + .../ryzom_ams_docs/html/search/files_65.html | 25 + .../ryzom_ams_docs/html/search/files_65.js | 4 + .../ryzom_ams_docs/html/search/files_66.html | 25 + .../ryzom_ams_docs/html/search/files_66.js | 4 + .../ryzom_ams_docs/html/search/files_67.html | 25 + .../ryzom_ams_docs/html/search/files_67.js | 4 + .../ryzom_ams_docs/html/search/files_68.html | 25 + .../ryzom_ams_docs/html/search/files_68.js | 4 + .../ryzom_ams_docs/html/search/files_69.html | 25 + .../ryzom_ams_docs/html/search/files_69.js | 7 + .../ryzom_ams_docs/html/search/files_6c.html | 25 + .../ryzom_ams_docs/html/search/files_6c.js | 8 + .../ryzom_ams_docs/html/search/files_6d.html | 25 + .../ryzom_ams_docs/html/search/files_6d.js | 7 + .../ryzom_ams_docs/html/search/files_70.html | 25 + .../ryzom_ams_docs/html/search/files_70.js | 4 + .../ryzom_ams_docs/html/search/files_71.html | 25 + .../ryzom_ams_docs/html/search/files_71.js | 4 + .../ryzom_ams_docs/html/search/files_72.html | 25 + .../ryzom_ams_docs/html/search/files_72.js | 5 + .../ryzom_ams_docs/html/search/files_73.html | 25 + .../ryzom_ams_docs/html/search/files_73.js | 18 + .../ryzom_ams_docs/html/search/files_74.html | 25 + .../ryzom_ams_docs/html/search/files_74.js | 12 + .../ryzom_ams_docs/html/search/files_75.html | 25 + .../ryzom_ams_docs/html/search/files_75.js | 5 + .../ryzom_ams_docs/html/search/files_77.html | 25 + .../ryzom_ams_docs/html/search/files_77.js | 5 + .../html/search/functions_5f.js | 3 +- .../html/search/functions_61.js | 7 +- .../html/search/functions_63.js | 73 +- .../html/search/functions_64.js | 11 +- .../html/search/functions_65.js | 9 +- .../html/search/functions_66.js | 2 +- .../html/search/functions_67.js | 204 +- .../html/search/functions_68.js | 6 +- .../html/search/functions_69.js | 12 +- .../html/search/functions_6c.js | 38 +- .../html/search/functions_6d.js | 7 +- .../html/search/functions_6e.js | 2 +- .../html/search/functions_6f.js | 2 +- .../html/search/functions_72.html | 25 + .../html/search/functions_72.js | 5 + .../html/search/functions_73.js | 130 +- .../html/search/functions_74.js | 6 +- .../html/search/functions_75.js | 11 +- .../html/search/functions_76.js | 2 +- .../html/search/functions_77.html | 25 + .../html/search/functions_77.js | 4 + .../ryzom_ams_docs/html/search/search.js | 12 +- .../html/search/variables_24.js | 153 +- .../html/sgroup__list_8php.html | 138 + .../ryzom_ams_docs/html/show__queue_8php.html | 138 + .../ryzom_ams_docs/html/show__reply_8php.html | 138 + ...pt-members.html => show__sgroup_8php.html} | 60 +- .../html/show__ticket_8php.html | 138 + .../html/show__ticket__info_8php.html | 138 + .../html/show__ticket__log_8php.html | 138 + .../html/support__group_8php.html | 117 + .../server/ryzom_ams_docs/html/sync_8php.html | 117 + .../ryzom_ams_docs/html/sync__cron_8php.html | 109 + .../ryzom_ams_docs/html/syncing_8php.html | 138 + .../ryzom_ams_docs/html/ticket_8php.html | 117 + .../html/ticket__category_8php.html | 117 + .../html/ticket__content_8php.html | 117 + .../html/ticket__info_8php.html | 117 + .../ryzom_ams_docs/html/ticket__log_8php.html | 117 + .../html/ticket__queue_8php.html | 117 + .../html/ticket__queue__handler_8php.html | 117 + .../html/ticket__reply_8php.html | 117 + .../html/ticket__user_8php.html | 117 + .../server/ryzom_ams_docs/html/todo.html | 22 +- .../ryzom_ams_docs/html/userlist_8php.html | 138 + ...ssDBLayer-members.html => users_8php.html} | 38 +- .../ryzom_ams_docs/html/www_2config_8php.html | 820 ++++++ .../www_2html_2autoload_2webusers_8php.html | 117 + ....html => www_2html_2inc_2logout_8php.html} | 70 +- .../html/www_2html_2inc_2settings_8php.html | 155 + .../html/www_2html_2inc_2show__user_8php.html | 138 + 287 files changed, 19275 insertions(+), 7643 deletions(-) delete mode 100644 code/ryzom/tools/server/ryzom_ams/www/html/error.php create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/doxygen/logo.png create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/add__sgroup_8php.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/add__user_8php.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/add__user__to__sgroup_8php.html rename code/ryzom/tools/server/ryzom_ams_docs/html/{classGui__Elements-members.html => assigned_8php.html} (63%) create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/change__info_8php.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/change__mail_8php.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/change__password_8php.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/change__permission_8php.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/change__receivemail_8php.html delete mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classAssigned-members.html delete mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classForwarded-members.html delete mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classHelpers-members.html delete mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classIn__Support__Group-members.html delete mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classMail__Handler-members.html delete mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classPagination-members.html delete mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classQuerycache-members.html delete mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classSupport__Group-members.html delete mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classTicket-members.html delete mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__Category-members.html delete mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__Content-members.html delete mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__Info-members.html delete mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__Log-members.html delete mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__Queue-members.html delete mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__Queue__Handler-members.html delete mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__Reply-members.html delete mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classTicket__User-members.html delete mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classUsers-members.html delete mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/classWebUsers-members.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/create__ticket_8php.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/createticket_8php.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/dashboard_8php.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/dblayer_8php.html delete mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/dir_d0847756a6376d36128ff0811ac4d74f.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/drupal__module_2ryzommanage_2autoload_2webusers_8php.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/drupal__module_2ryzommanage_2config_8php.html rename code/ryzom/tools/server/ryzom_ams_docs/html/{dir_7add0e0eba863b010b52e5fb777019f1.html => drupal__module_2ryzommanage_2inc_2logout_8php.html} (61%) create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/drupal__module_2ryzommanage_2inc_2settings_8php.html rename code/ryzom/tools/server/ryzom_ams_docs/html/{dir_c7daa497b9d1b9c868775e0964568556.html => drupal__module_2ryzommanage_2inc_2show__user_8php.html} (60%) create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/error_8php.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/files.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/forwarded_8php.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/func_2login_8php.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/globals.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/globals_func.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/globals_vars.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/gui__elements_8php.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/helpers_8php.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/in__support__group_8php.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/inc_2login_8php.html rename code/ryzom/tools/server/ryzom_ams_docs/html/{classSync-members.html => index_8php.html} (72%) rename code/ryzom/tools/server/ryzom_ams_docs/html/{dirs.html => info_8php.html} (71%) create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/install_8php.html rename code/ryzom/tools/server/ryzom_ams_docs/html/{dir_4e66b119222255be04e87a0dd87a3317.html => libinclude_8php.html} (57%) create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/logo.png create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/mail__cron_8php.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/mail__handler_8php.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/modify__email__of__sgroup_8php.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/mycrypt_8php.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/pagination_8php.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/querycache_8php.html rename code/ryzom/tools/server/ryzom_ams_docs/html/{dir_ba1cfc2c2eb4ebe254e0d7aa1e01cfcb.html => register_8php.html} (57%) create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/reply__on__ticket_8php.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/all_72.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/all_72.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/files_61.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/files_61.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/files_63.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/files_63.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/files_64.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/files_64.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/files_65.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/files_65.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/files_66.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/files_66.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/files_67.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/files_67.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/files_68.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/files_68.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/files_69.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/files_69.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/files_6c.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/files_6c.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/files_6d.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/files_6d.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/files_70.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/files_70.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/files_71.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/files_71.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/files_72.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/files_72.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/files_73.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/files_73.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/files_74.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/files_74.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/files_75.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/files_75.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/files_77.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/files_77.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_72.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_72.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_77.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/search/functions_77.js create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/sgroup__list_8php.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/show__queue_8php.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/show__reply_8php.html rename code/ryzom/tools/server/ryzom_ams_docs/html/{classMyCrypt-members.html => show__sgroup_8php.html} (54%) create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/show__ticket_8php.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/show__ticket__info_8php.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/show__ticket__log_8php.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/support__group_8php.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/sync_8php.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/sync__cron_8php.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/syncing_8php.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/ticket_8php.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/ticket__category_8php.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/ticket__content_8php.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/ticket__info_8php.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/ticket__log_8php.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/ticket__queue_8php.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/ticket__queue__handler_8php.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/ticket__reply_8php.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/ticket__user_8php.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/userlist_8php.html rename code/ryzom/tools/server/ryzom_ams_docs/html/{classDBLayer-members.html => users_8php.html} (61%) create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/www_2config_8php.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/www_2html_2autoload_2webusers_8php.html rename code/ryzom/tools/server/ryzom_ams_docs/html/{dir_732e39ca668b77f3e13244f204e87535.html => www_2html_2inc_2logout_8php.html} (57%) create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/www_2html_2inc_2settings_8php.html create mode 100644 code/ryzom/tools/server/ryzom_ams_docs/html/www_2html_2inc_2show__user_8php.html diff --git a/.hgignore b/.hgignore index f0a8e0993..42cd85ac7 100644 --- a/.hgignore +++ b/.hgignore @@ -208,6 +208,7 @@ code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/plugins code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/smarty code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/translations code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/ams_lib/libinclude.php +code/ryzom/tools/server/ryzom_ams/www/html/templates_c # Linux server compile code/ryzom/server/src/entities_game_service/entities_game_service diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/dblayer.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/dblayer.php index 534eea1b9..af826d084 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/dblayer.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/dblayer.php @@ -12,7 +12,7 @@ class DBLayer{ /** * The constructor. * Instantiates the PDO object attribute by connecting to the arguments matching database(the db info is stored in the $cfg global var) - * @param String, the name of the databases entry in the $cfg global var. + * @param $db String, the name of the databases entry in the $cfg global var. */ function __construct($db) { diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php index 18422474f..0f434fcf9 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/mail_handler.php @@ -265,7 +265,7 @@ class Mail_Handler{ /** * creates a new message id for a email about to send. - * @param $ticket_id the ticket id of the ticket that is mentioned in the email. + * @param $ticketId the ticket id of the ticket that is mentioned in the email. * @return returns a string, that consist out of some variable parts, a consistent part and the ticket_id. The ticket_id will be used lateron, if someone replies on the message, * to see to which ticket the reply should be added. */ @@ -281,8 +281,8 @@ class Mail_Handler{ /** * try to fetch the ticket_id out of the subject. - * The subject should have a substring of the form [Ticket #ticket_id], where ticket_id should be the integer ID of the ticket. - * @param $subject, the subject of an incomming email. + * The subject should have a substring of the form [Ticket \#ticket_id], where ticket_id should be the integer ID of the ticket. + * @param $subject the subject of an incomming email. * @return if the ticket's id is succesfully parsed, it will return the ticket_id, else it returns 0. */ function get_ticket_id_from_subject($subject){ diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/support_group.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/support_group.php index 919f82608..005f775c1 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/support_group.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/support_group.php @@ -140,7 +140,7 @@ class Support_Group{ /** * get list of all users that are enlisted to a support group. - * @param $id the id of the group we want to query + * @param $group_id the id of the group we want to query * @return an array of ticket_user objects that are in the support group. */ public static function getAllUsersOfSupportGroup($group_id) { diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_log.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_log.php index 70eef6b91..bfb679348 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_log.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_log.php @@ -91,7 +91,7 @@ class Ticket_Log{ /** * return constructed element based on TLogId - * @param $ticket_log id of the entry that we want to load into our object. + * @param $id ticket_log id of the entry that we want to load into our object. * @return constructed ticket_log object. */ public static function constr_TLogId( $id) { diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_queue.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_queue.php index 2d6752057..e72f1024d 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_queue.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/ticket_queue.php @@ -75,8 +75,8 @@ class Ticket_Queue{ * loads the 'created' query & params into the objects attributes. * This function creates dynamically a query based on the selected features. * @param $who specifies if we want to user the user_id or group_id to form the query. - * @param $user_id the user's id to whom the tickets should be assigned/not assigned - * @param $group_id the group's id to whom the tickets should be forwarded/not forwarded + * @param $userid the user's id to whom the tickets should be assigned/not assigned + * @param $groupid the group's id to whom the tickets should be forwarded/not forwarded * @param $what specifies what kind of tickets we want to return: waiting for support, waiting on user, closed * @param $how specifies if the tickets should be or shouldn't be assigned/forwarded to the group/user selected. */ diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/users.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/users.php index 1768f62ce..7dbb1bfda 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/users.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/users.php @@ -9,7 +9,7 @@ class Users{ /** * checks if entered values before registering are valid. - * @param $array with Username,Password, ConfirmPass and Email. + * @param $values array with Username,Password, ConfirmPass and Email. * @return string Info: Returns a string, if input data is valid then "success" is returned, else an array with errors */ public function check_Register($values){ @@ -239,7 +239,7 @@ class Users{ /** * generate a SALT. - * @param $length, which is by default 2 + * @param $length the length of the SALT which is by default 2 * @return a random salt of 2 chars */ public static function generateSALT( $length = 2 ) @@ -279,8 +279,9 @@ class Users{ /** * creates a user in the shard. - * incase the shard is offline it will place it in the ams_querycache. + * incase the shard is offline it will place it in the ams_querycache. You have to create a user first in the CMS/WWW and use the id for this function. * @param $values with name,pass and mail + * @param $user_id the extern id of the user (the id given by the www/CMS) * @return ok if it's get correctly added to the shard, else return lib offline and put in libDB, if libDB is also offline return liboffline. */ public static function createUser($values, $user_id){ diff --git a/code/ryzom/tools/server/ryzom_ams/ams_lib/cron/mail_cron.php b/code/ryzom/tools/server/ryzom_ams/ams_lib/cron/mail_cron.php index 9d669e866..557a57417 100644 --- a/code/ryzom/tools/server/ryzom_ams/ams_lib/cron/mail_cron.php +++ b/code/ryzom/tools/server/ryzom_ams/ams_lib/cron/mail_cron.php @@ -1,5 +1,10 @@ uId = $UId; @@ -47,7 +47,7 @@ class WebUsers extends Users{ /** * function that checks if a username exists already or not. * This function overrides the function of the base class. - * @takes $username the username in question + * @param $username the username in question * @return string Info: Returns 0 if the user is not in the web db, else a positive number is returned. */ protected function checkUserNameExists($username){ @@ -58,7 +58,7 @@ class WebUsers extends Users{ /** * function that checks if a email exists already or not. * This function overrides the function of the base class. - * @takes $email the email address in question. + * @param $email the email address in question. * @return string Info: Returns 0 if the email address is not in the web db, else a positive number is returned. */ protected function checkEmailExists($email){ diff --git a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/config.php b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/config.php index 7a2ea8ac6..023bb0941 100644 --- a/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/config.php +++ b/code/ryzom/tools/server/ryzom_ams/drupal_module/ryzommanage/config.php @@ -1,29 +1,31 @@ uId = $UId; @@ -48,7 +48,7 @@ class WebUsers extends Users{ /** * function that checks if a username exists already or not. * This function overrides the function of the base class. - * @takes $username the username in question + * @param $username the username in question * @return string Info: Returns 0 if the user is not in the web db, else a positive number is returned. */ protected function checkUserNameExists($username){ @@ -60,7 +60,7 @@ class WebUsers extends Users{ /** * function that checks if a email exists already or not. * This function overrides the function of the base class. - * @takes $email the email address in question. + * @param $email the email address in question. * @return string Info: Returns 0 if the email address is not in the web db, else a positive number is returned. */ protected function checkEmailExists($email){ diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/error.php b/code/ryzom/tools/server/ryzom_ams/www/html/error.php deleted file mode 100644 index b84bd7de7..000000000 --- a/code/ryzom/tools/server/ryzom_ams/www/html/error.php +++ /dev/null @@ -1,79 +0,0 @@ - - - - - - - - Error - - - -

    - 404 -

    -

    Not
    Found

    -
    -
    - The page your are looking for is not found. -
    Go Home -
    - - diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/func/add_sgroup.php b/code/ryzom/tools/server/ryzom_ams/www/html/func/add_sgroup.php index 960ec2c6b..14a9b9f87 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/func/add_sgroup.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/func/add_sgroup.php @@ -1,10 +1,16 @@ getPermission(); - $result['no_visible_elements'] = 'FALSE'; - $result['username'] = $_SESSION['user']; + //$result['permission'] = unserialize($_SESSION['ticket_user'])->getPermission(); + //$result['no_visible_elements'] = 'FALSE'; + //$result['username'] = $_SESSION['user']; //global $SITEBASE; //require($SITEBASE . '/inc/sgroup_list.php'); //$result= array_merge($result, sgroup_list()); diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/func/add_user.php b/code/ryzom/tools/server/ryzom_ams/www/html/func/add_user.php index a53208c5a..d12cf896c 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/func/add_user.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/func/add_user.php @@ -1,9 +1,18 @@ $_POST["Username"], 'Password' => $_POST["Password"], 'ConfirmPass' => $_POST["ConfirmPass"], 'Email' => $_POST["Email"]); $webUser = new WebUsers(); + + //check if the POST variables are valid, before actual registering $result = $webUser->check_Register($params); // if all are good then create user @@ -24,7 +33,7 @@ function add_user(){ helpers :: loadtemplate( 'register_feedback', $pageElements); exit; }else{ - // pass error + // pass error and reload template accordingly $result['prevUsername'] = $_POST["Username"]; $result['prevPassword'] = $_POST["Password"]; $result['prevConfirmPass'] = $_POST["ConfirmPass"]; @@ -36,7 +45,7 @@ function add_user(){ } } - +//use the valid userdata to create the new user. function write_user($newUser){ //create salt here, because we want it to be the same on the web/server diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/func/add_user_to_sgroup.php b/code/ryzom/tools/server/ryzom_ams/www/html/func/add_user_to_sgroup.php index e4d46e610..fe225a8f2 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/func/add_user_to_sgroup.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/func/add_user_to_sgroup.php @@ -1,27 +1,36 @@ getPermission()>1){ + //add it to the support group $result['RESULT_OF_ADDING'] = Support_Group::addUserToSupportGroup($user_id, $id); }else{ + //return error message. $result['RESULT_OF_ADDING'] = "NOT_MOD_OR_ADMIN"; } }else{ $result['RESULT_OF_ADDING'] = "USER_NOT_EXISTING"; } - $result['permission'] = unserialize($_SESSION['ticket_user'])->getPermission(); - $result['no_visible_elements'] = 'FALSE'; - $result['username'] = $_SESSION['user']; + //$result['permission'] = unserialize($_SESSION['ticket_user'])->getPermission(); + //$result['no_visible_elements'] = 'FALSE'; + //$result['username'] = $_SESSION['user']; //global $SITEBASE; //require_once($SITEBASE . 'inc/show_sgroup.php'); //$result= array_merge($result, show_sgroup()); diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/func/change_info.php b/code/ryzom/tools/server/ryzom_ams/www/html/func/change_info.php index 1fb0f10c0..7e65c4d09 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/func/change_info.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/func/change_info.php @@ -1,5 +1,11 @@ getUsername(); } @@ -20,8 +28,7 @@ function change_info(){ $webUser = new WebUsers($_POST['target_id']); //use current info to check for changes $current_info = $webUser->getInfo(); - - + $current_info['FirstName'] = filter_var($current_info['FirstName'], FILTER_SANITIZE_STRING); $current_info['LastName'] = filter_var($current_info['LastName'], FILTER_SANITIZE_STRING); $current_info['Country'] = filter_var($current_info['Country'], FILTER_SANITIZE_STRING); @@ -76,6 +83,7 @@ function change_info(){ $dbw->execute($query,$values); } + //reload the settings inc function before recalling the settings template. global $SITEBASE; require_once($SITEBASE . '/inc/settings.php'); $result = settings(); diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/func/change_mail.php b/code/ryzom/tools/server/ryzom_ams/www/html/func/change_mail.php index f04648343..095e545ac 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/func/change_mail.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/func/change_mail.php @@ -1,5 +1,11 @@ getUsername(); } $webUser = new WebUsers($_POST['target_id']); + //check if emailaddress is valid. $reply = $webUser->checkEmail($_POST['NewEmail']); global $SITEBASE; @@ -32,6 +41,7 @@ function change_mail(){ $result['prevNewEmail'] = filter_var($_POST["NewEmail"], FILTER_SANITIZE_EMAIL); if ($reply== "success"){ + //if validation was successful, update the emailaddress $status = WebUsers::setEmail($target_username, filter_var($_POST["NewEmail"], FILTER_SANITIZE_EMAIL) ); if($status == 'ok'){ $result['SUCCESS_MAIL'] = "OK"; diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/func/change_password.php b/code/ryzom/tools/server/ryzom_ams/www/html/func/change_password.php index 420d78103..51907a635 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/func/change_password.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/func/change_password.php @@ -1,5 +1,11 @@ getUsername(); //isAdmin is true when it's the admin, but the target_id != own id diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/func/change_receivemail.php b/code/ryzom/tools/server/ryzom_ams/www/html/func/change_receivemail.php index 5bfba0b8d..2c3fdc9b6 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/func/change_receivemail.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/func/change_receivemail.php @@ -1,5 +1,10 @@ getTUserId(); }else{ + //if a mod tries to make a ticket for someone else $author= Ticket_User::constr_ExternId($_POST['target_id'])->getTUserId(); } + //create the ticket & return the id of the newly created ticket. $ticket_id = Ticket::create_Ticket($title, $content, $category, $author, unserialize($_SESSION['ticket_user'])->getTUserId(),0, $_POST); + //redirect to the new ticket. if (Helpers::check_if_game_client()) { header("Location: ".$INGAME_WEBPATH."?page=show_ticket&id=".$ticket_id); }else{ diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/func/login.php b/code/ryzom/tools/server/ryzom_ams/www/html/func/login.php index 6841b7ffd..3c104e947 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/func/login.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/func/login.php @@ -1,11 +1,17 @@ load_With_TId($ticket_id); - + + //check if the user who executed this function is a mod/admin or the topic creator himself. if(($target_ticket->getAuthor() == unserialize($_SESSION['ticket_user'])->getTUserId()) || Ticket_User::isMod(unserialize($_SESSION['ticket_user'])) ){ try{ @@ -20,11 +27,15 @@ function reply_on_ticket(){ $content=""; } $hidden = 0; + if(isset($_POST['hidden']) && Ticket_User::isMod(unserialize($_SESSION['ticket_user']))){ $hidden = 1; } + + //create the reply Ticket::createReply($content, $author, $ticket_id, $hidden); + //try to update the status & priority in case these are set. if(isset($_POST['ChangeStatus']) && isset($_POST['ChangePriority']) && Ticket_User::isMod(unserialize($_SESSION['ticket_user']))){ $newStatus = filter_var($_POST['ChangeStatus'], FILTER_SANITIZE_NUMBER_INT); $newPriority = filter_var($_POST['ChangePriority'], FILTER_SANITIZE_NUMBER_INT); diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/inc/change_permission.php b/code/ryzom/tools/server/ryzom_ams/www/html/inc/change_permission.php index 35f561290..9c18aa036 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/inc/change_permission.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/inc/change_permission.php @@ -1,17 +1,26 @@ getTUserId(), $value); if (Helpers::check_if_game_client()) { header("Location: ".$INGAME_WEBPATH."?page=show_user&id=".$user_id); diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/inc/createticket.php b/code/ryzom/tools/server/ryzom_ams/www/html/inc/createticket.php index 3fdb54b84..3a2b371fc 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/inc/createticket.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/inc/createticket.php @@ -1,5 +1,10 @@ getTUserId(); $result['nrToDo'] = Ticket_Queue_Handler::getNrOfTicketsToDo(unserialize($_SESSION['ticket_user'])->getTUserId()); $result['nrAssignedWaiting'] = Ticket_Queue_Handler::getNrOfTicketsAssignedWaiting(unserialize($_SESSION['ticket_user'])->getTUserId()); diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/inc/error.php b/code/ryzom/tools/server/ryzom_ams/www/html/inc/error.php index eca129ff6..84de1083d 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/inc/error.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/inc/error.php @@ -1,5 +1,9 @@ getTUserId(); $queueArray = array(); @@ -21,8 +30,7 @@ function show_queue(){ $result['pagination_base_link'] = $WEBPATH."?page=show_queue&get=".$result['queue_view'] ; } - //form url to keep the getters constant - + //form url to keep the getters constant if (Helpers::check_if_game_client()) { $result['getURL'] = $INGAME_WEBPATH."?page=show_queue&get=" . $result['queue_view']; }else{ @@ -39,6 +47,7 @@ function show_queue(){ $what = filter_var($_GET['what'], FILTER_SANITIZE_STRING); $how = filter_var($_GET['how'], FILTER_SANITIZE_STRING); $who = filter_var($_GET['who'], FILTER_SANITIZE_STRING); + //create the custom queue $queue_handler->CreateQueue($userid, $groupid, $what, $how, $who); if (Helpers::check_if_game_client()) { @@ -77,6 +86,7 @@ function show_queue(){ $what = filter_var($_POST['what'], FILTER_SANITIZE_STRING); $how = filter_var($_POST['how'], FILTER_SANITIZE_STRING); $who = filter_var($_POST['who'], FILTER_SANITIZE_STRING); + //create the custom queue $queue_handler->CreateQueue($userid, $groupid, $what, $how, $who); if (Helpers::check_if_game_client()) { $result['pagination_base_link'] = $INGAME_WEBPATH."?page=show_queue&get=create&userid=".$userid."&groupid=".$groupid."&what=".$what."&how=".$how."&who=".$who; diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/inc/show_reply.php b/code/ryzom/tools/server/ryzom_ams/www/html/inc/show_reply.php index 96cec0a6a..9a0ecc9cc 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/inc/show_reply.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/inc/show_reply.php @@ -1,5 +1,10 @@ load_With_TId($reply->getTicket()); + //check if the user is allowed to see the reply if(( $ticket->getAuthor() == unserialize($_SESSION['ticket_user'])->getTUserId() && ! $reply->getHidden()) || Ticket_User::isMod(unserialize($_SESSION['ticket_user']) )){ $content = new Ticket_Content(); $content->load_With_TContentId($reply->getContent()); diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/inc/show_sgroup.php b/code/ryzom/tools/server/ryzom_ams/www/html/inc/show_sgroup.php index edf1ba0a9..76dbe7b18 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/inc/show_sgroup.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/inc/show_sgroup.php @@ -1,5 +1,11 @@ load_With_TId($result['ticket_id']); $result['ticket_title'] = $target_ticket->getTitle(); + + //return all logs related to a ticket. $ticket_logs = Ticket_Log::getLogsOfTicket( $result['ticket_id']); $log_action_array = Ticket_Log::getActionTextArray(); + //fetch information about each returned ticket in a format that is usable for the template $result['ticket_logs'] = Gui_Elements::make_table($ticket_logs, Array("getTLogId","getTimestamp","getAuthor()->getExternId","getAction","getArgument()"), Array("tLogId","timestamp","authorExtern","action","argument")); $i = 0; + //for each ticket add action specific informaton to the to-be-shown text: uses the query_backpart foreach( $result['ticket_logs'] as $log){ $webUser = new WebUsers($log['authorExtern']); $author = $webUser->getUsername(); diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/inc/show_user.php b/code/ryzom/tools/server/ryzom_ams/www/html/inc/show_user.php index aae4d2caa..0fafcfddd 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/inc/show_user.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/inc/show_user.php @@ -1,8 +1,15 @@ getElements() , Array("getSID","getType"), Array("id","type")); $pageResult['links'] = $pagination->getLinks(5); diff --git a/code/ryzom/tools/server/ryzom_ams/www/html/inc/userlist.php b/code/ryzom/tools/server/ryzom_ams/www/html/inc/userlist.php index 76074abcd..6f9117e11 100644 --- a/code/ryzom/tools/server/ryzom_ams/www/html/inc/userlist.php +++ b/code/ryzom/tools/server/ryzom_ams/www/html/inc/userlist.php @@ -1,5 +1,9 @@ ... \endif and \cond +# ... \endcond blocks. -ENABLED_SECTIONS = +ENABLED_SECTIONS = -# The MAX_INITIALIZER_LINES tag determines the maximum number of lines -# the initial value of a variable or macro consists of for it to appear in -# the documentation. If the initializer consists of more lines than specified -# here it will be hidden. Use a value of 0 to hide initializers completely. -# The appearance of the initializer of individual variables and macros in the -# documentation can be controlled using \showinitializer or \hideinitializer -# command in the documentation regardless of this setting. +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. MAX_INITIALIZER_LINES = 30 -# Set the SHOW_USED_FILES tag to NO to disable the list of files generated -# at the bottom of the documentation of classes and structs. If set to YES the -# list will mention the files that were used to generate the documentation. +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES the list +# will mention the files that were used to generate the documentation. +# The default value is: YES. SHOW_USED_FILES = YES -# If the sources in your project are distributed over multiple directories -# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy -# in the documentation. The default is NO. - -SHOW_DIRECTORIES = YES - -# Set the SHOW_FILES tag to NO to disable the generation of the Files page. -# This will remove the Files entry from the Quick Index and from the -# Folder Tree View (if specified). The default is YES. +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. SHOW_FILES = YES -# Set the SHOW_NAMESPACES tag to NO to disable the generation of the -# Namespaces page. -# This will remove the Namespaces entry from the Quick Index -# and from the Folder Tree View (if specified). The default is YES. +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. SHOW_NAMESPACES = NO # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via -# popen()) the command , where is the value of -# the FILE_VERSION_FILTER tag, and is the name of an input file -# provided by doxygen. Whatever the program writes to standard output -# is used as the file version. See the manual for examples. +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. -FILE_VERSION_FILTER = +FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated -# output files in an output format independent way. The create the layout file -# that represents doxygen's defaults, run doxygen with the -l option. -# You can optionally specify a file name after the option, if omitted -# DoxygenLayout.xml will be used as the name of the layout file. +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. -LAYOUT_FILE = +LAYOUT_FILE = -# The CITE_BIB_FILES tag can be used to specify one or more bib files -# containing the references data. This must be a list of .bib files. The -# .bib extension is automatically appended if omitted. Using this command -# requires the bibtex tool to be installed. See also -# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style -# of the bibliography can be controlled using LATEX_BIB_STYLE. To use this -# feature you need bibtex and perl available in the search path. +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. Do not use file names with spaces, bibtex cannot handle them. See +# also \cite for info how to create references. -CITE_BIB_FILES = +CITE_BIB_FILES = #--------------------------------------------------------------------------- -# configuration options related to warning and progress messages +# Configuration options related to warning and progress messages #--------------------------------------------------------------------------- -# The QUIET tag can be used to turn on/off the messages that are generated -# by doxygen. Possible values are YES and NO. If left blank NO is used. +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are -# generated by doxygen. Possible values are YES and NO. If left blank -# NO is used. +# generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. WARNINGS = YES -# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings -# for undocumented members. If EXTRACT_ALL is set to YES then this flag will -# automatically be disabled. +# If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. WARN_IF_UNDOCUMENTED = YES -# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for -# potential errors in the documentation, such as not documenting some -# parameters in a documented function, or documenting parameters that -# don't exist or using markup commands wrongly. +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some parameters +# in a documented function, or documenting parameters that don't exist or using +# markup commands wrongly. +# The default value is: YES. WARN_IF_DOC_ERROR = YES -# The WARN_NO_PARAMDOC option can be enabled to get warnings for -# functions that are documented, but have no documentation for their parameters -# or return value. If set to NO (the default) doxygen will only warn about -# wrong or incomplete parameter documentation, but not about the absence of -# documentation. +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO doxygen will only warn about wrong or incomplete parameter +# documentation, but not about the absence of documentation. +# The default value is: NO. WARN_NO_PARAMDOC = NO -# The WARN_FORMAT tag determines the format of the warning messages that -# doxygen can produce. The string should contain the $file, $line, and $text -# tags, which will be replaced by the file and line number from which the -# warning originated and the warning text. Optionally the format may contain -# $version, which will be replaced by the version of the file (if it could -# be obtained via FILE_VERSION_FILTER) +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# The default value is: $file:$line: $text. WARN_FORMAT = "$file:$line: $text" -# The WARN_LOGFILE tag can be used to specify a file to which warning -# and error messages should be written. If left blank the output is written -# to stderr. +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). -WARN_LOGFILE = +WARN_LOGFILE = #--------------------------------------------------------------------------- -# configuration options related to the input files +# Configuration options related to the input files #--------------------------------------------------------------------------- -# The INPUT tag can be used to specify the files and/or directories that contain -# documented source files. You may enter file names like "myfile.cpp" or -# directories like "/usr/src/myproject". Separate the files or directories -# with spaces. +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. +# Note: If this tag is empty the current directory is searched. -INPUT = ../../ryzom_ams/ams_lib/autoload ../../ryzom_ams/www/html/autoload info.php +INPUT = ../../ryzom_ams \ + info.php # This tag can be used to specify the character encoding of the source files -# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is -# also the default input encoding. Doxygen uses libiconv (or the iconv built -# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for -# the list of possible encodings. +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: http://www.gnu.org/software/libiconv) for the list of +# possible encodings. +# The default value is: UTF-8. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the -# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left -# blank the following patterns are tested: -# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh -# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py -# *.f90 *.f *.for *.vhd *.vhdl +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank the +# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii, +# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, +# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, +# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, +# *.qsf, *.as and *.js. FILE_PATTERNS = *.php -# The RECURSIVE tag can be used to turn specify whether or not subdirectories -# should be searched for input files as well. Possible values are YES and NO. -# If left blank NO is used. +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. -RECURSIVE = NO +RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. +# # Note that relative paths are relative to the directory from which doxygen is # run. -EXCLUDE = +EXCLUDE = ../../ryzom_ams/ams_lib/smarty \ + ../../ryzom_ams/ams_lib/plugins \ + ../../ryzom_ams/www/html/misc \ + ../../ryzom_ams/www/html/templates_c \ + ../../ryzom_ams/drupal # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded # from the input. +# The default value is: NO. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude -# certain files from those directories. Note that the wildcards are matched -# against the file with absolute path, so to exclude all test directories -# for example use the pattern */test/* +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* -EXCLUDE_PATTERNS = +EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories use the pattern */test/* -EXCLUDE_SYMBOLS = +EXCLUDE_SYMBOLS = Smarty_CacheResource_Apc \ + "Smarty_CacheResource_Memcache " \ + Smarty_CacheResource_Mysql \ + Smarty_Resource_Extendsall \ + Smarty_Resource_Mysql \ + Smarty_Resource_Mysqls \ + Resource \ + CacheResource -# The EXAMPLE_PATH tag can be used to specify one or more files or -# directories that contain example code fragments that are included (see -# the \include command). +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). -EXAMPLE_PATH = +EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the -# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left -# blank all files are included. +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. -EXAMPLE_PATTERNS = +EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be -# searched for input files to be used with the \include or \dontinclude -# commands irrespective of the value of the RECURSIVE tag. -# Possible values are YES and NO. If left blank NO is used. +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. EXAMPLE_RECURSIVE = NO -# The IMAGE_PATH tag can be used to specify one or more files or -# directories that contain image that are included in the documentation (see -# the \image command). +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). -IMAGE_PATH = +IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program -# by executing (via popen()) the command , where -# is the value of the INPUT_FILTER tag, and is the name of an -# input file. Doxygen will then use the output that the filter program writes -# to standard output. -# If FILTER_PATTERNS is specified, this tag will be -# ignored. +# by executing (via popen()) the command: +# +# +# +# where is the value of the INPUT_FILTER tag, and is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. -INPUT_FILTER = +INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern -# basis. -# Doxygen will compare the file name with each pattern and apply the -# filter if there is a match. -# The filters are a list of the form: -# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further -# info on how filters are used. If FILTER_PATTERNS is empty or if -# non of the patterns match the file name, INPUT_FILTER is applied. +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. -FILTER_PATTERNS = +FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using -# INPUT_FILTER) will be used to filter the input files when producing source -# files to browse (i.e. when SOURCE_BROWSER is set to YES). +# INPUT_FILTER ) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. FILTER_SOURCE_FILES = NO # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file -# pattern. A pattern will override the setting for FILTER_PATTERN (if any) -# and it is also possible to disable source filtering for a specific pattern -# using *.ext= (so without naming a filter). This option only has effect when -# FILTER_SOURCE_FILES is enabled. +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. -FILTER_SOURCE_PATTERNS = +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = #--------------------------------------------------------------------------- -# configuration options related to source browsing +# Configuration options related to source browsing #--------------------------------------------------------------------------- -# If the SOURCE_BROWSER tag is set to YES then a list of source files will -# be generated. Documented entities will be cross-referenced with these sources. -# Note: To get rid of all source code in the generated output, make sure also -# VERBATIM_HEADERS is set to NO. +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. SOURCE_BROWSER = NO -# Setting the INLINE_SOURCES tag to YES will include the body -# of functions and classes directly in the documentation. +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# classes and enums directly into the documentation. +# The default value is: NO. INLINE_SOURCES = NO -# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct -# doxygen to hide any special comment blocks from generated source code -# fragments. Normal C and C++ comments will always remain visible. +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. STRIP_CODE_COMMENTS = YES -# If the REFERENCED_BY_RELATION tag is set to YES -# then for each documented function all documented -# functions referencing it will be listed. +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# function all documented functions referencing it will be listed. +# The default value is: NO. REFERENCED_BY_RELATION = NO -# If the REFERENCES_RELATION tag is set to YES -# then for each documented function all documented entities -# called/used by that function will be listed. +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. REFERENCES_RELATION = NO -# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) -# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from -# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will -# link to the source code. -# Otherwise they will link to the documentation. +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES, then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. REFERENCES_LINK_SOURCE = YES -# If the USE_HTAGS tag is set to YES then the references to source code -# will point to the HTML generated by the htags(1) tool instead of doxygen -# built-in source browser. The htags tool is part of GNU's global source -# tagging system (see http://www.gnu.org/software/global/global.html). You -# will need version 4.8.6 or higher. +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see http://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the config file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. USE_HTAGS = NO -# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen -# will generate a verbatim copy of the header file for each class for -# which an include is specified. Set to NO to disable this. +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. VERBATIM_HEADERS = NO +# If the CLANG_ASSISTED_PARSING tag is set to YES, then doxygen will use the +# clang parser (see: http://clang.llvm.org/) for more acurate parsing at the +# cost of reduced performance. This can be particularly helpful with template +# rich C++ code for which doxygen's built-in parser lacks the necessary type +# information. +# Note: The availability of this option depends on whether or not doxygen was +# compiled with the --with-libclang option. +# The default value is: NO. + +CLANG_ASSISTED_PARSING = NO + +# If clang assisted parsing is enabled you can provide the compiler with command +# line options that you would normally use when invoking the compiler. Note that +# the include paths will already be set by doxygen for the files and directories +# specified with INPUT and INCLUDE_PATH. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_OPTIONS = + #--------------------------------------------------------------------------- -# configuration options related to the alphabetical class index +# Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- -# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index -# of all compounds will be generated. Enable this if the project -# contains a lot of classes, structs, unions or interfaces. +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. ALPHABETICAL_INDEX = YES -# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then -# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns -# in which this list will be split (can be a number in the range [1..20]) +# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in +# which the alphabetical index list will be split. +# Minimum value: 1, maximum value: 20, default value: 5. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. COLS_IN_ALPHA_INDEX = 5 -# In case all classes in a project start with a common prefix, all -# classes will be put under the same header in the alphabetical index. -# The IGNORE_PREFIX tag can be used to specify one or more prefixes that -# should be ignored while generating the index headers. +# In case all classes in a project start with a common prefix, all classes will +# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag +# can be used to specify a prefix (or a list of prefixes) that should be ignored +# while generating the index headers. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. -IGNORE_PREFIX = +IGNORE_PREFIX = #--------------------------------------------------------------------------- -# configuration options related to the HTML output +# Configuration options related to the HTML output #--------------------------------------------------------------------------- -# If the GENERATE_HTML tag is set to YES (the default) Doxygen will -# generate HTML output. +# If the GENERATE_HTML tag is set to YES doxygen will generate HTML output +# The default value is: YES. GENERATE_HTML = YES -# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `html' will be used as the default path. +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. HTML_OUTPUT = html -# The HTML_FILE_EXTENSION tag can be used to specify the file extension for -# each generated HTML page (for example: .htm,.php,.asp). If it is left blank -# doxygen will generate files with .html extension. +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. HTML_FILE_EXTENSION = .html -# The HTML_HEADER tag can be used to specify a personal HTML header for -# each generated HTML page. If it is left blank doxygen will generate a -# standard header. Note that when using a custom header you are responsible -# for the proper inclusion of any scripts and style sheets that doxygen -# needs, which is dependent on the configuration options used. -# It is advised to generate a default header using "doxygen -w html -# header.html footer.html stylesheet.css YourConfigFile" and then modify -# that header. Note that the header is subject to change so you typically -# have to redo this when upgrading to a newer version of doxygen or when -# changing the value of configuration settings such as GENERATE_TREEVIEW! +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. -HTML_HEADER = +HTML_HEADER = -# The HTML_FOOTER tag can be used to specify a personal HTML footer for -# each generated HTML page. If it is left blank doxygen will generate a -# standard footer. +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. -HTML_FOOTER = +HTML_FOOTER = -# The HTML_STYLESHEET tag can be used to specify a user-defined cascading -# style sheet that is used by each HTML page. It can be used to -# fine-tune the look of the HTML output. If the tag is left blank doxygen -# will generate a default style sheet. Note that doxygen will try to copy -# the style sheet file to the HTML output directory, so don't put your own -# style sheet in the HTML output directory as well, or it will be erased! +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. -HTML_STYLESHEET = +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user- +# defined cascading style sheet that is included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefor more robust against future updates. +# Doxygen will copy the style sheet file to the output directory. For an example +# see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note # that these files will be copied to the base HTML output directory. Use the -# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these -# files. In the HTML_STYLESHEET file, use the file name only. Also note that -# the files will be copied as-is; there are no commands or markers available. +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. -HTML_EXTRA_FILES = +HTML_EXTRA_FILES = -# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. -# Doxygen will adjust the colors in the style sheet and background images -# according to this color. Hue is specified as an angle on a colorwheel, -# see http://en.wikipedia.org/wiki/Hue for more information. -# For instance the value 0 represents red, 60 is yellow, 120 is green, -# 180 is cyan, 240 is blue, 300 purple, and 360 is red again. -# The allowed range is 0 to 359. +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the stylesheet and background images according to +# this color. Hue is specified as an angle on a colorwheel, see +# http://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_HUE = 220 -# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of -# the colors in the HTML output. For a value of 0 the output will use -# grayscales only. A value of 255 will produce the most vivid colors. +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use grayscales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_SAT = 100 -# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to -# the luminance component of the colors in the HTML output. Values below -# 100 gradually make the output lighter, whereas values above 100 make -# the output darker. The value divided by 100 is the actual gamma applied, -# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, -# and 100 does not change the gamma. +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML -# page will contain the date and time when the page was generated. Setting -# this to NO can help when comparing the output of multiple runs. +# page will contain the date and time when the page was generated. Setting this +# to NO can help when comparing the output of multiple runs. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. HTML_TIMESTAMP = YES -# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, -# files or namespaces will be aligned in HTML using tables. If set to -# NO a bullet list will be used. - -HTML_ALIGN_MEMBERS = YES - # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the -# page has loaded. For this to work a browser that supports -# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox -# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. HTML_DYNAMIC_SECTIONS = NO -# If the GENERATE_DOCSET tag is set to YES, additional index files -# will be generated that can be used as input for Apple's Xcode 3 -# integrated development environment, introduced with OSX 10.5 (Leopard). -# To create a documentation set, doxygen will generate a Makefile in the -# HTML output directory. Running make will produce the docset in that -# directory and running "make install" will install the docset in -# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find -# it at startup. -# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: http://developer.apple.com/tools/xcode/), introduced with +# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a +# Makefile in the HTML output directory. Running make will produce the docset in +# that directory and running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html # for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_DOCSET = NO -# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the -# feed. A documentation feed provides an umbrella under which multiple -# documentation sets from a single provider (such as a company or product suite) -# can be grouped. +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_FEEDNAME = "Doxygen generated docs" -# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that -# should uniquely identify the documentation set bundle. This should be a -# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen -# will append .docset to the name. +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_BUNDLE_ID = org.doxygen.Project -# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify # the documentation publisher. This should be a reverse domain-name style # string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_ID = org.doxygen.Publisher -# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_NAME = Publisher -# If the GENERATE_HTMLHELP tag is set to YES, additional index files -# will be generated that can be used as input for tools like the -# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) -# of the generated HTML documentation. +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on +# Windows. +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_HTMLHELP = NO -# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can -# be used to specify the file name of the resulting .chm file. You -# can add a path in front of the file if the result should not be +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be # written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. -CHM_FILE = +CHM_FILE = -# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can -# be used to specify the location (absolute path including file name) of -# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run -# the HTML help compiler on the generated index.hhp. +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler ( hhc.exe). If non-empty +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. -HHC_LOCATION = +HHC_LOCATION = -# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag -# controls if a separate .chi index file is generated (YES) or that -# it should be included in the master .chm file (NO). +# The GENERATE_CHI flag controls if a separate .chi index file is generated ( +# YES) or that it should be included in the master .chm file ( NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. GENERATE_CHI = NO -# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING -# is used to encode HtmlHelp index (hhk), content (hhc) and project file -# content. +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. -CHM_INDEX_ENCODING = +CHM_INDEX_ENCODING = -# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag -# controls whether a binary table of contents is generated (YES) or a -# normal table of contents (NO) in the .chm file. +# The BINARY_TOC flag controls whether a binary table of contents is generated ( +# YES) or a normal table of contents ( NO) in the .chm file. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. BINARY_TOC = NO -# The TOC_EXPAND flag can be set to YES to add extra items for group members -# to the contents of the HTML help documentation and to the tree view. +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and -# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated -# that can be used as input for Qt's qhelpgenerator to generate a -# Qt Compressed Help (.qch) of the generated HTML documentation. +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_QHP = NO -# If the QHG_LOCATION tag is specified, the QCH_FILE tag can -# be used to specify the file name of the resulting .qch file. -# The path specified is relative to the HTML output folder. +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. -QCH_FILE = +QCH_FILE = -# The QHP_NAMESPACE tag specifies the namespace to use when generating -# Qt Help Project output. For more information please see -# http://doc.trolltech.com/qthelpproject.html#namespace +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. QHP_NAMESPACE = org.doxygen.Project -# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating -# Qt Help Project output. For more information please see -# http://doc.trolltech.com/qthelpproject.html#virtual-folders +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- +# folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. QHP_VIRTUAL_FOLDER = doc -# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to -# add. For more information please see -# http://doc.trolltech.com/qthelpproject.html#custom-filters +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. -QHP_CUST_FILTER_NAME = +QHP_CUST_FILTER_NAME = -# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the -# custom filter to add. For more information please see -# -# Qt Help Project / Custom Filters. +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. -QHP_CUST_FILTER_ATTRS = +QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this -# project's -# filter section matches. -# -# Qt Help Project / Filter Attributes. +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. -QHP_SECT_FILTER_ATTRS = +QHP_SECT_FILTER_ATTRS = -# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can -# be used to specify the location of Qt's qhelpgenerator. -# If non-empty doxygen will try to run qhelpgenerator on the generated -# .qhp file. +# The QHG_LOCATION tag can be used to specify the location of Qt's +# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the +# generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. -QHG_LOCATION = +QHG_LOCATION = -# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files -# will be generated, which together with the HTML files, form an Eclipse help -# plugin. To install this plugin and make it available under the help contents -# menu in Eclipse, the contents of the directory containing the HTML and XML -# files needs to be copied into the plugins directory of eclipse. The name of -# the directory within the plugins directory should be the same as -# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before -# the help appears. +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_ECLIPSEHELP = NO -# A unique identifier for the eclipse help plugin. When installing the plugin -# the directory name containing the HTML and XML files should also have -# this name. +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. ECLIPSE_DOC_ID = org.doxygen.Project -# The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) -# at top of each HTML page. The value NO (the default) enables the index and -# the value YES disables it. Since the tabs have the same information as the -# navigation tree you can set this option to NO if you already set -# GENERATE_TREEVIEW to YES. +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. DISABLE_INDEX = NO # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index -# structure should be generated to display hierarchical information. -# If the tag value is set to YES, a side panel will be generated -# containing a tree-like index structure (just like the one that -# is generated for HTML Help). For this to work a browser that supports -# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). -# Windows users are probably better off using the HTML help feature. -# Since the tree basically has the same information as the tab index you -# could consider to set DISABLE_INDEX to NO when enabling this option. +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can +# further fine-tune the look of the index. As an example, the default style +# sheet generated by doxygen has an example that shows how to put an image at +# the root of the tree instead of the PROJECT_NAME. Since the tree basically has +# the same information as the tab index, you could consider setting +# DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_TREEVIEW = NO -# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values -# (range [0,1..20]) that doxygen will group on one line in the generated HTML -# documentation. Note that a value of 0 will completely suppress the enum -# values from appearing in the overview section. +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. ENUM_VALUES_PER_LINE = 4 -# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, -# and Class Hierarchy pages using a tree view instead of an ordered list. - -USE_INLINE_TREES = NO - -# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be -# used to set the initial width (in pixels) of the frame in which the tree -# is shown. +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. TREEVIEW_WIDTH = 250 -# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open -# links to external symbols imported via tag files in a separate window. +# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. EXT_LINKS_IN_WINDOW = NO -# Use this tag to change the font size of Latex formulas included -# as images in the HTML documentation. The default is 10. Note that -# when you change the font size after a successful doxygen run you need -# to manually remove any form_*.png images from the HTML output directory -# to force them to be regenerated. +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_FONTSIZE = 10 # Use the FORMULA_TRANPARENT tag to determine whether or not the images -# generated for formulas are transparent PNGs. Transparent PNGs are -# not supported properly for IE 6.0, but are supported on all modern browsers. -# Note that when changing this option you need to delete any form_*.png files -# in the HTML output before the changes have effect. +# generated for formulas are transparent PNGs. Transparent PNGs are not +# supported properly for IE 6.0, but are supported on all modern browsers. +# +# Note that when changing this option you need to delete any form_*.png files in +# the HTML output directory before the changes have effect. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_TRANSPARENT = YES -# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax -# (see http://www.mathjax.org) which uses client side Javascript for the -# rendering instead of using prerendered bitmaps. Use this if you do not -# have LaTeX installed or if you want to formulas look prettier in the HTML -# output. When enabled you also need to install MathJax separately and -# configure the path to it using the MATHJAX_RELPATH option. +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# http://www.mathjax.org) which uses client side Javascript for the rendering +# instead of using prerendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. USE_MATHJAX = NO -# When MathJax is enabled you need to specify the location relative to the -# HTML output directory using the MATHJAX_RELPATH option. The destination -# directory should contain the MathJax.js script. For instance, if the mathjax -# directory is located at the same level as the HTML output directory, then -# MATHJAX_RELPATH should be ../mathjax. The default value points to the -# mathjax.org site, so you can quickly see the result without installing -# MathJax, but it is strongly recommended to install a local copy of MathJax -# before deployment. +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. See the MathJax site (see: +# http://docs.mathjax.org/en/latest/output.html) for more details. +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility), NativeMML (i.e. MathML) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from http://www.mathjax.org before deployment. +# The default value is: http://cdn.mathjax.org/mathjax/latest. +# This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_RELPATH = http://www.mathjax.org/mathjax -# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension -# names that should be enabled during MathJax rendering. +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# This tag requires that the tag USE_MATHJAX is set to YES. -MATHJAX_EXTENSIONS = +MATHJAX_EXTENSIONS = -# When the SEARCHENGINE tag is enabled doxygen will generate a search box -# for the HTML output. The underlying search engine uses javascript -# and DHTML and should work on any modern browser. Note that when using -# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets -# (GENERATE_DOCSET) there is already a search function so this one should -# typically be disabled. For large projects the javascript based search engine -# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use + S +# (what the is depends on the OS and browser, but it is typically +# , /
    Ryzom Account Management System - +  1.0
    +
    Here are the data structures with brief descriptions:
    @@ -113,7 +115,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); onmouseover="return searchBox.OnSearchSelectShow()" onmouseout="return searchBox.OnSearchSelectHide()" onkeydown="return searchBox.OnSearchSelectKey(event)"> - All Classes Functions Variables + All Data Structures Files Functions Variables
    @@ -125,7 +127,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/classGui__Elements-members.html b/code/ryzom/tools/server/ryzom_ams_docs/html/assigned_8php.html similarity index 63% rename from code/ryzom/tools/server/ryzom_ams_docs/html/classGui__Elements-members.html rename to code/ryzom/tools/server/ryzom_ams_docs/html/assigned_8php.html index a4e7b9269..c3db913df 100644 --- a/code/ryzom/tools/server/ryzom_ams_docs/html/classGui__Elements-members.html +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/assigned_8php.html @@ -3,7 +3,7 @@ -Ryzom Account Management System: Member List +Ryzom Account Management System: /home/daan/ryzom/ryzomcore/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/assigned.php File Reference @@ -25,10 +25,12 @@
    + + @@ -48,8 +50,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
    AssignedHandles the assigning of a ticket to a user
    DBLayerHandles the database connections
    ForwardedHandles the forwarding of a ticket to a support_group
    Ryzom Account Management System - +  1.0
    - - - -
    make_table($inputList, $funcArray, $fieldArray)Gui_Elements [static]
    make_table_with_key_is_id($inputList, $funcArray, $idFunction)Gui_Elements [static]
    time_elapsed_string($ptime)Gui_Elements [static]
    + + + + +

    +Data Structures

    class  Assigned
     Handles the assigning of a ticket to a user. More...
    + + All Data Structures Files Functions Variables
    @@ -104,7 +108,7 @@ This is the complete list of members for diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/change__info_8php.html b/code/ryzom/tools/server/ryzom_ams_docs/html/change__info_8php.html new file mode 100644 index 000000000..9c768ce8c --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/change__info_8php.html @@ -0,0 +1,138 @@ + + + + + +Ryzom Account Management System: /home/daan/ryzom/ryzomcore/code/ryzom/tools/server/ryzom_ams/www/html/func/change_info.php File Reference + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + + + +
    +
    Ryzom Account Management System +  1.0 +
    + +
    +
    + + + + + +
    +
    + +
    +
    /home/daan/ryzom/ryzomcore/code/ryzom/tools/server/ryzom_ams/www/html/func/change_info.php File Reference
    +
    +
    + + + + +

    +Functions

     change_info ()
     This function is beign used to change the users personal info.
    +

    Function Documentation

    + +
    +
    + + + + + + + +
    change_info ()
    +
    +
    + +

    This function is beign used to change the users personal info.

    +

    It will first check if the user who executed this function is the person of whom the information is or if it's a mod/admin. If this is not the case the page will be redirected to an error page. afterwards the current info will be loaded, which will be used to determine what to update. After updating the information, the settings template will be reloaded. Errors made by invalid data will be shown also after reloading the template.

    +
    Author:
    Daan Janssens, mentored by Matthew Lagoe
    + +
    +
    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/change__mail_8php.html b/code/ryzom/tools/server/ryzom_ams_docs/html/change__mail_8php.html new file mode 100644 index 000000000..ba85773a8 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/change__mail_8php.html @@ -0,0 +1,138 @@ + + + + + +Ryzom Account Management System: /home/daan/ryzom/ryzomcore/code/ryzom/tools/server/ryzom_ams/www/html/func/change_mail.php File Reference + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + + + +
    +
    Ryzom Account Management System +  1.0 +
    + +
    +
    + + + + + +
    +
    + +
    +
    /home/daan/ryzom/ryzomcore/code/ryzom/tools/server/ryzom_ams/www/html/func/change_mail.php File Reference
    +
    +
    + + + + +

    +Functions

     change_mail ()
     This function is beign used to change the users emailaddress info.
    +

    Function Documentation

    + +
    +
    + + + + + + + +
    change_mail ()
    +
    +
    + +

    This function is beign used to change the users emailaddress info.

    +

    It will first check if the user who executed this function is the person of whom the emailaddress is or if it's a mod/admin. If this is not the case the page will be redirected to an error page. The emailaddress will be validated first. If the checking was successful the email will be updated and the settings template will be reloaded. Errors made by invalid data will be shown also after reloading the template.

    +
    Author:
    Daan Janssens, mentored by Matthew Lagoe
    + +
    +
    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/change__password_8php.html b/code/ryzom/tools/server/ryzom_ams_docs/html/change__password_8php.html new file mode 100644 index 000000000..1ad8abd36 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/change__password_8php.html @@ -0,0 +1,138 @@ + + + + + +Ryzom Account Management System: /home/daan/ryzom/ryzomcore/code/ryzom/tools/server/ryzom_ams/www/html/func/change_password.php File Reference + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + + + +
    +
    Ryzom Account Management System +  1.0 +
    + +
    +
    + + + + + +
    +
    + +
    +
    /home/daan/ryzom/ryzomcore/code/ryzom/tools/server/ryzom_ams/www/html/func/change_password.php File Reference
    +
    +
    + + + + +

    +Functions

     change_password ()
     This function is beign used to change the users password.
    +

    Function Documentation

    + +
    +
    + + + + + + + +
    change_password ()
    +
    +
    + +

    This function is beign used to change the users password.

    +

    It will first check if the user who executed this function is the person of whom the emailaddress is or if it's a mod/admin. If this is not the case the page will be redirected to an error page. If the executing user tries to change someone elses password, he doesn't has to fill in the previous password. The password will be validated first. If the checking was successful the password will be updated and the settings template will be reloaded. Errors made by invalid data will be shown also after reloading the template.

    +
    Author:
    Daan Janssens, mentored by Matthew Lagoe
    + +
    +
    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/change__permission_8php.html b/code/ryzom/tools/server/ryzom_ams_docs/html/change__permission_8php.html new file mode 100644 index 000000000..7ba8fdcbf --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/change__permission_8php.html @@ -0,0 +1,138 @@ + + + + + +Ryzom Account Management System: /home/daan/ryzom/ryzomcore/code/ryzom/tools/server/ryzom_ams/www/html/inc/change_permission.php File Reference + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + + + +
    +
    Ryzom Account Management System +  1.0 +
    + +
    +
    + + + + + +
    +
    + +
    +
    /home/daan/ryzom/ryzomcore/code/ryzom/tools/server/ryzom_ams/www/html/inc/change_permission.php File Reference
    +
    +
    + + + + +

    +Functions

     change_permission ()
     This function is beign used to change the permission of a ticket_user.
    +

    Function Documentation

    + +
    +
    + + + + + + + +
    change_permission ()
    +
    +
    + +

    This function is beign used to change the permission of a ticket_user.

    +

    It will first check if the user who executed this function is an admin. If this is not the case the page will be redirected to an error page. in case the $_GET['value'] is smaller than 4 and the user whoes permission is being changed is different from the admin(id 1), the change will be executed and the page will redirect to the users profile page.

    +
    Author:
    Daan Janssens, mentored by Matthew Lagoe
    + +
    +
    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/change__receivemail_8php.html b/code/ryzom/tools/server/ryzom_ams_docs/html/change__receivemail_8php.html new file mode 100644 index 000000000..3ad408528 --- /dev/null +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/change__receivemail_8php.html @@ -0,0 +1,138 @@ + + + + + +Ryzom Account Management System: /home/daan/ryzom/ryzomcore/code/ryzom/tools/server/ryzom_ams/www/html/func/change_receivemail.php File Reference + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + + + +
    +
    Ryzom Account Management System +  1.0 +
    + +
    +
    + + + + + +
    +
    + +
    +
    /home/daan/ryzom/ryzomcore/code/ryzom/tools/server/ryzom_ams/www/html/func/change_receivemail.php File Reference
    +
    +
    + + + + +

    +Functions

     change_receivemail ()
     This function is beign used to change the users receiveMail setting.
    +

    Function Documentation

    + +
    +
    + + + + + + + +
    change_receivemail ()
    +
    +
    + +

    This function is beign used to change the users receiveMail setting.

    +

    It will first check if the user who executed this function is the person of whom the setting is or if it's a mod/admin. If this is not the case the page will be redirected to an error page. it will check if the new value equals 1 or 0 and it will update the setting and redirect the page again.

    +
    Author:
    Daan Janssens, mentored by Matthew Lagoe
    + +
    +
    +
    + + + + +
    + +
    + + + + + + + diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/classAssigned-members.html b/code/ryzom/tools/server/ryzom_ams_docs/html/classAssigned-members.html deleted file mode 100644 index c18cd720c..000000000 --- a/code/ryzom/tools/server/ryzom_ams_docs/html/classAssigned-members.html +++ /dev/null @@ -1,125 +0,0 @@ - - - - - -Ryzom Account Management System: Member List - - - - - - - - - - - -
    - - -
    - - - - - - - - - - - -
    -
    Ryzom Account Management System - -
    - -
    -
    - - - - - -
    -
    -
    -
    Assigned Member List
    -
    -
    -This is the complete list of members for Assigned, including all inherited members. - - - - - - - - - - - - - - - -
    $ticketAssigned [private]
    $userAssigned [private]
    __construct()Assigned
    assignTicket($user_id, $ticket_id)Assigned [static]
    create()Assigned
    delete()Assigned
    getTicket()Assigned
    getUser()Assigned
    getUserAssignedToTicket($ticket_id)Assigned [static]
    isAssigned($ticket_id, $user_id=0)Assigned [static]
    load($ticket_id)Assigned
    set($values)Assigned
    setTicket($t)Assigned
    setUser($u)Assigned
    unAssignTicket($user_id, $ticket_id)Assigned [static]
    - - - - -
    - -
    - - - - - - - diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/classAssigned.html b/code/ryzom/tools/server/ryzom_ams_docs/html/classAssigned.html index c27cd4e70..185dbb584 100644 --- a/code/ryzom/tools/server/ryzom_ams_docs/html/classAssigned.html +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/classAssigned.html @@ -25,10 +25,12 @@
    Ryzom Account Management System - +  1.0
    - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - - - - - - - - + + + + + + + + - - - - + + + +

    Public Member Functions

     __construct ()
     A constructor.
     set ($values)
     sets the object's attributes.
     create ()
     creates a new 'assigned' entry.
     delete ()
     deletes an existing 'assigned' entry.
     load ($ticket_id)
     loads the object's attributes.
    getUser ()
     get user attribute of the object.
    getTicket ()
     get ticket attribute of the object.
     setUser ($u)
     set user attribute of the object.
     setTicket ($t)
     set ticket attribute of the object.
     __construct ()
     A constructor.
     set ($values)
     sets the object's attributes.
     create ()
     creates a new 'assigned' entry.
     delete ()
     deletes an existing 'assigned' entry.
     load ($ticket_id)
     loads the object's attributes.
     getUser ()
     get user attribute of the object.
     getTicket ()
     get ticket attribute of the object.
     setUser ($u)
     set user attribute of the object.
     setTicket ($t)
     set ticket attribute of the object.

    Static Public Member Functions

    static assignTicket ($user_id, $ticket_id)
     Assigns a ticket to a user or returns an error message.
    static unAssignTicket ($user_id, $ticket_id)
     Unassign a ticket being coupled to a user or return an error message.
    static getUserAssignedToTicket ($ticket_id)
     Get the (external) id of the user assigned to a ticket.
    static isAssigned ($ticket_id, $user_id=0)
     Check if a ticket is already assigned (in case the user_id param is used, it will check if it's assigned to that user)
    static assignTicket ($user_id, $ticket_id)
     Assigns a ticket to a user or returns an error message.
    static unAssignTicket ($user_id, $ticket_id)
     Unassign a ticket being coupled to a user or return an error message.
    static getUserAssignedToTicket ($ticket_id)
     Get the (external) id of the user assigned to a ticket.
    static isAssigned ($ticket_id, $user_id=0)
     Check if a ticket is already assigned (in case the user_id param is used, it will check if it's assigned to that user)

    Private Attributes

    $user
     The id of the user being assigned.
    $ticket
     The id of the ticket being assigned.
     $user
     The id of the user being assigned.
     $ticket
     The id of the ticket being assigned.

    Detailed Description

    Handles the assigning of a ticket to a user.

    This is being used to make someone responsible for the handling and solving of a ticket. The idea is that someone can easily assign a ticket to himself and by doing that, he makes aware to the other moderators that he will deal with the ticket in the future.

    Author:
    Daan Janssens, mentored by Matthew Lagoe

    Constructor & Destructor Documentation

    - +
    - + @@ -158,12 +154,12 @@ Private Attributes

    Member Function Documentation

    - +
    Assigned::__construct __construct ( )
    - + @@ -196,12 +192,12 @@ Private Attributes - +
    static Assigned::assignTicket static assignTicket ( user_id,
    - + @@ -215,12 +211,12 @@ Private Attributes - +
    Assigned::create create ( )
    - + @@ -234,12 +230,48 @@ Private Attributes - +
    Assigned::delete delete ( )
    - + + + + + +
    static Assigned::getUserAssignedToTicket getTicket ()
    +
    +
    + +

    get ticket attribute of the object.

    + +
    +
    + +
    +
    + + + + + + + +
    getUser ()
    +
    +
    + +

    get user attribute of the object.

    + +
    +
    + +
    +
    + + + @@ -260,12 +292,12 @@ Private Attributes - +
    static getUserAssignedToTicket ( ticket_id)
    - + @@ -297,12 +329,12 @@ Private Attributes - +
    static Assigned::isAssigned static isAssigned ( ticket_id,
    - + @@ -323,12 +355,12 @@ Private Attributes - +
    Assigned::load load ( ticket_id)
    - + @@ -348,12 +380,12 @@ Private Attributes - +
    Assigned::set set ( values)
    - + @@ -373,12 +405,12 @@ Private Attributes - +
    Assigned::setTicket setTicket ( t)
    - + @@ -398,12 +430,12 @@ Private Attributes - +
    Assigned::setUser setUser ( u)
    - + @@ -434,10 +466,41 @@ Private Attributes
    Returns:
    A string, if unassigning succeedded "SUCCESS_UNASSIGNED" will be returned, else "NOT_ASSIGNED" will be returned.
    + + +

    Field Documentation

    + +
    +
    +
    static Assigned::unAssignTicket static unAssignTicket ( user_id,
    + + + +
    $ticket [private]
    +
    +
    + +

    The id of the ticket being assigned.

    + +
    +
    + +
    +
    + + + + +
    $user [private]
    +
    +
    + +

    The id of the user being assigned.

    +

    The documentation for this class was generated from the following file:
      -
    • /home/daan/ryzom/ryzomcore/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/assigned.php
    • +
    • /home/daan/ryzom/ryzomcore/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/assigned.php
    @@ -445,7 +508,7 @@ Private Attributes
    Ryzom Account Management System - +  1.0
    - - - - - - - - + + + + + + + + - - + +

    Public Member Functions

     __construct ($db)
     The constructor.
     executeWithoutParams ($query)
     execute a query that doesn't have any parameters
     execute ($query, $params)
     execute a query that has parameters
     executeReturnId ($query, $params)
     execute a query (an insertion query) that has parameters and return the id of it's insertion
     __construct ($db)
     The constructor.
     executeWithoutParams ($query)
     execute a query that doesn't have any parameters
     execute ($query, $params)
     execute a query that has parameters
     executeReturnId ($query, $params)
     execute a query (an insertion query) that has parameters and return the id of it's insertion

    Private Attributes

    $PDO
     The PDO object, instantiated by the constructor.
     $PDO
     The PDO object, instantiated by the constructor.

    Detailed Description

    Handles the database connections.

    It uses PDO to connect to the different databases. It will use the argument of the constructor to setup a connection to the database with the matching entry in the $cfg global variable.

    Author:
    Daan Janssens, mentored by Matthew Lagoe

    Constructor & Destructor Documentation

    - +
    - + @@ -131,7 +130,7 @@ Private Attributes

    Instantiates the PDO object attribute by connecting to the arguments matching database(the db info is stored in the $cfg global var)

    Parameters:
    DBLayer::__construct __construct ( db)
    - +
    String,thename of the databases entry in the $cfg global var.
    $dbString, the name of the databases entry in the $cfg global var.
    @@ -139,12 +138,12 @@ Private Attributes

    Member Function Documentation

    - +
    - + @@ -176,12 +175,12 @@ Private Attributes - +
    DBLayer::execute execute ( query,
    - + @@ -213,12 +212,12 @@ Private Attributes - +
    DBLayer::executeReturnId executeReturnId ( query,
    - + @@ -237,10 +236,26 @@ Private Attributes
    Returns:
    returns a PDOStatement object
    + + +

    Field Documentation

    + +
    +
    +
    DBLayer::executeWithoutParams executeWithoutParams ( query)
    + + + +
    $PDO [private]
    +
    +
    + +

    The PDO object, instantiated by the constructor.

    +

    The documentation for this class was generated from the following file:
      -
    • /home/daan/ryzom/ryzomcore/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/dblayer.php
    • +
    • /home/daan/ryzom/ryzomcore/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/dblayer.php
    @@ -248,7 +263,7 @@ Private Attributes onmouseover="return searchBox.OnSearchSelectShow()" onmouseout="return searchBox.OnSearchSelectHide()" onkeydown="return searchBox.OnSearchSelectKey(event)"> - All Classes Functions Variables + All Data Structures Files Functions Variables
    @@ -260,7 +275,7 @@ Private Attributes diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/classForwarded-members.html b/code/ryzom/tools/server/ryzom_ams_docs/html/classForwarded-members.html deleted file mode 100644 index 71e9559d7..000000000 --- a/code/ryzom/tools/server/ryzom_ams_docs/html/classForwarded-members.html +++ /dev/null @@ -1,124 +0,0 @@ - - - - - -Ryzom Account Management System: Member List - - - - - - - - - - - -
    - - -
    - - - - - - - - - - - -
    -
    Ryzom Account Management System - -
    - -
    -
    - - - - - -
    -
    -
    -
    Forwarded Member List
    -
    -
    -This is the complete list of members for Forwarded, including all inherited members. - - - - - - - - - - - - - - -
    $groupForwarded [private]
    $ticketForwarded [private]
    __construct()Forwarded
    create()Forwarded
    delete()Forwarded
    forwardTicket($group_id, $ticket_id)Forwarded [static]
    getGroup()Forwarded
    getSGroupOfTicket($ticket_id)Forwarded [static]
    getTicket()Forwarded
    isForwarded($ticket_id)Forwarded [static]
    load($ticket_id)Forwarded
    set($values)Forwarded
    setGroup($g)Forwarded
    setTicket($t)Forwarded
    - - - - -
    - -
    - - - - - - - diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/classForwarded.html b/code/ryzom/tools/server/ryzom_ams_docs/html/classForwarded.html index 93dc9878c..d304f926b 100644 --- a/code/ryzom/tools/server/ryzom_ams_docs/html/classForwarded.html +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/classForwarded.html @@ -25,10 +25,12 @@ + Logo +
    Ryzom Account Management System - +  1.0
    @@ -48,8 +50,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
    @@ -89,59 +91,53 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');

    Handles the forwarding of a ticket to a support_group. More...

    - -

    List of all members.

    - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - - - - - - + + + + + + - - - - + + + +

    Public Member Functions

     __construct ()
     A constructor.
     set ($values)
     sets the object's attributes.
     create ()
     creates a new 'forwarded' entry.
     delete ()
     deletes an existing 'forwarded' entry.
     load ($ticket_id)
     loads the object's attributes.
    getGroup ()
     get group attribute of the object.
    getTicket ()
     get ticket attribute of the object.
     setGroup ($g)
     set group attribute of the object.
     setTicket ($t)
     set ticket attribute of the object.
     __construct ()
     A constructor.
     set ($values)
     sets the object's attributes.
     create ()
     creates a new 'forwarded' entry.
     delete ()
     deletes an existing 'forwarded' entry.
     load ($ticket_id)
     loads the object's attributes.
     getGroup ()
     get group attribute of the object.
     getTicket ()
     get ticket attribute of the object.
     setGroup ($g)
     set group attribute of the object.
     setTicket ($t)
     set ticket attribute of the object.

    Static Public Member Functions

    static forwardTicket ($group_id, $ticket_id)
     Forward a ticket to a group, also removes the previous group where it was forwarded to.
    static getSGroupOfTicket ($ticket_id)
     get the id of the group a ticket is forwarded to.
    static isForwarded ($ticket_id)
     check if the ticket is forwarded
    static forwardTicket ($group_id, $ticket_id)
     Forward a ticket to a group, also removes the previous group where it was forwarded to.
    static getSGroupOfTicket ($ticket_id)
     get the id of the group a ticket is forwarded to.
    static isForwarded ($ticket_id)
     check if the ticket is forwarded

    Private Attributes

    $group
     The id of the group to which the ticket is being forwarded.
    $ticket
     The id of the ticket being forwarded.
     $group
     The id of the group to which the ticket is being forwarded.
     $ticket
     The id of the ticket being forwarded.

    Detailed Description

    Handles the forwarding of a ticket to a support_group.

    This is being used to transfer tickets to different groups (eg Developers, Website-Team, SupportGroup etc..) The idea is that someone can easily forward a ticket to a group and by doing that, the moderators that are in that group will receive the ticket in their todo queue.

    Author:
    Daan Janssens, mentored by Matthew Lagoe

    Constructor & Destructor Documentation

    - +
    - + @@ -156,12 +152,12 @@ Private Attributes

    Member Function Documentation

    - +
    Forwarded::__construct __construct ( )
    - + @@ -175,12 +171,12 @@ Private Attributes - +
    Forwarded::create create ( )
    - + @@ -194,12 +190,12 @@ Private Attributes - +
    Forwarded::delete delete ( )
    - + @@ -232,12 +228,30 @@ Private Attributes - +
    static Forwarded::forwardTicket static forwardTicket ( group_id,
    - + + + + + +
    static Forwarded::getSGroupOfTicket getGroup ()
    +
    +
    + +

    get group attribute of the object.

    + +
    +
    + +
    +
    + + + @@ -258,12 +272,30 @@ Private Attributes - +
    static getSGroupOfTicket ( ticket_id)
    - + + + + + +
    static Forwarded::isForwarded getTicket ()
    +
    +
    + +

    get ticket attribute of the object.

    + +
    +
    + +
    +
    + + + @@ -284,12 +316,12 @@ Private Attributes - +
    static isForwarded ( ticket_id)
    - + @@ -310,12 +342,12 @@ Private Attributes - +
    Forwarded::load load ( ticket_id)
    - + @@ -335,12 +367,12 @@ Private Attributes - +
    Forwarded::set set ( values)
    - + @@ -360,12 +392,12 @@ Private Attributes - +
    Forwarded::setGroup setGroup ( g)
    - + @@ -383,10 +415,41 @@ Private Attributes + + +

    Field Documentation

    + +
    +
    +
    Forwarded::setTicket setTicket ( t)
    + + + +
    $group [private]
    +
    +
    + +

    The id of the group to which the ticket is being forwarded.

    + +
    +
    + +
    +
    + + + + +
    $ticket [private]
    +
    +
    + +

    The id of the ticket being forwarded.

    +

    The documentation for this class was generated from the following file:
      -
    • /home/daan/ryzom/ryzomcore/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/forwarded.php
    • +
    • /home/daan/ryzom/ryzomcore/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/forwarded.php
    @@ -394,7 +457,7 @@ Private Attributes onmouseover="return searchBox.OnSearchSelectShow()" onmouseout="return searchBox.OnSearchSelectHide()" onkeydown="return searchBox.OnSearchSelectKey(event)"> - All Classes Functions Variables + All Data Structures Files Functions Variables
    @@ -406,7 +469,7 @@ Private Attributes diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/classGui__Elements.html b/code/ryzom/tools/server/ryzom_ams_docs/html/classGui__Elements.html index 4753e9aed..9cc964462 100644 --- a/code/ryzom/tools/server/ryzom_ams_docs/html/classGui__Elements.html +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/classGui__Elements.html @@ -25,10 +25,12 @@ + Logo +
    Ryzom Account Management System - +  1.0
    @@ -48,8 +50,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
    @@ -87,29 +89,27 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');

    Helper class for generating gui related elements. More...

    - -

    List of all members.

    - - - - - - + + + + + +

    Static Public Member Functions

    static make_table ($inputList, $funcArray, $fieldArray)
     creates an array of information out of a list of objects which can be used to form a table.
    static make_table_with_key_is_id ($inputList, $funcArray, $idFunction)
     creates an array of information out of a list of objects which can be used to form a table with a key as id.
    static time_elapsed_string ($ptime)
     returns the elapsed time from a timestamp up till now.
    static make_table ($inputList, $funcArray, $fieldArray)
     creates an array of information out of a list of objects which can be used to form a table.
    static make_table_with_key_is_id ($inputList, $funcArray, $idFunction)
     creates an array of information out of a list of objects which can be used to form a table with a key as id.
    static time_elapsed_string ($ptime)
     returns the elapsed time from a timestamp up till now.

    Detailed Description

    Helper class for generating gui related elements.

    This class contains functions that generate data-arrays for tables, or other visual entities

    Author:
    Daan Janssens, mentored by Matthew Lagoe

    Member Function Documentation

    - +
    - + @@ -149,12 +149,12 @@ Static Public Member Functions - +
    static Gui_Elements::make_table static make_table ( inputList,
    - + @@ -181,7 +181,7 @@ Static Public Member Functions

    creates an array of information out of a list of objects which can be used to form a table with a key as id.

    -

    The idea is comparable to the make_table() function, though this time the results are stored in the index that is returned by the idFunction()

    +

    The idea is comparable to the make_table() function, though this time the results are stored in the index that is returned by the idFunction()

    Parameters:
    static Gui_Elements::make_table_with_key_is_id static make_table_with_key_is_id ( inputList,
    @@ -194,12 +194,12 @@ Static Public Member Functions - +
    $inputListthe list of objects of which we want to make a table.
    - + @@ -221,7 +221,7 @@ Static Public Member Functions
    The documentation for this class was generated from the following file:
      -
    • /home/daan/ryzom/ryzomcore/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/gui_elements.php
    • +
    • /home/daan/ryzom/ryzomcore/code/ryzom/tools/server/ryzom_ams/ams_lib/autoload/gui_elements.php
    @@ -229,7 +229,7 @@ Static Public Member Functions onmouseover="return searchBox.OnSearchSelectShow()" onmouseout="return searchBox.OnSearchSelectHide()" onkeydown="return searchBox.OnSearchSelectKey(event)"> - All Classes Functions Variables + All Data Structures Files Functions Variables
    @@ -241,7 +241,7 @@ Static Public Member Functions diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/classHelpers-members.html b/code/ryzom/tools/server/ryzom_ams_docs/html/classHelpers-members.html deleted file mode 100644 index fa7879da1..000000000 --- a/code/ryzom/tools/server/ryzom_ams_docs/html/classHelpers-members.html +++ /dev/null @@ -1,116 +0,0 @@ - - - - - -Ryzom Account Management System: Member List - - - - - - - - - - - -
    - - -
    -
    static Gui_Elements::time_elapsed_string static time_elapsed_string ( ptime)
    - - - - - - - - - - -
    -
    Ryzom Account Management System - -
    - -
    -
    - - - - - -
    -
    -
    -
    Helpers Member List
    -
    -
    -This is the complete list of members for Helpers, including all inherited members. - - - - - - -
    check_if_game_client()Helpers [static]
    check_login_ingame()Helpers [static]
    create_folders()Helpers [static]
    handle_language()Helpers [static]
    loadTemplate($template, $vars=array(), $returnHTML=false)Helpers [static]
    outputTime($time, $str=1)Helpers [static]
    - - - - -
    - -
    - - - - - - - diff --git a/code/ryzom/tools/server/ryzom_ams_docs/html/classHelpers.html b/code/ryzom/tools/server/ryzom_ams_docs/html/classHelpers.html index f6a32caf0..309b1b0d0 100644 --- a/code/ryzom/tools/server/ryzom_ams_docs/html/classHelpers.html +++ b/code/ryzom/tools/server/ryzom_ams_docs/html/classHelpers.html @@ -25,10 +25,12 @@ + Logo +
    Ryzom Account Management System - +  1.0
    @@ -48,8 +50,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');