Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/modules/objects/Script.cc @ 7474

Last change on this file since 7474 was 7474, checked in by dafrick, 14 years ago

Synchronizing Notifications.
In the course of that, notifications are not longer sent by creating a Notification and the calling notification.send() bur by letting the NotificationManager handle all this: NotificationManager::getInstance().sendNotification(message)
This made QuestNotification obsolete, thus it was removde.

Also did some work on synchronizing the Script class. It should work properly most of the time, but the current solution is unreliable and unsatisfactory. So this will change as soon as I know how.

  • Property svn:eol-style set to native
File size: 10.8 KB
Line 
1/*
2 *   ORXONOX - the hottest 3D action shooter ever to exist
3 *                    > www.orxonox.net <
4 *
5 *
6 *   License notice:
7 *
8 *   This program is free software; you can redistribute it and/or
9 *   modify it under the terms of the GNU General Public License
10 *   as published by the Free Software Foundation; either version 2
11 *   of the License, or (at your option) any later version.
12 *
13 *   This program is distributed in the hope that it will be useful,
14 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
15 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 *   GNU General Public License for more details.
17 *
18 *   You should have received a copy of the GNU General Public License
19 *   along with this program; if not, write to the Free Software
20 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 *
22 *   Author:
23 *      Benjamin Knecht
24 *   Co-authors:
25 *      Damian 'Mozork' Frick
26 *
27 */
28
29#include "Script.h"
30
31#include "core/command/CommandExecutor.h"
32#include "core/CoreIncludes.h"
33#include "core/EventIncludes.h"
34#include "core/GameMode.h"
35#include "core/LuaState.h"
36#include "core/XMLPort.h"
37#include "network/Host.h"
38#include "network/NetworkFunction.h"
39
40#include "PlayerManager.h"
41#include "infos/PlayerInfo.h"
42#include "interfaces/PlayerTrigger.h"
43#include "worldentities/pawns/Pawn.h"
44
45namespace orxonox
46{
47    CreateFactory(Script);
48
49    registerMemberNetworkFunction(Script, execute);
50
51    // Initializing constants.
52    /*static*/ const std::string Script::NORMAL = "normal";
53    /*static*/ const std::string Script::LUA = "lua";
54    /*static*/ const int Script::INF = -1;
55
56    /**
57    @brief
58        Constructor. Registers and initializes the object.
59    @param creator
60        The creator of this object.
61    */
62    Script::Script(BaseObject* creator) : BaseObject(creator), Synchronisable(creator)
63    {
64        RegisterObject(Script);
65
66        // Initialize variables.
67        this->luaState_ = NULL;
68        this->remainingExecutions_ = Script::INF;
69        this->mode_ = ScriptMode::normal;
70        this->onLoad_ = true;
71        this->times_ = Script::INF;
72        this->needsGraphics_ = false;
73
74        this->counter_ = 0.0f;
75
76        this->registerVariables();
77    }
78
79    /**
80    @brief
81        Destructor. Cleans up.
82    */
83    Script::~Script()
84    {
85        if(this->isInitialized() && this->luaState_ != NULL)
86            delete this->luaState_;
87    }
88
89    /**
90    @brief
91        Method for creating a Script object through XML.
92    @param xmlelement
93        The element.
94    @param mode
95        The mode.
96    */
97    void Script::XMLPort(Element& xmlelement, XMLPort::Mode mode)
98    {
99        SUPER(Script, XMLPort, xmlelement, mode);
100
101        XMLPortParam(Script, "code", setCode, getCode, xmlelement, mode);
102        XMLPortParamTemplate(Script, "mode", setMode, getMode, xmlelement, mode, const std::string&).defaultValues(Script::NORMAL);
103        XMLPortParam(Script, "onLoad", setOnLoad, isOnLoad, xmlelement, mode).defaultValues(true);
104        XMLPortParam(Script, "times", setTimes, getTimes, xmlelement, mode).defaultValues(Script::INF);
105        XMLPortParam(Script, "needsGraphics", setNeedsGraphics, getNeedsGraphics, xmlelement, mode).defaultValues(false);
106        XMLPortParam(Script, "forAll", setForAll, isForAll, xmlelement, mode).defaultValues(false);
107
108        XMLPortEventSink(Script, BaseObject, "trigger", trigger, xmlelement, mode);
109
110        if(this->isOnLoad()) // If the object is onLoad the code is executed at once for all clients.
111            this->execute(0);
112    }
113
114    /**
115    @brief
116        Creates a port that can be used to channel events and react to them.
117    @param xmlelement
118        The element.
119    @param mode
120        The mode.
121    */
122    void Script::XMLEventPort(Element& xmlelement, XMLPort::Mode mode)
123    {
124        SUPER(Script, XMLEventPort, xmlelement, mode);
125
126        XMLPortEventState(Script, BaseObject, "trigger", trigger, xmlelement, mode);
127    }
128
129    /**
130    @brief
131        Register the variables that need to be synchronized.
132    */
133    void Script::registerVariables(void)
134    {
135        registerVariable(code_, VariableDirection::ToClient);
136        registerVariable(needsGraphics_, VariableDirection::ToClient);
137        registerVariable(modeStr_, VariableDirection::ToClient, new NetworkCallback<Script>(this, &Script::modeChanged));
138    }
139
140    void Script::modeChanged(void)
141    {
142        this->setMode(this->modeStr_);
143    }
144
145    void Script::tick(float dt)
146    {
147        SUPER(Script, tick, dt);
148
149        if(!this->clientCallbacks_.empty())
150        {
151            if(this->counter_ < 2.0f)
152            {
153                this->counter_ += dt;
154            }
155            else
156            {
157                for(std::vector<unsigned int>::iterator it = this->clientCallbacks_.begin(); it != this->clientCallbacks_.end(); it++)
158                {
159                    this->execute(*it, true);
160                }
161                this->clientCallbacks_.clear();
162                this->counter_ = 0.0f;
163            }
164        }
165    }
166
167    /**
168    @brief
169        Is called when an event comes in trough the event port.
170    @param triggered
171        Whether the event is triggering or un-triggering.
172    @param trigger
173        The object that caused the event to be fired.
174    @return
175        Returns true if successful.
176    */
177    bool Script::trigger(bool triggered, BaseObject* trigger)
178    {
179        if(!triggered || !this->isActive()) // If the Script is inactive it cannot be executed.
180            return false;
181
182        COUT(4) << "Script (&" << this << ") triggered." << std::endl;
183
184        PlayerTrigger* pTrigger = orxonox_cast<PlayerTrigger*>(trigger);
185        Pawn* pawn = NULL;
186
187        // If the trigger is a PlayerTrigger.
188        if(pTrigger != NULL)
189        {
190            if(!pTrigger->isForPlayer())  // The PlayerTrigger is not exclusively for Pawns which means we cannot extract one.
191                return false;
192            else
193                pawn = pTrigger->getTriggeringPlayer();
194        }
195        else
196            return false;
197
198        if(pawn == NULL)  //TODO: Will this ever happen? If not, change in NotificationDispatcher as well.
199        {
200            COUT(4) << "The Script was triggered by an entity other than a Pawn. (" << trigger->getIdentifier()->getName() << ")" << std::endl;
201            return false;
202        }
203
204        // Extract the PlayerInfo from the Pawn.
205        PlayerInfo* player = pawn->getPlayer();
206
207        if(player == NULL)
208        {
209            COUT(3) << "The PlayerInfo* is NULL." << std::endl;
210            return false;
211        }
212
213        this->execute(player->getClientID());
214        return true;
215    }
216
217    /**
218    @brief
219        Executes the Scripts code for the input client, depending on the mode.
220    @param clientId
221        The Id of the client the Script should be executed for.
222    @param fromCallback
223        Whether this method is executed in response to the connectedCallback().
224    */
225    void Script::execute(unsigned int clientId, bool fromCallback)
226    {
227        if(GameMode::isServer())
228        {
229            // If the number of executions have been used up.
230            if(this->times_ != Script::INF && this->remainingExecutions_ == 0)
231                return;
232
233            // Decrement the number of remaining executions.
234            if(this->times_ != Script::INF)
235                this->remainingExecutions_--;
236        }
237
238        if(GameMode::isStandalone() || Host::getPlayerID() == clientId)
239        {
240            // If the code needs graphics to be executed but the GameMode doesn't show graphics the code isn't executed.
241            if(this->needsGraphics_ && !GameMode::showsGraphics())
242                return;
243
244            if(this->mode_ == ScriptMode::normal) // If the mode is 'normal'.
245                CommandExecutor::execute(this->code_);
246            else if(this->mode_ == ScriptMode::lua) // If it's 'lua'.
247            {
248                if(this->luaState_ == NULL)
249                    this->luaState_ = new LuaState();
250                this->luaState_->doString(this->code_);
251            }
252        }
253        if(!GameMode::isStandalone() && GameMode::isServer() && Host::getPlayerID() != clientId)
254        {
255            if(!fromCallback && this->isForAll())
256            {
257                const std::map<unsigned int, PlayerInfo*> clients = PlayerManager::getInstance().getClients();
258                for(std::map<unsigned int, PlayerInfo*>::const_iterator it = clients.begin(); it != clients.end(); it++)
259                {
260                    callMemberNetworkFunction(Script, execute, this->getObjectID(), it->first, it->first, false);
261                }
262            }
263            else
264            {
265                callMemberNetworkFunction(Script, execute, this->getObjectID(), clientId, clientId, false);
266            }
267        }
268    }
269
270    void Script::clientConnected(unsigned int clientId)
271    {
272        if(!GameMode::isStandalone() && GameMode::isServer() && this->isOnLoad())
273        {
274            if(clientId != 0)
275                //TODO: Do better. This is only a temporary fix.
276                this->clientCallbacks_.push_back(clientId);
277        }
278    }
279
280    /**
281    @brief
282        Sets the mode of the Script.
283    @param mode
284        The mode as a string.
285    */
286    void Script::setMode(const std::string& mode)
287    {
288        if(mode == Script::NORMAL)
289        {
290            this->setMode(ScriptMode::normal);
291            this->modeStr_ = Script::NORMAL;
292        }
293        else if(mode == Script::LUA)
294        {
295            this->setMode(ScriptMode::lua);
296            this->modeStr_ = Script::LUA;
297            // Creates a new LuaState.
298            if(this->luaState_ == NULL)
299                this->luaState_ = new LuaState();
300        }
301        else
302        {
303            COUT(2) << "Invalid mode '" << mode << "' in Script object. Setting to 'normal'." << std::endl;
304            this->setMode(ScriptMode::normal);
305            this->modeStr_ = Script::NORMAL;
306        }
307    }
308
309    /**
310    @brief
311        Get the mode of the Script.
312    @return
313        Returns the mode as a string.
314    */
315    const std::string& Script::getMode(void)
316    {
317        switch(this->mode_)
318        {
319            case ScriptMode::normal:
320                return Script::NORMAL;
321            case ScriptMode::lua:
322                return Script::LUA;
323            default: // This will never happen...
324                return Script::NORMAL;
325        }
326    }
327
328    /**
329    @brief
330        Set the number of times this Script is executed at the most.
331        -1 denotes infinity.
332    @param times
333        The number of times to be set.
334    */
335    void Script::setTimes(int times)
336    {
337        if(times >= -1)
338        {
339            this->times_ = times;
340            this->remainingExecutions_ = times;
341        }
342        else
343        {
344            COUT(2) << "Invalid times '" << times << "' in Script. Setting to infinity." << std::endl;
345            this->times_ = Script::INF;
346            this->remainingExecutions_ = Script::INF;
347        }
348    }
349
350}
Note: See TracBrowser for help on using the repository browser.