Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Save more on your purchases! discount-offer-chevron-icon
Savings automatically calculated. No voucher code required.
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Events
Videos
Audiobooks
Packt Hub
Free Learning
Arrow right icon
timer SALE ENDS IN
0 Days
:
00 Hours
:
00 Minutes
:
00 Seconds

How-To Tutorials

7018 Articles
article-image-warfare-unleashed-implementing-gameplay
Packt
10 Jul 2013
24 min read
Save for later

Warfare Unleashed Implementing Gameplay

Packt
10 Jul 2013
24 min read
(For more resources related to this topic, see here.) Equipping the entities The SceneNode base class was inherited by the Entity class. Entities are the central part of this chapter. It's all about the interaction between entities of different kinds. Before starting to implement all those interactions, it is reasonable to think about crucial properties our entities need to have. Introducing hitpoints Since, we are preparing our airplanes for the battlefield, we need to provide them with new specific attributes. To our class definition of Entity, we add a new member variable that memorizes the current hitpoints. Hitpoints ( HP ) are a measure for the hull integrity of an entity; the entity is destroyed as soon as the hitpoints reach or fall below zero. In addition to the member variable, we provide member functions that allow the modification of the hitpoints. We do not provide direct write access, however, the hitpoints can be decreased (the plane is damaged) or increased (the plane is repaired). Also, a destroy() function instantly destroys the entity. class Entity : public SceneNode{public:explicit Entity(int hitpoints);void repair(int points);void damage(int points);void destroy();int getHitpoints() const;bool isDestroyed() const;...private:int mHitpoints;...}; The implementation is as expected: repair() adds the specified hitpoints, damage() subtracts them, and destroy() sets them to zero. Storing entity attributes in data tables In our game, there are already two different airplanes with different attributes. For this chapter, we introduce a third one to make the game more interesting. With an increasing amount of new aircraft types, attributes such as speed, hitpoints, used texture, or fire rate may vary strongly among them. We need to think of a way to store those properties in a central place, allowing easy access to them. What we clearly want to avoid are case differentiations in every Aircraft method, since this makes the local logic code less readable, and spreads the attributes across different functions. Instead of if/else cascades or switch statements, we can store the attributes in a central table, and just access the table every time we need an attribute. Let's define the type of such a table entry in the case of an airplane. We choose the simplest way, and have a structure AircraftData with all members public. This type is defined in the file DataTables.hpp. struct AircraftData{int hitpoints;float speed;Textures::ID texture;}; While AircraftData is a single table entry, the whole table is represented as a sequence of entries, namely std::vector<AircraftData>. Next, we write a function that initializes the table for different aircraft types. We begin to define a vector of the correct size (Aircraft::TypeCount is the last enumerator of the enum Aircraft::Type, it contains the number of different aircraft types). Since the enumerators are consecutive and begin at zero, we can use them as indices in our STL container. We thus initialize all the attributes for different airplanes, and eventually return the filled table. std::vector<AircraftData> initializeAircraftData(){std::vector<AircraftData> data(Aircraft::TypeCount);data[Aircraft::Eagle].hitpoints = 100;data[Aircraft::Eagle].speed = 200.f;data[Aircraft::Eagle].texture = Textures::Eagle;data[Aircraft::Raptor].hitpoints = 20;data[Aircraft::Raptor].speed = 80.f;data[Aircraft::Raptor].texture = Textures::Raptor;...return data;} The global function initializeAircraftData() is declared in DataTables.hpp and defined in DataTables.cpp. It is used inside Aircraft.cpp, to initialize a global constant Table. This constant is declared locally in the .cpp file, so only the Aircraft internals can access it. In order to avoid name collisions in other files, we use an anonymous namespace. namespace{const std::vector<AircraftData> Table = initializeAircraftData();} Inside the Aircraft methods, we can access a constant attribute of the own plane type using the member variable mType as index. For example, Table[mType].hitpoints denotes the maximal hitpoints of the current aircraft. Data tables are only the first step of storing gameplay constants. For more flexibility, and to avoid recompiling the application, you can also store these constants externally, for example, in a simple text file or using a specific file format. The application initially loads these files, parses the values, and fills the data tables accordingly. Nowadays, it is very common to load gameplay information from external resources. There are text-based formats such as YAML or XML, as well as, many application-specific text and binary formats. There are also well-known C++ libraries such as Boost.Serialize (www.boost. org) that help with loading and saving data structures from C++. One possibility that has recently gained popularity consists of using script languages, most notably Lua (www.lua.org), in addition to C++. This has the advantage that not only constant data, but dynamic functionality can be outsourced and loaded during runtime. Displaying text We would like to add some text on the display, for example, to show the hitpoints or ammunition of different entities. Since this text information is supposed to be shown next to the entity, it stands to reason to attach it to the corresponding scene node. We therefore, create a TextNode class which inherits SceneNode as shown in the following code: class TextNode : public SceneNode{public:explicit TextNode(const FontHolder& fonts,const std::string& text);void setString(const std::string& text);private:virtual void drawCurrent(sf::RenderTarget& target,sf::RenderStates states) const;private:sf::Text mText;}; The implementation of the functions is not complicated. The SFML class sf::Text provides most of what we need. In the TextNode constructor, we retrieve the font from the resource holder and assign it to the text. TextNode::TextNode(const FontHolder& fonts, const std::string& text){mText.setFont(fonts.get(Fonts::Main));mText.setCharacterSize(20);setString(text);} The function to draw the text nodes just forwards the call to the SFML render target, as you know it from sprites. void TextNode::drawCurrent(sf::RenderTarget& target, sf::RenderStatesstates) const{target.draw(mText, states);} For the interface, mainly the following method is interesting. It assigns a new string to the text node, and automatically adapts to its size. centerOrigin() is a utility function we wrote; it sets the object's origin to its center, which simplifies positioning a lot. void TextNode::setString(const std::string& text){mText.setString(text);centerOrigin(mText);} In the Aircraft constructor, we create a text node and attach it to the aircraft itself. We keep a pointer mHealthDisplay as a member variable and let it point to the attached node. std::unique_ptr<TextNode> healthDisplay(new TextNode(fonts, ""));mHealthDisplay = healthDisplay.get();attachChild(std::move(healthDisplay)); In the method Aircraft::update(), we check for the current hitpoints, and convert them to a string, using our custom toString() function. The text node's string and relative position are set. Additionally, we set the text node's rotation to the negative aircraft rotation, which compensates the rotation in total. We do this in order to have the text always upright, independent of the aircraft's orientation. mHealthDisplay->setString(toString(getHitpoints()) + " HP");mHealthDisplay->setPosition(0.f, 50.f);mHealthDisplay->setRotation(-getRotation()); Creating enemies Enemies are other instances of the Aircraft class. They appear at the top of the screen and move downwards, until they fly past the bottom of the screen. Most properties are the same for the player and enemies, so we only explain the new aircraft functionality. Movement patterns By default, enemies fly downwards in a straight line. But it would be nice if different enemies moved differently, giving the feeling of a very basic artificial intelligence ( AI ). Thus, we introduce specific movement patterns. Such a pattern can be described as a sequence of directions to which the enemy airplane heads. A direction consists of an angle and a distance. struct Direction{Direction(float angle, float distance);float angle;float distance;}; Our data table for aircraft gets a new entry for the sequence of directions as shown in following code: struct AircraftData{int hitpoints;float speed;Textures::ID texture;std::vector<Direction> directions;}; Let's implement a zigzag movement pattern for the Raptor plane. First, it steers for 80 units in 45 degrees direction. Then, the angle changes to -45 degrees, and the plane traverses 160 units back. Last, it moves again 80 units in +45 degrees direction, until it arrives at its original x position. data[Aircraft::Raptor].directions.push_back(Direction( 45, 80));data[Aircraft::Raptor].directions.push_back(Direction(-45, 160));data[Aircraft::Raptor].directions.push_back(Direction( 45, 80)); For the Avenger plane, we use a slightly more complex pattern: it is essentially a zigzag, but between the two diagonal movements, the plane moves straight for 50 units. data[Aircraft::Avenger].directions.push_back(Direction(+45, 50));data[Aircraft::Avenger].directions.push_back(Direction( 0, 50));data[Aircraft::Avenger].directions.push_back(Direction(-45, 100));data[Aircraft::Avenger].directions.push_back(Direction( 0, 50));data[Aircraft::Avenger].directions.push_back(Direction(+45, 50)); The following figure shows the sequence of directions for both planes; the Raptor plane is located on the left, Avenger on the right: This way of defining movement is very simple, yet it enables a lot of possibilities. You can let the planes fly in any direction (also sideward or backwards); you can even approximate curves when using small intervals. Now, we look at the logic we have to implement to follow these movement patterns. To the Aircraft class, we add two member variables: mTravelledDistance, which denotes the distance already travelled for each direction, and mDirectionIndex, to know which direction the plane is currently taking. First, we retrieve the aircraft's movement pattern and store it as a reference to const named directions. We only proceed if there are movement patterns for the current type (otherwise the plane flies straight down). void Aircraft::updateMovementPattern(sf::Time dt){const std::vector<Direction>& directions= Table[mType].directions;if (!directions.empty()){ Second, we check if the current direction has already been passed by the plane (that is, the travelled distance is higher than the direction's distance). If so, the index is advanced to the next direction. The modulo operator allows a cycle; after finishing the last direction, the plane begins again with the first one. float distanceToTravel= directions[mDirectionIndex].distance;if (mTravelledDistance > distanceToTravel){mDirectionIndex= (mDirectionIndex + 1) % directions.size();mTravelledDistance = 0.f;} Now, we have to get a velocity vector out of the angle. First, we turn the angle by 90 degrees (by default, 0 degrees points to the right), but since our planes fly downwards, we work in a rotated coordinate system, such that we can use a minus to toggle between left/right. We also have to convert degrees to radians, using our function toRadian(). The velocity's x component is computed using the cosine of the angle multiplied with the maximal speed; analogue for the y component, where the sine is used. Eventually, the travelled distance is updated: float radians= toRadian(directions[mDirectionIndex].angle + 90.f);float vx = getMaxSpeed() * std::cos(radians);float vy = getMaxSpeed() * std::sin(radians);setVelocity(vx, vy);mTravelledDistance += getMaxSpeed() * dt.asSeconds();}} Note that if the distance to travel is no multiple of the aircraft speed, the plane will fly further than intended. This error is usually small, because there are many logic frames per second, and hardly noticeable, since each enemy will only be in the view for a short time. Spawning enemies It would be good if enemies were initially inactive, and the world created them as soon as they come closer to the player. By doing so, we do not need to process enemies that are relevant in the distant future; the scene graph can concentrate on updating and drawing active enemies. We create a structure nested inside the World class that represents a spawn point for an enemy. struct SpawnPoint{SpawnPoint(Aircraft::Type type, float x, float y);Aircraft::Type type;float x;float y;}; A member variable World::mEnemySpawnPoints of type std::vector<SpawnPoint> holds all future spawn points. As soon as an enemy position enters the battlefield, the corresponding enemy is created and inserted to the scene graph, and the spawn point is removed. The World class member function getBattlefieldBounds(), returns sf::FloatRect to the battlefield area, similar to getViewBounds(). The battlefield area extends the view area by a small rectangle at the top, inside which new enemies spawn before they enter the view. If an enemy's y coordinate lies below the battlefield's top member, the enemy will be created at its spawn point. Since enemies face downwards, they are rotated by 180 degrees. void World::spawnEnemies(){while (!mEnemySpawnPoints.empty()&& mEnemySpawnPoints.back().y> getBattlefieldBounds().top){SpawnPoint spawn = mEnemySpawnPoints.back();std::unique_ptr<Aircraft> enemy(new Aircraft(spawn.type, mTextures, mFonts));enemy->setPosition(spawn.x, spawn.y);enemy->setRotation(180.f);mSceneLayers[Air]->attachChild(std::move(enemy));mEnemySpawnPoints.pop_back();}} Now, let's insert the spawn points. addEnemy() effectively calls mEnemySpawnPoints.push_back(), and interprets the passed coordinates relative to the player's spawn position. After inserting all spawn points, we sort them by their y coordinates. By doing so, spawnEnemies() needs to check only the elements at the end of the sequence instead of iterating through it every time. void World::addEnemies(){addEnemy(Aircraft::Raptor, 0.f, 500.f);addEnemy(Aircraft::Avenger, -70.f, 1400.f);...std::sort(mEnemySpawnPoints.begin(), mEnemySpawnPoints.end(),[] (SpawnPoint lhs, SpawnPoint rhs){return lhs.y < rhs.y;});} Here is an example of the player facing four Avenger enemies. Above each, you see how many hitpoints it has left. Adding projectiles Finally, time to add what makes a game fun. Shooting down stuff is essential for our game. The code to interact with the W orld class is already defined, thanks to the actions in Player and to the existing Entity base class. All that's left is to define the projectiles themselves. We start with the Projectile class. We have normal machine gun bullets and homing missiles represented by the same class. This class inherits from the Entity class and is quite small, since it doesn't have anything special that differentiates it from other entities apart from collision tests, which we will talk about later. class Projectile : public Entity{public:enum Type{AlliedBullet,EnemyBullet,Missile,TypeCount};public:Projectile(Type type,const TextureHolder& textures);void guideTowards(sf::Vector2f position);bool isGuided() const;virtual unsigned int getCategory() const;virtual sf::FloatRect getBoundingRect() const;float getMaxSpeed() const;int getDamage() const;private:virtual void updateCurrent(sf::Time dt,CommandQueue& commands);virtual void drawCurrent(sf::RenderTarget& target,sf::RenderStates states) const;private:Type mType;sf::Sprite mSprite;sf::Vector2f mTargetDirection;}; Nothing fun or exciting here; we add some new helper functions such as the one to guide the missile towards a target. So let's have a quick look at the implementation. You might notice, we use the same data tables that we used in the Aircraft class to store data. Projectile::Projectile(Type type, const TextureHolder& textures): Entity(1), mType(type), mSprite(textures.get(Table[type].texture)){centerOrigin(mSprite);} The constructor simply creates a sprite with the texture we want for the projectile. We will check out the guide function when we actually implement the behavior of missiles. The rest of the functions don't hold anything particularly interesting. Draw the sprite and return a category for the commands and other data needed. To get an overview of the class hierarchy in the scene graph, here is an inheritance diagram of the current scene node types. The data table structures which are directly related to their corresponding entities are shown at the bottom of the following diagram: Firing bullets and missiles So let's try and shoot some bullets in the game. We start with adding two new actions in the Player class: Fire and LaunchMissile. We define the default key bindings for these to be the Space bar and M keys. Player::Player(){// Set initial key bindingsmKeyBinding[sf::Keyboard::Left] = MoveLeft;mKeyBinding[sf::Keyboard::Right] = MoveRight;mKeyBinding[sf::Keyboard::Up] = MoveUp;mKeyBinding[sf::Keyboard::Down] = MoveDown;mKeyBinding[sf::Keyboard::Space] = Fire;mKeyBinding[sf::Keyboard::M] = LaunchMissile;// ...}void Player::initializeActions(){// ...mActionBinding[Fire].action = derivedAction<Aircraft>(std::bind(&Aircraft::fire, _1));mActionBinding[LaunchMissile].action =derivedAction<Aircraft>(std::bind(&Aircraft::launchMissile, _1));} So when we press the keys bound to those two actions, a command will be fired which calls the aircraft's fire() and launchMissile() functions. However, we cannot put the actual code that fires the bullet or missile in those two functions. The reason is, because if we could, we would have no concept of how much time has elapsed. We don't want to fire a projectile for every frame. We want there to be some cool down until the next time we fire a bullet, to accomplish that we need to use the delta time passed in the aircraft's update() function. Instead, we mark what we want to fire by setting the Boolean flags mIsFiring or mIsLaunchingMissile to true in the Aircraft::fire() and the Aircraft::launchMissile() functions, respectively. Then we perform the actual logic in the update() function using commands. In order to make the code clearer to read, we have extracted it to its own function. void Aircraft::checkProjectileLaunch(sf::Time dt, CommandQueue&commands){if (mIsFiring && mFireCountdown <= sf::Time::Zero){commands.push(mFireCommand);mFireCountdown += sf::seconds(1.f / (mFireRateLevel+1));mIsFiring = false;}else if (mFireCountdown > sf::Time::Zero){mFireCountdown -= dt;}if (mIsLaunchingMissile){commands.push(mMissileCommand);mIsLaunchingMissile = false;}} We have a cool down for the bullets. When enough time has elapsed since the last bullet was fired, we can fire another bullet. The actual creation of the bullet is done using a command which we will look at later. After we spawn the bullet, we reset the countdown. Here, we use += instead of =; with a simple assignment, we would discard a little time remainder in each frame, generating a bigger error as time goes by. The time of the countdown is calculated using a member variable mFireCountdown in Aircraft. Like that, we can improve the aircraft's fire rate easily. So if the fire rate level is one, then we can fire a bullet every half a second, increase it to level two, and we get every third of a second. We also have to remember to keep ticking down the countdown member, even if the user is not trying to fire. Otherwise, the countdown would get stuck when the user released the Space bar. Next is the missile launch. We don't need a countdown here, because in the Player class, we made the input an event-based (not real-time based) input. bool Player::isRealtimeAction(Action action){switch (action){case MoveLeft:case MoveRight:case MoveDown:case MoveUp:case Fire:return true;default:return false;}} Since the switch statement does not identify LaunchMissile as a real-time input, the user has to release the M key before he can shoot another missile. The user wants to save his missiles for the moment he needs them. So, let's look at the commands that we perform, in order to actually shoot the projectiles. We define them in the constructor in order to have access to the texture holder. This shows one of the strengths of lambda expressions in C++11. Aircraft::Aircraft(Type type, const TextureHolder& textures){mFireCommand.category = Category::SceneAirLayer;mFireCommand.action =[this, &textures] (SceneNode& node, sf::Time){createBullets(node, textures);};mMissileCommand.category = Category::SceneAirLayer;mMissileCommand.action =[this, &textures] (SceneNode& node, sf::Time){createProjectile(node, Projectile::Missile, 0.f, 0.5f,textures);};} Now, we can pass the texture holder to the projectiles without any extra difficulty, and we don't even have to keep an explicit reference to the resources. This makes the Aircraft class and our code a lot simpler, since the reference does not need to exist in the update() function. The commands are sent to the air layer in the scene graph. This is the node where we want to create our projectiles. The missile is a bit simpler to create than bullets, that's why we call directly Aircraft::createProjectile(). So how do we create bullets then? void Aircraft::createBullets(SceneNode& node, const TextureHolder&textures) const{Projectile::Type type = isAllied()? Projectile::AlliedBullet : Projectile::EnemyBullet;switch (mSpreadLevel){case 1:createProjectile(node, type, 0.0f, 0.5f, textures);break;case 2:createProjectile(node, type, -0.33f, 0.33f, textures);createProjectile(node, type, +0.33f, 0.33f, textures);break;case 3:createProjectile(node, type, -0.5f, 0.33f, textures);createProjectile(node, type, 0.0f, 0.5f, textures);createProjectile(node, type, +0.5f, 0.33f, textures);break;}} For projectiles, we provide different levels of fire spread in order to make the game more interesting. The player can feel that progress is made, and that his aircraft becomes more powerful as he is playing. The function calls createProjectile() just as it was done for the missile. So how do we actually create the projectile and attach it to the scene graph? void Aircraft::createProjectile(SceneNode& node,Projectile::Type type, float xOffset, float yOffset,const TextureHolder& textures) const{std::unique_ptr<Projectile> projectile(new Projectile(type, textures));sf::Vector2f offset(xOffset * mSprite.getGlobalBounds().width,yOffset * mSprite.getGlobalBounds().height);sf::Vector2f velocity(0, projectile->getMaxSpeed());float sign = isAllied() ? -1.f : +1.f;projectile->setPosition(getWorldPosition() + offset * sign);projectile->setVelocity(velocity * sign);node.attachChild(std::move(projectile));} We create the projectile with an offset from the player and a velocity required by the projectile type. Also, depending on if this projectile is shot by an enemy or the player, we will have different directions. We do not want the enemy bullets to go upwards like the player's bullets or the other way around. Implementing gunfire for enemies is now a tiny step; instead of calling fire() when keys are pressed, we just call it always. We do this by adding the following code to the beginning of the checkProjectileLaunch() function: if (!isAllied())fire(); Now we have bullets that fly and split the sky. Homing missiles What would a modern aircraft be if it hadn't got an arsenal of homing missiles? This is where we start to add intelligence to our missiles; they should be capable of seeking enemies autonomously. Let's first look at what we need to implement on the projectile site. For homing missiles, the functions guideTowards() and isGuided(), as well as the variable mTargetDirection are important. Their implementation looks as follows: bool Projectile::isGuided() const{return mType == Missile;}void Projectile::guideTowards(sf::Vector2f position){assert(isGuided());mTargetDirection = unitVector(position - getWorldPosition());} The function unitVector() is a helper we have written. It divides a vector by its length, thus, always returns a vector of length one. The target direction is therefore a unit vector headed towards the target. In the function updateCurrent(), we steer our missile. We change the current missile's velocity by adding small contributions of the target direction vector to it. By doing so, the velocity vector continuously approaches the target direction, having the effect that the missile flies along a curve towards the target. approachRate is a constant that determines, to what extent the target direction contributes to the velocity. newVelocity, which is the weighted sum of the two vectors, is scaled to the maximum speed of the missile. It is assigned to the missile's velocity, and its angle is assigned to the missile's rotation. We use +90 here, because the missile texture points upwards (instead of right). void Projectile::updateCurrent(sf::Time dt,CommandQueue& commands){if (isGuided()){const float approachRate = 200.f;sf::Vector2f newVelocity = unitVector(approachRate* dt.asSeconds() * mTargetDirection + getVelocity());newVelocity *= getMaxSpeed();float angle = std::atan2(newVelocity.y, newVelocity.x);setRotation(toDegree(angle) + 90.f);setVelocity(newVelocity);}Entity::updateCurrent(dt, commands);} Note that there are many possibilities to guide a missile. Steering behaviors define a whole field of AI; they incorporate advanced mechanisms such as evasion, interception, and group behavior. Don't hesitate to search on the internet if you're interested. Now, we have guided the missile to a certain position, but how to retrieve that position? We want our missile to pursuit the closest enemy. For this, we switch from Projectile to the World class, where we write a new function. First, we store all currently active (that is, already spawned and not yet destroyed) enemies in the member variable mActiveEnemies. With the command facility, this task is almost trivial: void World::guideMissiles(){Command enemyCollector;enemyCollector.category = Category::EnemyAircraft;enemyCollector.action = derivedAction<Aircraft>([this] (Aircraft& enemy, sf::Time){if (!enemy.isDestroyed())mActiveEnemies.push_back(&enemy);}); Next, we have to find the nearest enemy for each missile. We set up another command, now for projectiles, that iterates through the active enemies to find the closest one. Here, distance() is a helper function that returns the distance between the centers of two scene nodes. Command missileGuider;missileGuider.category = Category::AlliedProjectile;missileGuider.action = derivedAction<Projectile>([this] (Projectile& missile, sf::Time){// Ignore unguided bulletsif (!missile.isGuided())return;float minDistance = std::numeric_limits<float>::max();Aircraft* closestEnemy = nullptr;FOREACH(Aircraft* enemy, mActiveEnemies){float enemyDistance = distance(missile, *enemy);if (enemyDistance < minDistance){closestEnemy = enemy;minDistance = enemyDistance;}} In case we found a closest enemy, we let the missile chase it. if (closestEnemy)missile.guideTowards(closestEnemy->getWorldPosition());}); After defining the second command, we push both to our queue, and reset the container of active enemies. Remember that the commands are not yet executed, they wait in the queue until they are invoked on the scene graph in World::update(). mCommandQueue.push(enemyCollector);mCommandQueue.push(missileGuider);mActiveEnemies.clear();} That's it, now we are able to fire and forget! The result looks as follows: Picking up some goodies Now we have implemented enemies and projectiles. But even if the player shot enemy airplanes down, and had exciting battles, he wouldn't remark that his success changes anything. You want to give the player the feeling that he is progressing in the game. Usual for this game genre are power-ups that the enemies drop when they are killed. So let's go ahead and implement that in our game. Now this is the same story as with the projectile. Most of the things we need have already been implemented; therefore, this will be quite easy to add. What we want is only an entity that, when the player touches it, applies an effect to the player and disappears. Not much work with our current framework. class Pickup : public Entity{public:enum Type{HealthRefill,MissileRefill,FireSpread,FireRate,TypeCount};public:Pickup(Type type,const TextureHolder& textures);virtual unsigned int getCategory() const;virtual sf::FloatRect getBoundingRect() const;void apply(Aircraft& player) const;protected:virtual void drawCurrent(sf::RenderTarget& target,sf::RenderStates states) const;private:Type mType;sf::Sprite mSprite;}; So, let's start looking at a few interesting parts. As usual, we have a data table, create a sprite and center it, so the constructor looks just as you would expect it. Let's investigate the apply() function, and how the data table is created. In apply(), a function object stored in the table is invoked with player as argument. The initializePickupData() function initializes the function objects, using std::bind() that redirects to the Aircraft member functions. void Pickup::apply(Aircraft& player) const{Table[mType].action(player);}std::vector<PickupData> initializePickupData(){std::vector<PickupData> data(Pickup::TypeCount);data[Pickup::HealthRefill].texture = Textures::HealthRefill;data[Pickup::HealthRefill].action= std::bind(&Aircraft::repair, _1, 25);data[Pickup::MissileRefill].texture = Textures::MissileRefill;data[Pickup::MissileRefill].action= std::bind(&Aircraft::collectMissiles, _1, 3);data[Pickup::FireSpread].texture = Textures::FireSpread;data[Pickup::FireSpread].action= std::bind(&Aircraft::increaseSpread, _1);data[Pickup::FireRate].texture = Textures::FireRate;data[Pickup::FireRate].action= std::bind(&Aircraft::increaseFireRate, _1);return data;} The pickups call already defined functions on the player aircraft that let us modify its state. These functions may repair it, refill it with missiles, or improve its firepower. It's nice when things just work out of the box. That's how the scene looks when two pickups (health and fire rate) are floating in the air. You may notice that the player's Eagle plane shoots two bullets at once, which is the result of a previously collected fire spread pickup.
Read more
  • 0
  • 0
  • 7272

article-image-data-driven-design
Packt
10 Jul 2013
21 min read
Save for later

Data-driven Design

Packt
10 Jul 2013
21 min read
(For more resources related to this topic, see here.) Loading XML files I have chosen to use XML files because they are so easy to parse. We are not going to write our own XML parser, rather we will use an open source library called TinyXML. TinyXML was written by Lee Thomason and is available under the zlib license from http://sourceforge.net/projects/tinyxml/. Once downloaded the only setup we need to do is to include a few of the files in our project: tinyxmlerror.cpp tinyxmlparser.cpp tinystr.cpp tinystr.h tinyxml.cpp tinyxml.h Also, at the top of tinyxml.h, add this line of code: #define TIXML_USE_STL By doing this we ensure that we are using the STL versions of the TinyXML functions. We can now go through a little of how an XML file is structured. It's actually fairly simple and we will only give a brief overview to help you get up to speed with how we will use it. Basic XML structure Here is a basic XML file: <?xml version="1.0" ?> <ROOT> <ELEMENT> </ELEMENT> </ROOT> The first line of the file defines the format of the XML file. The second line is our Root element; everything else is a child of this element. The third line is the first child of the root element. Now let's look at a slightly more complicated XML file: <?xml version="1.0" ?> <ROOT> <ELEMENTS> <ELEMENT>Hello,</ELEMENT> <ELEMENT> World!</ELEMENT> </ELEMENTS> </ROOT> As you can see we have now added children to the first child element. You can nest as many children as you like. But without a good structure, your XML file may become very hard to read. If we were to parse the above file, here are the steps we would take: Load the XML file. Get the root element, <ROOT>. Get the first child of the root element, <ELEMENTS>. For each child, <ELEMENT> of <ELEMENTS>, get the content. Close the file. Another useful XML feature is the use of attributes. Here is an example: <ROOT> <ELEMENTS> <ELEMENT text="Hello,"/> <ELEMENT text=" World!"/> </ELEMENTS> </ROOT> We have now stored the text we want in an attribute named text. When this file is parsed, we would now grab the text attribute for each element and store that instead of the content between the <ELEMENT></ELEMENT> tags. This is especially useful for us as we can use attributes to store lots of different values for our objects. So let's look at something closer to what we will use in our game: <?xml version="1.0" ?> <STATES> <!--The Menu State--> <MENU> <TEXTURES> <texture filename="button.png" ID="playbutton"/> <texture filename="exit.png" ID="exitbutton"/> </TEXTURES> <OBJECTS> <object type="MenuButton" x="100" y="100" width="400" height="100" textureID="playbutton"/> <object type="MenuButton" x="100" y="300" width="400" height="100" textureID="exitbutton"/> </OBJECTS> </MENU> <!--The Play State--> <PLAY> </PLAY> <!-- The Game Over State --> <GAMEOVER> </GAMEOVER> </STATES> This is slightly more complex. We define each state in its own element and within this element we have objects and textures with various attributes. These attributes can be loaded in to create the state. With this knowledge of XML you can easily create your own file structures if what we cover within this book is not to your needs. Implementing Object Factories We are now armed with a little XML knowledge but before we move forward, we are going to take a look at Object Factories. An object factory is a class that is tasked with the creation of our objects. Essentially, we tell the factory the object we would like it to create and it goes ahead and creates a new instance of that object and then returns it. We can start by looking at a rudimentary implementation: GameObject* GameObjectFactory::createGameObject(ID id) { switch(id) { case "PLAYER": return new Player(); break; case "ENEMY": return new Enemy(); break; // lots more object types } } This function is very simple. We pass in an ID for the object and the factory uses a big switch statement to look it up and return the correct object. Not a terrible solution but also not a particularly good one, as the factory will need to know about each type it needs to create and maintaining the switch statement for many different objects would be extremely tedious. We want this factory not to care about which type we ask for. It shouldn't need to know all of the specific types we want it to create. Luckily this is something that we can definitely achieve. Using Distributed Factories Through the use of Distributed Factories we can make a generic object factory that will create any of our types. Distributed factories allow us to dynamically maintain the types of objects we want our factory to create, rather than hard code them into a function (like in the preceding simple example). The approach we will take is to have the factory contain std::map that maps a string (the type of our object) to a small class called Creator whose only purpose is the creation of a specific object. We will register a new type with the factory using a function that takes a string (the ID) and a Creator class and adds them to the factory's map. We are going to start with the base class for all the Creator types. Create GameObjectFactory.h and declare this class at the top of the file. #include <string> #include <map> #include "GameObject.h" class BaseCreator { public: virtual GameObject* createGameObject() const = 0; virtual ~BaseCreator() {} }; We can now go ahead and create the rest of our factory and then go through it piece by piece. class GameObjectFactory { public: bool registerType(std::string typeID, BaseCreator* pCreator) { std::map<std::string, BaseCreator*>::iterator it = m_creators.find(typeID); // if the type is already registered, do nothing if(it != m_creators.end()) { delete pCreator; return false; } m_creators[typeID] = pCreator; return true; } GameObject* create(std::string typeID) { std::map<std::string, BaseCreator*>::iterator it = m_creators.find(typeID); if(it == m_creators.end()) { std::cout << "could not find type: " << typeID << "n"; return NULL; } BaseCreator* pCreator = (*it).second; return pCreator->createGameObject(); } private: std::map<std::string, BaseCreator*> m_creators; }; This is quite a small class but it is actually very powerful. We will cover each part separately starting with std::map m_creators. std::map<std::string, BaseCreator*> m_creators; This map holds the important elements of our factory, the functions of the class essentially either add or remove from this map. This becomes apparent when we look at the registerType function: bool registerType(std::string typeID, BaseCreator* pCreator) This function takes the ID we want to associate the object type with (as a string), and the creator object for that class. The function then attempts to find the type using the std::mapfind function: std::map<std::string, BaseCreator*>::iterator it = m_creators.find(typeID); If the type is found then it is already registered. The function then deletes the passed in pointer and returns false: if(it != m_creators.end()) { delete pCreator; return false; } If the type is not already registered then it can be assigned to the map and then true is returned: m_creators[typeID] = pCreator; return true; } As you can see, the registerType function is actually very simple; it is just a way to add types to the map. The create function is very similar: GameObject* create(std::string typeID) { std::map<std::string, BaseCreator*>::iterator it = m_creators.find(typeID); if(it == m_creators.end()) { std::cout << "could not find type: " << typeID << "n"; return 0; } BaseCreator* pCreator = (*it).second; return pCreator->createGameObject(); } The function looks for the type in the same way as registerType does, but this time it checks whether the type was not found (as opposed to found). If the type is not found we return 0, and if the type is found then we use the Creator object for that type to return a new instance of it as a pointer to GameObject. It is worth noting that the GameObjectFactory class should probably be a singleton. We won't cover how to make it a singleton in this article. Try implementing it yourself or see how it is implemented in the source code download. Fitting the factory into the framework With our factory now in place, we can start altering our GameObject classes to use it. Our first step is to ensure that we have a Creator class for each of our objects. Here is one for Player: class PlayerCreator : public BaseCreator { GameObject* createGameObject() const { return new Player(); } }; This can be added to the bottom of the Player.h file. Any object we want the factory to create must have its own Creator implementation. Another addition we must make is to move LoaderParams from the constructor to their own function called load. This stops the need for us to pass the LoaderParams object to the factory itself. We will put the load function into the GameObject base class, as we want every object to have one. class GameObject { public: virtual void draw()=0; virtual void update()=0; virtual void clean()=0; // new load function virtual void load(const LoaderParams* pParams)=0; protected: GameObject() {} virtual ~GameObject() {} }; Each of our derived classes will now need to implement this load function. The SDLGameObject class will now look like this: SDLGameObject::SDLGameObject() : GameObject() { } voidSDLGameObject::load(const LoaderParams *pParams) { m_position = Vector2D(pParams->getX(),pParams->getY()); m_velocity = Vector2D(0,0); m_acceleration = Vector2D(0,0); m_width = pParams->getWidth(); m_height = pParams->getHeight(); m_textureID = pParams->getTextureID(); m_currentRow = 1; m_currentFrame = 1; m_numFrames = pParams->getNumFrames(); } Our objects that derive from SDLGameObject can use this load function as well; for example, here is the Player::load function: Player::Player() : SDLGameObject() { } void Player::load(const LoaderParams *pParams) { SDLGameObject::load(pParams); } This may seem a bit pointless but it actually saves us having to pass through LoaderParams everywhere. Without it, we would need to pass LoaderParams through the factory's create function which would then in turn pass it through to the Creator object. We have eliminated the need for this by having a specific function that handles parsing our loading values. This will make more sense once we start parsing our states from a file. We have another issue which needs rectifying; we have two classes with extra parameters in their constructors (MenuButton and AnimatedGraphic). Both classes take an extra parameter as well as LoaderParams. To combat this we will add these values to LoaderParams and give them default values. LoaderParams(int x, int y, int width, int height, std::string textureID,int numFrames, int callbackID = 0, int animSpeed = 0) : m_x(x), m_y(y), m_width(width), m_height(height), m_textureID(textureID), m_numFrames(numFrames), m_callbackID(callbackID), m_animSpeed(animSpeed) { } In other words, if the parameter is not passed in, then the default values will be used (0 in both cases). Rather than passing in a function pointer as MenuButton did, we are using callbackID to decide which callback function to use within a state. We can now start using our factory and parsing our states from an XML file. Parsing states from an XML file The file we will be parsing is the following (test.xml in source code downloads): <?xml version="1.0" ?> <STATES> <MENU> <TEXTURES> <texture filename="assets/button.png" ID="playbutton"/> <texture filename="assets/exit.png" ID="exitbutton"/> </TEXTURES> <OBJECTS> <object type="MenuButton" x="100" y="100" width="400" height="100" textureID="playbutton" numFrames="0" callbackID="1"/> <object type="MenuButton" x="100" y="300" width="400" height="100" textureID="exitbutton" numFrames="0" callbackID="2"/> </OBJECTS> </MENU> <PLAY> </PLAY> <GAMEOVER> </GAMEOVER> </STATES> We are going to create a new class that parses our states for us called StateParser. The StateParser class has no data members, it is to be used once in the onEnter function of a state and then discarded when it goes out of scope. Create a StateParser.h file and add the following code: #include <iostream> #include <vector> #include "tinyxml.h" class GameObject; class StateParser { public: bool parseState(const char* stateFile, std::string stateID, std::vector<GameObject*> *pObjects); private: void parseObjects(TiXmlElement* pStateRoot, std::vector<GameObject*> *pObjects); void parseTextures(TiXmlElement* pStateRoot, std::vector<std::string> *pTextureIDs); }; We have three functions here, one public and two private. The parseState function takes the filename of an XML file as a parameter, along with the current stateID value and a pointer to std::vector of GameObject* for that state. The StateParser.cpp file will define this function: bool StateParser::parseState(const char *stateFile, string stateID, vector<GameObject *> *pObjects, std::vector<std::string> *pTextureIDs) { // create the XML document TiXmlDocument xmlDoc; // load the state file if(!xmlDoc.LoadFile(stateFile)) { cerr << xmlDoc.ErrorDesc() << "n"; return false; } // get the root element TiXmlElement* pRoot = xmlDoc.RootElement(); // pre declare the states root node TiXmlElement* pStateRoot = 0; // get this states root node and assign it to pStateRoot for(TiXmlElement* e = pRoot->FirstChildElement(); e != NULL; e = e->NextSiblingElement()) { if(e->Value() == stateID) { pStateRoot = e; } } // pre declare the texture root TiXmlElement* pTextureRoot = 0; // get the root of the texture elements for(TiXmlElement* e = pStateRoot->FirstChildElement(); e != NULL; e = e->NextSiblingElement()) { if(e->Value() == string("TEXTURES")) { pTextureRoot = e; } } // now parse the textures parseTextures(pTextureRoot, pTextureIDs); // pre declare the object root node TiXmlElement* pObjectRoot = 0; // get the root node and assign it to pObjectRoot for(TiXmlElement* e = pStateRoot->FirstChildElement(); e != NULL; e = e->NextSiblingElement()) { if(e->Value() == string("OBJECTS")) { pObjectRoot = e; } } // now parse the objects parseObjects(pObjectRoot, pObjects); return true; } There is a lot of code in this function so it is worth covering in some depth. We will note the corresponding part of the XML file, along with the code we use, to obtain it. The first part of the function attempts to load the XML file that is passed into the function: // create the XML document TiXmlDocument xmlDoc; // load the state file if(!xmlDoc.LoadFile(stateFile)) { cerr << xmlDoc.ErrorDesc() << "n"; return false; } It displays an error to let you know what happened if the XML loading fails. Next we must grab the root node of the XML file: // get the root element TiXmlElement* pRoot = xmlDoc.RootElement(); // <STATES> The rest of the nodes in the file are all children of this root node. We must now get the root node of the state we are currently parsing; let's say we are looking for MENU: // declare the states root node TiXmlElement* pStateRoot = 0; // get this states root node and assign it to pStateRoot for(TiXmlElement* e = pRoot->FirstChildElement(); e != NULL; e = e->NextSiblingElement()) { if(e->Value() == stateID) { pStateRoot = e; } } This piece of code goes through each direct child of the root node and checks if its name is the same as stateID. Once it finds the correct node it assigns it to pStateRoot. We now have the root node of the state we want to parse. <MENU> // the states root node Now that we have a pointer to the root node of our state we can start to grab values from it. First we want to load the textures from the file so we look for the <TEXTURE> node using the children of the pStateRoot object we found before: // pre declare the texture root TiXmlElement* pTextureRoot = 0; // get the root of the texture elements for(TiXmlElement* e = pStateRoot->FirstChildElement(); e != NULL; e = e->NextSiblingElement()) { if(e->Value() == string("TEXTURES")) { pTextureRoot = e; } } Once the <TEXTURE> node is found, we can pass it into the private parseTextures function (which we will cover a little later). parseTextures(pTextureRoot, std::vector<std::string> *pTextureIDs); The function then moves onto searching for the <OBJECT> node and, once found, it passes it into the private parseObjects function. We also pass in the pObjects parameter: // pre declare the object root node TiXmlElement* pObjectRoot = 0; // get the root node and assign it to pObjectRoot for(TiXmlElement* e = pStateRoot->FirstChildElement(); e != NULL; e = e->NextSiblingElement()) { if(e->Value() == string("OBJECTS")) { pObjectRoot = e; } } parseObjects(pObjectRoot, pObjects); return true; } At this point our state has been parsed. We can now cover the two private functions, starting with parseTextures. void StateParser::parseTextures(TiXmlElement* pStateRoot, std::vector<std::string> *pTextureIDs) { for(TiXmlElement* e = pStateRoot->FirstChildElement(); e != NULL; e = e->NextSiblingElement()) { string filenameAttribute = e->Attribute("filename"); string idAttribute = e->Attribute("ID"); pTextureIDs->push_back(idAttribute); // push into list TheTextureManager::Instance()->load(filenameAttribute, idAttribute, TheGame::Instance()->getRenderer()); } } This function gets the filename and ID attributes from each of the texture values in this part of the XML: <TEXTURES> <texture filename="button.png" ID="playbutton"/> <texture filename="exit.png" ID="exitbutton"/> </TEXTURES> It then adds them to TextureManager. TheTextureManager::Instance()->load(filenameAttribute, idAttribute, TheGame::Instance()->getRenderer()); The parseObjects function is quite a bit more complicated. It creates objects using our GameObjectFactory function and reads from this part of the XML file: <OBJECTS> <object type="MenuButton" x="100" y="100" width="400" height="100" textureID="playbutton" numFrames="0" callbackID="1"/> <object type="MenuButton" x="100" y="300" width="400" height="100" textureID="exitbutton" numFrames="0" callbackID="2"/> </OBJECTS> The parseObjects function is defined like so: void StateParser::parseObjects(TiXmlElement *pStateRoot, std::vector<GameObject *> *pObjects) { for(TiXmlElement* e = pStateRoot->FirstChildElement(); e != NULL; e = e->NextSiblingElement()) { int x, y, width, height, numFrames, callbackID, animSpeed; string textureID; e->Attribute("x", &x); e->Attribute("y", &y); e->Attribute("width",&width); e->Attribute("height", &height); e->Attribute("numFrames", &numFrames); e->Attribute("callbackID", &callbackID); e->Attribute("animSpeed", &animSpeed); textureID = e->Attribute("textureID"); GameObject* pGameObject = TheGameObjectFactory::Instance() ->create(e->Attribute("type")); pGameObject->load(new LoaderParams (x,y,width,height,textureID,numFrames,callbackID, animSpeed)); pObjects->push_back(pGameObject); } } First we get any values we need from the current node. Since XML files are pure text, we cannot simply grab ints or floats from the file. TinyXML has functions with which you can pass in the value you want to be set and the attribute name. For example: e->Attribute("x", &x); This sets the variable x to the value contained within attribute "x". Next comes the creation of a GameObject * class using the factory. GameObject* pGameObject = TheGameObjectFactory::Instance()->create(e->Attribute("type")); We pass in the value from the type attribute and use that to create the correct object from the factory. After this we must use the load function of GameObject to set our desired values using the values loaded from the XML file. pGameObject->load(new LoaderParams(x,y,width,height,textureID,numFrames,callbackID)); And finally we push pGameObject into the pObjects array, which is actually a pointer to the current state's object vector. pObjects->push_back(pGameObject); Loading the menu state from an XML file We now have most of our state loading code in place and can make use of this in the MenuState class. First we must do a little legwork and set up a new way of assigning the callbacks to our MenuButton objects, since this is not something we could pass in from an XML file. The approach we will take is to give any object that wants to make use of a callback an attribute named callbackID in the XML file. Other objects do not need this value and LoaderParams will use the default value of 0. The MenuButton class will make use of this value and pull it from its LoaderParams, like so: void MenuButton::load(const LoaderParams *pParams) { SDLGameObject::load(pParams); m_callbackID = pParams->getCallbackID(); m_currentFrame = MOUSE_OUT; } The MenuButton class will also need two other functions, one to set the callback function and another to return its callback ID: void setCallback(void(*callback)()) { m_callback = callback;} int getCallbackID() { return m_callbackID; } Next we must create a function to set callbacks. Any state that uses objects with callbacks will need an implementation of this function. The most likely states to have callbacks are menu states, so we will rename our MenuState class to MainMenuState and make MenuState an abstract class that extends from GameState. The class will declare a function that sets the callbacks for any items that need it and it will also have a vector of the Callback objects as a member; this will be used within the setCallbacks function for each state. class MenuState : public GameState { protected: typedef void(*Callback)(); virtual void setCallbacks(const std::vector<Callback>& callbacks) = 0; std::vector<Callback> m_callbacks; }; The MainMenuState class (previously MenuState) will now derive from this MenuState class. #include "MenuState.h" #include "GameObject.h" class MainMenuState : public MenuState { public: virtual void update(); virtual void render(); virtual bool onEnter(); virtual bool onExit(); virtual std::string getStateID() const { return s_menuID; } private: virtual void setCallbacks(const std::vector<Callback>& callbacks); // call back functions for menu items static void s_menuToPlay(); static void s_exitFromMenu(); static const std::string s_menuID; std::vector<GameObject*> m_gameObjects; }; Because MainMenuState now derives from MenuState, it must of course declare and define the setCallbacks function. We are now ready to use our state parsing to load the MainMenuState class. Our onEnter function will now look like this: bool MainMenuState::onEnter() { // parse the state StateParser stateParser; stateParser.parseState("test.xml", s_menuID, &m_gameObjects, &m_textureIDList); m_callbacks.push_back(0); //pushback 0 callbackID start from 1 m_callbacks.push_back(s_menuToPlay); m_callbacks.push_back(s_exitFromMenu); // set the callbacks for menu items setCallbacks(m_callbacks); std::cout << "entering MenuStaten"; return true; } We create a state parser and then use it to parse the current state. We push any callbacks into the m_callbacks array inherited from MenuState. Now we need to define the setCallbacks function: void MainMenuState::setCallbacks(const std::vector<Callback>& callbacks) { // go through the game objects for(int i = 0; i < m_gameObjects.size(); i++) { // if they are of type MenuButton then assign a callback based on the id passed in from the file if(dynamic_cast<MenuButton*>(m_gameObjects[i])) { MenuButton* pButton = dynamic_cast<MenuButton*>(m_gameObjects[i]); pButton->setCallback(callbacks[pButton->getCallbackID()]); } } } We use dynamic_cast to check whether the object is a MenuButton type; if it is then we do the actual cast and then use the objects callbackID as the index into the callbacks vector and assign the correct function. While this method of assigning callbacks could be seen as not very extendable and could possibly be better implemented, it does have a redeeming feature; it allows us to keep our callbacks inside the state they will need to be called from. This means that we won't need a huge header file with all of the callbacks in. One last alteration we need is to add a list of texture IDs to each state so that we can clear all of the textures that were loaded for that state. Open up GameState.h and we will add a protected variable. protected: std::vector<std::string> m_textureIDList; We will pass this into the state parser in onEnter and then we can clear any used textures in the onExit function of each state, like so: // clear the texture manager for(int i = 0; i < m_textureIDList.size(); i++) { TheTextureManager::Instance()-> clearFromTextureMap(m_textureIDList[i]); } Before we start running the game we need to register our MenuButton type with the GameObjectFactory. Open up Game.cpp and in the Game::init function we can register the type. TheGameObjectFactory::Instance()->registerType("MenuButton", new MenuButtonCreator()); We can now run the game and see our fully data-driven MainMenuState.
Read more
  • 0
  • 0
  • 5698

article-image-making-your-store-look-amazing
Packt
10 Jul 2013
6 min read
Save for later

Making Your Store Look Amazing

Packt
10 Jul 2013
6 min read
(For more resources related to this topic, see here.) Looks are everything on the web. If your store doesn't look enticing and professional to your customers then everything else is a waste. This article looks at how to make your VirtueMart look stunning. There are many different approaches to creating a hot-looking store. The one that is best for you or your client will depend upon your budget and your skill set. The sections in this article will cater to all budgets and skill sets. For example, we will cover the very simple task of finding and installing a free Joomla! template or installing a VirtueMart theme. Then we will look at the pros and cons of using two different professional frameworks namely Warp and Gantry. In the middle of all this, we will also look at the stunningly versatile Artisteer design software that won't quite give you the perfect professional job but does a very fine job of letting you choose just about every aspect of your design without any CSS/coding skills. Removing the Joomla! branding at the footer With each version of Joomla! and VirtueMart being better than the last one in terms of looks and performance, it is not unheard of to launch your store with the default looks of Joomla! and VirtueMart. The least you will probably want to do is remove the Powered by Joomla!® link at the footer of your store. This will make your store appear entirely your own and perhaps have a minor benefit to SEO as well by removing the outbound link. Getting ready Log in to your Joomla! control panel. This section was tested using the Beez_20 template but should work on any template where the same message appears. We will also be using the Firefox web browser search function but again, this is almost identical in other browsers. Identify the message to be removed on the frontend of your site as shown in the following screenshot: How to do it... This is going to be nice and easy so let's get started and perform the following steps: Navigate to Extensions | Template Manager from the main Joomla! drop-down menu as shown in the following screenshot: Now click on the Templates link (it is the one next door to the Styles link) as shown in the following screenshot: Scroll down until you see Beez_20 details and Files click on it as shown in the following screenshot: Now scroll down and click on Edit main page template . Next press Ctrl + F on your keyboard to bring up the Firefox search bar and enter <div id="footer"> as your search term. Firefox will present you with the following code: Delete everything between <p> and </p> both inclusive. Click on Save & Close . How it works... Check your Joomla! home page. We now have a nice clean and empty footer. We can add Joomla! and VirtueMart modules or just leave it empty. Installing a VirtueMart template In this section we will look at how to install a theme to make your store look great with a couple of clicks. There are a few things to consider first. Is your website just a store? That is, are all your pages going to be VirtueMart pages? If the answer is yes then this is definitely the section for you. Alternatively you might just have a few shop pages in amongst an extensive Joomla! based content site. If this is the case then you might be better off installing a Joomla! template and then setting VirtueMart to use that. If this describes your situation then the next section, Installing a Joomla! template is more appropriate for you. And there is a third option as well. You have content pages and a large number of VirtueMart pages. In this situation some experimentation and planning is required. You will either need to choose a Joomla! template that you are happy with for everything or a Joomla! template and a VirtueMart theme which look good together. Or you could use two templates. This last scenario is covered in the Creating and installing a template with Artisteer design software section. Getting ready Find a template which is either free or paid and download the files from the template provider's site (they will be in the form of a single compressed archive) on your computer. How to do it... Installing a VirtueMart template has never been as easy as it is in VirtueMart 2. Perform the following steps for the same: Navigate to Extensions | Extension Manager from the top Joomla! menu. Click on the Browse... button in the Upload Package File area, find and select your template file as shown in the following screenshot: Click on the Upload & Install button and you are done! How it works... The VirtueMart template is now installed. Take a look at your shiny new store. Installing a Joomla! Template As there is clearly something of a supply problem when it comes to VirtueMart-specific free templates, this section will look at installing a regular Joomla! template and using it in your VirtueMart store. Installing a Joomla! template is a very easy thing to do. But if you have never done it before read on. Getting ready Check the resources appendix for a choice of places to get free and paid templates. Download your chosen template on your desktop. It should be in the form of a ZIP file. Log in to your Joomla! admin area and read on. How to do it... This simple section is in two steps. First we upload the template then we set it as the active template. Select Extensions | Extension Manager from the top Joomla! menu. Click on the Browse... button in the Upload Package File area, find and select your template file as shown in the following screenshot: Click on the Upload & Install button. Now select Extensions | Template Manager . Click on the checkbox of the template you just installed and then click on Make Default . How it works... So what we did was to install the template through the usual Joomla! installation mechanism and once the template was installed we simply told Joomla! to use it. That's it. You can now go and assign all your modules to your new template.
Read more
  • 0
  • 0
  • 1859

article-image-understanding-express-routes
Packt
10 Jul 2013
10 min read
Save for later

Understanding Express Routes

Packt
10 Jul 2013
10 min read
(For more resources related to this topic, see here.) What are Routes? Routes are URL schema, which describe the interfaces for making requests to your web app. Combining an HTTP request method (a.k.a. HTTP verb) and a path pattern, you define URLs in your app. Each route has an associated route handler, which does the job of performing any action in the app and sending the HTTP response. Routes are defined using an HTTP verb and a path pattern. Any request to the server that matches a route definition is routed to the associated route handler. Route handlers are middleware functions, which can send the HTTP response or pass on the request to the next middleware in line. They may be defined in the app file or loaded via a Node module. A quick introduction to HTTP verbs The HTTP protocol recommends various methods of making requests to a Web server. These methods are known as HTTP verbs. You may already be familiar with the GET and the POST methods; there are more of them, about which you will learn in a short while. Express, by default, supports the following HTTP request methods that allow us to define flexible and powerful routes in the app: GET POST PUT DELETE HEAD TRACE OPTIONS CONNECT PATCH M-SEARCH NOTIFY SUBSCRIBE UNSUBSCRIBE GET, POST, PUT, DELETE, HEAD, TRACE, OPTIONS, CONNECT, and PATCH are part of the Hyper Text Transfer Protocol (HTTP) specification as drafted by the Internet Engineering Task Force (IETF) and the World Wide Web Consortium (W3C). M-SEARCH, NOTIFY, SUBSCRIBE, and UNSUBSCRIBE are specified by the UPnP Forum. There are some obscure HTTP verbs such as LINK, UNLINK, and PURGE, which are currently not supported by Express and the underlying Node HTTP library. Routes in Express are defined using methods named after the HTTP verbs, on an instance of an Express application: app.get(), app.post(), app.put(), and so on. We will learn more about defining routes in a later section. Even though a total of 13 HTTP verbs are supported by Express, you need not use all of them in your app. In fact, for a basic website, only GET and POST are likely to be used. Revisiting the router middleware This article would be incomplete without revisiting the router middleware. The router middleware is very special middleware. While other Express middlewares are inherited from Connect, router is implemented by Express itself. This middleware is solely responsible for empowering Express with Sinatra-like routes. Connect-inherited middlewares are referred to in Express from the express object (express.favicon(), express.bodyParser(), and so on). The router middleware is referred to from the instance of the Express app (app.router)  To ensure predictability and stability, we should explicitly add router to the middleware stack: app.use(app.router); The router middleware is a middleware system of its own. The route definitions form the middlewares in this stack. Meaning, a matching route can respond with an HTTP response and end the request flow, or pass on the request to the next middleware in line. This fact will become clearer as we work with some examples in the upcoming sections. Though we won't be directly working with the router middleware, it is responsible for running the whole routing show in the background. Without the router middleware, there can be no routes and routing in Express. Defining routes for the app we know how routes and route handler callback functions look like. Here is an example to refresh your memory: app.get('/', function(req, res) { res.send('welcome'); }); Routes in Express are created using methods named after HTTP verbs. For instance, in the previous example, we created a route to handle GET requests to the root of the website. You have a corresponding method on the app object for all the HTTP verbs listed earlier. Let's create a sample application to see if all the HTTP verbs are actually available as methods in the app object: var http = require('http'); var express = require('express'); var app = express(); // Include the router middleware app.use(app.router); // GET request to the root URL app.get('/', function(req, res) { res.send('/ GET OK'); }); // POST request to the root URL app.post('/', function(req, res) { res.send('/ POST OK'); }); // PUT request to the root URL app.put('/', function(req, res) { res.send('/ PUT OK'); }); // PATCH request to the root URL app.patch('/', function(req, res) { res.send('/ PATCH OK'); }); // DELETE request to the root URL app.delete('/', function(req, res) { res.send('/ DELETE OK'); }); // OPTIONS request to the root URL app.options('/', function(req, res) { res.send('/ OPTIONS OK'); }); // M-SEARCH request to the root URL app['m-search']('/', function(req, res) { res.send('/ M-SEARCH OK'); }); // NOTIFY request to the root URL app.notify('/', function(req, res) { res.send('/ NOTIFY OK'); }); // SUBSCRIBE request to the root URL app.subscribe('/', function(req, res) { res.send('/ SUBSCRIBE OK'); }); // UNSUBSCRIBE request to the root URL app.unsubscribe('/', function(req, res) { res.send('/ UNSUBSCRIBE OK'); }); // Start the server http.createServer(app).listen(3000, function() { console.log('App started'); }); We did not include the HEAD method in this example, because it is best left for the underlying HTTP API to handle it, which it already does. You can always do it if you want to, but it is not recommended to mess with it, because the protocol will be broken unless you implement it as specified. The browser address bar isn't capable of making any type of request, except GET requests. To test these routes we will have to use HTML forms or specialized tools. Let's use Postman, a Google Chrome plugin for making customized requests to the server. We learned that route definition methods are based on HTTP verbs. Actually, that's not completely true, there is a method called app.all() that is not based on an HTTP verb. It is an Express-specific method for listening to requests to a route using any request method: app.all('/', function(req, res, next) { res.set('X-Catch-All', 'true'); next(); }); Place this route at the top of the route definitions in the previous example. Restart the server and load the home page. Using a browser debugger tool, you can examine the HTTP header response added to all the requests made to the home page, as shown in the following screenshot: Something similar can be achieved using a middleware. But the app.all() method makes it a lot easier when the requirement is route specified. Route identifiers So far we have been dealing exclusively with the root URL (/) of the app. Let's find out how to define routes for other parts of the app. Routes are defined only for the request path. GET query parameters are not and cannot be included in route definitions. Route identifiers can be string or regular expression objects. String-based routes are created by passing a string pattern as the first argument of the routing method. They support a limited pattern matching capability. The following example demonstrates how to create string-based routes: // Will match /abcd app.get('/abcd', function(req, res) { res.send('abcd'); }); // Will match /acd app.get('/ab?cd', function(req, res) { res.send('ab?cd'); }); // Will match /abbcd app.get('/ab+cd', function(req, res) { res.send('ab+cd'); }); // Will match /abxyzcd app.get('/ab*cd', function(req, res) { res.send('ab*cd'); }); // Will match /abe and /abcde app.get('/ab(cd)?e', function(req, res) { res.send('ab(cd)?e'); }); The characters ?, +, *, and () are subsets of their Regular Expression counterparts.   The hyphen (-) and the dot (.) are interpreted literally by string-based route identifiers. There is another set of string-based route identifiers, which is used to specify named placeholders in the request path. Take a look at the following example: app.get('/user/:id', function(req, res) { res.send('user id: ' + req.params.id); }); app.get('/country/:country/state/:state', function(req, res) { res.send(req.params.country + ', ' + req.params.state); } The value of the named placeholder is available in the req.params object in a property with a similar name. Named placeholders can also be used with special characters for interesting and useful effects, as shown here: app.get('/route/:from-:to', function(req, res) { res.send(req.params.from + ' to ' + req.params.to); }); app.get('/file/:name.:ext', function(req, res) { res.send(req.params.name + '.' + req.params.ext.toLowerCase()); }); The pattern-matching capability of routes can also be used with named placeholders. In the following example, we define a route that makes the format parameter optional: app.get('/feed/:format?', function(req, res) { if (req.params.format) { res.send('format: ' + req.params.format); } else { res.send('default format'); } }); Routes can be defined as regular expressions too. While not being the most straightforward approach, regular expression routes help you create very flexible and powerful route patterns. Regular expression routes are defined by passing a regular expression object as the first parameter to the routing method. Do not quote the regular expression object, or else you will get unexpected results. Using regular expression to create routes can be best understood by taking a look at some examples. The following route will match pineapple, redapple, redaple, aaple, but not apple, and apples: app.get(/.+app?le$/, function(req, res) { res.send('/.+ap?le$/'); }); The following route will match anything with an a in the route name: app.get(/a/, function(req, res) { res.send('/a/'); }); You will mostly be using string-based routes in a general web app. Use regular expression-based routes only when absolutely necessary; while being powerful, they can often be hard to debug and maintain. Order of route precedence Like in any middleware system, the route that is defined first takes precedence over other matching routes. So the ordering of routes is crucial to the behavior of an app. Let's review this fact via some examples. In the following case, http://localhost:3000/abcd will always print "abc*" , even though the next route also matches the pattern: app.get('/abcd', function(req, res) { res.send('abcd'); }); app.get('/abc*', function(req, res) { res.send('abc*'); }); Reversing the order will make it print "abc*": app.get('/abc*', function(req, res) { res.send('abc*'); }); app.get('/abcd', function(req, res) { res.send('abcd'); }); The earlier matching route need not always gobble up the request. We can make it pass on the request to the next handler, if we want to. In the following example, even though the order remains the same, it will print "abc*" this time, with a little modification in the code. Route handler functions accept a third parameter, commonly named next, which refers to the next middleware in line. We will learn more about it in the next section: app.get('/abc*', function(req, res, next) { // If the request path is /abcd, don't handle it if (req.path == '/abcd') { next(); } else { res.send('abc*'); } }); app.get('/abcd', function(req, res) { res.send('abcd'); }); So bear it in mind that the order of route definition is very important in Express. Forgetting this will cause your app behave unpredictably. We will learn more about this behavior in the examples in the next section.
Read more
  • 0
  • 0
  • 6000

article-image-improving-snake-game
Packt
08 Jul 2013
41 min read
Save for later

Improving the Snake Game

Packt
08 Jul 2013
41 min read
The game Two new features were added to this second version of the game. First, we now keep track of the highest score achieved by a player, saving it through local storage. Even if the player closes the browser application, or turns off the computer, that value will still be safely stored in the player's hard drive, and will be loaded when the game starts again. Second, we use session storage to save the game state every time the player eats a fruit in the game, and whenever the player kills the snake. This is used as an extra touch of awesomeness, where after the player loses, we display a snapshot of all the individual level ups the player achieved in that game, as well as a snapshot of when the player hit a wall or run the snake into itself, as shown in the following screenshot: At the end of each game, an image is shown of each moment when the player acquired a level up, as well as a snapshot of when the player eventually died. This images are created through the canvas API (calling the toDataURL function), and the data that composes each image is saved throughout the game, and stored using the web storage API. With a feature such as this in place, we make the game much more fun, and potentially much more social. Imagine how powerful it would be if the player could post, not only his or her high score to their favorite social network website, but also pictures of their game at key moments. Of course, only the foundation of this feature is implemented in this article (in other words, we only take the snapshots of these critical moments in the game). Adding the actual functionality to send that data to a real social network application is left as an exercise for the reader. A general description and demonstration of each of the APIs used in the game are given in the following sections. For an explanation of how each piece of functionality was incorporated into the final game, look at the code section. For the complete source code for this game, check out the book's page from Packt Publishing's website. Web messaging Web messaging allows us to communicate with other HTML document instances, even if they're not in the same domain. For example, suppose our snake game, hosted at http://snake.fun-html5-games.com, is embedded into a social website through iframe (let's say this social website is hosted at http://www.awesome-html5-games.net). When the player achieves a new high score, we want to post that data from the snake game directly into the host page (the page with iframe from which the game is loaded). With the web messaging API, this can be done natively, without the need for any server-side scripting whatsoever. Before web messaging, documents were not allowed to communicate with documents in other domains mostly because of security. Of course, web applications can still be vulnerable to malicious external applications if we just blindly take messages from any application. However, the web messaging API provides some solid security measures to protect the page receiving the message. For example, we can specify the domains that the message is going to, so that other domains cannot intercept the message. On the receiving end, we can also check the origin from whence the message came, thus ignoring messages from any untrusted domains. Finally, the DOM is never directly exposed through this API, providing yet another layer of security. How to use it Similar to web workers, the way in which two or more HTML contexts can communicate through the web messaging API is by registering an event handler for the on-message event, and sending messages out by using the postMessage function: code1 The first step to using the web messaging API is to get a reference to some document with whom we wish to communicate. This can be done by getting the contentWindow property of an iframe reference, or by opening a new window and holding on to that reference. The document that holds this reference is called the parent document, since this is where the communication is initiated. Although a child window can communicate with its parent, this can only happen when and for as long as this relationship holds true. In other words, a window cannot communicate with just any window; it needs a reference to it, either through a parent-child relationship, or through a child-parent relationship. Once the child window has been referenced, the parent can fire messages to its children through the postMessage function. Of course, if the child window hasn't defined a callback function to capture and process the incoming messages, there is little purpose in sending those messages in the first place. Still, the parent has no way of knowing if a child window has defined a callback to process incoming messages, so the best we can do is assume (and hope) that the child window is ready to receive our messages. The parameters used in the postMessage function are fairly similar to the version used in web workers. That is, any JavaScript value can be sent (numbers, strings, Boolean values, object literals, and arrays, including typed arrays). If a function is sent as the first parameter of postMessage (either directly, or as part of an object), the browser will raise a DATA_CLONE_ERR: DOM Exception 25 error. The second parameter is a string, and represents the domain that we allow our message to be received by. This can be an absolute domain, a forward slash (representing the same origin domain as the document sending the message), or a wild card character (*), representing any domain. If the message is received by a domain that doesn't match the second parameter in postMessage, the entire message fails. When receiving the message, the child window first registers a callback on the message event. This function is passed a MessageEvent object, which contains the following attributes: event.data: It returns the data of the message event.origin: It returns the origin of the message, for server-sent events and cross-document messaging event.lastEventId: It returns the last event ID string, for server-sent events event.sourceReturns: It is the WindowProxy of the source window, for cross-document messaging event.portsReturns: It is the MessagePort array sent with the message, for cross-document messaging and channel messaging Source: http://www.w3.org/TR/webmessaging/#messageevent As an example of the sort of things we could use this feature for in the real world, and in terms of game development, imagine being able to play our snake game, but where the snake moves through a couple of windows. How creative is that?! Of course, in terms of being practical, this may not be the best way to play a game, but I find it hard to argue with the fact that this would indeed be a very unique and engaging presentation of an otherwise common game. With the help of the web messaging API, we can set up a snake, where the snake is not constrained to a single window. Imagine the possibilities when we combine this clever API with another very powerful HTML5 feature, which just happens to lend itself incredibly well to games – web sockets. By combining web messaging with web sockets, we could play a game of snake, not only across multiple windows, but also with multiple players at the same time. Perhaps each player would control the snake when it got inside a given window, and all players could see all windows at the same time, even though they are each using a separate computer. The possibilities are endless, really. Surprisingly, the code used to set up a multi-window port of snake is incredibly simple. The basic setup is the same, we have a snake that only moves in one direction at a time. We also have one or more windows where the snake can go. If we store each window in an array, we can calculate which screen the snake needs to be rendered in, given its current position. Finding out which screen the snake is supposed to be in, given its world position, is the trickiest part. For example, imagine that each window is 200 pixels wide. Now, suppose there are three windows opened. Each window's canvas is only 200 pixels wide as well, so when the snake is at position 350, it would be printed too far to the right in all of the canvases. So what we need to do is first determine the total world width (canvas width multiplied by the total number of canvases), calculate which window the snake is at (position/canvas width), then convert the position from world space down to canvas space, given the canvas the snake is in. First, lets define our structures in the parent document. The code for this is as follows: code2 When this script loads, we'll need a way to create new windows, where the snake will be able to move about. This can easily be done with a button that spawns a new window when clicked, then adding that window to our array of frames, so that we can iterate through that array, and tell every window where the snake is. The code for this is as follows: code3 Now, the real magic happens in the following method. All that we'll do is update the snake's position, then tell each window where the snake is. This will be done by converting the snake's position from world coordinates to canvas coordinates (since every canvas has the same width, this is easy to do for every canvas), then telling every window where the snake should be rendered within a canvas. Since that position is valid for every window, we also tell each window individually whether or not they should render the information we're sending them. Only the window that we calculate the snake is in, is told to go ahead and render. code4 That's really all there is to it. The code that makes up all the other windows is the same for all of them. In fact, we only open a bunch of windows pointing to the exact same script. As far as each window is concerned, they are the only window opened. All they do is take a bunch of data through the messaging API, then render that data if the shouldDraw flag is set. Otherwise, they just clear their canvas, and sit tight waiting for further instructions from their parent window. code5 Web storage Before HTML5 came along, the only way web developers had to store data on the client was through cookies. While limited in scope, cookies did what they were meant to, although they had several limitations. For one thing, whenever a cookie was saved to the client, every HTTP request after that included the data for that cookie. This meant that the data was always explicitly exposed, and each of those HTTP requests were heavily laden with extra data that didn't belong there. This is especially inefficient when considering web applications that may need to store relatively large amounts of data. With the new web storage API, these issues have been addressed and satisfied. There are now three different options for client storage, all of which solve a different problem. Keep in mind, however, that any and all data stored in the client is still exposed to the client in plain text, and is therefore not meant for a secure storage solution. These three storage solutions are session storage, local storage, and the IndexedDB NoSQL data store. Session storage allows us to store key-value data pairs that persist until the browser is closed (in other words, until the session finishes). Local storage is similar to session storage in every way, except that the duration that the data persists is longer. Even when a session is closed, data stored in a local storage still persists. That data in local storage is only cleared when the user specifically tells the browser to do so, or when the application itself deletes data from the storage. Finally, IndexedDB is a robust data store that allows us to store custom objects (not including objects that contains functions), then query the database for those objects. Of course, with much robustness comes great complexity. Although having a dedicated NoSQL database built in right into the browser may sound exciting, but don't be fooled. While using IndexedDB can be a fascinating addition to the world of HTML, it is also by no means a trivial task for beginners. Compared to local storage and session storage, IndexedDB has somewhat of a steep learning curve, since it involves mastering some complex database concepts. As mentioned earlier, the only real difference between local storage and session storage is the fact that session storage clears itself whenever the browser closes down. Besides that, everything about the two is exactly the same. Thus, learning how to use both will be a simple experience, since learning one also means learning the other. However, knowing when to use one over the other might take a bit more thinking on your part. For best results, try to focus on the unique characteristics and needs of your own application before deciding which one to use. More importantly, realize that it is perfectly legal to use both storage systems in the same application. The key is to focus on a unique feature, and decide what storage API best suits those specific needs. Both the local storage and session storage objects are instances of the class Storage. The interface defined by the storage class, through which we can interact with these storage objects, is defined as follows (source: Web Storage W3C Candidate Recommendation, December 08, 2011, http://www.w3.org/TR/webstorage/): getItem(key): It returns the current value associated with the given key. If the given key does not exist in the list associated with the object then this method must return null. setItem(key, value): It first checks if a key/value pair with the given key already exists in the list associated with the object. If it does not, then a new key/value pair must be added to the list, with the given key and with its value set to value. If the given key does exist in the list, then it must have its value updated to value. If it couldn't set the new value, the method must throw a QuotaExceededError exception. (Setting could fail if, for example, the user has disabled storage for the site, or if the quota has been exceeded.) removeItem(key): It causes the key/value pair with the given key to be removed from the list associated with the object, if it exists. If no item with that key exists, the method must do nothing. clear(): It automatically causes the list associated with the object to be emptied of all key/value pairs, if there are any. If there are none, then the method must do nothing. key(n): It returns the name of the nth key in the list. The order of keys is user-agent defined, but must be consistent within an object so long as the number of keys doesn't change. (Thus, adding or removing a key may change the order of the keys, but merely changing the value of an existing key must not.) If n is greater than or equal to the number of key/value pairs in the object, then this method must return null. The supported property names on a Storage object are the keys of each key/value pair currently present in the list associated with the object. length: It returns the number of key/value pairs currently present in the list associated with the object. Local storage The local storage mechanism is accessed through a property of the global object, which on browsers is the window object. Thus, we can access the storage property explicitly through window.localStorage, or implicitly as simply localStorage. code28 Since only DOMString values are allowed to be stored in localStorage, any other values other than strings are converted into a string before being stored in localStorage. That is, we can't store arrays, objects, functions, and so on in localStorage. Only plain JavaScript strings are allowed. code6 Now, while this might seem like a limitation to the storage API, this is in fact done by design. If your goal is to store complex data types for later use, localStorage wasn't necessarily designed to solve this problem. In those situations, we have a much more powerful and convenient storage solution, which we'll look at soon (that is, IndexedDB). However, there is a way to store complex data (including arrays, typed arrays, objects, and so on) in localStorage. The key lies in the wonderful JSON data format. Modern browsers have the very handy JSON object available in the global scope, where we can access two important functions, namely JSON.stringify and JSON.parse. With these two methods, we can serialize complex data, store that in localStorage, then unserialize the data retrieved from the storage, and continue using it in the application. code7 While this is a nice little trick, you will notice what can be a major limitation: JSON stringify does not serialize functions. Also, if you pay close attention to the way that JSON.stringify works, you will realize that>Person, the result will be a simple object literal with no constructor or prototype information. Still, given that localStorage was never intended to fill the role of object persistence (but rather, simple key-value string pairs), this should be seen as nothing more than a limited, yet very neat trick. Session storage Since the sessionStorage interface is identical to that of localStorage, there is no reason to repeat all of the information just described. For a more in-depth discussion about sessionStorage, look at the two previous sections, and replace the word "local" with "session". Everything mentioned above that applies to local storage is also true for session storage. Again, the only difference between the two is that any data saved on sessionStorage is erased when the session with the client ends (that is, whenever the browser is shut down). Some examples of how to use sessionStorage will be shown below. In the example, we will attempt to store a value in the sessionStorage if that value doesn't already exist. Remember, when we set a key-value pair to the storage, if that key already exists in the storage, then whatever value was associated with that key will be overwritten. If the key doesn't exist, it gets created automatically. code8 Note that we can also query the sessionStorage object for a specific key using the in operator, which returns a Boolean value shown as follows: code9 Finally, although we can check the total amount of keys in the storage through sessionStorage.length, that by itself may not be very useful if we don't know what all the different keys are. Thankfully, the sessionStorage.key function allows us to get a specific key, through which we can then get a hold of the value stored with that key. code10 Thus, we can query sessionStorage for a key at a given position, and receive the string key representing that key. Then, with the key we can get a hold of the value stored with that key. Note, however, that the order in which items are stored within the sessionStorage object is totally arbitrary. While some browsers may keep the list of stored items sorted alphabetically by key value, this is clearly specified in the HTML5 spec as a decision to be left up to browser makers. As exciting as the web storage API might seem so far, there are cases when our needs might be such that serializing and unserializing data, as we use local or session storage, might not be quite sufficient. For example, imagine we have a few hundred (or perhaps, several thousand) similar records stored in local storage (say we're storing enemy description cards that are part of an RPG game). Think about how you would do the following using local storage: Retrieve, in alphabetical order, the first five records stored Delete all records stored that contain a particular characteristic (such as an enemy that doesn't survive in water, for example) Retrieve up to three records stored that contain a particular characteristic (for example, the enemy has a Hit Point score of 42,000 or more) The point is this: any querying that we may want to make against the data stored in local storage or session storage, must be handled by our own code. In other words, we'd be spending a lot of time and effort writing code just to help us get to some data. Let alone the fact that any complex data stored in local or session storage is converted to literal objects, and any and all functions that were once part of those objects are now gone, unless we write even more code to handle some sort of custom unserializing. In case you have not guessed it by now, IndexedDB solves these and other problems very beautifully. At its heart, IndexedDB is a NoSQL database engine that allows us to store whole objects and index them for fast insertions, deletions, and retrievals. The database system also provides us with a powerful querying engine, so that we can perform very advanced computations on the data that we have persisted. The following figure shows some of the similarities between IndexedDB and a traditional relational database. In relational databases, data is stored as a group of rows within a specific table structure. In IndexedDB, on the other hand, data is grouped in broadly-defined buckets known as data stores. The architecture of IndexedDB is somewhat similar to the popular relational database systems used in most web development projects today. One core difference is that, whereas relational databases store data in a database, which is a collection of related tables, an IndexedDB system groups data in databases, which is a collection of data stores. While conceptually similar, in practice these two architectures are actually quite different. Note If you come from a relational database background, and the concept of databases, tables, columns, and rows makes sense to you, then you're well on your way to becoming an IndexedDB expert. As you'll see, there are some significant distinctions between both systems and methodologies. While you might be tempted to simply replace the words data store with tables, know that the difference between the two concepts extends beyond a name difference. One key feature of data stores is that they don't have any specific schema associated with them. In relational databases, a table is defined by its very particular structure. Each column is specified ahead of time, when the table is first created. Then, every record saved in such a table follows the exact same format. In NoSQL databases (which IndexedDB is a type of), a data store can hold any object, with whatever format they may have. Essentially, this concept would be the same as having a relational database table that has a different schema for each record in it. IDBFactory To get started with IndexedDB, we first need to create a database. This is done through an implementation of IDBFactory, which in the browser, is the window.indexedDB object. Deleting a database is also done through the indexedDB object, as we'll see soon. In order to open a database (or create one if it doesn't exist yet), we simply call the indexedDB.open method, passing in a database name, along with a version number. If no version number is supplied, the default version number of one will be used as shown in the following code snippet: code11 As you'll soon notice, every method for asynchronous requests in IndexedDB (such as indexedDB.open, for example), will return a request object of type IDBRequest, or an implementation of it. Once we have that request object, we can set up callback functions on its properties, which get executed as the various events related to them are fired, as shown in the following code snippet: code12 IDBOpenDBRequest As mentioned in the previous section, once we make an asynchronous request to the IndexedDB API, the immediately returned object will be of type IDBRequest. In the particular case of an open request, the object that is returned to us is of type IDBOpenDBRequest. Two events that we might want to listen to on this object were shown in the preceding code snippet (onerror and onsuccess). There is also a very important event, wherein we can create an object store, which is the foundation of this storage system. This event is the onupgradeneeded (that is, on upgrade needed) event. This will be fired when the database is first created and, as you might expect, whenever the version number used to open the database is higher than the last value used when the database was opened, as shown in the following code: code13 The call to createObjectStore made on the database object takes two parameters. The first is a string representing the name of the object store. This store can be thought of as a table in the world of relational databases. Of course, instead of inserting records into columns from a table, we insert whole objects into the data store. The second parameter is an object defining properties of the data store. One important attribute that this object must define is the keyPath object, which is what makes each object we store unique. The value assigned to this property can be anything we choose. Now, any objects that we persist in this data store must have an attribute with the same name as the one assigned to keyPath. In this example, our objects will need to have an attribute of myKey. If a new object is persisted, it will be indexed by the value of this property. Any additional objects stored that have the same value for myKey will replace any old objects with that same key. Thus, we must provide a unique value for this object every time we want a unique object persisted. Alternatively, we can let the browser provide a unique value for this key for us. Again, comparing this concept to a relational database, we can think of the keyPath object as being the same thing as a unique ID for a particular element. Just as most relational database systems will support some sort of auto increment, so does IndexedDB. To specify that we want auto-incremented values, we simply add the flag to the object store properties object when the data store is first created (or upgraded) as shown in the following code snippet: code14 Now we can persist an object without having to provide a unique value for the property myKey. As a matter of fact, we don't even need to provide this attribute at all as part of any objects we store here. IndexedDB will handle that for us. Take a look at the following diagram: Using Google Chrome's developer tools, we can see all of the databases and data stores we have created for our domain. Note that the primary object key, which has whatever name we give it during the creation of our data store, has IndexedDB-generated values, which, as we have specified, are incremented over the last value. With this simple, yet verbose boilerplate code in place, we can now start using our databases and data stores. From this point on, the actions we take on the database will be done on the individual data store objects, which are accessed through the database objects that created them. IDBTransaction The last general thing we need to remember when dealing with IndexDB, is that every interaction we have with the data store is done inside transactions. If something goes wrong during a transaction, the entire transaction is rolled back, and nothing takes effect. Similarly, if the transaction is successful, IndexedDB will automatically commit the transaction for us, which is a pretty handy bonus. To use transaction, we need to get a reference to our database, then request a transaction for a particular data store. Once we have a reference to a data store, we can perform the various functions related to the data store, such as putting data into it, reading data from it, updating data, and finally, deleting data from a data store. code15 To store an item in our data store we need to follow a couple of steps. Note that if anything goes wrong during this transaction, we simply catch whatever error is thrown by the browser, and execution continues uninterrupted because of the try/catch block. The first step to persisting objects in IndexedDB is to start a transaction. This is done by requesting a transaction object from the database we have opened earlier. A transaction is always related to a particular data store. Also, when requesting a transaction, we can specify what type of transaction we'd like to start. The possible types of transactions in IndexedDB are as follows: readwrite This transaction mode allows for objects to be stored into the data store, retrieved from it, updated, and deleted. In other words, readwrite mode allows for full CRUD functionality. readonly This transaction mode is similar to readwrite, but clearly restricts the interactions with the data store to only reading. Anything that would modify the data store is not allowed, so any attempt to create a new record (in other words, persisting a new object into the data store), update an existing object (in other words, trying to save an object that was already in the data store), or delete an object from the data store will result in the transaction failing, and an exception being raised. versionchange This transaction mode allows us to create or modify an object store or indexes used in the data store. Within a transaction of this mode, we can perform any action or operation, including modifying the structure of the database. Getting elements Simply storing data into a black box is not at all useful if we're not able to retrieve that data at a later point in time. With IndexedDB, this can be done in several different ways. More commonly, the data store where we persist the data is set up with one or more indexes, which keep the objects organized by a particular field. Again, for those accustomed to relational databases, this would be similar to indexing/applying a key to a particular table column. If we want to get to an object, we can query it by its unique ID, or we can search the data store for objects that fit particular characteristics, which we can do through indexed values of that object. To create an index on a data store, we must specify our intentions during the creation of the data store (inside the onupgradeneeded callback when the store is first created, or inside a transaction mode versionchange). The code for this is as follows: code16 In the preceding example, we create an index for the task attribute of our objects. The name of this index can be anything we want, and commonly is the same name as the object property to which it applies. In our case, we simply named it taskIndex. The possible settings we can configure are as follows: unique – if true, an object being stored with a duplicate value for the same attribute is rejected multiEntry – if true, and the indexed attribute is an array, each element will be indexed Note that zero or more indexes can be created for a data store. Just like any other database system, indexing your database/data store can really boost the performance of the storage container. However, just adding indexes for the fun it provides is not a good idea, as the size of your data store will grow accordingly. A good data store design is one where the specific context of the data store with respect to the application is taken into account, and each indexed field is carefully considered. The phrase to keep in mind when designing your data stores is the following: measure it twice, cut it once. Although any object can be saved in a data store (as opposed to a relational database, where the data stored must carefully follow the table structure, as defined by the table's schema), in order to optimize the performance of your application, try to build your data stores with the data that it will store in mind. It is true that any data can be smacked into any data store, but a wise developer considers the data being stored very carefully before committing it to a database. Once the data store is set up, and we have at least one meaningful index, we can start to pull data out of the data store. The easiest way to retrieve objects from a data store is to use an index, and query for a specific object, as shown in the following code: code17 The preceding function attempts to retrieve a single saved object from our data store. The search is made for an object with its task property that matches the task name supplied to the function. If one is found, it will be retrieved from the data store, and passed to the store object's request through the event object passed in to the callback function. If an error occurs in the process (for example, if the index supplied doesn't exist), the onerror event is triggered. Finally, if no objects in the data store match the search criteria, the resulting property passed in through the request parameter object will be null. Now, to search for multiple items, we can take a similar approach, but instead we request an IndexedDBCursor object. A cursor is basically a pointer to a particular result from a result set of zero or more objects. We can use the cursor to iterate through every object in the result set, until the current cursor points at no object (null), indicating that there are no more objects in the result set. code18 You will note a few things with the above code snippet. First, any object that goes into our IndexedDB data store is stripped of its DNA, and only a simple hash is stored in its stead. Thus, if the prototype information of each object we retrieve from the data store is important to the application, we will need to manually reconstruct each object from the data that we get back from the data store. Second, observe that we can filter the subset of the data store that we would like to take out of it. This is done with an IndexedDB Key Range object, which specifies the offset from which to start fetching data. In our case, we specified a lower bound of zero, meaning that the lowest primary key value we want is zero. In other words, this particular query requests all of the records in the data store. Finally, remember that the result from the request is not a single result or an array of results. Instead, all of the results are returned one at a time in the form of a cursor. We can check for the presence of a cursor altogether, then use the cursor if one is indeed present. Then, the way we request the next cursor is by calling the continue() function on the cursor itself. Another way to think of cursors is by imagining a spreadsheet application. Pretend that the 10 objects returned from our request each represent a row in this spreadsheet. So IndexedDB will fetch all 10 of those objects to memory, and send a pointer to the first result through the event.target.result property in the onsuccess callback. By calling cursor.continue(), we simply tell IndexedDB to now give us a reference to the next object in the result set (or, in other words, we ask for the next row in the spreadsheet). This goes on until the tenth object, after which no more objects exist in the result set (again, to go along with the spreadsheet metaphor, after we fetch the last row, the next row after that is null – it doesn't exist). As a result, the data store will call the onsuccess callback, and pass in a null object. If we attempt to read properties in this null reference, as though we were working with a real object returned from the cursor, the browser will throw a null pointer exception. Instead of trying to reconstruct an object from a cursor one property at a time, we could abstract this functionality away in a generic form. Since objects being persisted into the object store can't have any functions, we're not allowed to keep such functionality inside the object itself. However, thanks to JavaScript's ability to build an object from a reference to a constructor function, we can create a very generic object builder function as follows: code19 Deleting elements To remove specific elements from a data store, the same principles involved in retrieving data apply. In fact, the entire process looks fairly identical to retrieving data, only we call the delete function on the object store object. Needless to say, the transaction used in this action must be readwrite, since readonly limits the object so that no changes can be done to it (including deletion). The first way to delete an object is by passing the object's primary key to the delete function. This is shown as follows: code20 The difficulty with this first approach is that we need to know the ID of the object. In some cases, this would involve a prior transaction request where we'd retrieve the object based on some easier to get data. For example, if we want to delete all tasks with the attribute of complete set to true, we'd need to query the data store for those objects first, then use the IDs associated with each result, and use those values in the transaction where the objects are deleted. A second way to remove data from the data store is to simply call clear() on the object store object. Again, the transaction must be set to readwrite. Doing this will obliterate every last object in the data store, even if they're all of different types as shown in the following code snippet: code21 Finally, we can delete multiple records using a cursor. This is similar to the way we retrieve objects. As we iterate through the result set using the cursor, we can simply delete the object at whatever position the cursor is currently on. Upon deletion, the reference from the cursor object is set to null as shown in the following code snippet: code22 This is pretty much the same routine as fetching data. The only detail is that we absolutely need to supply an object's key. The key is the value stored in the object's keyPath attribute, which can be user-provided, or auto-generated. Fortunately for us, the cursor object returns at least two references to this key through the cursor.primaryKey property, as well as through the object's own property that references that value (in our case, we chose the keyPath attribute to be named myKey). The two upgrades we added to this second version of the game are simple, yet they add a lot of value to the game. We added a persistent high score engine, so users can actually keep track of their latest record, and have a sticky record of past successes. We also added a pretty nifty feature that takes a snapshot of the game board each time the player scores, as well as whenever the player ultimately dies out. Once the player dies, we display all of the snapshots we had collected throughout the game, allowing the player to save those images, and possibly share it with his or her friends. Saving the high score The first thing you probably noticed about the previous version of this game was that we had a placeholder for a high score, but that number never changed. Now that we know how to persist data, we can very easily take advantage of this, and persist a player's high score through various games. In a more realistic scenario, we'd probably send the high score data to a backend server, where every time the game is served, we can keep track of the overall high score, and every user playing the game would know about this global score. However, in our situation, the high score is local to a browser only, since none of the persistence APIs (local and session storage, as well as IndexedDB) share data across other browsers, or natively to a remote server. Since we want the high score to still exist in a player's browser even a month from now, after the computer has been powered off (along with the browser, of course) multiple times, storing this high score data on sessionStorage would be silly. We could store this single number either in IndexedDB or in localStorage. Since we don't care about any other information associated with this score (such as the date when the score was achieved, and so on), all we're storing really is just the one number. For this reason, I think localStorage is a much better choice, because it can all be done in as few as 5 lines of code. Using IndexedDB would work, but would be like using a cannon to kill a mosquito: code23 This function is pretty straight forward. The two values we pass it are the actual score to set as the new high score (this value will be both saved to localStorage, as well as displayed to the user), and the HTML element where the value will be shown. First, we retrieve the existing value saved under the key high-score, and convert it to a number. We could have used the function parseInt(), but multiplying a string by a number does the same thing, but with a slightly faster execution. Next, we check if that value evaluated to something real. In other words, if there was no high-score value saved in local storage, then the variable score would have been evaluated to undefined multiplied by one, which is not a number. If there is a value saved with the key high-score, but that value is not something that can be converted into a number (such as a string of letters and such), we know that it is not a valid value. In this case, we set the incoming score as the new high score. This would work out in the case where the current persisted value is invalid, or not there (which would be the case the very first time the game loads). Next, once we have a valid score retried from local storage, we check if the new value is higher than the old, persisted value. If we have a higher score, we persist that value, and display it to the screen. If the new value is not higher than the existing value, we don't persist anything, but display the saved value, since that is the real high score at the time. Taking screenshots of the game This feature is not as trivial as saving the user's high score, but is nonetheless very straightforward to implement. Since we don't care about snapshots that we captured more than one game ago, we'll use sessionStorage to save data from the game, in real time as the player progresses. Behind the scenes, all we do to take these snapshots is save the game state into sessionStorage, then at the end of the game we retrieve all of the pieces that we'd been saving, and reconstruct the game at those points in time into an invisible canvas. We then use the canvas.toDataURL() function to extract that data as an image: code24 Each time the player eats a fruit, we call this function, passing it a reference to the snake (our hero in this game), and the fruit (the goal of this game) objects. What we do is really quite simple: we create an array representing the state of the snake and of the fruit at each event that we capture. Each element in this array is a string representing the serialized array that keeps track of where the fruit was, and where each body part of the snake was located as well. First, we check if this object currently exists in sessionStorage. For the first time we start the game, this object will not yet exist. Thus, we create an object that references those two objects, namely the snake and the fruit object. Next, we stringify the buffers keeping track of the locations of the elements we want to track. Each time we add a new event, we simply append to those two buffers. Of course, if the user closes down the browser, that data will be erased by the browser itself, since that's how sessionStorage works. However, we probably don't want to hold on to data from a previous game, so we also need a way to clear out our own data after each game. code25 Easy enough. All we need is to know the name of the key that we use to hold each element. For our purposes, we simply call the snapshots of the snake eating "eat", and the buffer with the snapshot of the snake dying "die". So before each game starts, we can simply call clearEvent() with those two global key values, and the cache will be cleared a new each time. Next, as each event takes place, we simply call the first function we defined, sending it the appropriate data as shown in the following code snippet: code26 Finally, whenever we wish to display all of these snapshots, we just need to create a separate canvas with the same dimensions as the one used in the game (so that the buffers we saved don't go out of bounds), and draw the buffers to that canvas. The reason we need a separate canvas element is because we don't want to draw on the same canvas that the player can see. This way, the process of producing these snapshots is more seamless and natural. Once each state is drawn, we can extract each image, resize it, and display it back to the user as shown in the following code: code27 Observe that we simply draw the points representing the snake and the fruit into that canvas. All of the other points in the canvas are ignored, meaning that we generate a transparent image. If we want the image to have an actual background color (even if it is just white), we can either call fillRect() over the entire canvas surface before drawing the snake and the fruit, or we can traverse each pixel in the pixelData array from the rendering context, and set the alpha channel to 100 percent opaque. Even if we set a color to each pixel by hand, but leave off the alpha channel, we'd have colorful pixels, but 100 percent transparent. Summary In this article we took a few extra steps into the fascinating world of 2D rendering using the long-awaited canvas API. We took advantage of the canvas' ability to export images to make our game more engaging, and potentially more social. We also made the game more engaging and social by adding a persistence layer on top of the game, whereby we were able to save a player's high score. Two other new powerful features of HTML5, web messaging and IndexedDB, were explored in this article, although there were no uses for these features in this version of the game. The web messaging API provides a mechanism for two or more windows to communicate directly through message passing. The exciting bit is that these windows (or HTML contexts) do not need to be in the same domain. Although this could sound like a security issue, there are several systems in place to ensure that cross-document and cross-domain messaging is secure and efficient. The web storage interface brings with it three distinct solutions for long term data persistence on the client. These are session storage, local storage, and IndexedDB. While IndexedDB is a full-blown, built-in, fully transactional and asynchronous NoSQL object store, local and session storage provide a very simple key-value pair storage for simpler needs. All three of these systems introduce great benefits and gains over the traditional cookie-based data storage, including the fact that the total amount of data that can be persisted in the browser is much greater, and none of the data saved in the user's browser ever travels back and forth between the server and the client through HTTP requests. Resources for Article :   Further resources on this subject: Interface Designing for Games in iOS [Article] Unity 3D Game Development: Don't Be a Clock Blocker [Article] Making Money with Your Game [Article]
Read more
  • 0
  • 0
  • 7653

article-image-installing-drupal
Packt
04 Jul 2013
14 min read
Save for later

Installing Drupal

Packt
04 Jul 2013
14 min read
(For more resources related to this topic, see here.) Assumptions To get Drupal up and running, you will need all of the following: A domain A web host Access to the web host's filesystem or You need a local testing environment, which takes care of the first three things For building sites, either a web host or a local testing environment will meet your needs. A site built on a web-accessible domain can be shared via the Internet, whereas sites built on local test machines will need to be moved to a web host before they can be used for your course.  In these instructions, we are assuming the use of phpMyAdmin, an open source, browser-based tool, for administering your database. A broad range of similar tools exist, and these general instructions can be used with most of these other tools. Information on phpMyAdmin is available at http://www.phpmyadmin.net; information on other browser-based database administration tools can be found at http://en.wikipedia.org/wiki/PhpMyAdmin#Similar_products. The domain The domain is the address on the Web from where people can access your site. If you are building this site as part of your work, you will probably be using the domain associated with your school or organization. If you are hosting this on your own server, you can buy a domain for under US $10.00 a year. Enter purchase domain name in Google, and you will have a plethora of options. The web host Your web host provides you with the server space on which to run your site. Within many schools, your website will be hosted by your school. In other environments, you might need to arrange for your own web host by using a hosting company. In selecting a web host, you need to be sure that they run software that meets or exceeds the recommended software versions. Web server Drupal is developed and tested extensively in an Apache environment. Drupal also runs on other web servers, including Microsoft IIS and Nginx. PHP version Drupal 7 will run on PHP 5.2.5 or higher; however, PHP 5.3 is recommended. The Drupal 8 release will require PHP 5.3.10. MySQL version Drupal 7 will run on MySQL 5.0.15 or higher, and requires the PHP Data Objects ( PDO ) extension for PHP. Drupal 7 has also been tested with MariaDB as a drop-in replacement, and Version 5.1.44 or greater is recommended. PDO is a consistent way for programmers to write code that interacts with the database. You can find out more about PDO and how to install it at http://drupal.org/requirements/pdo. Drupal can technically use any database that PDO supports, but MySQL is by far the most tested and best supported. Third-party modules are required to use Drupal with other database systems. You can find these modules listed at http://drupal.org/project/modules/?f[0]=im_vid_3%3A13158&f[1]=drupal_core%3A103&f[2]=bs_project_sandbox%3A0. FTP and shell access to your web host Your web host should also offer FTP access to your web server. You will need FTP (or SFTP) access in order to upload the Drupal codebase to your web space. Shell access, or SSH access, is not essential for basic site maintenance. However, SSH access can simplify maintaining your site, so contracting with a web host that provides SSH access is recommended. A local testing environment Alternatively, you can set up a local testing environment for your site. This allows you to set up Drupal and other applications on your computer. A local testing environment can be a great tool for learning a piece of software. Fortunately, open source tools can automate the process of setting up your testing environment. PC users can use XAMPP (http://www.apachefriends.org) to set up a local testing environment; Mac users can use MAMP (http://www.mamp.info). If you are working in a local testing environment set up via XAMPP or MAMP, you have all the pieces you need to start working with Drupal: your domain, your web host, the ability to move files into your web directory, and phpMyAdmin. Setting up a local environment using MAMP (Mac only) While Apple's operating system includes most of the programs required to run Drupal, setting up a testing environment can be tricky for inexperienced users. Installing MAMP allows you to create a preconfigured local environment quickly and easily using the following steps: Download the latest version of MAMP from http://www.mamp.info/en/index.html. Note that the paid version of the program will download as well. Feel free to pay for the software if you wish, but the free version will be sufficient for our needs. Navigate to where you downloaded the .zip file, and double-click to unzip it. Once it is unzipped, double click on the .pkg file that was contained in the .zip file. Follow the directions in the wizard until you reach the Installation Type screen. If you want to use only the free version of the program, click on the Customize button: In the Custom Install on "Macintosh HD" window, uncheck the MAMP PRO option and click on the Install button to install the application: Navigate to /Applications/MAMP and open the MAMP application. The Apache and MySQL servers will start, and the start page will open in your default web browser. If the start page opens, MAMP is installed correctly. Setting up a local environment using XAMPP (Windows only) Download the latest version of XAMPP from http://www.apachefriends.org/en/xampp-windows.html#641. Download the .zip version. Navigate to where you downloaded the file, right-click, and select Extract All... . Enter C: as the destination and click on Extract . Navigate to C:xampp and double-click the xampp-control application to start XAMPP Control Panel Application : Click on the Start buttons next to Apache and MySql . Open a web browser, and enter http://localhost or http://127.0.0.1 in the address bar, and you should see the following start page: Navigate to http://localhost/security/index.php, and enter a password for MySQL's root user. Make sure to remember this password or write it down in your notebook because we will need it later. Configuring your local environment for Drupal Now that we have the programs required to run Drupal (Apache, MySQL, and PHP), we need to modify some of their settings to match Drupal's system requirements. PHP configuration As mentioned before, Drupal 7 requires Version 5.2.5 or higher, and as of the writing of this book MAMP includes Version 5.4.4 (or you can switch to Version 5.2.17) and XAMPP includes Version 5.4.7. PHP configuration settings are found in the program's php.ini file. For MAMP, the php.ini file is located in /Applications/MAMP/bin/php/[php version number]/conf, where the php version number is either 5.4.4 or 5.2.17. For XAMPP, the php.ini file is located in C:xamppphp. Open the file in a text editor (not a word processor), find the Resource Limits section of the file and edit the values to match the following values: max_execution_time = 60;max_input_time = 120;memory_limit = 128M;error_reporting = E_ALL & ~E_NOTICE The last line is optional and is used if you want to display error messages in the browser, instead of only in the logs. MySQL configuration As mentioned before, Drupal 7 requires MySQL Version 5.0.15 or higher. MAMP includes Version 5.5.25 and XAMPP includes Version 5.5.27. MySQL's configuration settings are contained in a my.cnf or my.ini file. MAMP does not use a my.cnf file by default, so we need to copy the my-medium.cnf file from the /Applications/MAMP/Library/support-files directory to the /Applications/MAMP/conf folder. After copying the file, rename it to my.cnf. For XAMPP, the my.ini file is located in the C:xamppmysqlbin directory. Open the my.cnf or my.ini file in a text editor, find the following settings and edit them to match the following values: # * Fine Tuning#key_buffer = 16Mkey_buffer_size = 32Mmax_allowed_packet = 16Mthread_stack = 512Kthread_cache_size = 8max_connections = 300## * Query Cache Configuration#query_cache_type = 1query_cache_limit = 15Mquery_cache_size = 46Mjoin_buffer_size = 5M# Sort buffer size for ORDER BY and GROUP BY queries, data# gets spun out to disc if it does not fitsort_buffer_size = 10Minnodb_flush_method = O_DIRECTinnodb_file_per_table = 1innodb_flush_log_at_trx_commit = 2innodb_log_buffer_size = 4Minnodb_additional_mem_pool_size = 20M# num cpu's/cores *2 is a good base line for innodb_thread_concurrencyinnodb_thread_concurrency = 4 After you have made the edits, you have to stop and restart the servers for the changes to take effect. Once you have restarted the servers, we are ready to install Drupal! The most effective way versus the easy way There are many different ways to install Drupal. People familiar with working via the command line can install Drupal very quickly without an FTP client or any web-based tools to create and administer databases. The instructions in this book are geared towards people who would rather not use the command line. These instructions attempt to get you through the technical pieces as painlessly as possible, to speed up the process of building a site that supports teaching and learning. Installing Drupal - the quick version The following steps will get you up and running with your Drupal site. This quick-start version gives an overview of the steps required for most setups. A more detailed version follows immediately after this section. Once you are familiar with the setup process, installing a Drupal site takes between five to ten minutes. Download the core Drupal codebase from http://drupal.org/project/drupal. Extract the codebase on your local machine. Using phpMyAdmin, create a database on your server. Write down the name of the database. Using phpMyAdmin, create a user on the database using the following SQL statement: GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTERON databasename.*TO 'username'@'localhost' IDENTIFIED BY 'password'; You will have created the databasename in step 3; write down the username and password values, as you will need them to complete the install. Upload the Drupal codebase to your web folder. Navigate to the URL of your site. Follow the instructions of the install wizard. You will need your databasename (created in step 3), as well as the username and password for your database user (created in step 4). Installing Drupal - the detailed version This version goes over each step in more detail and includes screenshots. Download the core Drupal codebase from http://drupal.org/project/drupal. Extract the codebase on your local machine. The Drupal codebase (and all modules and themes) are compressed into a tarball, or a file that is first tarred, and then gzipped. Such compressed files end in .tar.gz. On Macs and Linux machines, tar.gz files can be extracted automatically using tools that come preinstalled with the operating system. On PC's, you can use 7-zip, an open source compression utility available at http://www.7-zip.org. In your web browser, navigate to your system's URL for phpMyAdmin. If you are using a different tool for creating and managing your database, use that tool to create your database and database user. As shown in the following screenshot, create the database on your server. Click on the Create button to create your database. Store your database name in a safe place. You will need to know your database name to complete your installation. To create your database user, click on the SQL tab as shown in the following screenshot. In the text area, enter the following SQL statement: GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTERON databasename.*TO 'username'@'localhost' IDENTIFIED BY 'password'; For databasename, use the name of the database you created in step 4. Replace the username and password with a username and password of your choice. Once you have entered the correct values, click on the Go button to create the user with rights on your database: Store the username and the password of your database user in a safe place. You will need them to complete the installation. Create and/or locate the directory from where you want Drupal to run. In this example, we are running Drupal from within a folder named drupal7; this means that our site will be available at http://ourdomain.org/drupal7. Running Drupal in a subfolder can make things a little trickier. If at all possible, copy the Drupal files directly into your web root. Using your FTP client, upload the Drupal codebase to your web folder: Navigate to the URL of your site. The automatic install wizard will appear on your screen: Click the Save and continue button with the Standard option selected. Click the Save and continue button with the English (built-in) option selected. To complete the Set up database screen, you will need the database name (created in step 4) and the database username and password (created in step 6). Select MySQL, MariaDB, or equivalent as the Database type and then enter these values in their respective text boxes as seen in the following screenshot: Most installs will not need to use any of settings under ADVANCED OPTIONS . However, if your database is located on a server other than localhost, you will need to adjust the settings as shown in the next screenshot. In most basic hosting setups, your database is accessible at localhost . To verify the name or location of your database host, you can use phpMyAdmin (as shown in the screenshot under step 4) or contact an administrator for your web server. For the vast majority of installs, none of the advanced options will need to be adjusted. Click on the Save and continue button. You will see a progress meter as Drupal installs itself on your web server. On the Configure site screen, you can enter some general information about your site, and create the first user account. The first user account has full rights over every aspect of your site. When you have finished with the settings on this page, click on the Save and continue button. When the install is finished, you will see the following splash screen: Additional details on installing Drupal are available in the handbook at http://drupal.org/documentation/install. Enabling core modules For a full description of the modules included in Drupal core, see http://drupal.org/node/1283408. To see the modules included in Drupal core, navigate to Modules or admin/modules. As shown in the following screenshot, the Standard installation profile enables the most commonly used core modules. (For clarity, we have divided the screenshot of the single screen in two parts.) Assigning rights to the authenticated user role Within your Drupal site, you can use roles to assign specific permissions to groups of users. Anonymous users are all people visiting the site who are not site members; all site members (that is, all people with a username and password) belong to the authenticated user role. To assign rights to specific roles, navigate to People | Permissions | Roles or admin/people/permissions/roles. As shown in the preceding screenshot, click on the edit permissions link for authenticated users. The Comment module: Authenticated users can see comments and post comments. These rights have the comments going into a moderation queue for approval, as we haven't checked the Skip comment approval box. The Node module: Authenticated users can see published content. The Search module: Authenticated users can search the site. The User module: Authenticated users can change their own username. Once these options have been selected, click on the Save permissions button at the bottom of the page. Summary In this article, we installed the core Drupal codebase, enabled some core modules, and assigned rights to the authenticated user role. We are now ready to start building a feature-rich site that will help support teaching and learning. In the next article, we will take a look around your new site and begin to get familiar with how to make your site do what you want. Resources for Article : Further resources on this subject: Creating Content in Drupal 7 [Article] Drupal and Ubercart 2.x: Install a Ready-made Drupal Theme [Article] Introduction to Drupal Web Services [Article]
Read more
  • 0
  • 0
  • 2524
Unlock access to the largest independent learning library in Tech for FREE!
Get unlimited access to 7500+ expert-authored eBooks and video courses covering every tech area you can think of.
Renews at €18.99/month. Cancel anytime
article-image-using-prezi-online-presentation-software-tool
Packt
03 Jul 2013
6 min read
Save for later

Using Prezi - The Online Presentation Software Tool

Packt
03 Jul 2013
6 min read
(For more resources related to this topic, see here.) So, what is Prezi? Prezi is a web-based presentation tool. It has awesome features you can use to create compelling, attention-grabbing, and memorable presentations. We will get to the cool features later. First, let me talk about it being a web-based presentation tool. By "web-based," I mean that you create your presentations online using this application program. You create presentations online, present it from the web, and have a link through which your audience can access it later. You can also use Prezi as a collaboration tool by sharing your Prezi and allowing members of your group to edit the presentation with you in real time. During a Prezi meeting, up to 10 members of your group can make changes on a Prezi as you brainstorm your ideas or discuss your points. So, while it is largely a presentation tool, Prezi can also function as a tool for brainstorming and discussion. This is possible because of the way Prezi has been laid out and the various flowcharts you can build to illustrate your points. In this sense, Prezi also functions as a mind mapping tool. So, Prezi is an online presentation tool that you can also use for brainstorming, mind mapping, and real-time collaboration. It comes with features that you can harness to create highly visual and engaging presentations. Now, there are several presentation tools online, but Prezi is different. Working with most slideshow presentations is like working with a stack of index cards or poster boards. We add text and illustrations on each card, then we present our cards one at a time. First you view the first slide, then the second, and so on, as shown in the following diagram: In slideshow presentations, information is presented one slide at a time Similar to slideshow presentation tools, you use Prezi to present your ideas by adding text and illustrations on a given space. What makes Prezi awesome, however, are the panning and zooming features you should use to make a greater impact on your audience. When it comes to making a presentation using Prezi, imagine yourself in front of a huge white board the size of a wall. You begin by typing text and illustrations on this board. In Prezi, this is referred to as the canvas. It is like having an exhibit, but you would want to guide your viewers' eyes through the texts and illustrations you have placed on your canvas. Which one should they look at first? Next? So, you add numbers to each item. This will be the path your viewers' eyes would take as they travel from one element to another, as shown in the following diagram: In Prezi, you guide your viewer's eyes by adding a numbered path Now imagine you have a video camera. Imagine viewing your whiteboard and focusing on the first item, then moving the video (without turning it off) to view the second item. In Prezi, a similar panning movement occurs as you move from one frame to the next following the path you have indicated, as shown in the following diagram: A Prezi presentation is like viewing your presentation through a video camera, as it pans across the canvas andzooming into focus points at certain times This sense of movement created by panning makes the presentation eye-catching. The motion produced helps to keep your viewer's attention as you move from one item on your presentation to the next. Imagine if, given the preceding diagram, we pan 1-2-4-3 instead of 1-2-3-4. Can you imagine how different that would be? Or how about if we were to place the elements in a straight row? Will it be as attractive? Think of the many different ways you can be creative with this Prezi feature! Installation Prezi is a web-based tool, and it is best to work on it online. You can get started in four easy steps as follows: Go to http://prezi.com/. Click on Sign Up Now located in the middle of the screen, or Sign Up located in the upper right-hand corner. Choose one of the following options for a Prezi license: Public: This is free. You will have 100 MB of storage, which is enough for a few presentations. You can edit and share the Prezi presentations you create. Your presentations will be public. Enjoy: For a fee, you will have 500 MB of storage. On top of that, you have the option to make your presentations private and use your own logo. Subscribers also get premium customer support where you can get answers within one day. Pro: If you upgrade to a Pro license, you'll get 2 GB of storage. In addition to having the option to have private presentations and using your own logo, you will also be able to download Prezi Desktop. You use Prezi Desktop to work offline securely. Student and Teacher Licenses: You can register for an Enjoy Edu license for free if your school has an official website. Use a school e-mail account when signing up. You can create private presentations, use your own logo, and have 500 MB of storage space. If you want the ability to work offline or need more space, you can upgrade to a Pro Edu license for a fee. Decide which option is most appropriate for you and then click on the button that says Try Now. Follow the prompts for signing up. You will be asked for your name, e-mail address, and a password. It may be a good idea for you to write down the e-mail and password you used before you forget. You should also check the box signifying that you agree to Prezi's terms of use. If you have a Facebook account, you might like to log in using your Facebook account information. Just click on Log in with Facebook. Once you have signed up, you can log in and use Prezi any time you wish by going to http://prezi.com/ and putting in your e-mail address and password. And that's it! Now you're ready to create your first Prezi! Summary In this article we saw what is Prezi, its uses, and installation. Resources for Article : Further resources on this subject: Turning your PowerPoint presentation into a Prezi [Article] Getting Started with Impressive Presentations [Article] Implementation of SASS [Article]
Read more
  • 0
  • 0
  • 4969

Packt
03 Jul 2013
9 min read
Save for later

Performance Tuning – Systems Running BPEL Processes

Packt
03 Jul 2013
9 min read
(For more resources related to this topic, see here.) One of the main objectives of performance tuning is to achieve quick response time and more throughput. The application architect should work with business to understand the performance goals and set realistic performance expectations. The architect also needs to work with the infrastructure team to design the system's infrastructure and configuration of SOA components based on the performance expectations. The performance tuning step assists us in identifying the optimal settings for infrastructure and application components. We should know the current performance level and performance goals before tuning the system. The high-level plan for a performance tuning exercise for an application platform is as follows: Document the performance/response time before making any changes also known as the baseline current performance Document the configuration parameters for existing systems also known as the baseline current configurations Document what is the expected end result and backup the entire system once and also at intermediate points during performance tuning Document the performance benefits due to new values of the configuration parameter(s) and update the configuration and performance baselines with suggested values based on the Service Level Agreements (SLAs) The Oracle SOA Suite is a couple of applications developed by Oracle using J2EE technologies. One of the core applications is SOA-Infra, which is similar to any other J2EE web application. The other major components of the SOA Suite are BPEL Process, Mediator, Business Rules, and Human Task. All the Oracle SOA Suite components are deployed in a WebLogic server platform and also can deploy other platforms such as WebSphere. There are separate tuning parameters available for each of the Oracle SOA Suite applications and sub systems. Depending on your SOA composite application's deployment design one needs to holistically align the configuration parameters of Oracle SOA Suite components in addition to the WebLogic server platform, underlying Java Virtual Machine, Operating Systems, and/or Load Balancers to get the optimal system performance. Please note that by simply adding more hardware or increasing the hosting system's resources such as CPU and memory you may not improve the performance of an application platform. The application design needs to be capable of utilizing these additional resources for application performance and scalability benefits. The Java Virtual Machine The Java Virtual Machine (JVM) allows you to write programs once and run them anywhere. Usually, JVMs are optimized for an operating system in combination with the underlying hardware platform. The leading JVM vendors are Sun HotSpot JVM and Oracle JRockit for Windows, Unix, and Linux platform. Please note that Oracle is the vendor for both JRockit and Sun HotSpot JVMs. The Sun HotSpot JVM is widely used and is the default choice for Oracle SOA Suite implementations. Therefore, we will discuss the Oracle Sun HotSpot JVM performance tuning in detail. Garbage collection process Garbage collection (GC) is a process of JVM that removes the unused Java objects from the JVM heap to recycle the JVM resources. The configuration of the underlying JVM for the WebLogic servers affects the Oracle BPEL Process Manager performance. The details will be discussed in the following sections. The Java heap memory space is divided into three sections: young, tenured, and permanent generation, as shown in the following diagram. Minor garbage collection occurs at the young generation space and major garbage collection occurs at the tenured space. Young generation Young generation memory space is used whenever we create objects in Java. That is why the young generation memory space is also known as new space. The Java objects enter the young generation space and they either get the garbage collected or move into the tenured generation space. Usually the young generation memory space is smaller than the tenured generation space, therefore it has shorter collection times as compared to the full GC in tenured space. The garbage collection process in the young generation is known as Minor Garbage Collection (minorGC). Tenured generation The Java objects in the tenured generation space last longer than the young generation space. Usually, the tenured generation memory space is bigger than the young generation space and it takes more time as compared to minor garbage collection occurring in the young generation. The garbage collection process in the tenured generation is known as Major Garbage Collection (majorGC). Permanent generation Permanent generation holds the data needed by VM to describe objects (describing classes and methods). It is recommended that we always allocate enough permanent generation space to load Oracle SOA Suite library classes in a JVM. The default permanent generation size is not sufficient for Oracle SOA Suite. There is no garbage collection process in permanent generation space for a JVM. Garbage collection tuning The main objective of garbage collection tuning is to achieve less frequent garbage collection with less pause time. Tuning the garbage collection process for a JVM is very important because an application that spends about 10 percent of its time in garbage collection can lose about 75 percent of its throughput when scaled out to multiple processors. In this section, we will learn the options to tune JVM to run the SOA Suite optimally. The best way to tune the JVM and eliminate memory-related errors is to adjust JVM memory parameters. The JVM parameters are usually part of the WebLogic server start-up scripts. The default install values are not sufficient and may not provide an optimal runtime performance for an Oracle SOA Suite implementation. You need to ensure that the default JVM parameters are updated. The recommended heap size for Oracle SOA Suite is 4 GB. Some of the major JVM parameters for JDK 6 are as follows: code1 During performance tuning, it is important to ensure that all endpoint applications are responding properly. When you adjust the minimum and maximum heap parameters, please ensure that you adjust the other parameters as well. The PermSize parameter can be constant but the NewSize and MaxNewSize parameters require tuning. It is recommended for better performance that we keep the same values for following pairs for JVM parameters: Minimum heap size (-Xms) and maximum heap size (-Xmx) -XX:PermSize and -XX:MaxPermSize -XX:NewSize and -XX:MaxNewSize Choosing the garbage collection algorithm One of the key JVM parameters is the choice of the garbage collectors. Two of the major garbage collectors are listed as follows for reference: Mark and sweep (-XX:+UseConcMarkSweepGC) Mark and sweep garbage collection algorithm minimizes the garbage collection pause time that enables us to avoid impacting the production transaction during the GC process. This algorithm will run the tenured generation concurrently with the execution of the application. Throughput collector parallel collector (XX:+UseParallelGC) That throughput collector distributes the garbage collection load across CPUs; therefore maximizing throughput. It is ideal for using with multiprocessor machines usually with four or more processors. Select NewSize Set -XX:NewSize to be one-fourth of the size of the maximum heap size. The larger the young generation space means smaller the tenured space. One can't specify the tenured generation size directly; the size of the tenured generation space is determined by the total heap size and NewSize. code1 The higher values of NewSize will result in a lesser minor garbage collection. To verify the JVM performance, use Oracle SOA Suite Enterprise Manager Console. Select the WebLogic domain and use the JVM performance to view the performance details. The other available options are to use the WebLogic Console or JVisualVM. Select heap size Set the values of -Xms and -Xmx to around 80 percent of the available physical memory on a server. The remaining 20 percent memory is left for the operating system and other processes. Setting the value of -Xmx to a very high value will result in a slower majorGC that occurs less frequently. Setting too high values for a JVM heap size can cause wasted memory and performance impact during a majorGC. Please note that on a 32-bit OS server(s) a JVM can only use a maximum 2.5 GB. Garbage collection tool – JVisualVM JVisualVM is a GUI-based JVM monitoring, troubleshooting, and profiling tool that can be used to diagnose JVM performance, JVM garbage collection processes to tune JVM parameters for an application platform such as Oracle SOA Suite. The JVisualVM tool is part of JDK Version 6 that combines several JVM utilities such as JConsole, jstat, jinfo, jstack, and jmap under one roof. To start the JVisualVM, go to the bin directory of the JDK install and invoke the jvisualvm application. The Java Visual VM tool provides a view of all the Java parameters set for a WebLogic server environment, as shown in the following screenshot: Using the JVisualVM tool one can see the live CPU usage, GC activity, and Heap activity for an application such as Oracle SOA Suite, as shown in the following screenshot: You can also view the JVM performance details for the garbage collection activity by adding the following parameters as part of the WebLogic start-up scripts: –XX:+PrintGC: Outputs the basic information at every garbage collection –XX:+PrintGCDetails: Provides the size of live objects before and after garbage collection for the various generations, the total available space for each generation, and the length of time the collection took –XX:+PrintGCTimeStamps: Provides a timestamp at the start of each collection that helps us correlate garbage collection logs with other logged events –XX:+PrintGCTimeStamps: Provides a timestamp at the start of each collection that helps us correlate garbage collection logs with other logged events You are recommended to create a table with JVM parameters and its impact on the performance during the tuning process, as shown in the following table using excel or word. In the tuning process always analyze GC activities for pause time, frequency, and average memory footprint.
Read more
  • 0
  • 0
  • 1179

article-image-introduction-modern-opengl
Packt
02 Jul 2013
16 min read
Save for later

Introduction to Modern OpenGL

Packt
02 Jul 2013
16 min read
(For more resources related to this topic, see here.) Setting up the OpenGL v3.3 core profile on Visual Studio 2010 using the GLEW and freeglut libraries We will start with a very basic example in which we will set up the modern OpenGL v3.3 core profile. This example will simply create a blank window and clear the window with red color. OpenGL or any other graphics API for that matter requires a window to display graphics in. This is carried out through platform specific codes. Previously, the GLUT library was invented to provide windowing functionality in a platform independent manner. However, this library was not maintained with each new OpenGL release. Fortunately, another independent project, freeglut, followed in the GLUT footsteps by providing similar (and in some cases better) windowing support in a platform independent way. In addition, it also helps with the creation of the OpenGL core/compatibility profile contexts. The latest version of freeglut may be downloaded from http://freeglut.sourceforge.net. The version used in the source code accompanying this book is v2.8.0. After downloading the freeglut library, you will have to compile it to generate the libs/dlls. The extension mechanism provided by OpenGL still exists. To aid with getting the appropriate function pointers, the GLEW library is used. The latest version can be downloaded from http://glew.sourceforge.net. The version of GLEW used in the source code accompanying this book is v1.9.0. If the source release is downloaded, you will have to build GLEW first to generate the libs and dlls on your platform. You may also download the pre-built binaries. Prior to OpenGL v3.0, the OpenGL API provided support for matrices by providing specific matrix stacks such as the modelview, projection, and texture matrix stacks. In addition, transformation functions such as translate, rotate, and scale, as well as projection functions were also provided. Moreover, immediate mode rendering was supported, allowing application programmers to directly push the vertex information to the hardware. In OpenGL v3.0 and above, all of these functionalities are removed from the core profile, whereas for backward compatibility they are retained in the compatibility profile. If we use the core profile (which is the recommended approach), it is our responsibility to implement all of these functionalities including all matrix handling and transformations. Fortunately, a library called glm exists that provides math related classes such as vectors and matrices. It also provides additional convenience functions and classes. For all of the demos in this book, we will use the glm library. Since this is a headers only library, there are no linker libraries for glm. The latest version of glm can be downloaded from http://glm.g-truc.net. The version used for the source code in this book is v0.9.4.0. There are several image formats available. It is not a trivial task to write an image loader for such a large number of image formats. Fortunately, there are several image loading libraries that make image loading a trivial task. In addition, they provide support for both loading as well as saving of images into various formats. One such library is the SOIL image loading library. The latest version of SOIL can be downloaded from http://www.lonesock.net/soil.html. Once we have downloaded the SOIL library, we extract the file to a location on the hard disk. Next, we set up the include and library paths in the Visual Studio environment. The include path on my development machine is D:LibrariessoilSimple OpenGL Image Librarysrc whereas, the library path is set to D:LibrariessoilSimple OpenGL Image LibrarylibVC10_Debug. Of course, the path for your system will be different than mine but these are the folders that the directories should point to. These steps will help us to set up our development environment. For all of the recipes in this book, Visual Studio 2010 Professional version is used. Readers may also use the free express edition or any other version of Visual Studio (for example, Ultimate/Enterprise). Since there are a myriad of development environments, to make it easier for users on other platforms, we have provided premake script files as well. The code for this recipe is in the Chapter1/GettingStarted directory. How to do it... Let us setup the development environment using the following steps: After downloading the required libraries, we set up the Visual Studio 2010 environment settings. We first create a new Win32 Console Application project as shown in the preceding screenshot. We set up an empty Win32 project as shown in the following screenshot: Next, we set up the include and library paths for the project by going into the Project menu and selecting project Properties . This opens a new dialog box. In the left pane, click on the Configuration Properties option and then on VC++ Directories . In the right pane, in the Include Directories field, add the GLEW and freeglut subfolder paths. Similarly, in the Library Directories , add the path to the lib subfolder of GLEW and freeglut libraries as shown in the following screenshot: Next, we add a new .cpp file to the project and name it main.cpp. This is the main source file of our project. You may also browse through Chapter1/ GettingStarted/GettingStarted/main.cpp which does all this setup already. Let us skim through the Chapter1/ GettingStarted/GettingStarted/main.cpp file piece by piece. #include <GL/glew.h> #include <GL/freeglut.h> #include <iostream> These lines are the include files that we will add to all of our projects. The first is the GLEW header, the second is the freeglut header, and the final include is the standard input/output header. In Visual Studio, we can add the required linker libraries in two ways. The first way is through the Visual Studio environment (by going to the Properties menu item in the Project menu). This opens the project's property pages. In the configuration properties tree, we collapse the Linker subtree and click on the Input item. The first field in the right pane is Additional Dependencies. We can add the linker library in this field as shown in the following screenshot: The second way is to add the glew32.lib file to the linker settings programmatically. This can be achieved by adding the following pragma: #pragma comment(lib, "glew32.lib") The next line is the using directive to enable access to the functions in the std namespace. This is not mandatory but we include this here so that we do not have to prefix std:: to any standard library function from the iostream header file. using namespace std; The next lines define the width and height constants which will be the screen resolution for the window. After these declarations, there are five function definitions . The OnInit() function is used for initializing any OpenGL state or object, OnShutdown() is used to delete an OpenGL object, OnResize() is used to handle the resize event, OnRender() helps to handle the paint event, and main() is the entry point of the application. We start with the definition of the main() function. const int WIDTH = 1280; const int HEIGHT = 960; int main(int argc, char** argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA); glutInitContextVersion (3, 3); glutInitContextFlags (GLUT_CORE_PROFILE | GLUT_DEBUG); glutInitContextProfile(GLUT_FORWARD_COMPATIBLE); glutInitWindowSize(WIDTH, HEIGHT); The first line glutInit initializes the GLUT environment. We pass the command line arguments to this function from our entry point. Next, we set up the display mode for our application. In this case, we request the GLUT framework to provide support for a depth buffer, double buffering (that is a front and a back buffer for smooth, flicker-free rendering), and the format of the frame buffer to be RGBA (that is with red, green, blue, and alpha channels). Next, we set the required OpenGL context version we desire by using the glutInitContextVersion. The first parameter is the major version of OpenGL and the second parameter is the minor version of OpenGL. For example, if we want to create an OpenGL v4.3 context, we will call glutInitContextVersion (4, 3). Next, the context flags are specified: glutInitContextFlags (GLUT_CORE_PROFILE | GLUT_DEBUG); glutInitContextProfile(GLUT_FORWARD_COMPATIBLE); In OpenGL v4.3, we can register a callback when any OpenGL related error occurs. Passing GLUT_DEBUG to the glutInitContextFlags functions creates the OpenGL context in debug mode which is needed for the debug message callback. For any version of OpenGL including OpenGL v3.3 and above, there are two profiles available: the core profile (which is a pure shader based profile without support for OpenGL fixed functionality) and the compatibility profile (which supports the OpenGL fixed functionality). All of the matrix stack functionality glMatrixMode(*), glTranslate*, glRotate*, glScale*, and so on, and immediate mode calls such as glVertex*, glTexCoord*, and glNormal* of legacy OpenGL, are retained in the compatibility profile. However, they are removed from the core profile. In our case, we will request a forward compatible core profile which means that we will not have any fixed function OpenGL functionality available. Next, we set the screen size and create the window: glutInitWindowSize(WIDTH, HEIGHT); glutCreateWindow("Getting started with OpenGL 3.3"); Next, we initialize the GLEW library. It is important to initialize the GLEW library after the OpenGL context has been created. If the function returns GLEW_OK the function succeeds, otherwise the GLEW initialization fails. glewExperimental = GL_TRUE; GLenum err = glewInit(); if (GLEW_OK != err){ cerr<<"Error: "<<glewGetErrorString(err)<<endl; } else { if (GLEW_VERSION_3_3) { cout<<"Driver supports OpenGL 3.3nDetails:"<<endl; } } cout<<"tUsing glew "<<glewGetString(GLEW_VERSION)<<endl; cout<<"tVendor: "<<glGetString (GL_VENDOR)<<endl; cout<<"tRenderer: "<<glGetString (GL_RENDERER)<<endl; cout<<"tVersion: "<<glGetString (GL_VERSION)<<endl; cout<<"tGLSL: "<<glGetString(GL_SHADING_LANGUAGE_VERSION)<<endl; The glewExperimental global switch allows the GLEW library to report an extension if it is supported by the hardware but is unsupported by the experimental or pre-release drivers. After the function is initialized, the GLEW diagnostic information such as the GLEW version, the graphics vendor, the OpenGL renderer, and the shader language version are printed to the standard output. Finally, we call our initialization function OnInit() and then attach our uninitialization function OnShutdown() as the> glutCloseFunc method—the close callback function which will be called when the window is about to close. Next, we attach our display and reshape function to their corresponding callbacks. The main function is terminated with a call to the glutMainLoop() function> which starts the application's main loop. OnInit(); glutCloseFunc(OnShutdown); glutDisplayFunc(OnRender); glutReshapeFunc(OnResize); glutMainLoop(); return 0; } There's more… The remaining functions are defined as follows: void OnInit() { glClearColor(1,0,0,0); cout<<"Initialization successfull"<<endl; } void OnShutdown() { cout<<"Shutdown successfull"<<endl; } void OnResize(int nw, int nh) { } void OnRender() { glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); glutSwapBuffers(); } For this simple example, we set the clear color to red (R:1, G:0, B:0, A:0). The first three are the red, green, and blue channels and the last is the alpha channel which is used in alpha blending. The only other function defined in this simple example is the OnRender() function, which is our display callback function that is called on the paint event. This function first clears the color and depth buffers to the clear color and clear depth values respectively. Similar to the color buffer, there is another buffer called the depth buffer. Its clear value can be set using the glClearDepth function. It is used for hardware based hidden surface removal. It simply stores the depth of the nearest fragment encountered so far. The incoming fragment's depth value overwrites the depth buffer value based on the depth clear function specified for the depth test using the> glDepthFunc function. By default the depth value gets overwritten if the current fragment's depth is lower than the existing depth in the depth buffer. The glutSwapBuffers function is then called to set the current back buffer as the current front buffer that is shown on screen. This call is required in a double buffered OpenGL application. Running the code gives us the output shown in the following screenshot. Designing a GLSL shader class We will now have a look at how to set up shaders. Shaders are special programs that are run on the GPU. There are different shaders for controlling different stages of the programmable graphics pipeline. In the modern GPU, these include the vertex shader (which is responsible for calculating the clip-space position of a vertex), the tessellation control shader (which is responsible for determining the amount of tessellation of a given patch), the tessellation evaluation shader (which computes the interpolated positions and other attributes on the tessellation result), the geometry shader (which processes primitives and can add additional primitives and vertices if needed), and the fragment shader (which converts a rasterized fragment into a colored pixel and a depth). The modern GPU pipeline highlighting the different shader stages is shown in the following figure. Note that the tessellation control/evaluation shaders are only available in the hardware supporting OpenGL v4.0 and above. Since the steps involved in shader handling as well as compiling and attaching shaders for use in OpenGL applications are similar, we wrap these steps in a simple class we call GLSLShader. Getting ready The GLSLShader class is defined in the GLSLShader.[h/cpp] files. We first declare the constructor and destructor which initialize the member variables. The next three functions, LoadFromString, LoadFromFile, and CreateAndLinkProgram handle the shader compilation, linking, and program creation. The next two functions, Use and UnUse functions bind and unbind the program. Two std::map datastructures are used. They store the attribute's/uniform's name as the key and its location as the value. This is done to remove the redundant call to get the attribute's/uniform's location each frame or when the location is required to access the attribute/uniform. The next two functions, AddAttribute and AddUniform add the locations of the attribute and uniforms into their respective std::map (_attributeList and _uniformLocationList). class GLSLShader { public: GLSLShader(void); ~GLSLShader(void); void LoadFromString(GLenum whichShader, const string& source); void LoadFromFile(GLenum whichShader, const string& filename); void CreateAndLinkProgram(); void Use(); void UnUse(); void AddAttribute(const string& attribute); void AddUniform(const string& uniform); GLuint operator[](const string& attribute); GLuint operator()(const string& uniform); void DeleteShaderProgram(); private: enum ShaderType{VERTEX_SHADER,FRAGMENT_SHADER,GEOMETRY_SHADER}; GLuint _program; int _totalShaders; GLuint _shaders[3]; map<string,GLuint> _attributeList; map<string,GLuint> _uniformLocationList; }; To make it convenient to access the attribute and uniform locations from their maps , we declare the two indexers. For attributes, we overload the square brackets ([]) whereas for uniforms, we overload the parenthesis operation (). Finally, we define a function DeleteShaderProgram for deletion of the shader program object. Following the function declarations are the member fields. How to do it In a typical shader application, the >usage of the GLSLShader object is as follows: Create the GLSLShader object either on stack (for example, GLSLShader shader;) or on the heap (for example, GLSLShader* shader=new GLSLShader();) Call LoadFromFile on the GLSLShader object reference Call CreateAndLinkProgram on the GLSLShader object reference Call Use on the GLSLShader object reference to bind the shader object Call AddAttribute/AddUniform to store locations of all of the shader's attributes and uniforms respectively Call UnUse on the GLSLShader object reference to unbind the shader object Note that the above steps are required at initialization only. We can set the values of the uniforms that remain constant throughout the execution of the application in the Use/UnUse block given above. At the rendering step, we access uniform(s), if we have uniforms that change each frame (for example, the modelview matrices). We first bind the shader by calling the GLSLShader::Use function. We then set the uniform by calling the glUniform{*} function, invoke the rendering by calling the glDraw{*} function, and then unbind the shader (GLSLShader::UnUse). Note that the glDraw{*} call passes the attributes to the GPU. How it works In a typical OpenGL shader application, the shader specific functions and their sequence of execution are as follows: glCreateShader glShaderSource glCompileShader glGetShaderInfoLog Execution of the above four functions creates a shader object. After the shader object is created, a shader program object is >created using the following set of functions in the following sequence: glCreateProgram glAttachShader glLinkProgram glGetProgramInfoLog Note that after the shader program has been linked, we can safely delete the shader object. There's more In the GLSLShader class, the first four steps are handled in the LoadFromString function and the later four steps are handled by the CreateAndLinkProgram member function. After the shader program object has been created, we can set the program for execution on the GPU. This process is called shader binding This is carried out by the glUseProgram function which is called through the Use/UnUse functions in the GLSLShader class. To enable communication between the application and the shader, there are two different kinds of fields available in the shader. The first are the attributes which may change during shader execution across different shader stages. All per-vertex attributes fall in this category. The second are the uniforms which remain constant throughout the shader execution. Typical examples include the modelview matrix and the texture samplers. In order to communicate with the shader program, the application must obtain the location of an attribute/uniform after the shader program is bound. The location identifies the attribute/uniform. In the GLSLShader class, for convenience, we store the locations of attributes and uniforms in two separate std::map objects. For accessing any attribute/uniform location, we provide an indexer in the GLSLShader class. In cases where there is an error in the compilation or linking stage, the shader log is printed to the console. Say for example, our GLSLshader object is called shader and our shader contains a uniform called MVP. We can first add it to the map of GLSLShader by calling shader.AddUniform("MVP"). This function adds the uniform's location to the map. Then when we want to access the uniform, we directly call shader("MVP") and it returns the location of our uniform.
Read more
  • 0
  • 0
  • 4744

article-image-optimizing-performance
Packt
02 Jul 2013
14 min read
Save for later

Optimizing Performance

Packt
02 Jul 2013
14 min read
(For more resources related to this topic, see here.) Improving relevance and Quality Score AdWords rewards advertisers who choose relevant keywords and write compelling ads with good Quality Scores. The better your Quality Scores, the less you'll need to pay for each click, resulting in more profits for you. This ecosystem evolved to benefit users, Google, and advertisers. If the ads on Google were irrelevant and of poor quality, users would get frustrated and not click on them, and Google would lose revenue. From an advertiser's perspective, when users click on irrelevant ads, they tend to leave your website, costing you money and not contributing to your bottom line. AdWords was designed to encourage high-quality ads, and as an advertiser you'll reap many benefits from optimizing them to improve relevance. Getting ready First, check your Quality Scores to identify low quality keywords to focus on. Go to the Campaigns tab. Click on the Keywords tab. Go to Columns and choose Customize columns. From the Attributes section, choose Qual. score. Click on Apply and you will see an extra column with your Quality Scores. In your Keywords tab, sort the Qual. score column to review low Quality Score keywords. Generally, Quality Score 1 to 3 is considered low, 4 to 6 is average with room for improvement, 7 to 9 is good, and 10 is considered great. Another way you can identify low-quality keywords is with filters. Create a keyword filter to see all keywords that are below a certain Quality Score. Download this report to have an easy to refer to summary of all keywords you'll need to focus on. How to do it... To improve your Quality Scores, follow these 10 tips: Start with low Quality Score keywords that get the most impressions. This is where you'll have the biggest impact. Re-organize your keywords into more tightly themed ad groups. If a keyword has a low Quality Score, try moving it to its own ad group with more specific ad text and its own negative keywords. Your broad match keywords may be getting expanded to irrelevant variations. Try changing them to a more specific match type. Add negative keywords to eliminate irrelevant impressions and increase your CTR. For example, add free as a negative keyword to eliminate someone looking for free products and services online. Run a search terms report to see what queries are triggering clicks and get new negative keyword ideas. Some of your low quality keywords may not be relevant to your website. If a keyword has a very low Quality Score and rarely shows, it could be negatively impacting the rest of your account. Consider deleting it. Write new ads for your low Quality Score keywords, placing each keyword in your ad text, ideally in your headline. Test multiple ad versions to see which one resonates better with your customers. Experiment with different calls-to-action, promotions, and ways to describe the unique benefits of your products and services. Pause the lower performing ads in each ad group, if you are testing multiple variations to ensure that ads getting a better CTR show more often. Try implementing dynamic keyword insertion to have AdWords automatically insert your keywords into the ad titles or description lines. Choose more specific landing pages. Your landing page should be relevant to your keywords and contain your keywords on the page. If it does not, consider creating new landing pages for your most important keywords. How it works... Quality Score is a measure of relevance and is calculated by taking into account the following factors: Your keyword's CTR: Your CTR is like an online voting system; people in the search auction vote on how relevant your ads are with their clicks. Your display URL's CTR: Your display URL's past CTR affects your Quality Scores. How relevant your keywords are: Some keywords you choose will be more relevant to your business than others. If you sell snowboards, but would like to run on a keyword like "snow," a generic term that's not as relevant to your business, you will receive a much lower Quality Score. Pick specific keywords that clearly describe your products and stay away from general keywords that could apply to many different businesses. The relevance of your ads to your keywords: Your ads need to include your keywords in the ad text. If you have too many keywords for them all to be reflected in your ad copy, create additional, smaller ad groups. When a searched keywords is included in an ad text, that term is highlighted by Google in your ad, helping it stand out even more on the Google search results page. Landing page quality: The keywords you choose should be included in your ad text and further mirrored on your landing page. In addition to your landing page being relevant to your keywords, it also needs to be transparent and easy to navigate. Historical account performance: Advertisers who continue to choose poor quality keywords will receive low Quality Scores when adding new keywords. This system helps Google discourage advertisers who continue to choose irrelevant keywords and encourage advertisers who create relevant, quality keywords and ads. Performance in the regions you are targeting: The regions you target via your campaign settings page will affect your Quality Scores. Performance on the devices you are targeting: You may get different Quality Scores on mobile and tablet devices, if your keywords perform differently depending on device. Quality Score is dynamic and is calculated every time a search triggers your ad. In order to achieve better Quality Scores, you'll need to focus on tying together all of the various elements that comprise Quality Score. Increasing relevance helps you achieve a better ad rank and pay less for each click. The Quality Score algorithm is designed to reward relevancy and encourage advertisers to create high-quality accounts, which will in turn help you achieve better ROI with AdWords. There's more… The more general your keywords are, the more difficult it will be to obtain a high Quality Score for them, even after following all of the recommended AdWords best practices. In such cases, you'll need to weigh if the lower Quality Score is worth the traffic and conversions you get from these keywords. Keep in mind that if you continue to choose low-quality keywords, this will hurt your overall account performance. Improving ad rank Your ad position is going to heavily impact visibility and traffic, with the top-ranked ads receiving the most clicks. Obviously, the more competitive your keywords are, the more costly it will be to have your ads show in the #1 spot. However, there are specific shortand long-term strategies that will help you obtain the best possible ad rank. Getting ready First, isolate the keywords that are not ranked optimally: Identify keywords that are not showing on the first page of Google's search results If you have a specific ad position in mind, use filters in your Keywords tab to see which keywords are not meeting this criteria Quickly diagnose your keywords to figure out if they are showing or are restricted by Quality Scores and bids. On your Keywords tab, click on Keyword details and select Diagnose keywords. How to do it... To improve your ad rank, you can: Increase your bid Improve your Quality Score Increasing your bids is the easy fix short-term solution. However, continuing to increase how much you spend on each click when your ad rank slips is not going to be profitable in the long run. The long-term strategy to improving ad position is to raise your Quality Scores. To improve Quality Score, start with the following: Refine your campaign structure, breaking out related keywords into their own ad groups, which will help you write more relevant ads. Refine ads with more compelling ad copy, using keywords in ad text. Pause lower CTR ads if you are running multiple ad variations. Add negative keywords to weed out impressions that are not relevant and are weighing down your CTR. How it works... Your ad rank determines your ad position, or where your ads show in relation to other advertisers. The ad rank formula consists of your Quality Score and your bid: Ad Rank = Quality Score x Max CPC Ad rank is calculated each time your ad enters the ad auction. This means that for each new query your ads could appear in a different position. There's more… The higher your Quality Score, the less you'll need to bid to maintain your ad rank. This strategy helps AdWords ensure high quality ads on Google.com and encourages advertisers to optimize their accounts. Changing keyword match types Keyword match types control who sees your ads and how the keywords you have chosen are expanded to match other relevant queries. Using too many of your keywords in the most restrictive match types can limit your traffic, while using too many broad keywords can generate some or a lot of irrelevant clicks. Getting ready Determine which keywords you might want to change match types for. Here are a couple of common edits advertisers make: Broad match keywords with low Quality Scores and no conversions. Change to phrase or exact match to restrict variations. Exact match keywords with no impressions. Change to more general match type to broaden reach. How to do it... To change a single keyword's match type: Go to the Campaigns tab. Click on the Keywords tab or click on a specific campaign and ad group first. In your keyword table, click on the keyword you'd like to edit. Before you can proceed, you might need to agree to the system warning by clicking on Yes, I understand. The system warns you that if you edit a keyword, it will be deleted and treated as a new keyword in AdWords. You can check the Don't show this message again checkbox so you don't have to see this warning each time you edit a keyword. Next, you'll be able to choose a different match type from the drop-down menu. In this screenshot, we are choosing to change a broad keyword to a more specific match type. Click on Save. To change match types for multiple keywords: From your Keywords tab, check all of the keywords you'd like to edit. From the Edit drop-down menu, choose Change match type. Choose what you'd like to change your match type from and to. Since changing a match type deletes the old keyword and creates a new one, you have the option to create duplicate versions of the keywords you have selected and add them in the new match types. To use that option, check Duplicate keywords and change match type in duplicates. You can preview your changes before they go live by clicking on Preview changes. Click on Make changes. How it works... Changing a keyword's match type deletes the old keyword and creates a brand new keyword in your account. It also resets a keyword's history to 0, but performance data will still be available for all deleted keywords. Scheduling ads to run during key days and times Many advertisers choose to run AdWords campaigns only during hours when they have customer support available. If you have a limited budget, you might want to focus your ad budgets on days and times your customers are most likely to be looking for you. Getting ready Determine if ad scheduling is necessary and appropriate for your business. Advertisers that may benefit from this include businesses that operate primarily during specific hours. For example, a website with customer support available to take calls during business hours only, or a pizza delivery service that only delivers evenings. Review performance by day and hour of day, keeping in mind that you will see fewer clicks and impressions during less busy times, so you have focus on conversion rates and CPA instead. Some advertisers get great conversion rates during off peak hours, late at night and in the early mornings, when fewer advertisers are competing in the ad auction. Keep in mind how your customers interact with you. If you rely on calls and only have customer support during specific hours, make sure your ads are focused on when you have the proper support available. How to do it... To enable ad scheduling: Go to the Campaigns tab. Click on the specific campaign you'd like to edit. Go to the Settings tab. Select Ad schedule. Click on Edit ad schedule. Click on + Create custom schedule. From the drop-down menu, choose to create a schedule for all days, Monday through Friday, or specific days of the week, and then set your hours. Click on +Add to add additional parameters. Click on Save. How it works... Ad scheduling helps you control when your ads appear to potential customers. Ad scheduling is set at the campaign level, which means that it applies to all keywords and ads within a single campaign. By default, AdWords campaigns are set to run all days of the week and all hours of the day. There's more… When you set up ad scheduling, keep in mind your account's time zone. You can find out your time zone by going to My Account | Preferences. AdWords will also reference your time zone as you create a custom schedule for each campaign. You cannot change your time zone. Expanding your keyword list Expanding your keywords will be one of your main strategies to increase clicks as well as conversions. Just as markets evolve and search patterns change, your keywords also need to be updated in order not to become stagnant. Here we will discuss several tools you can use to build up and refresh your keyword list. Getting ready Review your website and compare your list of products and services to your AdWords account. Are your current keywords covering all of the categories you specialize in? Are there other ways to describe some of your key offerings? Who are your main competitors and are they doing PPC? How to do it... To expand your keyword list, try one of the following strategies. Automated keyword suggestions To see automated keyword ideas relevant to your website, follow these steps: Click on the Campaigns tab. Go into a specific campaign and ad group. Clock on + Add keywords above your ad group's current keyword summary. AdWords will suggest new sample keywords based on a scan of your website grouped into related categories. Click to expand each category and review the suggested keywords. If you like a keyword, click on Add to move it to the Add keywords box. Do not simply add all of the automated suggestions, as not all of them will be specific enough. You as a business owner know your audience best and should pick and choose only the keywords that are the most relevant. Make sure that you are not adding keywords that may be already present in your other campaigns or ad groups. Click on Save after adding all of the relevant keywords. Search terms report Review your search terms report regularly and add any relevant keywords that resulted in clicks and conversions. Click on Add as keyword recipe after viewing your search terms to add them to your account. Competitor keywords Use websites such as spyfu.com to see what keywords your competitors' ads are appearing on and to download their keyword lists. Enter a competitor's URL into the search box to uncover profitable keywords you missed. You can download a competitor's full keyword list, sort, and filter it, or export it to an AdWords-friendly format. The tool can even organize a domain's keywords into targeted ad groups so you have less manual work to do. Google's keyword tool In addition to entering your own domain into Google's keyword tool, try typing in a competitor's website and see what keywords are being recommended. How it works... Adding new relevant keywords to your AdWords account will help drive more impressions and clicks. With new and unique keywords, you can capitalize on previously untapped opportunities to drive new leads and sales.
Read more
  • 0
  • 0
  • 2478
article-image-getting-started-oracle-data-guard
Packt
02 Jul 2013
13 min read
Save for later

Getting Started with Oracle Data Guard

Packt
02 Jul 2013
13 min read
(For more resources related to this topic, see here.) What is Data Guard? Data Guard, which was introduced as the standby database in Oracle database Version 7.3 under the name of Data Guard with Version 9 i , is a data protection and availability solution for Oracle databases. The basic function of Oracle Data Guard is to keep a synchronized copy of a database as standby, in order to make provision, incase the primary database is inaccessible to end users. These cases are hardware errors, natural disasters, and so on. Each new Oracle release added new functionalities to Data Guard and the product became more and more popular with offerings such as data protection, high availability, and disaster recovery for Oracle databases. Using Oracle Data Guard, it's possible to direct user connections to a Data Guard standby database automatically with no data loss, in case of an outage in the primary database. Data Guard also offers taking advantage of the standby database for reporting, test, and backup offloading. Corruptions on the primary database may be fixed automatically by using the non-corrupted data blocks on the standby database. There will be minimal outages (seconds to minutes) on the primary database in planned maintenances such as patching and hardware changes by using the switchover feature of Data Guard, which changes the roles of the primary and standby databases. All of these features are available with Data Guard, which doesn't require an installation but a cloning and configuration of the Oracle database. A Data Guard configuration consists of two main components: primary database and standby database. The primary database is the database for which we want to take precaution for its inaccessibility. Fundamentally, changes on the data of the primary database are passed through the standby database and these changes are applied to the standby database in order to keep it synchronized. The following figure shows the general structure of Data Guard: Let's look at the standby database and its properties more closely. Standby database It is possible to configure a standby database simply by copying, cloning, or restoring a primary database to a different server. Then the Data Guard configurations are made on the databases in order to start the transfer of redo information from primary to standby and also to start the apply process on the standby database. Primary and standby databases may exist on the same server; however, this kind of configuration should only be used for testing. In a production environment, the primary and standby database servers are generally preferred to be on separate data centers. Data Guard keeps the primary and standby databases synchronized by using redo information. As you may know, transactions on an Oracle database produce redo records. This redo information keeps all of the changes made to the database. The Oracle database first creates redo information in memory (redo log buffers). Then they're written into online redo logfiles, and when an online redo logfile is full, its content is written into an archived redo log. An Oracle database can run in the ARCHIVELOG mode or the NOARCHIVELOG mode. In the ARCHIVELOG mode, online redo logfiles are written into archived redo logs and in the NOARCHIVELOG mode, redo logfiles are overwritten without being archived as they become full. In a Data Guard environment, the primary database must be in the ARCHIVELOG mode. In Data Guard, transfer of the changed data from the primary to standby database is achieved by redo with no alternative. However, the apply process of the redo content to the standby database may vary. The different methods on the apply process reveal different type of standby databases. There were two kinds of standby databases before Oracle database Version 11 g , which were: physical standby database and logical standby database. Within Version 11 g we should mention a third type of standby database which is snapshot standby. Let's look at the properties of these standby database types. Physical standby database The Physical standby database is a block-based copy of the primary database. In a physical standby environment, in addition to containing the same database objects and same data, the primary and standby databases are identical on a block-for-block basis. Physical standby databases use Redo Apply method to apply changes. Redo Apply uses Managed recovery process ( MRP ) in order to manage application of the change in information on redo. In Version 11 g , a physical standby database can be accessible in read-only mode while Redo Apply is working, which is called Active Data Guard. Using the Active Data Guard feature, we can offload report jobs from the primary to physical standby database. Physical standby database is the only option that has no limitation on storage vendor or data types to keep a synchronized copy of the primary database. Logical standby database Logical standby database is a feature introduced in Version 9 i R2. In this configuration, redo data is first converted into SQL statements and then applied to the standby database. This process is called SQL Apply. This method makes it possible to access the standby database permanently and allows read/write while the replication of data is active. Thus, you're also able to create database objects on the standby database that don't exist on the primary database. So a logical standby database can be used for many other purposes along with high availability and disaster recovery. Due to the basics of SQL Apply, a logical standby database will contain the same data as the primary database but in a different structure on the disks. One discouraging aspect of the logical standby database is the unsupported data types, objects, and DDLs. The following data types are not supported to be replicated in a logical standby environment: BFILE Collections (including VARRAYS and nested tables) Multimedia data types (including Spatial, Image, and Oracle Text) ROWID and UROWID User-defined types The logical standby database doesn't guarantee to contain all primary data because of the unsupported data types, objects, and DDLs. Also, SQL Apply consumes more hardware resources. Therefore, it certainly brings more performance issues and administrative complexities than Redo Apply. Snapshot standby database Principally, a snapshot standby database is a special condition of a physical standby database. Snapshot standby is a feature that is available with Oracle Database Version 11 g . When you convert a Physical standby database into a snapshot standby database, it becomes accessible for read/write. You can run tests on this database and change the data. When you're finished with the snapshot standby database, it's possible to reverse all the changes made to the database and turn it back to a physical standby again. An important point here is that a snapshot standby database can't run Redo Apply. Redo transfer continues but standby is not able to apply redo. Oracle Data Guard evolution It has been a long time that the Oracle Data Guard technology has been in the database administrator's life and it apparently evolved from the beginning until 11 g R2. Let's look at this evolution closely through the different database versions. Version 7.3 – stone age The functionality of keeping a duplicate database in a separate server, which can be synchronized with the primary database, came with Oracle database Version 7.3 under the name of standby database. This standby database was constantly in recovery mode waiting for the archived redo logs to be synchronized. However, this feature was not able to automate the transfer of archived redo logs. Database administrators had to find a way to transfer archived redo logs and apply them to the standby server continuously. This was generally accomplished by a script running in the background. The only aim of Version 7.3 of the standby database was disaster recovery. It was not possible to query the standby database or to open it for any purpose other than activating it in the event of failure of the primary database. Once the standby database was activated, it couldn't be returned to the standby recovery mode again. Version 8 i – first age Oracle database Version 8 i brought the much-awaited features to the standby database and made the archived log shipping and apply process automatic, which is now called managed standby environment and managed recovery, respectively. However, some users were choosing to apply the archived logs manually because it was not possible to set a delay in the managed recovery mode. This mode was bringing the risk of the accidental operations to reflect standby database quickly. Along with the "managed" modes, 8 i made it possible to open a standby database with the read-only option and allowed it to be used as a reporting database. Even though there were new features that made the tool more manageable and practical, there were still serious deficiencies. For example, when we added a datafile or created a tablespace on the primary database, these changes were not being replicated to the standby database. Database administrators had to take care of this maintenance on the standby database. Also when we opened the primary database with resetlogs or restored a backup control file, we had to re-create the standby database. Version 9 i – middle age First of all, with this version Oracle8 i standby database was renamed to Oracle9 i Data Guard. 9 i Data Guard includes very important new features, which makes the product much more reliable and functional. The following features were included: Oracle Data Guard Broker management framework, which is used to centralize and automate the configuration, monitoring, and management of Oracle Data Guard installations, was introduced with this version. Zero data loss on failover was guaranteed as a configuration option. Switchover was introduced, which made it possible to change the roles of primary and standby. This made it possible to accomplish a planned maintenance on the primary database with very less service outage. Standby database administration became simpler because new datafiles on the primary database are created automatically on standby and if there are missing archived logs on standby, which is called gap; Data Guard detects and transmits the missing logs to standby automatically. Delay option was added, which made it possible to configure a standby database that is always behind the primary in a specified time delay. Parallel recovery increased recovery performance on the standby database. In Version 9 i Release 2, which was introduced in May 2002, one year after Release 1, there were again very important features announced. They are as follows: Logical standby database was introduced, which we've mentioned earlier in this article Three data protection modes were ready to use: Maximum Protection, Maximum Availability, and Maximum Performance, which offered more flexibility on configuration The Cascade standby database feature made it possible to configure a second standby database, which receives its redo data from the first standby database Version 10 g – new age The 10 g version again introduced important features of Data Guard but we can say that it perhaps fell behind expectations because of the revolutionary changes in release 9 i . The following new features were introduces in Version 10 g : One of the most important features of 10 g was the Real-Time Apply. When running in Real-Time Apply mode, the standby database applies changes on the redo immediately after receiving it. Standby does not wait for the standby redo logfile to be archived. This provides faster switchover and failover. Flashback database support was introduced, which made it unnecessary to configure a delay in the Data Guard configuration. Using flashback technology, it was possible to flash back a standby database to a point in time. With 10 g Data Guard, if we open a primary database with resetlogs it was not required to re-create the standby database. Standby was able to recover through resetlogs. Version 10 g made it possible to use logical standby databases in the database software rolling upgrades of the primary database. This method made it possible to lessen the service outage time by performing switchover to the logical standby database. 10 g Release 2 also introduced new features to Data Guard, but these features again were not satisfactory enough to make a jump to the Data Guard technology. The two most important features were Fast-Start Failover and the use of Guaranteed restore point: Fast-start failover automated and accelerated the failover operation when the primary database was lost. This option strengthened the disaster recovery role of Oracle Data Guard. Guaranteed restore point was not actually a Data Guard feature. It was a database feature, which made it possible to revert a database to the moment that Guaranteed restore point was created, as long as there is sufficient disk space for the flashback logs. Using this feature following scenario became possible: Activate a physical standby database after stopping Redo Apply, use it for testing with read/write operations, then revert the changes, make it standby again and synchronize it with the primary. Using a standby database read/write was offering a great flexibility to users but the archived log shipping was not able to continue while the standby is read/write and this was causing data loss on the possible primary database failure. Version 11 g – modern age Oracle database version 11 g offered the expected jump in the Data Guard technology, especially with two new features, which are called Active Data Guard and snapshot standby. The following features were introduced: Active Data Guard has been a milestone in Data Guard history, which enables a query from a physical standby database while the media recovery is active. Snapshot standby is a feature to use a physical standby database read/write for test purposes. As we mentioned, this was possible with 10 g R2 Guaranteed restore point feature but 11 g provided the continuous archived log shipping in the time period that standby is read/write with snapshot standby. It has been possible to compress redo traffic in a Data Guard configuration, which is useful in excessive redo generation rates and resolving gaps. Compression of redo when resolving gaps was introduced in 11 g R1 and compression of all redo data was introduced in 11 g R2. Use of the physical standby databases for the rolling upgrades of database software was enabled, aka Transient Logical Standby. It became possible to include different operating systems in a Data Guard configuration such as Windows and Linux. Lost-write, which is a serious data corruption type arising from the misinformation of storage subsystem on completing the write of a block, can be detected in an 11 g Data Guard configuration. Recovery is automatically stopped in such a case. RMAN fast incremental backup feature "Block Change Tracking" can be run on an Active Data Guard enabled standby database. Another very important enhancement in 11 g was Automatic Block Corruption Repair feature that was introduced with 11 g R2. With this feature, a corrupted data block in the primary database can be automatically replaced with an uncorrupted copy from a physical standby database in Active Data Guard mode and vice versa. We've gone through the evolution of Oracle Data Guard from its beginning until today. As you may notice, Data Guard started its life as a very simple database property revealed to keep a synchronized database copy with a lot of manual work and now it's a complicated tool with advanced automation, precaution, and monitoring features. Now let's move on with the architecture and components of Oracle Data Guard 11 g R2.
Read more
  • 0
  • 0
  • 4153

article-image-breaching-wireless-security
Packt
01 Jul 2013
5 min read
Save for later

Breaching Wireless Security

Packt
01 Jul 2013
5 min read
(For more resources related to this topic, see here.) Different types of attacks We will now discuss each one of these attacks briefly. The probing and discovery attacks are accomplished by sending out probes and looking for the wireless networks. We have used several tools for discovery so far, but they have all been passive in how they discover information. A passive probing tool can detect the SSID of a network even when it is cloaked, as we have shown with the Kismet tool. With active probing, we are sending out probes with the SSID in it. This type of probing will not discover a hidden or cloaked SSID. An active probing tool for this is NetStumbler (www.netstumbler.com). With an active probe, the tool will actively send out probes and elicit responses from the access points to gather information. It is very difficult to prevent an attacker from gathering information about our wireless access points; this is because an access point has to be available for connection. We can cloak or hide the SSID. The next step an attacker will carry out is performing the surveillance of the network. This is the technique we used with Kismet, airodump-ng, and ssidsniff. An example of the output of the Kismet tool is shown in the next screenshot: All three of these tools are passive, so they do not probe the network for information. They just capture it from the wireless frequency that is received from the network. Each of these tools can discover the hidden SSID of a network, and again, are passive tools. Once the attacker has discovered the target network, they will move to the surveillance step and attempt to gather more information about the target. For this, we can again use any of the three tools we previously mentioned. The information that an attacker is looking for are as follows: Whether or not the network is protected The encryption level used The signal strength and the GPS coordinates When an attacker is scanning a network, he or she is looking for an "easy" target. This is the motive of most of the attackers; they want an easy way in, and almost always, they target the weakest link. The next step that an attacker will typically pursue is Denial of Service (DoS); unfortunately, this is one area we really cannot do much about. This is because, in the case of a wireless signal, the network can be jammed by using simple and inexpensive tools; so if an attacker wants to perform a DoS attack, there is really not much that we can do to prevent it. So we will not spend any more time on this attack. The next attack method is one that is shared between the "wired" network world and the wireless world. The attack of masquerading, or spoofing as it is sometimes referred to, involves impersonating an authorized client on a network. One of the protection mechanisms we have within our wireless networks is the capability to restrict or filter a client based on their Media Access Control (MAC) address. This address is that of the network card itself; it is how data is delivered on our networks. There are a number of ways to change the MAC address; we have tools, and we can also change it from the command line in Linux. The simplest way to change our MAC address is to use the macchanger tool. An example of how to use this tool to change an address is shown in the next screenshot: In the Windows world, we can do it in another way; but it involves editing the registry, which might be too difficult for some of you. The hardware address is in the registry; you can find it by searching for the term wireless within the registry. An example of this registry entry is shown in the following screenshot: The last category of attacks that we will cover here is the rogue access point. This is an attack that takes advantage of the fact that all wireless networks have a particular level of power that they transmit. What we do for this attack is create an access point with more power than the access point we are masquerading as; this results in a stronger signal being received by the client software. When would anyone take a three-bar signal over a five-bar signal? The answer for that would be: never; that is why the attack is so powerful. An attacker can create an access point as a rogue access point; there is no way for most clients to tell whether the access point is real or not. There really is nothing that you can do to stop this attack effectively. This is why it is a common attack used in areas that have a public hotspot. We do have a recommended mechanism you can use to help mitigate the impact of this type of attack. If you look at the example that is shown in the next screenshot, can you identify which one of the access points with the same name is the correct one? This is an example of what most clients see when they are using Windows. From this list, there is no way of knowing which one of the access points is the real one. Summary Thus in this article we covered, albeit briefly, the steps that an attacker typically uses when preparing for an attack. Resources for Article : Further resources on this subject: Tips and Tricks on BackTrack 4 [Article] BackTrack Forensics [Article] BackTrack 5: Attacking the Client [Article]
Read more
  • 0
  • 0
  • 2072

article-image-hubs
Packt
28 Jun 2013
8 min read
Save for later

Hubs

Packt
28 Jun 2013
8 min read
(For more resources related to this topic, see here.) Moving up one level While PersistentConnection seems very easy to work with, it is the lowest level in SignalR. It does provide the perfect abstraction for keeping a connection open between a client and a server, but that's just about all it does provide. Working with different operations is not far from how you would deal with things in a regular socket connection, where you basically have to parse whatever is coming from a client and figure out what operation is being asked to be performed based on the input. SignalR provides a higher level of abstraction that removes this need and you can write your server-side code in a more intuitive manner. In SignalR, this higher level of abstraction is called a Hub. Basically, a Hub represents an abstraction that allows you to write classes with methods that take different parameters, as you would with any API in your application, and then makes it completely transparent on the client—at least for JavaScript. This resembles a concept called Remote Procedure Call (RPC), with many incarnations of it out there. For our chat application at this stage, we basically just want to be able to send a message from a client to the server and have it send the message to all of the other clients connected. To do this, we will now move away from the PersistentConnection and introduce a new class called Hub using the following steps: First, start off by deleting the ChatConnection class from your Web project. Now we want to add a Hub implementation instead. Right-click on the SignalRChat project and select Add | New Item. In the dialog, chose Class and give it a name Chat.cs. This is the class that will represent our Hub. Make it inherit from Hub: Public class Chat : Hub Add the necessary import statement at the top of the file: using Microsoft.AspNet.SignalR.Hubs; In the class we will add a simple method that the clients will call to send a message. We call the method Send and take one parameter into it; a string which contains the message being sent by the client: Public void Send(string message){} From the base class of Hub, we get a few things that we can use. For now we'll be using the Clients property to broadcast to all other clients connected to the Hub. On the Clients property, you'll find an All property which is dynamic; on this we can call anything and the client will just have to subscribe to the method we call, if the client is interested. It is possible to change the name of the Hub to not be the same as the class name. An attribute called HubName() can be placed in front of the class to give it a new name. The attribute takes one parameter; the name you want for your Hub. Similarly, for methods inside your Hub, you can use an attribute called HubMethodName() to give the method a different name. The next thing we need to do is to go into the Global.asax.cs file, and make some changes. Firstly, we remove the .MapConnection(…) line and replace it with a .MapHubs() line. This will make all Hubs in your application automatically accessible from a default URL. All Hubs in the application will be mapped to /signalr/<name of hub>; so more concretely the path will be: http:// <your-site>:port/signalr/<name of hub>. We're going with the defaults for now. It should cover the needs on the server-side code. Moving into the JavaScript/HTML part of things, SignalR comes with a JavaScript proxy generator that can generate JavaScript proxies from your Hubs mapped using .MapHubs(). This is also subject to the same default URL but will follow the configuration given to .MapHubs().We will need to include a script reference in the HTML code right after the line that references the SignalR JavaScript file. We add the following: <script src = "/signalr/hubs" type="text/javascript"></script> This will include the generated proxies for our JavaScript client. What this means is that we get whatever is exposed on a Hub generated for us and we can start using it straight away. Before we get started with the concrete implementation for our web client, we can move all of the custom code revitalizing the Rich Client, for PersistentConnection altogether. We then want to get to our proxy, and work with it. It sits on the connection object that SignalR adds to jQuery. So, for us, that means an object called chat will be there. On the the chat object, sit two important properties, one representing the client functions that get invoked when the server "calls" something on the client. And the second one is the property representing the server and all of the functionalities that we can call from the client. Let's start by hooking up the client and its methods. Earlier we implemented in the Hub sitting on the server a call to addMessage() with the message. This can be added to the client property inside the chat Hub instance: Basically, whenever the server calls that method, our client counterpart will be called. Now what we need to do is to start the Hub and print out when we are connected to the chat window: $.connection.hub.start().done(function() {$("#chatWindow").val("Connectedn");}); Then we need to hook up the click event on the button and call the server to send messages. Again, we use the server property sitting on the chat hub instance in the client, which corresponds to a method on the Hub: $("#sendButton").click(function() {chat.server.send($("#messageTextBox").val());$("#messageTextBox").val("");}); You should now have something that looks as follows: You may have noticed that the send function on the client is in camelCase and the server-side C# code has it in PascalCase. SignalR automatically translates between the two case types. In general, camelCase is the preferred and the most broadly used casing style in JavaScript—while Pascal being the most used in C#. You should now be having a full sample in HTML/JavaScript that looks like the following screenshot: Running it should produce the same result as before, with the exception of the .NET terminal client, which also needs alterations. In fact, let's just get rid of the code inside Program.cs and start over. The client API is a bit rougher in C#; this comes from the more statically typed nature of C#. Sure, it is possible—technically—to get pretty close to what has been done in JavaScript, but it hasn't been a focal point for the SignalR team. Basically, we need a different connection than the PersistentConnection class. We'll be needing a HubConnection class. From the HubConnection class we can create a proxy for the chat Hub: As with JavaScript, we can hook up client-side methods that get invoked when the server calls any client. Although as mentioned, not as elegantly as in JavaScript. On the chat Hub instance, we get a method called On(), which can be used to specify a client-side method corresponding to the client call from the server. So we set addMessage to point to a method which, in our case, is for now just an inline lambda expression. Now we need, as with PersistentConnection, to start the connection and wait until it's connected: hubConnection.Start().Wait(); Now we can get user input and send it off to the server. Again, as with client methods called from the server, we have a slightly different approach than with JavaScript; we call the Invoke method giving it the name of the method to call on the server and any arguments. The Invoke() method does take a parameter, so you can specify any number of arguments which will then be sent to the server: The finished result should look something like the following screenshot, and now work in full correspondence with the JavaScript chat: Summary Exposing our functionality through Hubs makes it easier to consume on the client, at least on JavaScript based clients, due to the proxy generation. It basically brings it to the client as if it was on the client. With the Hub you also get the ability to call the client from the server in a more natural manner. One of the things often important for applications is the ability to ?lter out messages so you only get messages relevant for your context. Resources for Article : Further resources on this subject: Working with Microsoft Dynamics AX and .NET: Part 1 [Article] Working with Microsoft Dynamics AX and .NET: Part 2 [Article] Deploying .NET-based Applications on to Microsoft Windows CE Enabled Smart Devices [Article]
Read more
  • 0
  • 0
  • 1946
article-image-introduction-hlsl-language
Packt
28 Jun 2013
8 min read
Save for later

Introduction to HLSL language

Packt
28 Jun 2013
8 min read
(For more resources related to this topic, see here.) Distance/Height-based fog Distance/Height-based fog is an approximation to the fog you would normally see outdoors. Even in the clearest of days, you should be able to see some fog far in the distance. The main benefit of adding the fog effect is that it helps the viewer estimate how far different elements in the scene are based on the amount of fog covering them. In addition to the realism this effect adds, it has the additional benefit of hiding the end of the visible range. Without fog to cover the far plane, it becomes easier to notice when far scene elements are clipped by the cameras far plane. By tuning the height of the fog you can also add a darker atmosphere to your scene as demonstrated by the following image: This recipe will demonstrate how distance/height-based fog can be added to our deferred directional light calculation. See the How it works… section for details about adding the effect to other elements of your rendering code. Getting ready We will be passing additional fog specific parameters to the directional light's pixel shader through a new constant buffer. The reason for separating the fog values into their own constant buffer is to allow the same parameters to be used by any other shader that takes fog into account. To create the new constant buffer use the following buffer descriptor: Constant buffer descriptor parameter   Value   Usage   D3D11_USAGE_DYNAMIC   BindFlags   D3D11_BIND_CONSTANT_BUFFER   CPUAccessFlags   D3D11_CPU_ACCESS_WRITE   ByteWidth   48   The reset of the descriptor fields should be set to zero. All the fog calculations will be handled in the deferred directional light pixel shader. How to do it... Our new fog constant buffer is declared in the pixel shader as follows: cbuffer cbFog : register( b2 ){float3 FogColor : packoffset( c0 );float FogStartDepth : packoffset( c0.w );float3 FogHighlightColor : packoffset( c1 );float FogGlobalDensity : packoffset( c1.w );float3 FogSunDir : packoffset( c2 );FogHeightFalloff : packoffset( c2.w );} The helper function used for calculating the fog is as follows: float3 ApplyFog(float3 originalColor, float eyePosY, float3eyeToPixel){float pixelDist = length( eyeToPixel );float3 eyeToPixelNorm = eyeToPixel / pixelDist;// Find the fog staring distance to pixel distancefloat fogDist = max(pixelDist - FogStartDist, 0.0);// Distance based fog intensityfloat fogHeightDensityAtViewer = exp( -FogHeightFalloff * eyePosY );float fogDistInt = fogDist * fogHeightDensityAtViewer;// Height based fog intensityfloat eyeToPixelY = eyeToPixel.y * ( fogDist / pixelDist );float t = FogHeightFalloff * eyeToPixelY;const float thresholdT = 0.01;float fogHeightInt = abs( t ) > thresholdT ?( 1.0 - exp( -t ) ) / t : 1.0;// Combine both factors to get the final factorfloat fogFinalFactor = exp( -FogGlobalDensity * fogDistInt *fogHeightInt );// Find the sun highlight and use it to blend the fog colorfloat sunHighlightFactor = saturate(dot(eyeToPixelNorm, FogSunDir));sunHighlightFactor = pow(sunHighlightFactor, 8.0);float3 fogFinalColor = lerp(FogColor, FogHighlightColor,sunHighlightFactor);return lerp(fogFinalColor, originalColor, fogFinalFactor);} The Applyfog function takes the color without fog along with the camera height and the vector from the camera to the pixel the color belongs to and returns the pixel color with fog. To add fog to the deferred directional light, change the directional entry point to the following code: float4 DirLightPS(VS_OUTPUT In) : SV_TARGET{// Unpack the GBufferfloat2 uv = In.Position.xy;//In.UV.xy;SURFACE_DATA gbd = UnpackGBuffer_Loc(int3(uv, 0));// Convert the data into the material structureMaterial mat;MaterialFromGBuffer(gbd, mat);// Reconstruct the world positionfloat2 cpPos = In.UV.xy * float2(2.0, -2.0) - float2(1.0, -1.0);float3 position = CalcWorldPos(cpPos, gbd.LinearDepth);// Get the AO valuefloat ao = AOTexture.Sample(LinearSampler, In.UV);// Calculate the light contributionfloat4 finalColor;finalColor.xyz = CalcAmbient(mat.normal, mat.diffuseColor.xyz) * ao;finalColor.xyz += CalcDirectional(position, mat);finalColor.w = 1.0;// Apply the fog to the final colorfloat3 eyeToPixel = position - EyePosition;finalColor.xyz = ApplyFog(finalColor.xyz, EyePosition.y,eyeToPixel);return finalColor;} With this change, we apply the fog on top of the lit pixels color and return it to the light accumulation buffer. How it works… Fog is probably the first volumetric effect implemented using a programmable pixel shader as those became commonly supported by GPUs. Originally, fog was implemented in hardware (fixed pipeline) and only took distance into account. As GPUs became more powerful, the hardware distance based fog was replaced by a programmable version that also took into account things such as height and sun effect. In reality, fog is just particles in the air that absorb and reflect light. A ray of light traveling from a position in the scene travels, the camera interacts with the fog particles, and gets changed based on those interactions. The further this ray has to travel before it reaches the camera, the larger the chance is that this ray will get either partially or fully absorbed. In addition to absorption, a ray traveling in a different direction may get reflected towards the camera and add to the intensity of the original ray. Based on the amount of particles in the air and the distance a ray has to travel, the light reaching our camera may contain more reflection and less of the original ray which leads to a homogenous color we perceive as fog. The parameters used in the fog calculation are: FogColor: The fog base color (this color's brightness should match the overall intensity so it won't get blown by the bloom) FogStartDistance: The distance from the camera at which the fog starts to blend in FogHighlightColor: The color used for highlighting pixels with pixel to camera vector that is close to parallel with the camera to sun vector FogGlobalDensity: Density factor for the fog (the higher this is the denser the fog will be) FogSunDir: Normalized sun direction FogHeightFalloff: Height falloff value (the higher this value, the lower is the height at which the fog disappears will be) When tuning the fog values, make sure the ambient colors match the fog. This type of fog is designed for outdoor environments, so you should probably disable it when lighting interiors. You may have noticed that the fog requires the sun direction. We already store the inversed sun direction for the directional light calculation. You can remove that value from the directional light constant buffer and use the fog vector instead to avoid the duplicate values This recipe implements the fog using the exponential function. The reason for using the exponent function is because of its asymptote on the negative side of its graph. Our fog implementation uses that asymptote to blend the fog in from the starting distances. As a reminder, the exponent function graph is as follows: The ApplyFog function starts off by finding the distance our ray traveled in the fog (fogDepth). In order to take the fog's height into account, we also look for the lowest height between the camera and the pixel we apply the fog to which we then use to find how far our ray travels vertically inside the fog (fogHeight). Both distance values are negated and multiplied by the fog density to be used as the exponent. The reason we negate the distance values is because it's more convenient to use the negative side of the exponential functions graph which is limited to the range 0 to 1. As the function equals 1 when the exponent is 0, we have to invert the results (stored in fogFactors). At this point we have one factor for the height which gets larger the further the ray travels vertically into the fog and a factor that gets larger the further the ray travels in the fog in any direction. By multiplying both factors with each other we get the combined fog effect on the ray: the higher the result is, the more the original ray got absorbed and light got reflected towards the camera in its direction (this is stored in fogFinalFactor). Before we can compute the final color value, we need to find the fog's color based on the camera and sun direction. We assume that the sun intensity is high enough to get more of its light rays reflected towards the camera direction and sun direction are close to parallel. We use the dot product between the two to determine the angle and narrow the result by raising it to the power of 8 (the result is stored in sunHighlightFactor). The result is used to lerp between the fog base color and the fog color highlighted by the sun. Finally, we use the fog factor to linearly interpolate between the input color and the fog color. The resulting color is then returned from the helper function and stored into the light accumulation buffer. As you can see, the changes to the directional light entry point are very minor as most of the work is handled inside the helper function ApplyFog. Adding the fog calculation to the rest of the deferred and forward light sources should be pretty straightforward. One thing to take into consideration is that fog also has to be applied to scene elements that don't get lit, like the sky or emissive elements. Again, all you have to do is call ApplyFog to get the final color with the fog effect. Summary In this article, we learned how to apply fog effect and add atmospheric scenes to the images. Resources for Article : Further resources on this subject: Creating and Warping 3D Text with Away3D 3.6 [Article] 3D Vector Drawing and Text with Papervision3D: Part 1 [Article] 3D Vector Drawing and Text with Papervision3D: Part 2 [Article]
Read more
  • 0
  • 0
  • 23689

article-image-important-features-tuxedo
Packt
28 Jun 2013
10 min read
Save for later

The important features of Tuxedo

Packt
28 Jun 2013
10 min read
(For more resources related to this topic, see here.) Security Security is one of biggest concerns for a Tuxedo administrator, and that is why Tuxedo provides mainly three levels of security features; in addition, Kerberos can be added for more. First level: This is provided by the operating system. This security imposes restrictions on the clients and the administrator. Second level: This is provided by Tuxedo. By default, any client program can join a Tuxedo application, but an application can be configured to ensure that all clients joining the application need to provide the password. There are many ways to restrict the client's access to the application. Third level: This is provided by an authentication service that checks for the combination of user identification, password, and client name, and it can connect to the Tuxedo application only if it passes this security. There are mainly five types of incremental security provided by Tuxedo, which are as follows: No authentication (NONE): This level might be used in a development environment or in physically secured environments, as the clients do not need to be authenticated to join the application. Application password (APP-PW): This is a single password for the entire application; all the clients must provide this password to join the application. End user authentication (USER_AUTH): In this level, the client needs to provide a username and password; this is to customize the security for applicationspecific users to ensure that they can access the Tuxedo application. Optional access control (ACL): For all the previously mentioned levels, information needs to be provided by the client and the administrator so that access can be controlled to services, application queues, and events with access control lists. This level allows you to configure access only for those resources that need security; there is restricted access to a certain set of services while still allowing unprotected access to other services. Mandatory access control (MANDATORY_ACL): This is very similar to the ACL level; the only difference is that the resources without an ACL are considered restricted, that is, access is not granted to resources that do not have an ACL permission. In this section, we have discussed all the different types of security features that you may like to consider to secure your Tuxedo application. Data-dependent routing (DDR) Data-dependent routing (or context-based routing) is used to enable a client to send requests for a service to have multiple/distributed copies of it; this is determined by the data in the requested message. Once an administrator has set up data-dependent routing for an application, the client requests can be routed automatically to servers based on the data in the requests. This DDR can be used in three different ways; we will discuss this in the following sections. Horizontally partitioned When a Tuxedo service is associated with a horizontally partitioned database (which means the database has been divided into segments), each segment is used to store a different category of information. As an example, there are different sections in a hospital (for example, ENT and cardiology). So, the same service can be called from different clients, but the request will be routed to the service-particular department server as the database was designed as horizontally partitioned. Rule-based servers A rule-based server is a server that determines whether service requests meet certain application-specific criteria before forwarding them to service routines. For example, in Banking, they have a Primer customer and a Normal customer, a distinction made for certain business reasons (high-value transactions), and the same service is served using two different servers. So, the features of the rule-based servers can be used when you want to handle requests that are almost the same, by taking slightly different actions for business reasons. Distributed applications In a distributed application, you may have a mix of local and remote clients that communicate with multiple servers that could be distributed in multiple geographic locations or machines across organizations over the network. A request is sent for a particular service; it is determined by the data identifying the server that can fulfill the request. The Tuxedo system selects a server to receive the request by matching the data to the routing criteria provided in the Bulletin Board. The ROUTING section of the UBBCONFIG file provides information for data-dependent routing of service requests using the FML, XML, and VIEWS types of buffer. You can also use DDR as a load-balancing mechanism by routing certain requests to a specific server. Data encryption Data encryption means converting data into a coded format that is unintelligible to all users except the user for which the data is intended. When encrypted data arrives at its destination, it is decrypted to convert back to its original format. Encrypting does not increase the data size, but it adds to processing time as the system needs to encrypt and decrypt the data. The data is compressed during encryption, so you may gain some time as less data is being sent across the network. When data is compressed, it helps to increase data security because the data is somewhat scrambled during compression. In the UBBCONFIG file, ENCRYPTION_REQUIRED can be specified in any of the four sections in the configuration hierarchy: the RESOURCES, MACHINES, GROUPS, or SERVICES section. Setting ENCRYPTION_REQUIRED to Y at a particular level means that encryption is required for all the running processes at that level or below it. Data compression This option shrinks an application buffer so that it can be transmitted more quickly over a network to different machine(s). You can set a maximum threshold value for the buffer size to ensure that it automatically compresses the buffer when it crosses the mark. The buffer gets compressed, but it gets decompressed as it reaches the destination. Data compression happens before the data is transported between machines; it improves network performance and enhances the security to a limit as it involves coding/decoding the data. In the UBBCONFIG file, the parameter CMPLIMT under the MACHINES section helps you set the threshold message size for messages bound to remote processes and local processes respectively, on which the automatic data compression will take place. Load balancing The load-balancing technique is used to distribute service requests evenly among servers that offer the same service(s) to ensure that some servers will not be overburdened while some are idle or infrequently used. The LDBAL parameter in the RESOURCES section of the UBBCONFIG file can be used to set the load balancing with Y. Also, the LOAD parameter under the SERVICES section refers to a number; this is very much a relative number you need to come up with according to the time required to execute that service. The statistics are generated based on this weightage for each server, and the Bulletin Board preserves this for each machine. Each Bulletin Board keeps a track of the increasing load associated with each of its servers; this is to ensure that when all the servers are busy, the Tuxedo system can select the one with the minimum load. You do need to do this if you have only a single service in one server, or servers in a single-queue (MSSQ) configuration. In this section, we have discussed various important features of Tuxedo – security, routing, encryption, compression, and load balancing. Each of these features should be considered more carefully during the application, design, and deployment stage to make your application run more efficiently and to address your various business needs without any custom code. Administering the Tuxedo queue (/Q) Tuxedo provides a reliable queue based on the XA-compliant resource manager (TMS – Transaction Manager Server), which provides the framework to store messages in a reliable storage and forward it to different components. These could be services, clients, or components within different Tuxedo processes. The purpose of a queue is to perform time-independent communication. Any client or server can store onto (enqueue) and retrieve (dequeue) a message from the queue. Tuxedo provides the TMQUEUE server, which provides this enqeuing and dequing service. Tuxedo also provides a server called TMQFORWARD, which dequeues a message and forwards it to other services. Messages can be retrieved in any of several ordering schemes, including Last In, First Out (LIFO), First In, First Out (FIFO), priority order, and time-based order. More than one client or server can access the same queue. There are three primary tasks for a Tuxedo administrator to carry out. They are configuring, maintaining, and monitoring the Tuxedo queue. Configuration of resources for /Q There are three servers (TMS, TMQUEUE, and TMQFORWARD) provided by the Tuxedo system that need to be configured in the SERVER section of the UBBCONFIG file. Also, there must be a server group defined for each queue space as per the application usage or design. The TMSNAME and OPENINFO parameters need to be set accordingly; please see the following example in the UBBCONFIG file: *GROUPSTMQUEUE-G1 GRPNO=1 TMSNAME=TMS_QM OPENINFO="TUXEDO/QM:/dev/deviceONE:QueueSpace1"*SERVERSTMQUEUE SRVGRP="TMQUEUE-G1" SRVID=1550 RESTART=Y GRACE=0 CLOPT="-s CUSTOMER:TMQUEUE" In this example, the -s flag of the CLOPT parameter is used to name the queue space served by a given instance of the server. We mentioned that the environment variable for the queue (/Q) is QMCONFIG, which needs to be set in the environment. Creation of queue space and queues To use a queue, we need to create the queue devices, queue space, and queues, in that order; qmadmin is the tool provided by the Tuxedo system to do this for us. %UnixPromt%> qmadmin> crdl /home/applicationQ/CustomerQdev 0 500 At this point, your device is created with a size of 500 physical pages beginning/ offset at block 0; you need to create the queue space next. > qspacecreateQueue space name: QueueSpace1IPC Key for queue space: 12345Size of queue space in disk pages: 300Number of queues in queue space: 3Number of concurrent transactions in queue space: 4Number of concurrent processes in queue space: 4Number of messages in queue space: 20Error queue name: ErrorQInitialize extents (y, n [default=n]): yBlocking factor [default=16]: 16 You have created the queue space and now you need to create a queue using the same qmadmin command. > qcreateQueue name: CustomerQueue order (priority, time, fifo, lifo): fifoOut-of-ordering enqueuing (top, msgid, [default=none]): noneRetries [default=0]: 2Retry delay in seconds [default=0]: 30High limit for queue capacity warning (b for bytes used, B for blocksused,% for percent used, m for messages [default=100%]): 80%Reset (low) limit for queue capacity warning [default=0%]: 0%Queue capacity command:No default queue capacity commandQueue Customer created You have created the Customer queue; use the same steps to create ErrorQ before you exit out of this tool. Note that if you need more space for the queue space, you can do that by using the qaddext command. Monitoring /Q Using qmadmin as the command-line tool or using the Tuxedo console, you can configure, monitor, and change queues in a Tuxedo application. It has a rich set of options (30+) to choose from. In this section, we discussed Tuxedo /Q, which is one of the most important components of Tuxedo for reliable messaging. Tuxedo itself uses /Q for its internal process communication. The Tuxedo administrator is responsible for defining servers and creating queue spaces and queues using qmadmin, as described, and must define at least one queue server group with TMS_QM as the transaction manager server for the group. Summary In this article, we have discussed various important features of Tuxedo – security, routing, encryption, compression, and load balancing. Each of these features should be considered more carefully during the application, design, and deployment stage to make your application run more efficiently and to address your various business needs without any custom code. Also, we discussed responsibilities of Tuxedo administrator for administering the Tuxedo queue. Resources for Article : Further resources on this subject: Oracle Integration and Consolidation Products [Article] Extending Oracle VM Management [Article] High Availability: Oracle 11g R1 R2 Real Application Clusters (RAC) [Article]
Read more
  • 0
  • 0
  • 5949
Modal Close icon
Modal Close icon