<commit_before>#include <Common.h>
#include <Logging/Logger.h>
#include <Network/PacketContainer.h>

#include "Network/GameConnection.h"
#include "Network/PacketWrappers/ActorControlPacket.h"
#include "Network/PacketWrappers/PlayerSetupPacket.h"
#include "Network/PacketWrappers/ServerNoticePacket.h"
#include "Network/PacketWrappers/EventStartPacket.h"
#include "Network/PacketWrappers/EventPlayPacket.h"
#include "Network/PacketWrappers/EventFinishPacket.h"
#include "Network/PacketWrappers/DirectorPlayScenePacket.h"

#include "Territory/Territory.h"
#include "ServerMgr.h"
#include "Framework.h"

#include "Action/EventAction.h"

using namespace Sapphire::Common;
using namespace Sapphire::Network::Packets;
using namespace Sapphire::Network::Packets::Server;

void Sapphire::Entity::Player::addEvent( Event::EventHandlerPtr pEvent )
{
  m_eventHandlerMap[ pEvent->getId() ] = pEvent;
}

std::map< uint32_t, Sapphire::Event::EventHandlerPtr >& Sapphire::Entity::Player::eventList()
{
  return m_eventHandlerMap;
}

Sapphire::Event::EventHandlerPtr Sapphire::Entity::Player::getEvent( uint32_t eventId )
{
  auto it = m_eventHandlerMap.find( eventId );
  if( it != m_eventHandlerMap.end() )
    return it->second;

  return Event::EventHandlerPtr( nullptr );
}

size_t Sapphire::Entity::Player::getEventCount()
{
  return m_eventHandlerMap.size();
}

void Sapphire::Entity::Player::removeEvent( uint32_t eventId )
{
  auto it = m_eventHandlerMap.find( eventId );
  if( it != m_eventHandlerMap.end() )
  {
    auto tmpEvent = it->second;
    m_eventHandlerMap.erase( it );
  }
}

void Sapphire::Entity::Player::checkEvent( uint32_t eventId )
{
  auto pEvent = getEvent( eventId );

  if( pEvent && !pEvent->hasPlayedScene() )
    eventFinish( eventId, 1 );
}

void Sapphire::Entity::Player::directorPlayScene( uint32_t eventId, uint32_t scene, uint32_t flags, uint32_t eventParam3,
                                                  uint32_t eventParam4, uint32_t eventParam5 )
{
  if( flags & 0x02 )
    setStateFlag( PlayerStateFlag::WatchingCutscene );

  auto pEvent = getEvent( eventId );
  if( !pEvent )
  {
    Logger::error( "Could not find event #{0}, event has not been started!", eventId );
    return;
  }

  pEvent->setPlayedScene( true );
  pEvent->setEventReturnCallback( nullptr );
  auto eventPlay = std::make_shared< DirectorPlayScenePacket >( getId(), getId(), pEvent->getId(),
                                                                scene, flags, eventParam3, eventParam4, eventParam5 );

  queuePacket( eventPlay );
}

void Sapphire::Entity::Player::eventStart( uint64_t actorId, uint32_t eventId,
                                           Event::EventHandler::EventType eventType, uint8_t eventParam1,
                                           uint32_t eventParam2, Event::EventHandler::EventFinishCallback callback )
{

  auto newEvent = Event::make_EventHandler( actorId, eventId, eventType, eventParam2 );

  newEvent->setEventFinishCallback( callback );

  addEvent( newEvent );

  setStateFlag( PlayerStateFlag::InNpcEvent );

  auto eventStart = std::make_shared< EventStartPacket >( getId(), actorId, eventId,
                                                          eventType, eventParam1, eventParam2 );

  queuePacket( eventStart );

}

void Sapphire::Entity::Player::playScene( uint32_t eventId, uint32_t scene,
                                          uint32_t flags, uint32_t eventParam2,
                                          uint32_t eventParam3 )
{
  playScene( eventId, scene, flags, eventParam2, eventParam3, nullptr );
}

void Sapphire::Entity::Player::playScene( uint32_t eventId, uint32_t scene,
                                          uint32_t flags, Event::EventHandler::SceneReturnCallback eventCallback )
{
  playScene( eventId, scene, flags, 0, 0, eventCallback );
}

void Sapphire::Entity::Player::playScene( uint32_t eventId, uint32_t scene, uint32_t flags )
{
  playScene( eventId, scene, flags, 0, 0, nullptr );
}

void Sapphire::Entity::Player::playScene( uint32_t eventId, uint32_t scene,
                                          uint32_t flags, uint32_t eventParam2,
                                          uint32_t eventParam3, Event::EventHandler::SceneReturnCallback eventCallback )
{
  playScene( eventId, scene, flags, eventParam2, eventParam3, 0, eventCallback );
}

void Sapphire::Entity::Player::playGilShop( uint32_t eventId, uint32_t flags, uint32_t param,
                                            Event::EventHandler::SceneReturnCallback eventCallback )
{
  auto pEvent = bootstrapSceneEvent( eventId, flags );
  if( !pEvent )
    return;

  pEvent->setPlayedScene( true );
  pEvent->setEventReturnCallback( eventCallback );
  pEvent->setSceneChainCallback( nullptr );

  auto openGilShopPacket = makeZonePacket< Server::FFXIVIpcEventPlay255 >( getId() );
  openGilShopPacket->data().eventId = eventId;
  openGilShopPacket->data().sceneFlags = flags;
  openGilShopPacket->data().actorId = getId();
  switch( param )
  {
    case 1:
    {
      openGilShopPacket->data().params[ 0 ] = 0x02;
      openGilShopPacket->data().params[ 1 ] = 1;
      openGilShopPacket->data().params[ 2 ] = 0x64;
      break;
    }
    case 2:
    {
      openGilShopPacket->data().params[ 0 ] = 0xA2;
      openGilShopPacket->data().params[ 1 ] = 2;
      break;
    }
  }

  openGilShopPacket->data().scene = 10;

  queuePacket( openGilShopPacket );
}

Sapphire::Event::EventHandlerPtr Sapphire::Entity::Player::bootstrapSceneEvent( uint32_t eventId, uint32_t flags )
{

  auto pEvent = getEvent( eventId );
  if( !pEvent )
  {
    Logger::error( "Could not find event #{0}, event has not been started!", eventId );
    return nullptr;
  }

  if( flags & 0x02 )
    setStateFlag( PlayerStateFlag::WatchingCutscene );

  return pEvent;
}

void Sapphire::Entity::Player::playScene( uint32_t eventId, uint32_t scene,
                                          uint32_t flags, uint32_t eventParam2,
                                          uint32_t eventParam3, uint32_t eventParam4,
                                          Event::EventHandler::SceneReturnCallback eventCallback )
{
  auto pEvent = bootstrapSceneEvent( eventId, flags );
  if( !pEvent )
    return;

  pEvent->setPlayedScene( true );
  pEvent->setEventReturnCallback( eventCallback );
  pEvent->setSceneChainCallback( nullptr );
  auto eventPlay = std::make_shared< EventPlayPacket >( getId(), pEvent->getActorId(), pEvent->getId(),
                                                        scene, flags, eventParam2, eventParam3, eventParam4 );

  queuePacket( eventPlay );
}

void Sapphire::Entity::Player::playSceneChain( uint32_t eventId, uint32_t scene, uint32_t flags,
                                               uint32_t eventParam2, uint32_t eventParam3, uint32_t eventParam4,
                                               Sapphire::Event::EventHandler::SceneChainCallback sceneChainCallback )
{
  auto pEvent = bootstrapSceneEvent( eventId, flags );
  if( !pEvent )
    return;

  pEvent->setPlayedScene( true );
  pEvent->setSceneChainCallback( sceneChainCallback );
  pEvent->setEventReturnCallback( nullptr );
  auto eventPlay = std::make_shared< EventPlayPacket >( getId(), pEvent->getActorId(), pEvent->getId(),
                                                        scene, flags, eventParam2, eventParam3, eventParam4 );

  queuePacket( eventPlay );
}

void Sapphire::Entity::Player::playSceneChain( uint32_t eventId, uint32_t scene, uint32_t flags,
                                               uint32_t eventParam2, uint32_t eventParam3,
                                               Sapphire::Event::EventHandler::SceneChainCallback sceneChainCallback )
{
  playSceneChain( eventId, scene, flags, eventParam2, eventParam3, 0, sceneChainCallback );
}

void Sapphire::Entity::Player::playSceneChain( uint32_t eventId, uint32_t scene, uint32_t flags,
                                               Sapphire::Event::EventHandler::SceneChainCallback sceneChainCallback )
{
  playSceneChain( eventId, scene, flags, 0, 0, 0, sceneChainCallback );
}

void Sapphire::Entity::Player::eventFinish( uint32_t eventId, uint32_t freePlayer )
{
  auto pEvent = getEvent( eventId );

  if( !pEvent )
  {
    Logger::error( "Could not find event #{0}, event has not been started!", eventId );
    return;
  }

  if( getEventCount() > 1 && pEvent->getEventType() != Event::EventHandler::Nest )
  {
    // this is the parent of a nested event, we can't finish it until the parent finishes
    return;
  }

  switch( pEvent->getEventType() )
  {
    case Event::EventHandler::Nest:
    {
      queuePacket( std::make_shared< EventFinishPacket >( getId(), pEvent->getId(),
                                                          pEvent->getEventType(), pEvent->getEventParam() ) );
      removeEvent( pEvent->getId() );

      auto callback = pEvent->getEventFinishCallback();

      if( callback )
        callback( *this, pEvent->getActorId() );
      else
      {
        auto events = eventList();
        for( auto it : events )
        {
          if( !it.second->hasPlayedScene() )
          {
            queuePacket( std::make_shared< EventFinishPacket >( getId(), it.second->getId(),
                                                                it.second->getEventType(),
                                                                it.second->getEventParam() ) );
            removeEvent( it.second->getId() );
          }
        }
      }
      break;
    }
    default:
    {
      queuePacket( std::make_shared< EventFinishPacket >( getId(), pEvent->getId(),
                                                          pEvent->getEventType(), pEvent->getEventParam() ) );
      break;
    }
  }

  if( hasStateFlag( PlayerStateFlag::WatchingCutscene ) )
    unsetStateFlag( PlayerStateFlag::WatchingCutscene );

  removeEvent( pEvent->getId() );

  if( freePlayer == 1 )
    unsetStateFlag( PlayerStateFlag::InNpcEvent );
}

void Sapphire::Entity::Player::eventActionStart( uint32_t eventId,
                                                 uint32_t action,
                                                 World::Action::ActionCallback finishCallback,
                                                 World::Action::ActionCallback interruptCallback,
                                                 uint64_t additional )
{
  auto pEventAction = World::Action::make_EventAction( getAsChara(), eventId, action,
                                                finishCallback, interruptCallback, additional, m_pFw );

  auto pEvent = getEvent( eventId );

  if( !pEvent && getEventCount() )
  {
    // We're trying to play a nested event, need to start it first.
    eventStart( getId(), eventId, Event::EventHandler::Nest, 0, 0 );
    pEvent = getEvent( eventId );
  }
  else if( !pEvent )
  {
    Logger::error( "Could not find event #{0}, event has not been started!", eventId );
    return;
  }

  if( pEvent )
    pEvent->setPlayedScene( true );

  setCurrentAction( pEventAction );
  pEventAction->start();
}

void Sapphire::Entity::Player::eventItemActionStart( uint32_t eventId,
                                                     uint32_t action,
                                                     World::Action::ActionCallback finishCallback,
                                                     World::Action::ActionCallback interruptCallback,
                                                     uint64_t additional )
{
//  Action::ActionPtr pEventItemAction = Action::make_EventItemAction( getAsChara(), eventId, action,
//                                                                     finishCallback, interruptCallback, additional );
//
//  setCurrentAction( pEventItemAction );
//
//  pEventItemAction->onStart();
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void Sapphire::Entity::Player::onLogin()
{
  auto pServerMgr = m_pFw->get< Sapphire::World::ServerMgr >();
  auto motd = pServerMgr->getConfig().motd;

  std::istringstream ss( motd );
  std::string msg;
  while( std::getline( ss, msg, ';' ) )
  {
    sendNotice( msg );
  }
}

void Sapphire::Entity::Player::onZoneStart()
{

}

void Sapphire::Entity::Player::onZoneDone()
{

}

void Sapphire::Entity::Player::onDeath()
{

}

// TODO: slightly ugly here and way too static. Needs too be done properly
void Sapphire::Entity::Player::onTick()
{

  // add 3 seconds to total play time
  m_playTime += 3;

  bool sendUpdate = false;

  if( !isAlive() || !isLoadingComplete() )
    return;

  uint32_t addHp = static_cast< uint32_t >( getMaxHp() * 0.1f + 1 );
  uint32_t addMp = static_cast< uint32_t >( getMaxMp() * 0.06f + 1 );
  uint32_t addTp = 100;

  if( !m_actorIdTohateSlotMap.empty() )
  {
    addHp = static_cast< uint32_t >( getMaxHp() * 0.01f + 1 );
    addMp = static_cast< uint32_t >( getMaxMp() * 0.02f + 1 );
    addTp = 60;
  }

  if( m_hp < getMaxHp() )
  {

    if( m_hp + addHp < getMaxHp() )
      m_hp += addHp;
    else
      m_hp = getMaxHp();

    sendUpdate = true;
  }

  if( m_mp < getMaxMp() )
  {

    if( m_mp + addMp < getMaxMp() )
      m_mp += addMp;
    else
      m_mp = getMaxMp();

    sendUpdate = true;
  }

  if( m_tp < 1000 )
  {
    if( m_tp + addTp < 1000 )
      m_tp += addTp;
    else
      m_tp = 1000;

    sendUpdate = true;
  }

  if( sendUpdate )
    sendStatusUpdate();
}
<commit_msg>rename<commit_after>#include <Common.h>
#include <Logging/Logger.h>
#include <Network/PacketContainer.h>

#include "Network/GameConnection.h"
#include "Network/PacketWrappers/ActorControlPacket.h"
#include "Network/PacketWrappers/PlayerSetupPacket.h"
#include "Network/PacketWrappers/ServerNoticePacket.h"
#include "Network/PacketWrappers/EventStartPacket.h"
#include "Network/PacketWrappers/EventPlayPacket.h"
#include "Network/PacketWrappers/EventFinishPacket.h"
#include "Network/PacketWrappers/DirectorPlayScenePacket.h"

#include "Territory/Territory.h"
#include "ServerMgr.h"
#include "Framework.h"

#include "Action/EventAction.h"

using namespace Sapphire::Common;
using namespace Sapphire::Network::Packets;
using namespace Sapphire::Network::Packets::Server;

void Sapphire::Entity::Player::addEvent( Event::EventHandlerPtr pEvent )
{
  m_eventHandlerMap[ pEvent->getId() ] = pEvent;
}

std::map< uint32_t, Sapphire::Event::EventHandlerPtr >& Sapphire::Entity::Player::eventList()
{
  return m_eventHandlerMap;
}

Sapphire::Event::EventHandlerPtr Sapphire::Entity::Player::getEvent( uint32_t eventId )
{
  auto it = m_eventHandlerMap.find( eventId );
  if( it != m_eventHandlerMap.end() )
    return it->second;

  return Event::EventHandlerPtr( nullptr );
}

size_t Sapphire::Entity::Player::getEventCount()
{
  return m_eventHandlerMap.size();
}

void Sapphire::Entity::Player::removeEvent( uint32_t eventId )
{
  auto it = m_eventHandlerMap.find( eventId );
  if( it != m_eventHandlerMap.end() )
  {
    auto tmpEvent = it->second;
    m_eventHandlerMap.erase( it );
  }
}

void Sapphire::Entity::Player::checkEvent( uint32_t eventId )
{
  auto pEvent = getEvent( eventId );

  if( pEvent && !pEvent->hasPlayedScene() )
    eventFinish( eventId, 1 );
}

void Sapphire::Entity::Player::directorPlayScene( uint32_t eventId, uint32_t scene, uint32_t flags, uint32_t eventParam3,
                                                  uint32_t eventParam4, uint32_t eventParam5 )
{
  if( flags & 0x02 )
    setStateFlag( PlayerStateFlag::WatchingCutscene );

  auto pEvent = getEvent( eventId );
  if( !pEvent )
  {
    Logger::error( "Could not find event #{0}, event has not been started!", eventId );
    return;
  }

  pEvent->setPlayedScene( true );
  pEvent->setEventReturnCallback( nullptr );
  auto eventPlay = std::make_shared< DirectorPlayScenePacket >( getId(), getId(), pEvent->getId(),
                                                                scene, flags, eventParam3, eventParam4, eventParam5 );

  queuePacket( eventPlay );
}

void Sapphire::Entity::Player::eventStart( uint64_t actorId, uint32_t eventId,
                                           Event::EventHandler::EventType eventType, uint8_t eventParam1,
                                           uint32_t eventParam2, Event::EventHandler::EventFinishCallback callback )
{

  auto newEvent = Event::make_EventHandler( actorId, eventId, eventType, eventParam2 );

  newEvent->setEventFinishCallback( callback );

  addEvent( newEvent );

  setStateFlag( PlayerStateFlag::InNpcEvent );

  auto eventStart = std::make_shared< EventStartPacket >( getId(), actorId, eventId,
                                                          eventType, eventParam1, eventParam2 );

  queuePacket( eventStart );

}

void Sapphire::Entity::Player::playScene( uint32_t eventId, uint32_t scene,
                                          uint32_t flags, uint32_t eventParam2,
                                          uint32_t eventParam3 )
{
  playScene( eventId, scene, flags, eventParam2, eventParam3, nullptr );
}

void Sapphire::Entity::Player::playScene( uint32_t eventId, uint32_t scene,
                                          uint32_t flags, Event::EventHandler::SceneReturnCallback eventCallback )
{
  playScene( eventId, scene, flags, 0, 0, eventCallback );
}

void Sapphire::Entity::Player::playScene( uint32_t eventId, uint32_t scene, uint32_t flags )
{
  playScene( eventId, scene, flags, 0, 0, nullptr );
}

void Sapphire::Entity::Player::playScene( uint32_t eventId, uint32_t scene,
                                          uint32_t flags, uint32_t eventParam2,
                                          uint32_t eventParam3, Event::EventHandler::SceneReturnCallback eventCallback )
{
  playScene( eventId, scene, flags, eventParam2, eventParam3, 0, eventCallback );
}

void Sapphire::Entity::Player::playGilShop( uint32_t eventId, uint32_t flags, uint32_t param1,
                                            Event::EventHandler::SceneReturnCallback eventCallback )
{
  auto pEvent = bootstrapSceneEvent( eventId, flags );
  if( !pEvent )
    return;

  pEvent->setPlayedScene( true );
  pEvent->setEventReturnCallback( eventCallback );
  pEvent->setSceneChainCallback( nullptr );

  auto openGilShopPacket = makeZonePacket< Server::FFXIVIpcEventPlay255 >( getId() );
  openGilShopPacket->data().eventId = eventId;
  openGilShopPacket->data().sceneFlags = flags;
  openGilShopPacket->data().actorId = getId();
  switch( param1 )
  {
    case 1:
    {
      openGilShopPacket->data().params[ 0 ] = 0x02;
      openGilShopPacket->data().params[ 1 ] = 1;
      openGilShopPacket->data().params[ 2 ] = 0x64;
      break;
    }
    case 2:
    {
      openGilShopPacket->data().params[ 0 ] = 0xA2;
      openGilShopPacket->data().params[ 1 ] = 2;
      break;
    }
  }

  openGilShopPacket->data().scene = 10;

  queuePacket( openGilShopPacket );
}

Sapphire::Event::EventHandlerPtr Sapphire::Entity::Player::bootstrapSceneEvent( uint32_t eventId, uint32_t flags )
{

  auto pEvent = getEvent( eventId );
  if( !pEvent )
  {
    Logger::error( "Could not find event #{0}, event has not been started!", eventId );
    return nullptr;
  }

  if( flags & 0x02 )
    setStateFlag( PlayerStateFlag::WatchingCutscene );

  return pEvent;
}

void Sapphire::Entity::Player::playScene( uint32_t eventId, uint32_t scene,
                                          uint32_t flags, uint32_t eventParam2,
                                          uint32_t eventParam3, uint32_t eventParam4,
                                          Event::EventHandler::SceneReturnCallback eventCallback )
{
  auto pEvent = bootstrapSceneEvent( eventId, flags );
  if( !pEvent )
    return;

  pEvent->setPlayedScene( true );
  pEvent->setEventReturnCallback( eventCallback );
  pEvent->setSceneChainCallback( nullptr );
  auto eventPlay = std::make_shared< EventPlayPacket >( getId(), pEvent->getActorId(), pEvent->getId(),
                                                        scene, flags, eventParam2, eventParam3, eventParam4 );

  queuePacket( eventPlay );
}

void Sapphire::Entity::Player::playSceneChain( uint32_t eventId, uint32_t scene, uint32_t flags,
                                               uint32_t eventParam2, uint32_t eventParam3, uint32_t eventParam4,
                                               Sapphire::Event::EventHandler::SceneChainCallback sceneChainCallback )
{
  auto pEvent = bootstrapSceneEvent( eventId, flags );
  if( !pEvent )
    return;

  pEvent->setPlayedScene( true );
  pEvent->setSceneChainCallback( sceneChainCallback );
  pEvent->setEventReturnCallback( nullptr );
  auto eventPlay = std::make_shared< EventPlayPacket >( getId(), pEvent->getActorId(), pEvent->getId(),
                                                        scene, flags, eventParam2, eventParam3, eventParam4 );

  queuePacket( eventPlay );
}

void Sapphire::Entity::Player::playSceneChain( uint32_t eventId, uint32_t scene, uint32_t flags,
                                               uint32_t eventParam2, uint32_t eventParam3,
                                               Sapphire::Event::EventHandler::SceneChainCallback sceneChainCallback )
{
  playSceneChain( eventId, scene, flags, eventParam2, eventParam3, 0, sceneChainCallback );
}

void Sapphire::Entity::Player::playSceneChain( uint32_t eventId, uint32_t scene, uint32_t flags,
                                               Sapphire::Event::EventHandler::SceneChainCallback sceneChainCallback )
{
  playSceneChain( eventId, scene, flags, 0, 0, 0, sceneChainCallback );
}

void Sapphire::Entity::Player::eventFinish( uint32_t eventId, uint32_t freePlayer )
{
  auto pEvent = getEvent( eventId );

  if( !pEvent )
  {
    Logger::error( "Could not find event #{0}, event has not been started!", eventId );
    return;
  }

  if( getEventCount() > 1 && pEvent->getEventType() != Event::EventHandler::Nest )
  {
    // this is the parent of a nested event, we can't finish it until the parent finishes
    return;
  }

  switch( pEvent->getEventType() )
  {
    case Event::EventHandler::Nest:
    {
      queuePacket( std::make_shared< EventFinishPacket >( getId(), pEvent->getId(),
                                                          pEvent->getEventType(), pEvent->getEventParam() ) );
      removeEvent( pEvent->getId() );

      auto callback = pEvent->getEventFinishCallback();

      if( callback )
        callback( *this, pEvent->getActorId() );
      else
      {
        auto events = eventList();
        for( auto it : events )
        {
          if( !it.second->hasPlayedScene() )
          {
            queuePacket( std::make_shared< EventFinishPacket >( getId(), it.second->getId(),
                                                                it.second->getEventType(),
                                                                it.second->getEventParam() ) );
            removeEvent( it.second->getId() );
          }
        }
      }
      break;
    }
    default:
    {
      queuePacket( std::make_shared< EventFinishPacket >( getId(), pEvent->getId(),
                                                          pEvent->getEventType(), pEvent->getEventParam() ) );
      break;
    }
  }

  if( hasStateFlag( PlayerStateFlag::WatchingCutscene ) )
    unsetStateFlag( PlayerStateFlag::WatchingCutscene );

  removeEvent( pEvent->getId() );

  if( freePlayer == 1 )
    unsetStateFlag( PlayerStateFlag::InNpcEvent );
}

void Sapphire::Entity::Player::eventActionStart( uint32_t eventId,
                                                 uint32_t action,
                                                 World::Action::ActionCallback finishCallback,
                                                 World::Action::ActionCallback interruptCallback,
                                                 uint64_t additional )
{
  auto pEventAction = World::Action::make_EventAction( getAsChara(), eventId, action,
                                                finishCallback, interruptCallback, additional, m_pFw );

  auto pEvent = getEvent( eventId );

  if( !pEvent && getEventCount() )
  {
    // We're trying to play a nested event, need to start it first.
    eventStart( getId(), eventId, Event::EventHandler::Nest, 0, 0 );
    pEvent = getEvent( eventId );
  }
  else if( !pEvent )
  {
    Logger::error( "Could not find event #{0}, event has not been started!", eventId );
    return;
  }

  if( pEvent )
    pEvent->setPlayedScene( true );

  setCurrentAction( pEventAction );
  pEventAction->start();
}

void Sapphire::Entity::Player::eventItemActionStart( uint32_t eventId,
                                                     uint32_t action,
                                                     World::Action::ActionCallback finishCallback,
                                                     World::Action::ActionCallback interruptCallback,
                                                     uint64_t additional )
{
//  Action::ActionPtr pEventItemAction = Action::make_EventItemAction( getAsChara(), eventId, action,
//                                                                     finishCallback, interruptCallback, additional );
//
//  setCurrentAction( pEventItemAction );
//
//  pEventItemAction->onStart();
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void Sapphire::Entity::Player::onLogin()
{
  auto pServerMgr = m_pFw->get< Sapphire::World::ServerMgr >();
  auto motd = pServerMgr->getConfig().motd;

  std::istringstream ss( motd );
  std::string msg;
  while( std::getline( ss, msg, ';' ) )
  {
    sendNotice( msg );
  }
}

void Sapphire::Entity::Player::onZoneStart()
{

}

void Sapphire::Entity::Player::onZoneDone()
{

}

void Sapphire::Entity::Player::onDeath()
{

}

// TODO: slightly ugly here and way too static. Needs too be done properly
void Sapphire::Entity::Player::onTick()
{

  // add 3 seconds to total play time
  m_playTime += 3;

  bool sendUpdate = false;

  if( !isAlive() || !isLoadingComplete() )
    return;

  uint32_t addHp = static_cast< uint32_t >( getMaxHp() * 0.1f + 1 );
  uint32_t addMp = static_cast< uint32_t >( getMaxMp() * 0.06f + 1 );
  uint32_t addTp = 100;

  if( !m_actorIdTohateSlotMap.empty() )
  {
    addHp = static_cast< uint32_t >( getMaxHp() * 0.01f + 1 );
    addMp = static_cast< uint32_t >( getMaxMp() * 0.02f + 1 );
    addTp = 60;
  }

  if( m_hp < getMaxHp() )
  {

    if( m_hp + addHp < getMaxHp() )
      m_hp += addHp;
    else
      m_hp = getMaxHp();

    sendUpdate = true;
  }

  if( m_mp < getMaxMp() )
  {

    if( m_mp + addMp < getMaxMp() )
      m_mp += addMp;
    else
      m_mp = getMaxMp();

    sendUpdate = true;
  }

  if( m_tp < 1000 )
  {
    if( m_tp + addTp < 1000 )
      m_tp += addTp;
    else
      m_tp = 1000;

    sendUpdate = true;
  }

  if( sendUpdate )
    sendStatusUpdate();
}
<|endoftext|>


<commit_before>#include <silicium/byte.hpp>
#include <boost/test/unit_test.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/unordered_map.hpp>
#include <unordered_map>

BOOST_AUTO_TEST_CASE(byte_zero)
{
	Si::byte b = Si::byte::zero;
	BOOST_STATIC_ASSERT(sizeof(b) == 1);
	BOOST_CHECK_EQUAL(0, static_cast<char>(b));
	BOOST_CHECK_EQUAL(0, static_cast<signed char>(b));
	BOOST_CHECK_EQUAL(0, static_cast<unsigned char>(b));
	BOOST_CHECK_EQUAL(0, *reinterpret_cast<char *>(&b));
}

BOOST_AUTO_TEST_CASE(byte_std_hash)
{
	std::unordered_map<Si::byte, int> m;
	m.insert(std::make_pair(Si::byte::one, 1));
}

BOOST_AUTO_TEST_CASE(byte_boost_hash)
{
	boost::unordered_map<Si::byte, int> m;
	m.insert(std::make_pair(Si::byte::one, 1));
}

BOOST_AUTO_TEST_CASE(byte_ostream)
{
	BOOST_CHECK_EQUAL("0", boost::lexical_cast<std::string>(Si::byte::zero));
	BOOST_CHECK_EQUAL("1", boost::lexical_cast<std::string>(Si::byte::one));
	BOOST_CHECK_EQUAL("0", boost::lexical_cast<std::string>(Si::byte::minimum));
	BOOST_CHECK_EQUAL("255", boost::lexical_cast<std::string>(Si::byte::maximum));
}
<commit_msg>test std::set<Si::byte><commit_after>#include <silicium/byte.hpp>
#include <boost/test/unit_test.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/unordered_map.hpp>
#include <unordered_map>
#include <set>

BOOST_AUTO_TEST_CASE(byte_zero)
{
	Si::byte b = Si::byte::zero;
	BOOST_STATIC_ASSERT(sizeof(b) == 1);
	BOOST_CHECK_EQUAL(0, static_cast<char>(b));
	BOOST_CHECK_EQUAL(0, static_cast<signed char>(b));
	BOOST_CHECK_EQUAL(0, static_cast<unsigned char>(b));
	BOOST_CHECK_EQUAL(0, *reinterpret_cast<char *>(&b));
}

BOOST_AUTO_TEST_CASE(byte_std_hash)
{
	std::unordered_map<Si::byte, int> m;
	m.insert(std::make_pair(Si::byte::one, 1));
}

BOOST_AUTO_TEST_CASE(byte_boost_hash)
{
	boost::unordered_map<Si::byte, int> m;
	m.insert(std::make_pair(Si::byte::one, 1));
}

BOOST_AUTO_TEST_CASE(byte_ostream)
{
	BOOST_CHECK_EQUAL("0", boost::lexical_cast<std::string>(Si::byte::zero));
	BOOST_CHECK_EQUAL("1", boost::lexical_cast<std::string>(Si::byte::one));
	BOOST_CHECK_EQUAL("0", boost::lexical_cast<std::string>(Si::byte::minimum));
	BOOST_CHECK_EQUAL("255", boost::lexical_cast<std::string>(Si::byte::maximum));
}

BOOST_AUTO_TEST_CASE(byte_relational)
{
	std::set<Si::byte> s;
	s.emplace(Si::byte::zero);
	s.emplace(Si::byte::one);
	BOOST_CHECK_EQUAL(1, s.count(Si::byte::zero));
	BOOST_CHECK_EQUAL(1, s.count(Si::byte::one));
	BOOST_CHECK_EQUAL(0, s.count(Si::byte::maximum));
}
<|endoftext|>


<commit_before>/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.

Author: Leonardo de Moura
*/
#include "frontend.h"
#include "environment.h"
#include "operator_info.h"
#include "kernel_exception.h"
#include "builtin.h"
#include "pp.h"
#include "test.h"
using namespace lean;

static void tst1() {
    frontend f;
    f.add_uvar("tst");
    frontend c = f.mk_child();
    lean_assert(c.get_uvar("tst") == f.get_uvar("tst"));
    lean_assert(f.get_environment().has_children());
}

static void tst2() {
    operator_info op1 = infixl(name("and"), 10);
    operator_info op2 = infixr(name("implies"), 20);
    lean_assert(op1.get_precedence() == 10);
    lean_assert(op1.get_fixity() == fixity::Infixl);
    op1.add_internal_name(name{"Bool","And"});
    operator_info op3 = infixl(name("+"), 10);
    op3.add_internal_name(name{"Int", "plus"});
    op3.add_internal_name(name{"Real", "plus"});
    std::cout << op3.get_internal_names() << "\n";
    lean_assert(length(op3.get_internal_names()) == 2);
    operator_info op4 = op3.copy();
    op4.add_internal_name(name{"Complex", "plus"});
    std::cout << op4.get_internal_names() << "\n";
    lean_assert(length(op3.get_internal_names()) == 2);
    lean_assert(length(op4.get_internal_names()) == 3);
    lean_assert(op4.get_fixity() == fixity::Infixl);
    lean_assert(op4.get_op_name() == op3.get_op_name());
    lean_assert(prefix("tst", 20).get_fixity() == fixity::Prefix);
    lean_assert(postfix("!", 20).get_fixity() == fixity::Postfix);
    lean_assert(length(mixfixl({"if", "then", "else"}, 10).get_op_name_parts()) == 3);
    lean_assert(mixfixc({"if", "then", "else", "endif"}, 10).get_fixity() == fixity::Mixfixc);
    std::cout << mixfixc({"if", "then", "else", "endif"}, 10).get_op_name_parts() << "\n";
    std::cout << op4 << "\n";
    std::cout << op2 << "\n";
    std::cout << mixfixc({"if", "then", "else", "endif"}, 10) << "\n";
}

static void tst3() {
    frontend f;
    std::cout << "====================\n";
    std::cout << f << "\n";
}

static void tst4() {
    frontend f;
    formatter fmt = mk_pp_formatter(f);
    context c;
    c = extend(c, "x", Bool);
    c = extend(c, "y", Bool);
    c = extend(c, "x", Bool, Var(1));
    c = extend(c, "x", Bool, Var(2));
    c = extend(c, "z", Bool, Var(1));
    c = extend(c, "x", Bool, Var(4));
    std::cout << fmt(c) << "\n";
}

static void tst5() {
    std::cout << "=================\n";
    frontend f;
    formatter fmt = mk_pp_formatter(f);
    f.add_var("A", Type());
    f.add_var("x", Const("A"));
    object const & obj = f.find_object("x");
    lean_assert(obj);
    lean_assert(obj.get_name() == "x");
    std::cout << fmt(obj) << "\n";
    object const & obj2 = f.find_object("y");
    lean_assert(!obj2);
    try {
        f.get_object("y");
        lean_unreachable();
    } catch (unknown_name_exception & ex) {
        std::cout << ex.what() << " " << ex.get_name() << "\n";
    }
}

class alien_cell : public neutral_object_cell {
    unsigned m_data[128];
public:
    alien_cell() {}
    unsigned & get_data(unsigned i) { return m_data[i]; }
    virtual char const * keyword() const { return "alien"; }
};

static void tst6() {
    std::cout << "=================\n";
    frontend f;
    environment env;
    env.add_neutral_object(new alien_cell());
    formatter fmt = mk_pp_formatter(f);
    std::cout << fmt(env) << "\n";
}

static formatter mk() {
    frontend f;
    return mk_pp_formatter(f);
}

static void tst7() {
    formatter fmt = mk();
    std::cout << fmt(And(Const("x"), Const("y"))) << "\n";
}

int main() {
    tst1();
    tst2();
    tst3();
    tst4();
    tst5();
    tst6();
    tst7();
    return has_violations() ? 1 : 0;
}
<commit_msg>Add another pp example<commit_after>/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.

Author: Leonardo de Moura
*/
#include "frontend.h"
#include "environment.h"
#include "operator_info.h"
#include "kernel_exception.h"
#include "builtin.h"
#include "pp.h"
#include "test.h"
using namespace lean;

static void tst1() {
    frontend f;
    f.add_uvar("tst");
    frontend c = f.mk_child();
    lean_assert(c.get_uvar("tst") == f.get_uvar("tst"));
    lean_assert(f.get_environment().has_children());
}

static void tst2() {
    operator_info op1 = infixl(name("and"), 10);
    operator_info op2 = infixr(name("implies"), 20);
    lean_assert(op1.get_precedence() == 10);
    lean_assert(op1.get_fixity() == fixity::Infixl);
    op1.add_internal_name(name{"Bool","And"});
    operator_info op3 = infixl(name("+"), 10);
    op3.add_internal_name(name{"Int", "plus"});
    op3.add_internal_name(name{"Real", "plus"});
    std::cout << op3.get_internal_names() << "\n";
    lean_assert(length(op3.get_internal_names()) == 2);
    operator_info op4 = op3.copy();
    op4.add_internal_name(name{"Complex", "plus"});
    std::cout << op4.get_internal_names() << "\n";
    lean_assert(length(op3.get_internal_names()) == 2);
    lean_assert(length(op4.get_internal_names()) == 3);
    lean_assert(op4.get_fixity() == fixity::Infixl);
    lean_assert(op4.get_op_name() == op3.get_op_name());
    lean_assert(prefix("tst", 20).get_fixity() == fixity::Prefix);
    lean_assert(postfix("!", 20).get_fixity() == fixity::Postfix);
    lean_assert(length(mixfixl({"if", "then", "else"}, 10).get_op_name_parts()) == 3);
    lean_assert(mixfixc({"if", "then", "else", "endif"}, 10).get_fixity() == fixity::Mixfixc);
    std::cout << mixfixc({"if", "then", "else", "endif"}, 10).get_op_name_parts() << "\n";
    std::cout << op4 << "\n";
    std::cout << op2 << "\n";
    std::cout << mixfixc({"if", "then", "else", "endif"}, 10) << "\n";
}

static void tst3() {
    frontend f;
    std::cout << "====================\n";
    std::cout << f << "\n";
}

static void tst4() {
    frontend f;
    formatter fmt = mk_pp_formatter(f);
    context c;
    c = extend(c, "x", Bool);
    c = extend(c, "y", Bool);
    c = extend(c, "x", Bool, Var(1));
    c = extend(c, "x", Bool, Var(2));
    c = extend(c, "z", Bool, Var(1));
    c = extend(c, "x", Bool, Var(4));
    std::cout << fmt(c) << "\n";
}

static void tst5() {
    std::cout << "=================\n";
    frontend f;
    formatter fmt = mk_pp_formatter(f);
    f.add_var("A", Type());
    f.add_var("x", Const("A"));
    object const & obj = f.find_object("x");
    lean_assert(obj);
    lean_assert(obj.get_name() == "x");
    std::cout << fmt(obj) << "\n";
    object const & obj2 = f.find_object("y");
    lean_assert(!obj2);
    try {
        f.get_object("y");
        lean_unreachable();
    } catch (unknown_name_exception & ex) {
        std::cout << ex.what() << " " << ex.get_name() << "\n";
    }
}

class alien_cell : public neutral_object_cell {
    unsigned m_data[128];
public:
    alien_cell() {}
    unsigned & get_data(unsigned i) { return m_data[i]; }
    virtual char const * keyword() const { return "alien"; }
};

static void tst6() {
    std::cout << "=================\n";
    frontend f;
    environment env;
    env.add_neutral_object(new alien_cell());
    formatter fmt = mk_pp_formatter(f);
    std::cout << fmt(env) << "\n";
}

static formatter mk() {
    frontend f;
    return mk_pp_formatter(f);
}

static void tst7() {
    formatter fmt = mk();
    std::cout << fmt(And(Const("x"), Const("y"))) << "\n";
}

static void tst8() {
    frontend fe;
    formatter fmt = mk_pp_formatter(fe);
    fe.add_infixl("<-$->", 10, const_name(mk_refl_fn()));
    std::cout << fmt(fe.find_object("Trivial")) << "\n";
}

int main() {
    tst1();
    tst2();
    tst3();
    tst4();
    tst5();
    tst6();
    tst7();
    tst8();
    return has_violations() ? 1 : 0;
}
<|endoftext|>


<commit_before>#ifndef CORE_ANIMATIONS_REPEAT_HPP_
#define CORE_ANIMATIONS_REPEAT_HPP_

#include <algorithm>
#include <memory>

#include <core/animation.hpp>

template <typename Value>
class Repeat: public Animation<Value>
{
public:
    Repeat(int counter, AnimationPtr<Value> &&animation):
        m_initialCounter(std::max(0, counter)),
        m_animation(std::move(animation)),
        m_currentCounter(m_initialCounter)
    {}

    virtual Value nextValue(const int32_t deltaTime) override
    {
        if (isFinished()) {
            return m_animation->getCurrentValue();
        }

        if (m_animation->isFinished()) {
            --m_currentCounter;

            if (isFinished()) {
                return m_animation->getCurrentValue();
            } else {
                m_animation->reset(m_animation->getCurrentValue());
                return m_animation->getCurrentValue();
            }
        } else {
            return m_animation->nextValue(deltaTime);
        }
    }

    virtual Value getCurrentValue() const override
    {
        return m_animation->getCurrentValue();
    }

    virtual bool isFinished() const override
    {
        return m_currentCounter <= 0;
    }

    virtual void reset(const Value &value) override
    {
        m_currentCounter = m_initialCounter;
        m_animation->reset(value);
    }

private:
    const int m_initialCounter;
    AnimationPtr<Value> m_animation;
    int m_currentCounter;
};

#endif  // CORE_ANIMATIONS_REPEAT_HPP_
<commit_msg>Use nil animation in repeat combinator (#118)<commit_after>#ifndef CORE_ANIMATIONS_REPEAT_HPP_
#define CORE_ANIMATIONS_REPEAT_HPP_

#include <algorithm>
#include <memory>

#include <core/animation.hpp>
#include <core/animations/nil.hpp>

template <typename Value>
class Repeat: public Animation<Value>
{
public:
    Repeat(int counter, AnimationPtr<Value> &&animation):
        m_initialCounter(std::max(0, counter)),
        m_animation(animation ? std::move(animation) : AnimationPtr<Value>(new Nil<Value>())),
        m_currentCounter(m_initialCounter)
    {}

    virtual Value nextValue(const int32_t deltaTime) override
    {
        if (isFinished()) {
            return m_animation->getCurrentValue();
        }

        if (m_animation->isFinished()) {
            --m_currentCounter;

            if (isFinished()) {
                return m_animation->getCurrentValue();
            } else {
                m_animation->reset(m_animation->getCurrentValue());
                return m_animation->getCurrentValue();
            }
        } else {
            return m_animation->nextValue(deltaTime);
        }
    }

    virtual Value getCurrentValue() const override
    {
        return m_animation->getCurrentValue();
    }

    virtual bool isFinished() const override
    {
        return m_currentCounter <= 0;
    }

    virtual void reset(const Value &value) override
    {
        m_currentCounter = m_initialCounter;
        m_animation->reset(value);
    }

private:
    const int m_initialCounter;
    AnimationPtr<Value> m_animation;
    int m_currentCounter;
};

#endif  // CORE_ANIMATIONS_REPEAT_HPP_
<|endoftext|>


<commit_before>#line 2 "togo/gfx/renderer/types.hpp"
/**
@copyright MIT license; see @ref index or the accompanying LICENSE file.
*/

#pragma once

#include <togo/config.hpp>
#include <togo/types.hpp>
#include <togo/hash/hash.hpp>
#include <togo/memory/types.hpp>
#include <togo/gfx/types.hpp>
#include <togo/collection/fixed_array.hpp>

#if (TOGO_CONFIG_RENDERER == TOGO_RENDERER_OPENGL)
	#include <togo/gfx/renderer/opengl.hpp>
#endif

namespace togo {
namespace gfx {

template<class R>
union ResourceSlot;

template<class R>
struct ResourceHole {
	gfx::ResourceID<R> id;
	gfx::ResourceSlot<R>* next;
};

template<class R>
union ResourceSlot {
	gfx::ResourceID<R> id;
	R res;
	gfx::ResourceHole<R> hole;
};

namespace {
	constexpr u32 npot_s(u32 x, unsigned b) {
		return x | (x >> b);
	}

	constexpr u32 npot(u32 x) {
		// NB: Does not kindly handle x = 0
		/*
			--x;
			x |= x >> 1;
			x |= x >> 2;
			x |= x >> 4;
			x |= x >> 8;
			x |= x >> 16;
			++x;
		*/
		return 1 + npot_s(npot_s(npot_s(npot_s(npot_s(x - 1, 1), 2), 4), 8), 16);
	}
} // anonymous namespace

// N > 0 assumed
template<class R, unsigned N>
struct ResourceArray {
	enum : unsigned {
		NUM_SLOTS = N,
		ID_ADD = npot(NUM_SLOTS),
		INDEX_MASK = ID_ADD - 1,
	};

	u32_fast _num;
	u32 _id_gen;
	gfx::ResourceSlot<R>* _first_hole;
	gfx::ResourceSlot<R> _slots[NUM_SLOTS];

	ResourceArray()
		: _num(0)
		, _id_gen(ID_ADD)
		, _first_hole(_slots)
		, _slots()
	{
		auto* const last = _slots + (NUM_SLOTS - 1);
		for (auto* slot = _slots; slot != last; ++slot) {
			slot->hole.next = slot + 1;
		}
		last->hole.next = nullptr;
	}
};

struct RenderObject {
	// TODO
};

using generator_func_type = void (
	gfx::RenderNode& /*node*/,
	unsigned /*layer_index*/,
	unsigned /*generator_index*/,
	gfx::RenderObject const* /*objects_begin*/,
	gfx::RenderObject const* /*objects_end*/
);

struct GeneratorUnit {
	// TODO: Block for generator-defined configuration. KVS?
	hash32 name_hash;
	hash32 shader;
	hash32 input;
	hash32 output;
	generator_func_type* func;
};

struct Layer {
	enum class Order : u32 {
		back_front,
		front_back,
	};

	hash32 name_hash;
	FixedArray<hash32, 4> rts;
	hash32 dst;
	Order order;
	FixedArray<gfx::GeneratorUnit, TOGO_GFX_LAYER_NUM_GENERATORS> layout;
	FixedArray<char, 32> name;
};

struct Pipe {
	hash32 name_hash;
	FixedArray<gfx::Layer, TOGO_GFX_PIPE_NUM_LAYERS> layers;
	FixedArray<char, 32> name;
};

struct Viewport {
	// TODO: resources
	hash32 name_hash;
	u32 pipe;
	hash32 output_rt;
	hash32 output_dst;
	FixedArray<char, 32> name;
};

struct RenderConfig {
	// TODO: shared_resources
	FixedArray<gfx::Viewport, TOGO_GFX_CONFIG_NUM_VIEWPORTS> viewports;
	FixedArray<gfx::Pipe, TOGO_GFX_CONFIG_NUM_PIPES> pipes;
};

struct RenderCmdKey {
	u64 key;
	void* data;
};

struct RenderNode {
	gfx::RenderCmdKey keys[TOGO_GFX_NODE_NUM_COMMANDS];
	u8 buffer[TOGO_GFX_NODE_BUFFER_SIZE];
};

struct Renderer {
	Allocator* _allocator;

	gfx::RendererImpl _impl;
	gfx::RenderConfig _config;

	gfx::ResourceArray<gfx::VertexBuffer, TOGO_GFX_NUM_VERTEX_BUFFERS> _vertex_buffers;
	gfx::ResourceArray<gfx::IndexBuffer, TOGO_GFX_NUM_INDEX_BUFFERS> _index_buffers;
	gfx::ResourceArray<gfx::Texture, TOGO_GFX_NUM_TEXTURES> _textures;
	gfx::ResourceArray<gfx::Uniform, TOGO_GFX_NUM_UNIFORMS> _uniforms;
	gfx::ResourceArray<gfx::Shader, TOGO_GFX_NUM_PROGRAMS> _shaders;

	gfx::RenderNode _nodes[TOGO_GFX_NUM_NODES];

	~Renderer() = default;
	Renderer(Renderer&&) = default;
	Renderer& operator=(Renderer&&) = default;

	Renderer() = delete;
	Renderer(Renderer const&) = delete;
	Renderer& operator=(Renderer const&) = delete;

	Renderer(
		Allocator& allocator,
		RendererImpl&& impl
	)
		: _allocator(&allocator)
		, _impl(impl)
		, _config()
		, _vertex_buffers()
		, _index_buffers()
		, _textures()
		, _uniforms()
		, _shaders()
		, _nodes()
	{}
};

} // namespace gfx
} // namespace togo
<commit_msg>gfx/renderer: removed generic properties in GeneratorUnit.¹<commit_after>#line 2 "togo/gfx/renderer/types.hpp"
/**
@copyright MIT license; see @ref index or the accompanying LICENSE file.
*/

#pragma once

#include <togo/config.hpp>
#include <togo/types.hpp>
#include <togo/hash/hash.hpp>
#include <togo/memory/types.hpp>
#include <togo/gfx/types.hpp>
#include <togo/collection/fixed_array.hpp>

#if (TOGO_CONFIG_RENDERER == TOGO_RENDERER_OPENGL)
	#include <togo/gfx/renderer/opengl.hpp>
#endif

namespace togo {
namespace gfx {

template<class R>
union ResourceSlot;

template<class R>
struct ResourceHole {
	gfx::ResourceID<R> id;
	gfx::ResourceSlot<R>* next;
};

template<class R>
union ResourceSlot {
	gfx::ResourceID<R> id;
	R res;
	gfx::ResourceHole<R> hole;
};

namespace {
	constexpr u32 npot_s(u32 x, unsigned b) {
		return x | (x >> b);
	}

	constexpr u32 npot(u32 x) {
		// NB: Does not kindly handle x = 0
		/*
			--x;
			x |= x >> 1;
			x |= x >> 2;
			x |= x >> 4;
			x |= x >> 8;
			x |= x >> 16;
			++x;
		*/
		return 1 + npot_s(npot_s(npot_s(npot_s(npot_s(x - 1, 1), 2), 4), 8), 16);
	}
} // anonymous namespace

// N > 0 assumed
template<class R, unsigned N>
struct ResourceArray {
	enum : unsigned {
		NUM_SLOTS = N,
		ID_ADD = npot(NUM_SLOTS),
		INDEX_MASK = ID_ADD - 1,
	};

	u32_fast _num;
	u32 _id_gen;
	gfx::ResourceSlot<R>* _first_hole;
	gfx::ResourceSlot<R> _slots[NUM_SLOTS];

	ResourceArray()
		: _num(0)
		, _id_gen(ID_ADD)
		, _first_hole(_slots)
		, _slots()
	{
		auto* const last = _slots + (NUM_SLOTS - 1);
		for (auto* slot = _slots; slot != last; ++slot) {
			slot->hole.next = slot + 1;
		}
		last->hole.next = nullptr;
	}
};

struct RenderObject {
	// TODO
};

using generator_func_type = void (
	gfx::RenderNode& /*node*/,
	unsigned /*layer_index*/,
	unsigned /*generator_index*/,
	gfx::RenderObject const* /*objects_begin*/,
	gfx::RenderObject const* /*objects_end*/
);

struct GeneratorUnit {
	// TODO: Block for generator-defined configuration. KVS?
	hash32 name_hash;
	generator_func_type* func;
};

struct Layer {
	enum class Order : u32 {
		back_front,
		front_back,
	};

	hash32 name_hash;
	FixedArray<hash32, 4> rts;
	hash32 dst;
	Order order;
	FixedArray<gfx::GeneratorUnit, TOGO_GFX_LAYER_NUM_GENERATORS> layout;
	FixedArray<char, 32> name;
};

struct Pipe {
	hash32 name_hash;
	FixedArray<gfx::Layer, TOGO_GFX_PIPE_NUM_LAYERS> layers;
	FixedArray<char, 32> name;
};

struct Viewport {
	// TODO: resources
	hash32 name_hash;
	u32 pipe;
	hash32 output_rt;
	hash32 output_dst;
	FixedArray<char, 32> name;
};

struct RenderConfig {
	// TODO: shared_resources
	FixedArray<gfx::Viewport, TOGO_GFX_CONFIG_NUM_VIEWPORTS> viewports;
	FixedArray<gfx::Pipe, TOGO_GFX_CONFIG_NUM_PIPES> pipes;
};

struct RenderCmdKey {
	u64 key;
	void* data;
};

struct RenderNode {
	gfx::RenderCmdKey keys[TOGO_GFX_NODE_NUM_COMMANDS];
	u8 buffer[TOGO_GFX_NODE_BUFFER_SIZE];
};

struct Renderer {
	Allocator* _allocator;

	gfx::RendererImpl _impl;
	gfx::RenderConfig _config;

	gfx::ResourceArray<gfx::VertexBuffer, TOGO_GFX_NUM_VERTEX_BUFFERS> _vertex_buffers;
	gfx::ResourceArray<gfx::IndexBuffer, TOGO_GFX_NUM_INDEX_BUFFERS> _index_buffers;
	gfx::ResourceArray<gfx::Texture, TOGO_GFX_NUM_TEXTURES> _textures;
	gfx::ResourceArray<gfx::Uniform, TOGO_GFX_NUM_UNIFORMS> _uniforms;
	gfx::ResourceArray<gfx::Shader, TOGO_GFX_NUM_PROGRAMS> _shaders;

	gfx::RenderNode _nodes[TOGO_GFX_NUM_NODES];

	~Renderer() = default;
	Renderer(Renderer&&) = default;
	Renderer& operator=(Renderer&&) = default;

	Renderer() = delete;
	Renderer(Renderer const&) = delete;
	Renderer& operator=(Renderer const&) = delete;

	Renderer(
		Allocator& allocator,
		RendererImpl&& impl
	)
		: _allocator(&allocator)
		, _impl(impl)
		, _config()
		, _vertex_buffers()
		, _index_buffers()
		, _textures()
		, _uniforms()
		, _shaders()
		, _nodes()
	{}
};

} // namespace gfx
} // namespace togo
<|endoftext|>


<commit_before>/**
 ** \file runner/runner.cc
 ** \brief Implementation of runner::Runner.
 */

//#define ENABLE_DEBUG_TRACES
#include "libport/compiler.hh"

#include <sstream>
#include <libport/foreach.hh>
#include <libport/symbol.hh>

#include "kernel/uconnection.hh"

#include "ast/clone.hh"
#include "object/atom.hh"
#include "object/urbi-exception.hh"
#include "object/idelegate.hh"
#include "runner/runner.hh"

namespace runner
{

/// Address of \a Runner seen as a \c Job (Runner has multiple inheritance).
#define JOB(Runner) static_cast<scheduler::Job*> (Runner)

/// Address of \c this seen as a \c Job (Runner has multiple inheritance).
#define ME JOB (this)

#define AST(Ast) "{{{" << Ast << "}}}"

/// Job echo.
#define JECHO(Title, Ast)                                       \
  ECHO ("job " << ME << ", " Title ": " << AST(Ast))

/** Call this macro at the very beginning of the evaluation of an AST
 * node.  Nodes that should yield are that doing something useful.  For
 * instance in "1;2" we have 3 nodes: two values in a Semicolon.  The
 * Semicolon does not do anything useful, it simply recurses in its
 * left hand side.  However, the "1" does something useful: it calculates
 * a value (even though calculating the constant "1" is somewhat trivial),
 * so it should most probably start with a YIELD.
 *
 * The Ast is expected to be named "e".  */
#define YIELD()                                 \
  do                                            \
  {                                             \
    ECHO ("job " << ME << " yielding on AST: "  \
          << &e << ' ' << AST(e));              \
    yield ();                                   \
  } while (0)

#define CATCH_FLOW_EXCEPTION(Type, Keyword, Error)              \
  catch (Type flow_exception)                                   \
  {                                                             \
    if (e.toplevel_get ())                                      \
    {                                                           \
      object::PrimitiveError error(Keyword, Error);             \
      error.location_set(flow_exception.location_get());        \
      raise_error_(error);                                      \
      continue;                                                 \
    }                                                           \
    else                                                        \
      throw;                                                    \
  }

  void
  Runner::raise_error_ (const object::UrbiException& ue)
  {
    std::ostringstream o;
    o << "!!! " << ue.location_get () << ": " << ue.what ();
    send_message_ ("error", o.str ());
    foreach(ast::Call* c, call_stack_)
    {
      o.str("");
      o << "!!!    called from: " << c->location_get () << ": "
	<< c->name_get ();
      send_message_ ("error", o.str ());
    }
    // Reset the current value: there was an error so whatever value it has,
    // it must not be used.
    current_.reset ();
  }

  void
  Runner::send_message_ (const std::string& tag, const std::string& msg)
  {
    UConnection& c = lobby_->value_get().connection;
    c << UConnection::send (msg.c_str(), tag.c_str()) << UConnection::endl;
  }

  void
  Runner::work ()
  {
    assert (ast_);

    ECHO ("job " << ME << " starting evaluation of AST: " << ast_
          << ' ' << AST(*ast_));
    operator() (*ast_);
  }

  /*---------------------.
  | Regular operator().  |
  `---------------------*/

  void
  Runner::operator() (ast::And& e)
  {
    // lhs will be evaluated in another Runner, while rhs will be evaluated
    // in this one.

    JECHO ("lhs", e.lhs_get ());
    Runner lhs (*this);
    lhs.ast_ = &e.lhs_get ();
    lhs.start_job ();

    PING ();

    JECHO ("rhs", e.rhs_get ());

    eval (e.rhs_get ());

    // Wait for lhs to terminate
    yield_until_terminated (lhs);

    PING ();

    current_ = object::void_class;
  }

  // Apply a function written in Urbi
  object::rObject
  Runner::apply_urbi (rObject scope, const rObject& func,
		      const object::objects_type& args)
  {
    // The called function.
    ast::Function* fn;
    // Effective (evaluated) argument iterator.
    object::objects_type::const_iterator ei;
    // Formal argument iterator.
    ast::symbols_type::const_iterator fi;

    PING ();
    // Create a new object to store the arguments unless a scope has been
    // given for this purpose.
    rObject bound_args;
    if (scope != object::nil_class)
      bound_args = scope;
    else
    {
      bound_args = new object::Object;
      bound_args->locals_set(true);
    }

    // Fetch the called function.
    fn = &func.cast<object::Code> ()->value_get ();

    // Check the arity.
    object::check_arg_count (fn->formals_get().size(), args.size() - 1,
			     "");

    // Bind formal and effective arguments if the caller has not provided
    // a scope. If a scope has been given, it is up to the caller to set
    // it up.
    ei = args.begin();
    if (scope == object::nil_class)
    {
      bound_args->slot_set (libport::Symbol("self"), *ei);
      // self is also the proto of the function outer scope, so that
      // we look for non-local identifiers in the target itself.
      bound_args->proto_add (*ei);
    }

    // Now bind the non-target arguments.
    ++ei;
    for (fi = fn->formals_get().begin();
	 fi != fn->formals_get().end() && ei != args.end();
	 ++fi, ++ei)
      bound_args->slot_set (**fi, *ei);
    ECHO("bound args: " << *bound_args);
    // Change the current context and call. But before, yield() so that
    // an infinite recursion gets a better chance to be detected.
    std::swap(bound_args, locals_);
    YIELD ();

    try {
      current_ = eval (*fn->body_get());
    }
    catch (ast::BreakException& be)
    {
      object::PrimitiveError error("break", "outside a loop");
      error.location_set(be.location_get());
      raise_error_(error);
    }
    catch (ast::ReturnException& re)
    {
      current_ = re.result_get();
    }
    catch (object::UrbiException& ue)
    {
      std::swap(bound_args, locals_);
      throw ue;
    };
    std::swap(bound_args, locals_);

    return current_;
  }

  object::rObject
  Runner::apply (rObject scope, const rObject& func,
                 const object::objects_type& args)
  {
    {
      // Check if any argument is void
      bool first = true;
      foreach (rObject arg, args)
      {
	if (!first && arg == object::void_class)
	  throw object::WrongArgumentType ("");
	first = false;
      }
    }

    switch (func->kind_get ())
    {
      case object::Object::kind_primitive:
        PING ();
        current_ = func.cast<object::Primitive>()->value_get()(*this, args);
	break;
      case object::Object::kind_delegate:
        PING();
        current_ = func.cast<object::Delegate>()->value_get()
          ->operator()(*this, args);
	break;
      case object::Object::kind_code:
        PING ();
	current_ = apply_urbi (scope, func, args);
	break;
      default:
        PING ();
        current_ = func;
	break;
    }

    return current_;
  }

  void
  Runner::operator() (ast::Call& e)
  {
    /*-------------------------.
    | Evaluate the arguments.  |
    `-------------------------*/
    PING ();

    // Iterate over arguments, with a special case for the target.
    ast::exps_type::const_iterator i, i_end;
    i = e.args_get ().begin ();
    i_end = e.args_get ().end ();

    rObject tgt;
    tgt = target(*i);

    // No target?  Abort the call.  This can happen (for instance) when you
    // do a.b () and a does not exist (lookup error).
    if (!tgt)
      return;

    // Gather the arguments, including the target.
    object::objects_type args;
    args.push_back (tgt);
    PING ();
    for (++i; i != i_end; ++i)
    {
      eval (**i);
      passert ("argument without a value: " << **i, current_);
      if (current_ == object::void_class)
      {
	object::WrongArgumentType wt("");
	wt.location_set((*i)->location_get());
	throw wt;
      }
      PING ();
      args.push_back (current_);
    }

    /*---------------------.
    | Decode the message.  |
    `---------------------*/

    rObject val;

    // We may have to run a primitive, or some code.
    try {
      // Ask the target for the handler of the message.
      val = tgt->slot_get (e.name_get ());
    }
    catch (object::UrbiException& ue)
    {
      ue.location_set (e.location_get ());
      raise_error_ (ue);
      current_.reset ();
      return;
    }
    // FIXME: Do we need to issue an error message here?
    if (!val)
      return;
    call_stack_.push_front(&e);
    try {
      apply (object::nil_class, val, args);
    }
    catch (object::UrbiException& ue)
    {
      ue.location_set (e.location_get ());
      throw;
    };
    call_stack_.pop_front();

    // Because while returns 0, we can't have a call that returns 0
    // (a function that runs a while for instance).
    // passert ("no value: " << e, current_);
    ECHO (AST(e) << " result: " << *current_);
  }

  void
  Runner::operator() (ast::Float& e)
  {
    YIELD ();

    current_ = new object::Float (e.value_get());
    ECHO ("result: " << *current_);
  }

  void
  Runner::operator() (ast::Function& e)
  {
    YIELD ();

    PING ();
    current_ = new object::Code (*ast::clone(e));
  }

  void
  Runner::operator() (ast::If& e)
  {
    // Evaluate the test.
    JECHO ("test", e.test_get ());
    operator() (e.test_get());

    YIELD();

    if (IS_TRUE(current_))
    {
      JECHO ("then", e.thenclause_get ());
      operator() (e.thenclause_get());
    }
    else
    {
      JECHO ("else", e.elseclause_get ());
      operator() (e.elseclause_get());
    }
  }

  void
  Runner::operator() (ast::List& e)
  {
    typedef std::list<object::rObject> objects;
    typedef std::list<ast::Exp*> exps;
    // list values
    objects values;
    exps::const_iterator i;
    YIELD ();

    PING ();
    // Evaluate every expression in the list
    // FIXME: parallelized?
    for (i = e.value_get ().begin ();
         i != e.value_get ().end ();
         ++i)
    {
      operator() (**i);
      values.push_back(current_);
    }
    current_ = new object::List (values);
    ECHO ("result: " << *current_);
  }

  void
  Runner::operator() (ast::Nary& e)
  {
    // FIXME: other flavor support.
    ast::exec_exps_type::iterator i;
    current_ = object::void_class;
    for (i = e.children_get().begin(); i != e.children_get().end(); ++i)
    {
      current_.reset ();
      JECHO ("child", *i);
      try {
        operator() (*i);
      }
      catch (object::UrbiException& ue)
      {
        raise_error_ (ue);
        continue;
      }
      CATCH_FLOW_EXCEPTION(ast::BreakException, "break", "outside a loop")
        CATCH_FLOW_EXCEPTION(ast::ReturnException, "return",
                             "outside a function");

      if (e.toplevel_get () && current_.get ())
      {
        ECHO ("toplevel: returning a result to the connection.");
        lobby_->value_get ().connection.new_result (current_);
        current_.reset ();
      }

      /* Allow some time to pass before we execute what follows.  If
         we don't do this, the ;-operator would act almost like the
         |-operator because it would always start to execute its RHS
         immediately.  */
      YIELD ();
    }

    // FIXME: I am very afraid that because of the YIELD above, some
    // command are added right before this clear().  Hence the assert.
    assert (i == e.children_get().end());
    if (e.toplevel_get ())
      e.clear();
  }

  void
  Runner::operator() (ast::Noop&)
  {
    current_.reset ();
  }

  void
  Runner::operator() (ast::Pipe& e)
  {
    // lhs
    JECHO ("lhs", e.lhs_get ());
    operator() (e.lhs_get());

    // rhs:  start the execution immediately.
    JECHO ("rhs", e.rhs_get ());
    operator() (e.rhs_get());
  }

  void
  Runner::operator() (ast::Scope& e)
  {
    // To each scope corresponds a "locals" object which stores the
    // local variables.  It points to the previous current scope to
    // implement lexical scoping.
    rObject locals;
    rObject target;
    locals = new object::Object;
    locals->locals_set(true);
    // FIXME: is this the correct order? depends on getSlot algorithm.
    locals->proto_add (locals_);
    if (e.target_get())
    {
      target = eval(*e.target_get());
      // Be safe, do not inherit from VisibilityScope but copy the slots.
      rObject vscope = object::object_class->slot_get("VisibilityScope");
      locals->slot_set("setSlot", vscope->slot_get("setSlot"));
      locals->slot_set("updateSlot", vscope->slot_get("updateSlot"));
      locals->slot_set("self", target);
      locals->slot_set("__target", target);
      locals->proto_add(target);
    }

    std::swap(locals, locals_);
    super_type::operator()(e.body_get());
    std::swap(locals, locals_);
  }

  void
  Runner::operator() (ast::String& e)
  {
    YIELD ();

    PING ();
    current_ = new object::String(e.value_get());
    ECHO ("result: " << *current_);
  }

  void
  Runner::operator() (ast::Tag&)
  {
    // FIXME: Some code is missing here.
  }

  void
  Runner::operator() (ast::While& e)
  {
    bool broken;

    // Evaluate the test.
    while (true)
    {
      // FIXME: YIELD if second iteration for "while;".

      JECHO ("while test", e.test_get ());
      operator() (e.test_get());
      if (!IS_TRUE(current_))
        break;

      if (e.flavor_get() == ast::flavor_semicolon)
        YIELD();

      JECHO ("while body", e.body_get ());

      broken = false;
      try {
        operator() (e.body_get());
      }
      catch (ast::BreakException&)
      {
        // FIXME: Fix for flavor "," and "&".
        if (e.flavor_get() == ast::flavor_semicolon ||
            e.flavor_get() == ast::flavor_pipe)
          broken = true;
      };
      if (broken)
        break;
    }
    // As far as I know, `while' doesn't return a value in URBI.
    current_.reset ();
  }

  void
  Runner::operator() (ast::Throw& e)
  {
    if (e.kind_get() == ast::break_exception)
      throw ast::BreakException(e.location_get());
    else if (e.kind_get() == ast::return_exception)
    {
      if (e.value_get())
        operator() (*e.value_get());
      else
        current_.reset();
      throw ast::ReturnException(e.location_get(), current_);
    }
  }

  void
  Runner::operator() (ast::Stmt& e)
  {
    JECHO ("expression", e.expression_get ());
    operator() (e.expression_get());
  }

  void
  Runner::operator() (ast::Message& e)
  {
    send_message_(e.tag_get(), e.text_get());
  }

} // namespace runner
<commit_msg>Create a sub-scope to bind arguments even when one is provided by the user<commit_after>/**
 ** \file runner/runner.cc
 ** \brief Implementation of runner::Runner.
 */

//#define ENABLE_DEBUG_TRACES
#include "libport/compiler.hh"

#include <sstream>
#include <libport/foreach.hh>
#include <libport/symbol.hh>

#include "kernel/uconnection.hh"

#include "ast/clone.hh"
#include "object/atom.hh"
#include "object/urbi-exception.hh"
#include "object/idelegate.hh"
#include "runner/runner.hh"

namespace runner
{

/// Address of \a Runner seen as a \c Job (Runner has multiple inheritance).
#define JOB(Runner) static_cast<scheduler::Job*> (Runner)

/// Address of \c this seen as a \c Job (Runner has multiple inheritance).
#define ME JOB (this)

#define AST(Ast) "{{{" << Ast << "}}}"

/// Job echo.
#define JECHO(Title, Ast)                                       \
  ECHO ("job " << ME << ", " Title ": " << AST(Ast))

/** Call this macro at the very beginning of the evaluation of an AST
 * node.  Nodes that should yield are that doing something useful.  For
 * instance in "1;2" we have 3 nodes: two values in a Semicolon.  The
 * Semicolon does not do anything useful, it simply recurses in its
 * left hand side.  However, the "1" does something useful: it calculates
 * a value (even though calculating the constant "1" is somewhat trivial),
 * so it should most probably start with a YIELD.
 *
 * The Ast is expected to be named "e".  */
#define YIELD()                                 \
  do                                            \
  {                                             \
    ECHO ("job " << ME << " yielding on AST: "  \
          << &e << ' ' << AST(e));              \
    yield ();                                   \
  } while (0)

#define CATCH_FLOW_EXCEPTION(Type, Keyword, Error)              \
  catch (Type flow_exception)                                   \
  {                                                             \
    if (e.toplevel_get ())                                      \
    {                                                           \
      object::PrimitiveError error(Keyword, Error);             \
      error.location_set(flow_exception.location_get());        \
      raise_error_(error);                                      \
      continue;                                                 \
    }                                                           \
    else                                                        \
      throw;                                                    \
  }

  void
  Runner::raise_error_ (const object::UrbiException& ue)
  {
    std::ostringstream o;
    o << "!!! " << ue.location_get () << ": " << ue.what ();
    send_message_ ("error", o.str ());
    foreach(ast::Call* c, call_stack_)
    {
      o.str("");
      o << "!!!    called from: " << c->location_get () << ": "
	<< c->name_get ();
      send_message_ ("error", o.str ());
    }
    // Reset the current value: there was an error so whatever value it has,
    // it must not be used.
    current_.reset ();
  }

  void
  Runner::send_message_ (const std::string& tag, const std::string& msg)
  {
    UConnection& c = lobby_->value_get().connection;
    c << UConnection::send (msg.c_str(), tag.c_str()) << UConnection::endl;
  }

  void
  Runner::work ()
  {
    assert (ast_);

    ECHO ("job " << ME << " starting evaluation of AST: " << ast_
          << ' ' << AST(*ast_));
    operator() (*ast_);
  }

  /*---------------------.
  | Regular operator().  |
  `---------------------*/

  void
  Runner::operator() (ast::And& e)
  {
    // lhs will be evaluated in another Runner, while rhs will be evaluated
    // in this one.

    JECHO ("lhs", e.lhs_get ());
    Runner lhs (*this);
    lhs.ast_ = &e.lhs_get ();
    lhs.start_job ();

    PING ();

    JECHO ("rhs", e.rhs_get ());

    eval (e.rhs_get ());

    // Wait for lhs to terminate
    yield_until_terminated (lhs);

    PING ();

    current_ = object::void_class;
  }

  // Apply a function written in Urbi
  object::rObject
  Runner::apply_urbi (rObject scope, const rObject& func,
		      const object::objects_type& args)
  {
    // The called function.
    ast::Function* fn;
    // Effective (evaluated) argument iterator.
    object::objects_type::const_iterator ei;
    // Formal argument iterator.
    ast::symbols_type::const_iterator fi;

    PING ();
    // Create a new object to store the arguments. If a scope has been
    // provided, it is the first parent.
    rObject bound_args = new object::Object;
    bound_args->locals_set(true);
    if (scope != object::nil_class)
      bound_args->proto_add (scope);

    // Fetch the called function.
    fn = &func.cast<object::Code> ()->value_get ();

    // Check the arity.
    object::check_arg_count (fn->formals_get().size(), args.size() - 1,
			     "");

    // Bind formal and effective arguments if the caller has not provided
    // a scope. If a scope has been given, it is up to the caller to set
    // it up.
    ei = args.begin();
    if (scope == object::nil_class)
    {
      bound_args->slot_set (libport::Symbol("self"), *ei);
      // self is also the proto of the function outer scope, so that
      // we look for non-local identifiers in the target itself.
      bound_args->proto_add (*ei);
    }

    // Now bind the non-target arguments.
    ++ei;
    for (fi = fn->formals_get().begin();
	 fi != fn->formals_get().end() && ei != args.end();
	 ++fi, ++ei)
      bound_args->slot_set (**fi, *ei);
    ECHO("bound args: " << *bound_args);
    // Change the current context and call. But before, yield() so that
    // an infinite recursion gets a better chance to be detected.
    std::swap(bound_args, locals_);
    YIELD ();

    try {
      current_ = eval (*fn->body_get());
    }
    catch (ast::BreakException& be)
    {
      object::PrimitiveError error("break", "outside a loop");
      error.location_set(be.location_get());
      raise_error_(error);
    }
    catch (ast::ReturnException& re)
    {
      current_ = re.result_get();
    }
    catch (object::UrbiException& ue)
    {
      std::swap(bound_args, locals_);
      throw ue;
    };
    std::swap(bound_args, locals_);

    return current_;
  }

  object::rObject
  Runner::apply (rObject scope, const rObject& func,
                 const object::objects_type& args)
  {
    {
      // Check if any argument is void
      bool first = true;
      foreach (rObject arg, args)
      {
	if (!first && arg == object::void_class)
	  throw object::WrongArgumentType ("");
	first = false;
      }
    }

    switch (func->kind_get ())
    {
      case object::Object::kind_primitive:
        PING ();
        current_ = func.cast<object::Primitive>()->value_get()(*this, args);
	break;
      case object::Object::kind_delegate:
        PING();
        current_ = func.cast<object::Delegate>()->value_get()
          ->operator()(*this, args);
	break;
      case object::Object::kind_code:
        PING ();
	current_ = apply_urbi (scope, func, args);
	break;
      default:
        PING ();
        current_ = func;
	break;
    }

    return current_;
  }

  void
  Runner::operator() (ast::Call& e)
  {
    /*-------------------------.
    | Evaluate the arguments.  |
    `-------------------------*/
    PING ();

    // Iterate over arguments, with a special case for the target.
    ast::exps_type::const_iterator i, i_end;
    i = e.args_get ().begin ();
    i_end = e.args_get ().end ();

    rObject tgt;
    tgt = target(*i);

    // No target?  Abort the call.  This can happen (for instance) when you
    // do a.b () and a does not exist (lookup error).
    if (!tgt)
      return;

    // Gather the arguments, including the target.
    object::objects_type args;
    args.push_back (tgt);
    PING ();
    for (++i; i != i_end; ++i)
    {
      eval (**i);
      passert ("argument without a value: " << **i, current_);
      if (current_ == object::void_class)
      {
	object::WrongArgumentType wt("");
	wt.location_set((*i)->location_get());
	throw wt;
      }
      PING ();
      args.push_back (current_);
    }

    /*---------------------.
    | Decode the message.  |
    `---------------------*/

    rObject val;

    // We may have to run a primitive, or some code.
    try {
      // Ask the target for the handler of the message.
      val = tgt->slot_get (e.name_get ());
    }
    catch (object::UrbiException& ue)
    {
      ue.location_set (e.location_get ());
      raise_error_ (ue);
      current_.reset ();
      return;
    }
    // FIXME: Do we need to issue an error message here?
    if (!val)
      return;
    call_stack_.push_front(&e);
    try {
      apply (object::nil_class, val, args);
    }
    catch (object::UrbiException& ue)
    {
      ue.location_set (e.location_get ());
      throw;
    };
    call_stack_.pop_front();

    // Because while returns 0, we can't have a call that returns 0
    // (a function that runs a while for instance).
    // passert ("no value: " << e, current_);
    ECHO (AST(e) << " result: " << *current_);
  }

  void
  Runner::operator() (ast::Float& e)
  {
    YIELD ();

    current_ = new object::Float (e.value_get());
    ECHO ("result: " << *current_);
  }

  void
  Runner::operator() (ast::Function& e)
  {
    YIELD ();

    PING ();
    current_ = new object::Code (*ast::clone(e));
  }

  void
  Runner::operator() (ast::If& e)
  {
    // Evaluate the test.
    JECHO ("test", e.test_get ());
    operator() (e.test_get());

    YIELD();

    if (IS_TRUE(current_))
    {
      JECHO ("then", e.thenclause_get ());
      operator() (e.thenclause_get());
    }
    else
    {
      JECHO ("else", e.elseclause_get ());
      operator() (e.elseclause_get());
    }
  }

  void
  Runner::operator() (ast::List& e)
  {
    typedef std::list<object::rObject> objects;
    typedef std::list<ast::Exp*> exps;
    // list values
    objects values;
    exps::const_iterator i;
    YIELD ();

    PING ();
    // Evaluate every expression in the list
    // FIXME: parallelized?
    for (i = e.value_get ().begin ();
         i != e.value_get ().end ();
         ++i)
    {
      operator() (**i);
      values.push_back(current_);
    }
    current_ = new object::List (values);
    ECHO ("result: " << *current_);
  }

  void
  Runner::operator() (ast::Nary& e)
  {
    // FIXME: other flavor support.
    ast::exec_exps_type::iterator i;
    current_ = object::void_class;
    for (i = e.children_get().begin(); i != e.children_get().end(); ++i)
    {
      current_.reset ();
      JECHO ("child", *i);
      try {
        operator() (*i);
      }
      catch (object::UrbiException& ue)
      {
        raise_error_ (ue);
        continue;
      }
      CATCH_FLOW_EXCEPTION(ast::BreakException, "break", "outside a loop")
        CATCH_FLOW_EXCEPTION(ast::ReturnException, "return",
                             "outside a function");

      if (e.toplevel_get () && current_.get ())
      {
        ECHO ("toplevel: returning a result to the connection.");
        lobby_->value_get ().connection.new_result (current_);
        current_.reset ();
      }

      /* Allow some time to pass before we execute what follows.  If
         we don't do this, the ;-operator would act almost like the
         |-operator because it would always start to execute its RHS
         immediately.  */
      YIELD ();
    }

    // FIXME: I am very afraid that because of the YIELD above, some
    // command are added right before this clear().  Hence the assert.
    assert (i == e.children_get().end());
    if (e.toplevel_get ())
      e.clear();
  }

  void
  Runner::operator() (ast::Noop&)
  {
    current_.reset ();
  }

  void
  Runner::operator() (ast::Pipe& e)
  {
    // lhs
    JECHO ("lhs", e.lhs_get ());
    operator() (e.lhs_get());

    // rhs:  start the execution immediately.
    JECHO ("rhs", e.rhs_get ());
    operator() (e.rhs_get());
  }

  void
  Runner::operator() (ast::Scope& e)
  {
    // To each scope corresponds a "locals" object which stores the
    // local variables.  It points to the previous current scope to
    // implement lexical scoping.
    rObject locals;
    rObject target;
    locals = new object::Object;
    locals->locals_set(true);
    // FIXME: is this the correct order? depends on getSlot algorithm.
    locals->proto_add (locals_);
    if (e.target_get())
    {
      target = eval(*e.target_get());
      // Be safe, do not inherit from VisibilityScope but copy the slots.
      rObject vscope = object::object_class->slot_get("VisibilityScope");
      locals->slot_set("setSlot", vscope->slot_get("setSlot"));
      locals->slot_set("updateSlot", vscope->slot_get("updateSlot"));
      locals->slot_set("self", target);
      locals->slot_set("__target", target);
      locals->proto_add(target);
    }

    std::swap(locals, locals_);
    super_type::operator()(e.body_get());
    std::swap(locals, locals_);
  }

  void
  Runner::operator() (ast::String& e)
  {
    YIELD ();

    PING ();
    current_ = new object::String(e.value_get());
    ECHO ("result: " << *current_);
  }

  void
  Runner::operator() (ast::Tag&)
  {
    // FIXME: Some code is missing here.
  }

  void
  Runner::operator() (ast::While& e)
  {
    bool broken;

    // Evaluate the test.
    while (true)
    {
      // FIXME: YIELD if second iteration for "while;".

      JECHO ("while test", e.test_get ());
      operator() (e.test_get());
      if (!IS_TRUE(current_))
        break;

      if (e.flavor_get() == ast::flavor_semicolon)
        YIELD();

      JECHO ("while body", e.body_get ());

      broken = false;
      try {
        operator() (e.body_get());
      }
      catch (ast::BreakException&)
      {
        // FIXME: Fix for flavor "," and "&".
        if (e.flavor_get() == ast::flavor_semicolon ||
            e.flavor_get() == ast::flavor_pipe)
          broken = true;
      };
      if (broken)
        break;
    }
    // As far as I know, `while' doesn't return a value in URBI.
    current_.reset ();
  }

  void
  Runner::operator() (ast::Throw& e)
  {
    if (e.kind_get() == ast::break_exception)
      throw ast::BreakException(e.location_get());
    else if (e.kind_get() == ast::return_exception)
    {
      if (e.value_get())
        operator() (*e.value_get());
      else
        current_.reset();
      throw ast::ReturnException(e.location_get(), current_);
    }
  }

  void
  Runner::operator() (ast::Stmt& e)
  {
    JECHO ("expression", e.expression_get ());
    operator() (e.expression_get());
  }

  void
  Runner::operator() (ast::Message& e)
  {
    send_message_(e.tag_get(), e.text_get());
  }

} // namespace runner
<|endoftext|>