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-wireshark-working-packet-streams
Packt
11 Mar 2013
3 min read
Save for later

Wireshark: Working with Packet Streams

Packt
11 Mar 2013
3 min read
(For more resources related to this topic, see here.) Working with Packet Streams While working on network capture, there can be multiple instances of network activities going on. Consider a small example where you are simultaneously browsing multiple websites through your browser. Several TCP data packets will be flowing across your network for all these multiple websites. So it becomes a bit tedious to track the data packets belonging to a particular stream or session. This is where Follow TCP stream comes into action. Now when you are visiting multiple websites, each site maintains its own stream of data packets. By using the Follow TCP stream option we can apply a filter that locates packets only specific to a particular stream. To view the complete stream, select your preferred TCP packet (for example, a GET or POST request). Right-clicking on it will bring up the option Follow TCP Stream. Once you click on Follow TCP Stream, you will notice that a new filter rule is applied to Wireshark and the main capture window reflects all those data packets that belong to that stream. This can be helpful in figuring out what different requests/responses have been generated through a particular session of network interaction. If you take a closer look at the filter rule applied once you follow a stream, you will see a rule similar to tcp.stream eq <Number>. Here Number reflects the stream number which has to be followed to get various data packets. An additional operation that can be carried out here is to save the data packets belonging to a particular stream. Once you have followed a particular stream, go to File | Save As. Then select Displayed to save only the packets belonging to the viewed stream. Similar to following the TCP stream, we also have the option to follow the UDP and SSL streams. The two options can be reached by selecting the particular protocol type (UDP or SSL) and right-clicking on it. The particular follow option will be highlighted according to the selected protocol. The Wireshark menu icons also provide some quick navigation options to migrate through the captured packets. These icons include: Go back in packet history (1): This option traces you back to the last analyzed/selected packet. Clicking on it multiple times keeps pushing you back to your selection history. Go forward in packet history (2): This option pushes you forward in the series of packet analysis. Go to last packet (5): This option jumps your selection to the last packet in your capture window.: This option is useful in directly going to a specific packet number. Go to the first packet (4): This option takes you to the first packet in your current display of the capture window. Go to last packet (5): This option jumps your selection to the last packet in your capture window. Summary In this article, we learned how to work with packet streams. Resources for Article : Further resources on this subject: BackTrack 5: Advanced WLAN Attacks [Article] BackTrack 5: Attacking the Client [Article] Debugging REST Web Services [Article]
Read more
  • 0
  • 0
  • 16575

article-image-article-yui-test
Packt
11 Mar 2013
5 min read
Save for later

YUI Test

Packt
11 Mar 2013
5 min read
(For more resources related to this topic, see here.) YUI Test is one of the most popular JavaScript unit testing frameworks. Although YUI Test is part of the Yahoo! User Interface (YUI) JavaScript library (YUI is an open source JavaScript and CSS library that can be used to build Rich Internet Applications), it can be used to test any independent JavaScript code that does not use the YUI library. YUI Test provides a simple syntax for creating JavaScript test cases that can run either from the browser or from the command line; it also provides a clean mechanism for testing asynchronous (Ajax) JavaScript code. If you are familiar with the syntax of xUnit frameworks (such as JUnit), you will find yourself familiar with the YUI Test syntax. In YUI Test, there are different ways to display test results . You can display the test results in the browser console or develop your custom test runner pages to display the test results. It is preferable to develop custom test runner pages in order to display the test results in all the browsers because some browsers do not support the console object. The console object is supported in Firefox with Firebug installed, Safari 3+, Internet Explorer 8+, and Chrome. Before writing your first YUI test, you need to know the structure of a custom YUI test runner page. We will create the test runner page, BasicRunner.html, that will be the basis for all the test runner pages used in this article. In order to build the test runner page, first of all you need to include the YUI JavaScript file yui-min.js—from the Yahoo! Content Delivery Network (CDN)—in the BasicRunner.html file, as follows: code 1 At the time of this writing, the latest version of YUI Test is 3.6.0, which is the one used in this chapter. After including the YUI JavaScript file, we need to create and configure a YUI instance using the YUI().use API, as follows: code 1 The YUI().use API takes the list of YUI modules to be loaded. For the purpose of testing, we need the YUI 'test' and 'console' modules (the 'test' module is responsible for creating the tests, while the 'console' module is responsible for displaying the test results in a nifty console component). Then, the YUI().use API takes the test's callback function that is called asynchronously once the modules are loaded. The Y parameter in the callback function represents the YUI instance. As shown in the following code snippet taken from the BasicRunner.html file, you can write the tests in the provided callback and then create a console component using the Y.Console object: code 1 The console object is rendered as a block element by setting the style attribute to 'block', and the results within the console can be displayed in the sequence of their executions by setting the newestOnTop attribute to false . Finally, the console component is created on the log div element. Now you can run the tests, and they will be displayed automatically by the YUI console component. The following screenshot shows the BasicRunner.html file's console component without any developed tests: Image Writing your first YUI test The YUI test can contain test suites, test cases, and test functions. A YUI test suite is a group of related test cases. Each test case includes one or more test functions for the JavaScript code. Every test function should contain one or more assertion in order to perform the tests and verify the outputs. The YUI Test.Suite object is responsible for creating a YUI test suite, while the YUI Test.Case object creates a YUI test case. The add method of the Test.Suite object is used for attaching the test case object to the test suite. The following code snippet shows an example of a YUI test suite: code 1 As shown in the preceding code snippet, two test cases are created. The first test case is named testcase1; it contains two test functions, testFunction1 and testFunction2. In YUI Test, you can create a test function simply by starting the function name with the word "test". The second test case is named testcase2; and it contains a single test function, testAnotherFunction. A test suite is created with the name Testsuite. Finally, testcase1 and testcase2 are added to the Testsuite test suite. In YUI Test, you have the option of creating a friendly test name for the test function, as follows: code 1 The "some Testcase" test case contains two tests with the names "The test should do X" and "The test should do Y". Summary In this article, we learned about the basics of YUI Test framework which is used for testing JavaScript applications, and also a walkthrough on how to write your YUI test using the Test.Suite object (that is responsible for creating a YUI test suite) and the Test.Case object (for creating a YUI test case). Resources for Article :   Further resources on this subject: Using Javascript Effects to enhance your Joomla! website for Visitors [Article] Exclusive Offer On jQuery Books: Winner Of 2010 Open-Source JavaScript Libraries [Article] Basics of Exception Handling Mechanism in JavaScript Testing [Article]
Read more
  • 0
  • 0
  • 2311

article-image-xendesktop-architecture
Packt
08 Mar 2013
12 min read
Save for later

The XenDesktop architecture

Packt
08 Mar 2013
12 min read
(For more resources related to this topic, see here.) Virtual desktops can reside in two locations: Hosted at the data center: In this case, you host the desktop/application at the data center and provide users with these desktops through different types of technologies (remote desktop client and Citrix ReceiverXenDesktop). Hosted at the client-side machine: In this case, users access the virtual desktop or application through a VM that is loaded at the client side through different types of client-side virtualizations (level 1, level 2, or streaming). Hosted desktops can be provided using: VM or physical PC : In this type, users are accessing the desktop on an OS that is installed to a dedicated VM or physical machine. You typically install the OS to the VM and clone the VM using hypervisor or storage cloning technology, or install the OS to a physical machine using automatic installation technology, enabling users to access the desktop. The problem with this method is that storage consumption is very high since you will have to load the VM/PC with the OS. Additionally, adding/removing/managing the infrastructure is sometimes complex and time consuming. Streamed OS to VM or physical PC : This type is the same as the VM or physical PC type; however, an OS is not installed on the VM or the physical machine but rather streamed to them. Machines (virtual or physical) will boot from the network, and load the required virtual hosted desktop (VHD) over the network and boot from the VHD again. It has a lot of benefits, including simplicity and storage optimization since an OS is not installed. A single VHD can support thousands of users. Hosted shared desktop : In this model, you provide users with access to a desktop that is hosted on the server and published to users. This method is implemented with Microsoft Terminal Services and XenApp plus Terminal services. This provides high scalability in terms of the number of users per server scale but provides limited security and limited environment isolation for the user. Now that we have visited the different types of DV and how they are delivered and located, let's see what the pros and cons are of each type: Virtualization Type Pros Cons Usage Hosted PC - not streamed PCs are located at the DC, allowing better control. Can be used in a consolidation scenario along with refurbished PCs. Superior performance, since a physical HW provides HW resources to the OS and the OS is preinstalled. Management/deployment is difficult, especially in large-scale deployment scenarios. Operating system deployment and maintenance is an overwhelming process. Low storage optimization and utilization since each OS is preinstalled. High electricity consumption since all of those PCs (normal or blade) are consuming electricity and HVAC resources. Requires users to be connected to the DC if they wish to access the VD. Suitable for refurbished PCs and reusing old PCs. Suitable for task workers, call center agents, and individuals with similar workloads. Hosted VM - not streamed VMs are located at the DC, allowing better control. Superior performance since the OS is preinstalled, but performance is less than what is provided with physical PCs. High consolidation scenario using server virtualization; it provides green IT for your DC. Management/deployment is difficult, especially in large-scale deployment scenarios. Low storage optimization and utilization since each OS is preinstalled, thus requiring a storage side optimization/de-duplication technique that might add costs for some vendors. Hypervisor management could add a costs for some vendors. Requires users to be connected to the DC if they wish to access the VD. Suitable for task workers and call center agents. Some organizations use this model to give users access to server-level hardware resources such as rendering and CAD software. Hosted PC - streamed PCs are located at the DC, allowing better control. Can be used in a consolidation scenario along with refurbished PCs. Superior performance since a physical HW provides HW resources to the OS. High storage optimization and utilization since each OS is streamed on demand. Deployment is difficult, especially in large-scale deployment scenarios. High electricity consumption since all of those PCs (normal or blade) are consuming electricity and HVAC resources. Requires high storage performance to provide write cache storage for the OS. Requires the deployment of a streaming technology provider within the architecture. PCs must be connected to streaming servers over the network to stream the OS's VHD. Requires users to be connected to the DC if they wish to access the VD. Suitable for task workers and call center agents. Some organizations use this model to give users access to server-level hardware resources such as rendering and CAD software. Hosted VMs - Streamed VMs are located at the DC, allowing better control. Superior performance, but performance less than what is provided with physical PCs. High consolidation scenario using of server virtualization; it provides green IT for your DC. High storage optimization and utilization since each OS is streamed on demand. Requires high storage performance to provide write cache storage for the OS. Requires the deployment of a streaming technology provider within the architecture. Hyper-v management could add some costs for some vendors. VMs must be connected to streaming servers over the network to stream the OS's VHD. Requires users to be connected to the DC if they wish to access the VD. Hosted for users who need hosted environment but do not need powerful resources and isolated environment that is not shared between them. Hosted shared desktops Desktop is located in the DC, allowing better control. High capacity for server/user ratio. Server consolidation can be achieved by hosting server on top of hypervisor technology. Easy to manage, deploy, and administrate. Different types of users will require different types of applications, HW resources, and security, which cannot be fully achieved in HSD mode. Resource isolation for users cannot be fully achieved as in other models. Customization for the OS, application, and for profile management could be a little tricky. Requires users to be connected to the DC if they wish to access the VD. Suitable for most VDs, task workers, call center agents, and PC users. Level 1 client hypervisors Since VMs are hosted on the clients, the load is offloaded from the DC and less HW investments are required. Users can use their VD without being connected to the network. Client HW can be utilized, including graphic cards and sound devices. VD are not located on the DC, thus could be less secure for some organizations. Might not fit with organizations that allow users to use their own personal computers. Could require some HW investments at the client HW side to allow adequate performance. The client is connected to a central storage for the purpose of backing up the company's data. This type might fit into smaller environments, but for larger environments, management could be hard or even impossible. Suitable for mobile users and power users who are not connected to the data center at all times. Level 2 client hypervisors Since VMs are hosted on the clients, the load is offloaded from the DC and less HW investments are required. Users can use their VD without being connected to the network. Client HW can be utilized, including graphic cards and sound devices. Allows the users to mix the usage of virtual and physical desktops. VD are not located on the DC, thus could be less secure for some organizations. Might not fit with organizations that allows users to use their own personal computers. Could require some HW investments at the client HW side to allow adequate performance. The client is connected to a central storage for the purpose of backing up the company's data backup. This type might fit into smaller environments, but for larger environments, management could be hard or even impossible. Suitable for mobile users and power users who are not connected to the data center at all times. XenDesktop components Now that we have reviewed the DV delivery methods, let us see the components that are used by XenDesktop to build the DV infrastructure. The XenDesktop infrastructure can be divided into two major blocks the delivery block and the virtualization block, as shown in the next two sections. Delivery block This section is responsible for the VD delivery part, including authenticating users, remote access, redirecting users to the correct servers, and so on. This part consists of the following servers: The Desktop Delivery Controller Description : The Desktop Delivery Controller (DDC) is the core service within the XenDesktop deployment. Think of it as the glue that holds everything together within the XenDesktop infrastructure. It is responsible for authenticating the user, managing the user's connections, and redirecting the user to the assigned VD. Workload type: The DDC handles an HTTP/HTTPS connection sent from the web interface services to its XML service, passing the user's credentials and querying for the user's assigned VD. High availability: This is achieved by introducing extra DDCs in the infrastructure within the farm. To achieve high availability and load balancing between them, these DDCs will be configured in the web interface that will be loaded to balance them. Web interface (WI) Description : The web interface is a very critical part of the XenDesktop infrastructure. The web interface has three very important tasks to handle within the XenDesktop infrastructure. They are as follows: 1. It receives the user credentials either explicitly, by a pass-through, or by other means 2. It passes the credentials to the DDC 3. It passes the list of assigned VDs to the user after communicating with the DDC XML service 4. It passes the connection information to the client inside the ica file so the client can establish the connection Workload type: The WI receives an HTTP/HTTPS connection from users initiating connections to the XenDesktop farm either internally and externally. High availability: This is achieved by installing multiple WI servers and load balancing the traffic between the WI servers, using either Windows NLB or other external hardware load balancer devices, such as Citrix NetScaler. Licensing server Description : The licensing server is the one responsible for supplying the DDC with the required license when the virtual desktop agent tries to acquire one from the DDC. Workload type: When you install the DDC, a license server is required to be configured to provide the licenses to the DDC. When you install the license server, you import the licenses you purchased from Citrix to the licensing server. High availability: This can be achieved in two ways, by either installing a licensing server on Windows Cluster or installing another offline licensing server that could be brought online in the case of the first server failure. Database server Description : The Database server is responsible for holding the static and dynamic data as configuration and session information. XenDesktop 5 no longer uses the IMA data store as the central database in which the con figuration information is stored. Instead, a Microsoft SQL Server database is used as the data store for both configuration and session information. Workload type: The database server receives the DB connections from DDC and provisioning servers (PVS), querying for farm/site configuration. It is important to note that the XenDesktop creates a DB that is different from a provisioning services farm database; both databases could be located on the same servers, same instance or different instances, or different servers. High availability: This can be achieved by using SQL clustering plus Microsoft Windows or SQL replication. Virtualization block The virtualization block provides the VD service to the users through the delivery block; I often see that the virtualization block is referenced as a hypervisor stack, but as you saw previously, this can be done using blade PCs or PCs located in the DC, so don't miss that. The virtualization block consists of: Desktop hosting infrastructure Description : This is the infrastructure that hosts the virtual desktop that is accessed by users. This could be either a virtual machine infrastructure, which could be XenServer, Hyper-v, or VMware VSPHERE ESXI servers, or a physical machine infrastructure, for either regular PCs or blade ones. Workload type: Hosts OSs that will provide the desktops to users that are accessing them from the delivery network. The OS could be running as either installed or streamed and can be either physical or virtual. High availability: Depends on the type of DHI used but can be developed by using clustering for hypervisors or adding extra HW in the case of the physical ones. Provisioning servers infrastructure Description : The provisioning servers are usually coupled with the XenDesktop deployment. Most of the organizations I worked with never deployed preinstalled OSs to VMs or physical machines, but rather streamed the OSs to physical or virtual machines since they provide huge storage optimization and utilization. Provisioning servers are the servers that actually stream the OS in the form of VHD over the network to the machines requesting the streamed OS. Workload type: Provisioning servers stream the OS as VHD files over the network to the machines, which can be either physical or virtual. High availability: This can be achieved by adding extra PVS servers to the provisioning servers' farm and configuring them in the DHCP servers or in the bootstrap file. XenDesktop system requirements XenDesktop has different components; each has its own requirements. The detailed requirements for these components can be checked and reviewed on the Citrix XenDesktop requirements website: http://support.citrix.com/proddocs/topic/xendesktop-rho/cds-sys-reqs-wrapper-rho.html Summary In this article, we saw what the XenDesktop architecture consists of and its various components. Resources for Article : Further resources on this subject: Application Packaging in VMware ThinApp 4.7 Essentials [Article] Designing a XenApp 6 Farm [Article] Managing Citrix Policies [Article]
Read more
  • 0
  • 0
  • 6018

article-image-modules
Packt
08 Mar 2013
6 min read
Save for later

Modules

Packt
08 Mar 2013
6 min read
(For more resources related to this topic, see here.) We are going to try mastering modules which will help us break up our application into sensible components. Our configuration file for modules is given as follows: var guidebookConfig = function($routeProvider) { $routeProvider .when('/', { controller: 'ChaptersController', templateUrl: 'view/chapters.html' }) .when('/chapter/:chapterId', { controller: 'ChaptersController', templateUrl: 'view/chapters.html' }) .when('/addNote/:chapterId', { controller: 'AddNoteController', templateUrl: 'view/addNote.html' }) .when('/deleteNote/:chapterId/:noteId', { controller: 'DeleteNoteController', templateUrl: 'view/addNote.html' }) ; }; var Guidebook = angular.module('Guidebook', []). config(guidebookConfig); The last line is where we define our Guidebook module. This creates a namespace for our application. Look at how we defined the add and delete note controllers: Guidebook.controller('AddNoteController', function ($scope, $location, $routeParams, NoteModel) { var chapterId = $routeParams.chapterId; $scope.cancel = function() { $location.path('/chapter/' + chapterId); } $scope.createNote = function() { NoteModel.addNote(chapterId, $scope.note.content); $location.path('/chapter/' + chapterId); } } ); Guidebook.controller('DeleteNoteController', function ($scope, $location, $routeParams, NoteModel) { var chapterId = $routeParams.chapterId; NoteModel.deleteNote(chapterId, $routeParams.noteId); $location.path('/chapter/' + chapterId); } ); Since Guidebook is a module, we're really making a call to module.controller() to define our controller. We also called Guidebook.service(), which is really just module.service(), to define our models. One advantage of defining controllers, models, and other components as part of a module is that it groups them together under a common name. This way, if we have multiple applications on the same page, or content from another framework, it's easy to remember which components belong to which application. The second advantage of defining components as part of a module is that AngularJS will do some heavy lifting for us. Take our NoteModel, for instance. We defined it as a service by calling module.service(). AngularJS provides some extra functionality to services within a module. One example of that extra functionality is dependency injection. This is why in our DeleteNoteController, we can include our note model simply by asking for it in the controller's function: Guidebook.controller('DeleteNoteController', function ($scope, $location, $routeParams, NoteModel) { var chapterId = $routeParams.chapterId; NoteModel.deleteNote(chapterId, $routeParams.noteId); $location.path('/chapter/' + chapterId); } ); This only works because we registered both DeleteNoteController and NoteModel as parts of the same module. Let's talk a little more about NoteModel. When we define controllers on our module, we call module.controller(). For our models, we called module.service(). Why isn't there a module.model()? Well, it turns out models are a good bit more complicated than controllers. In our Guidebook application, our models were relatively simple, but what if we were mapping our models to some external API, or a full- fledged relational database? Because there are so many different types of models with vastly different levels of complexity, AngularJS provides three ways to define a model: as a service, as a factory, and as a provider. We'll now see how we define a service in our NoteModel: Guidebook.service('NoteModel', function() { this.getNotesForChapter = function(chapterId) { ... }; this.addNote = function(chapterId, noteContent) { ... }; this.deleteNote = function(chapterId, noteId) { ... }; }); It turns out this is the simplest type of model. We're simply defining an object with a number of functions that our controllers can call. This is all we needed in our case, but what if we were doing something a little more complicated? Let's pretend that instead of using HTML5 local storage to store and retrieve our note data, we're getting it from a web server somewhere. We'll need to add some logic to set up and manage the connection with that server. The service definition can help us a little here with the setup logic; we're returning a function, so we could easily add some initialization logic. When we get into managing the connection, though, things get a little messy. We'll probably want a way to refresh the connection, in case it drops. Where would that go? With our service definition, our only option is to add it as a function like we have for getNotesForChapter(). This is really hacky; we're now exposing how the model is implemented to the controller, and worse, we would be allowing the controller to refresh the connection. In this case, we would be better off using a factory. Here's what our NoteModel would look like if we had defined it as a factory: Guidebook.factory('NoteModel', function() { return { getNotesForChapter: function(chapterId) { ... }, addNote: function(chapterId, noteContent) { ... }, deleteNote: function(chapterId, noteId) { ... } }; }); Now we can add more complex initialization logic to our model. We could define a few functions for managing the connection privately in our factory initialization, and give our data methods references to them. The following is what that might look like: Guidebook.factory('NoteModel', function() { var refreshConnection = function() { ... }; return { getNotesForChapter: function(chapterId) { ... refreshConnection(); ... }, addNote: function(chapterId, noteContent) { ... refreshConnection(); ... }, deleteNote: function(chapterId, noteId) { ... refreshConnection(); ... } }; }); Isn't that so much cleaner? We've once again kept our model's internal workings completely separate from our controller. Let's get even crazier. What if we backed our models with a complex database server with multiple endpoints? How could we configure which endpoint to use? With a service or a factory, we could initialize this in the model. But what if we have a whole bunch of models? Do we really want to hardcode that logic into each one individually? At this point, the model shouldn't be making this decision. We want to choose our endpoints in a configuration file somewhere. Reading from a configuration file in either a service or a factory will be awkward. Models should focus solely on storing and retrieving data, not messing around with configuration updates. Provider to the rescue! If we define our NoteModel as a provider, we can do all kinds of configuration from some neatly abstracted configuration file. Here's how our NoteModel looks if we convert it into a provider: Guidebook.provider('NoteModel', function() { this.endpoint = 'defaultEndpoint'; this.setEndpoint = function(newEndpoint) { this.endpoint = newEndpoint; }; this.$get = function() { var endpoint = this.endpoint; var refreshConnection = function() { // reconnect to endpoint }; return { getNotesForChapter: function(chapterId) { ... refreshConnection(); ... }, addNote: function(chapterId, noteContent) { ... refreshConnection(); ... }, deleteNote: function(chapterId, noteId) { ... refreshConnection(); ... } }; }; }); Now if we add a configuration file to our application, we can configure the endpoint from outside the model: Guidebook.config(function(NoteModelProvider) { NoteModelProvider.setEndpoint('anEndpoint'); }); Providers give us a very powerful architecture. If we have several models, and we have several endpoints, we can configure all of them in one single configuration file. This is ideal, as our models remain single-purpose and our controllers still have no knowledge of the internals of the models they depend on. Summary Thus we saw in this article modules provide an easy, flexible way to create components in AngularJS. We've seen three different ways to define a model, and we've discussed the benefits of using a module to create an application namespace. Resources for Article : Further resources on this subject: Framework Comparison: Backbase AJAX framework Vs Other Similar Framework (Part 1) [Article] Getting Started With Spring MVC - Developing the MVC components [Article] ASP.NET MVC Framework [Article]
Read more
  • 0
  • 0
  • 9456

Packt
08 Mar 2013
19 min read
Save for later

Painting – Multi-finger Paint

Packt
08 Mar 2013
19 min read
(For more resources related to this topic, see here.) What is multi-touch? The genesis of multi-touch on Mac OS X was the ability to perform two finger scrolling on a trackpad. The technology was further refined on mobile touch screen devices such as the iPod Touch, iPhone, and iPad. And it has also matured on the Mac OS X platform to allow the use of multi-touch or magic trackpad combined with one or more fingers and a motion to interact with the computer. Gestures are intuitive and allow us to control what is on the screen with fluid motions. Some of the things that we can do using multi-touch are as follows: Two finger scrolling: This is done by placing two fingers on the trackpad and dragging in a line Tap or pinch to zoom : This is done by tapping once with a single finger, or by placing two fingers on the trackpad and dragging them closer to each other Swipe to navigate: This is done by placing one or more fingers on the trackpad and quickly dragging in any direction followed by lifting all the fingers Rotate : This is done by placing two fingers on the trackpad and turning them in a circular motion while keeping them on the trackpad But these gestures just touch the surface of what is possible with multi-touch hardware. The magic trackpad can detect and track all 10 of our fingers with ease. There are plenty of new things that can be done with multi-touch — we are just waiting for someone to invent them. Implementing a custom view Multi-touch events are sent to the NSView objects. So before we can invent that great new multi-touch thing, we first need to understand how to implement a custom view. Essentially, a custom view is a subclass of NSView that overrides some of the behavior of the NSView object. Primarily, it will override the drawRect: method and some of the event handling methods. Time for action — creating a GUI with a custom view By now we should be familiar with creating new Xcode projects so some of the steps here are very high level. Let's get started! Create a new Xcode project with Automatic Reference Counting enabled and these options enabled as follows: Option Value Product Name Multi-Finger Paint Company Identifier com.yourdomain Class Prefix Your initials After Xcode creates the new project, design an icon and drag it in to the App Icon field on the TARGET Summary. Remember to set the Organization in the Project Document section of the File inspector. Click on the filename MainMenu.xib in the project navigator. Select the Multi-Finger Paint window and in the Size inspector change its Width and Height to 700 and 600 respectively. Enable both the Minimum Size and Maximum Size Constraints values. From the Object Library , drag a custom view into the window. In the Size inspector , change the Width and Height of the custom view to 400 and 300 respectively. Center the window using the guides that appear. From the File menu, select New>, then select the File…option. Select the Mac OS X Cocoa Objective-C class template and click on the Next button. Name the class BTSFingerView and select subclass of NSView. It is very important that the subclass is NSView. If we make a mistake and select the wrong subclass, our App won't work. Click on the button titled Create to create the .h and .m files. Click on the filename BTSFingerView.m and look at it carefully. It should look similar to the following code: // // BTSFingerView.m // Multi-Finger Paint // // Created by rwiebe on 12-05-23. // Copyright (c) 2012 BurningThumb Software. All rights reserved. // #import "BTSFingerView.h" @implementation BTSFingerView - (id)initWithFrame:(NSRect)frame { self = [super initWithFrame:frame]; if (self) { // Initialization code here. } return self; } - (void)drawRect:(NSRect)dirtyRect { // Drawing code here. } @end By default, custom views do not receive events (keyboard, mouse, trackpad, and so on) but we need our custom view to receive events. To ensure our custom view will receive events, add the following code to the BTSFingerView.m file to accept the first responder: /* ** - (BOOL) acceptsFirstResponder ** ** Make sure the view will receive ** events. ** ** Input: none ** ** Output: YES to accept, NO to reject */ - (BOOL) acceptsFirstResponder { return YES; } And, still in the BTSFingerView.m file, modify the initWithFrame method to allow the view to accept touch events from the trackpad as follows: - (id)initWithFrame:(NSRect)frame { self = [super initWithFrame:frame]; if (self) { // Initialization code here. // Accept trackpad events [self setAcceptsTouchEvents: YES]; } return self; } Once we are sure our custom view will receive events, we can start the process of drawing its content. This is done in the drawRect: method. Add the following code to the drawRect: method to clear it with a transparent color and draw a focus ring if the view is first responder: /* ** - (void)drawRect:(NSRect)dirtyRect ** ** Draw the view content ** ** Input: dirtyRect - the rectangle to draw ** ** Output: none */ - (void)drawRect:(NSRect)dirtyRect { // Drawing code here. // Preserve the graphics content // so that other things we draw // don't get focus rings [NSGraphicsContext saveGraphicsState]; // color the background transparent [[NSColor clearColor] set]; // If this view has accepted first responder // it should draw the focus ring if ([[self window] firstResponder] == self) { NSSetFocusRingStyle(NSFocusRingAbove); } // Fill the view with fully transparent // color so that we can see through it // to whatever is below [[NSBezierPath bezierPathWithRect:[self bounds]] fill]; // Restore the graphics content // so that other things we draw // don't get focus rings [NSGraphicsContext restoreGraphicsState]; } Next, we need to go back into the .xib file, and select our custom view, and then select the Identity Inspector where we will see that in the section titled Custom Class, the Class field contains NSView as the class. Finally, to connect this object to our new custom view program code, we need to change the Class to BTSFingerView as shown in the following screenshot: What just happened? We created our Xcode project and implemented a custom NSView object that will receive events. When we run the project we notice that the focus ring is drawn so that we can be confident the view has accepted the firstResponder status. How to receive multi-touch events Because our custom view accepts first responder, the Mac OS will automatically send events to it. We can override the methods that process the events that we want to handle in our view. Specifically, we can override the following events and process them to handle multi-touch events in our custom view: - (void)touchesBeganWithEvent:(NSEvent *)event - (void)touchesMovedWithEvent:(NSEvent *)event - (void)touchesEndedWithEvent:(NSEvent *)event - (void)touchesCancelledWithEvent:(NSEvent *)event Time for action — drawing our fingers When the multi-touch or magic trackpad is touched, our custom view methods will be invoked and we will be able to draw the placement of our fingers on the trackpad in our custom view. In Xcode, click on the filename BTSFingerView.h in the project navigator and add the following highlighted property: // // BTSFingerView.h // Multi-Finger Paint // // Created by rwiebe on 12-05-23. // Copyright (c) 2012 BurningThumb Software. All rights reserved. // #import <Cocoa/Cocoa.h> @interface BTSFingerView : NSView // A reference to the object that will // store the currently active touches @property (strong) NSMutableDictionary *m_activeTouches; @end In Xcode, click on the file BTSFingerView.m in the project navigator and add the following program code to synthesize the property: // // BTSFingerView.m // Multi-Finger Paint // // Created by rwiebe on 12-05-23. // Copyright (c) 2012 BurningThumb Software. All rights reserved. // #import "BTSFingerView.h" @implementation BTSFingerView // Synthesize the object that will // store the currently active touches @synthesize m_activeTouches; Add the following code to the initWithFrame: method in the BTSFingerView.m file to create the dictionary object that will be used to store the active touch objects: - (id)initWithFrame:(NSRect)frame { self = [super initWithFrame:frame]; if (self) { // Initialization code here. // Create the mutable dictionary that // will hold the list of currently active // touch events m_activeTouches = [[NSMutableDictionary alloc] init]; } return self; } Add the following code to the BTSFingerView.m file to add BeganWith touch events to the dictionary of active touches: /** ** - (void)touchesBeganWithEvent:(NSEvent *)event ** ** Invoked when a finger touches the trackpad ** ** Input: event - the touch event ** ** Output: none */ - (void)touchesBeganWithEvent:(NSEvent *)event { // Get the set of began touches NSSet *l_touches = [event touchesMatchingPhase:NSTouchPhaseBegan inView:self]; // For each began touch, add the touch // to the active touches dictionary // using its identity as the key for (NSTouch *l_touch in l_touches) { [m_activeTouches setObject:l_touch forKey:l_touch. identity]; } // Redisplay the view [self setNeedsDisplay:YES]; } Add the following code to the BTSFingerView.m file to add moved touch events to the dictionary of active touches: /** ** - (void)touchesMovedWithEvent:(NSEvent *)event ** ** Invoked when a finger moves on the trackpad ** ** Input: event - the touch event ** ** Output: none */ - (void)touchesMovedWithEvent:(NSEvent *)event { // Get the set of move touches NSSet *l_touches = [event touchesMatchingPhase:NSTouchPhaseMoved inView:self]; // For each move touch, update the touch // in the active touches dictionary // using its identity as the key for (NSTouch *l_touch in l_touches) { // Update the touch only if it is found // in the active touches dictionary if ([m_activeTouches objectForKey:l_touch.identity]) { [m_activeTouches setObject:l_touch forKey:l_touch.identity]; } } // Redisplay the view [self setNeedsDisplay:YES]; } Add the following code to the BTSFingerView.m file to remove the touch from the dictionary of active touches when the touch ends: /** ** - (void)touchesEndedWithEvent:(NSEvent *)event ** ** Invoked when a finger lifts off the trackpad ** ** Input: event - the touch event ** ** Output: none */ - (void)touchesEndedWithEvent:(NSEvent *)event { // Get the set of ended touches NSSet *l_touches = [event touchesMatchingPhase:NSTouchPhaseEnded inView:self]; // For each ended touch, remove the touch // from the active touches dictionary // using its identity as the key for (NSTouch *l_touch in l_touches) { [m_activeTouches removeObjectForKey:l_touch.identity]; } // Redisplay the view [self setNeedsDisplay:YES]; } Add the following code to the BTSFingerView.m file to remove the touch from the dictionary of active touches when the touch is cancelled: /** ** - (void)touchesCancelledWithEvent:(NSEvent *)event ** ** Invoked when a touch is cancelled ** ** Input: event - the touch event ** ** Output: none */ - (void)touchesCancelledWithEvent:(NSEvent *)event { // Get the set of cancelled touches NSSet *l_touches = [event touchesMatchingPhase:NSTouchPhaseCancelled inView:self]; // For each cancelled touch, remove the touch // from the active touches dictionary // using its identity as the key for (NSTouch *l_touch in l_touches) { [m_activeTouches removeObjectForKey:l_touch.identity]; } // Redisplay the view [self setNeedsDisplay:YES]; } When we touch the trackpad we are going to draw a "finger cursor" in our custom view. We need to decide how big we want that cursor to be and the color that we want the cursor to be. Then we can add a series of #define to the file named BTSFingerView.h to define that value: // Define the size of the cursor that // will be drawn in the view for each // finger on the trackpad #define D_FINGER_CURSOR_SIZE 20 // Define the color values that will // be used for the finger cursor #define D_FINGER_CURSOR_RED 1.0 #define D_FINGER_CURSOR_GREEN 0.0 #define D_FINGER_CURSOR_BLUE 0.0 #define D_FINGER_CURSOR_ALPHA 0.5 Now we can add the program code to our drawRect: implementation that will draw the finger cursors in the custom view. // For each active touch for (NSTouch *l_touch in m_activeTouches.allValues) { // Create a rectangle reference to hold the // location of the cursor NSRect l_cursor; // Determine where the touch point NSPoint l_touchNP = [l_touch normalizedPosition]; // Calculate the pixel position of the touch point l_touchNP.x = l_touchNP.x * [self bounds].size.width; l_touchNP.y = l_touchNP.y * [self bounds].size.height; // Calculate the rectangle around the cursor l_cursor.origin.x = l_touchNP.x - (D_FINGER_CURSOR_SIZE / 2); l_cursor.origin.y = l_touchNP.y - (D_FINGER_CURSOR_SIZE / 2); l_cursor.size.width = D_FINGER_CURSOR_SIZE; l_cursor.size.height = D_FINGER_CURSOR_SIZE; // Set the color of the cursor [[NSColor colorWithDeviceRed: D_FINGER_CURSOR_RED green: D_FINGER_CURSOR_GREEN blue: D_FINGER_CURSOR_BLUE alpha: D_FINGER_CURSOR_ALPHA] set]; // Draw the cursor as a circle [[NSBezierPath bezierPathWithOvalInRect: l_cursor] fill]; } What just happened? We implemented the methods required to keep track of the touches and to draw the location of the touches in our custom view. If we run the App now, and move the mouse pointer over the view area, and then touch the trackpad, we will see red circles that track our fingers being drawn in the view as shown in the following screenshot: What is an NSBezierPath? A Bezier Path consists of straight and curved line segments that can be used to draw recognizable shapes. In our program code, we use Bezier Paths to draw a rectangle and a circle but a Bezier Path can be used to draw many other shapes. How to manage the mouse cursor One of the interesting things about the trackpad and the mouse is the association between a single finger touch and the movement of the mouse cursor. Essentially, Mac OS X treats a single finger movement as if it was a mouse movement. The problem with this is that when we move just a single finger on the trackpad, the mouse cursor will move away from our NSView causing it to lose focus so that when we lift our finger we need to move the mouse cursor back to our NSView to receive touch events. Time for action — detaching the mouse cursor from the mouse hardware The solution to this problem is to detach the mouse cursor from the mouse hardware (typically called capturing the mouse) whenever a touch event is active so that the cursor is not moved by touch events. In addition, since a "stuck" mouse cursor may be cause for concern to our App user, we can hide the mouse cursor when touches are active. In Xcode, click on the file named BTSFingerView.h in the project navigator and add the following flag to the interface: @interface BTSFingerView : NSView { // Define a flag so that touch methods can behave // differently depending on the visibility of // the mouse cursor BOOL m_cursorIsHidden; } In Xcode, click on the file named BTSFingerView.m in the project navigator. Add the following code to the beginning of the touchesBeganWithEvent: method to detach and hide the mouse cursor when a touch begins. We only want to do this one time so it is guarded by a BOOL flag and an if statement to make sure we don't do it for every touch that begins. - (void)touchesBeganWithEvent:(NSEvent *)event { // If the mouse cursor is not already hidden, if (NO == m_cursorIsHidden) { // Detach the mouse cursor from the mouse // hardware so that moving the mouse (or a // single finger) will not move the cursor CGAssociateMouseAndMouseCursorPosition(false); // Hide the mouse cursor [NSCursor hide]; // Remember that we detached and hid the // mouse cursor m_cursorIsHidden = YES; } Add the following code to the end of the touchesEndedWithEvent: method to attach and unhide the mouse cursor when all touches end. We use a BOOL flag to remember the state of the cursor so that the touchesBeganWithEvent: method will re-hide it when the next touch begins. // If there are no remaining active touches if (0 == [m_activeTouches count]) { // Attach the mouse cursor to the mouse // hardware so that moving the mouse (or a // single finger) will move the cursor CGAssociateMouseAndMouseCursorPosition(true); // Show the mouse cursor [NSCursor unhide]; // Remember that we attached and unhid the // mouse cursor so that the next touch that // begins will detach and hide it m_cursorIsHidden = NO; } // Redisplay the view [self setNeedsDisplay:YES]; } Add the following code to the end of the touchesCancelledWithEvent: method to attach and unhide the mouse cursor when all touches end. We use a BOOL flag to remember the state of the cursor so that the touchesBeganWithEvent: method will re-hide it when the next touch begins. // If there are no remaining active touches if (0 == [m_activeTouches count]) { // Attach the mouse cursor to the mouse // hardware so that moving the mouse (or a // single finger) will move the cursor CGAssociateMouseAndMouseCursorPosition(true); // Show the mouse cursor [NSCursor unhide]; // Remember that we attached and unhid the // mouse cursor so that the next touch that // begins will detach and hide it m_cursorIsHidden = NO; } // Redisplay the view [self setNeedsDisplay:YES]; } While we are looking at the movement of the mouse, we also notice that the focus ring for our custom view is being drawn regardless of whether or not the mouse cursor is over our view. Since touch events will only be sent to our view if the mouse cursor is over it, we want to change the program code so that the focus ring only appears when the mouse cursor is over the custom view. This is something we can do with another BOOL flag. Add the following code to the file to define a BOOL flag that will allow us to determine if the mouse cursor is over our custom view: // Define a flag so that view methods can behave // differently depending on the position of the // mouse cursor BOOL m_mouseIsInFingerView; In the file named BTSFingerView.m, add the following code to create a tracking rectangle that matches the bounds of our custom view. Once the tracking rectangle is active, the methods mouseEntered: and mouseExited: will be automatically invoked as the mouse cursor enters and exits our custom view. /** ** - (void)viewDidMoveToWindow ** ** Informs the receiver that it has been added to ** a new view hierarchy. ** ** We need to make sure the view window is valid ** and when it is, we can add the tracking rect ** ** Once the tracking rect is added the mouseEntered: ** and mouseExited: events will be sent to our view ** */ - (void)viewDidMoveToWindow { // Is the views window valid if ([self window] != nil) { // Add a tracking rect such that the // mouseEntered; and mouseExited: methods // will be automatically invoked [self addTrackingRect:[self bounds] owner:self userData:NULL assumeInside:NO]; } } In the file named BTSFingerView.m, add the following code to implement the mouseEntered: and mouseExited: methods. In those methods, we set the BOOL flag so that the drawRect: method knows whether or not to draw the focus ring. /** ** - (void)mouseEntered: ** ** Informs the receiver that the mouse cursor ** entered a tracking rectangle ** ** Since we only have a single tracking rect ** we know the mouse is over our custom view ** */ - (void)mouseEntered:(NSEvent *)theEvent { // Set the flag so that other methods know // the mouse cursor is over our view m_mouseIsInFingerView = YES; // Redraw the view so that the focus ring // will appear [self setNeedsDisplay:YES]; } /** ** - (void)mouseExited: ** ** Informs the receiver that the mouse cursor ** exited a tracking rectangle ** ** Since we only have a single tracking rect ** we know the mouse is not over our custom view ** */ - (void)mouseExited:(NSEvent *)theEvent { // Set the flag so that other methods know // the mouse cursor is not over our view m_mouseIsInFingerView = NO; // Redraw the view so that the focus ring // will not appear [self setNeedsDisplay:YES]; } Finally, in the drawRect: method, change the program code that draws the focus ring to only do so if the mouse cursor is in the tracking rectangle: // If this view has accepted first responder // it should draw the focus ring but only if // the mouse cursor is over this view if ( ([[self window] firstResponder] == self) && (YES == m_mouseIsInFingerView) ) { NSSetFocusRingStyle(NSFocusRingAbove); } What just happened? We implemented the program code that will prevent the mouse cursor from moving out of our custom view when touch events are active. In doing so we noticed that our focus ring behavior could be improved. Therefore we added additional program code to ensure the focus ring is visible only when the mouse pointer is over our view. Performing 2D drawing in a custom view Mac OS X provides a number of ways to perform drawing. The methods provided range from very simple methods to very complex methods. For our multi-finger painting program we are going to use the core graphics APIs designed to draw a path. We are going to collect each stroke as a series of points and construct a path from those points so that we can draw the stroke. Each active touch event will have a corresponding active stroke object that needs to be drawn in our custom view. When a stroke is finished, and the App user lifts the finger, we are going to send the finished stroke to another custom view so that it is drawn only one time and not each time fingers move. The optimization of using the second view will ensure our finger tracking is not slowed down too much by drawing. Before we can begin drawing, we need to create two new objects that will be used to store individual points and strokes. The program code for these two objects is not shown but the objects are included in the Multi-Finger Paint Xcode project. The two objects are as follows: BTSPoint BTSStroke The BTSPoint object is a wrapper for an NSPoint structure. The NSPoint structure needs to be wrapped in an object so that it can be stored in an NSArray object. It has a single instance variable: NSPoint m_point; It implements the following methods which allows it to be initialized: return the point (x and y), return just the x value, or return just the y value. For more information on the object, we can read the source code file in the project: - (id) initWithNSPoint:(NSPoint)a_point; - (NSPoint) point; - (CGFloat)x; - (CGFloat)y; The BTSStroke object is a wrapper for an array of BTSPoint objects, a color, and a stroke width. It is used to store strokes that are drawn in our custom NSView. It has the following instance variables and properties: float m_red; float m_green; float m_blue; float m_alpha; float m_width; @property (strong) NSMutableArray *m_points; It implements the following methods which allows it to be initialized: a new point to be added, return the array of points, return any of the color components, and return the stroke width. For more information on the object, we can read the source code file in the project: - (id) initWithWidth:(float)a_width red:(float)a_red green:(float)a_green blue:(float)a_blue alpha:(float)a_alpha; - (void) addPoint:(BTSPoint *)a_point; - (NSMutableArray *) points; - (float)red; - (float)green; - (float)blue; - (float)alpha; - (float)width;
Read more
  • 0
  • 0
  • 2259

article-image-working-apps-splunk
Packt
08 Mar 2013
6 min read
Save for later

Working with Apps in Splunk

Packt
08 Mar 2013
6 min read
(For more resources related to this topic, see here.) Defining an app In the strictest sense, an app is a directory of configurations and, sometimes, code. The directories and files inside have a particular naming convention and structure. All configurations are in plain text, and can be edited using your choice of text editor. Apps generally serve one or more of the following purposes: A container for searches, dashboards, and related configurations: This is what most users will do with apps. This is not only useful for logical grouping, but also for limiting what configurations are applied and at what time. This kind of app usually does not affect other apps. Providing extra functionality: Many objects can be provided in an app for use by other apps. These include field extractions, lookups, external commands, saved searches, workflow actions, and even dashboards. These apps often have no user interface at all; instead they add functionality to other apps. Configuring a Splunk installation for a specific purpose: In a distributed deployment, there are several different purposes that are served by the multiple installations of Splunk. The behavior of each installation is controlled by its configuration, and it is convenient to wrap those configurations into one or more apps. These apps completely change the behavior of a particular installation. Included apps Without apps, Splunk has no user interface, rendering it essentially useless. Luckily, Splunk comes with a few apps to get us started. Let's look at a few of these apps: gettingstarted: This app provides the help screens that you can access from the launcher. There are no searches, only a single dashboard that simply includes an HTML page. search: This is the app where users spend most of their time. It contains the main search dashboard that can be used from any app, external search commands that can be used from any app, admin dashboards, custom navigation, custom css, a custom app icon, a custom app logo, and many other useful elements. splunk_datapreview: This app provides the data preview functionality in the admin interface. It is built entirely using JavaScript and custom REST endpoints. SplunkDeploymentMonitor: This app provides searches and dashboards to help you keep track of your data usage and the health of your Splunk deployment. It also defines indexes, saved searches, and summary indexes. It is a good source for more advanced search examples. SplunkForwarder and SplunkLightForwarder: These apps, which are disabled by default, simply disable portions of a Splunk installation so that the installation is lighter in weight. If you never create or install another app, and instead simply create saved searches and dashboards in the app search, you can still be quite successful with Splunk. Installing and creating more apps, however, allows you to take advantage of others' work, organize your own work, and ultimately share your work with others. Installing apps Apps can either be installed from Splunkbase or uploaded through the admin interface. To get started, let's navigate to Manager | Apps, or choose Manage apps... from the App menu as shown in the following screenshot: Installing apps from Splunkbase If your Splunk server has direct access to the Internet, you can install apps from Splunkbase with just a few clicks. Navigate to Manager | Apps and click on Find more apps online. The most popular apps will be listed as follows: Let's install a pair of apps and have a little fun. First, install Geo Location Lookup Script (powered by MAXMIND) by clicking on the Install free button. You will be prompted for your splunk.com login. This is the same login that you created when you downloaded Splunk. If you don't have an account, you will need to create one. Next, install the Google Maps app. This app was built by a Splunk customer and contributed back to the Splunk community. This app will prompt you to restart Splunk. Once you have restarted and logged back in, check the App menu. Google Maps is now visible, but where is Geo Location Lookup Script? Remember that not all apps have dashboards; nor do they necessarily have any visible components at all. Using Geo Location Lookup Script Geo Location Lookup Script provides a lookup script to provide geolocation information for IP addresses. Looking at the documentation, we see this example: eventtype=firewall_event | lookup geoip clientip as src_ip You can find the documentation for any Splunkbase app by searching for it at splunkbase.com, or by clicking on Read more next to any installed app by navigating to Manager | Apps | Browse more apps. Let's read through the arguments of the lookup command: geoip: This is the name of the lookup provided by Geo Location Lookup Script. You can see the available lookups by going to Manager | Lookups | Lookup definitions. clientip: This is the name of the field in the lookup that we are matching against. as src_ip: This says to use the value of src_ip to populate the field before it; in this case, clientip. I personally find this wording confusing. In my mind, I read this as "using" instead of "as". Included in the ImplementingSplunkDataGenerator app (available at http://packtpub.com/) is a sourcetype instance named impl_splunk_ips, which looks like this: 2012-05-26T18:23:44 ip=64.134.155.137 The IP addresses in this fictitious log are from one of my websites. Let's see some information about these addresses: sourcetype="impl_splunk_ips" | lookup geoip clientip AS ip | top client_country This gives us a table similar to the one shown in the following screenshot: That's interesting. I wonder who is visiting my site from Slovenia! Using Google Maps Now let's do a similar search in the Google Maps app. Choose Google Maps from the App menu. The interface looks like the standard search interface, but with a map instead of an event listing. Let's try this remarkably similar (but not identical) query using a lookup provided in the Google Maps app: sourcetype="impl_splunk_ips" | lookup geo ip The map generated looks like this: Unsurprisingly, most of the traffic to this little site came from my house in Austin, Texas. Installing apps from a file It is not uncommon for Splunk servers to not have access to the Internet, particularly in a datacenter. In this case, follow these steps: Download the app from splunkbase.com. The file will have a .spl or .tgz extension. Navigate to Manager | Apps. Click on Install app from file. Upload the downloaded file using the form provided. Restart if the app requires it. Configure the app if required. That's it. Some apps have a configuration form. If this is the case, you will see a Set up link next to the app when you go to Manager | Apps. If something goes wrong, contact the author of the app. If you have a distributed environment, in most cases the app only needs to be installed on your search head. The components that your indexers need will be distributed automatically by the search head. Check the documentation for the app.
Read more
  • 0
  • 0
  • 4316
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-osgi-life-cycle
Packt
07 Mar 2013
5 min read
Save for later

OSGi life cycle

Packt
07 Mar 2013
5 min read
(For more resources related to this topic, see here.) OSGi applications are described as living entities; by this we mean that these applications appear to evolve as the lifecycles of their constituent bundles are lived. The lifecycle layer facilitates this functionality. OSGi bundles are dynamically installed, resolved, started, updated, stopped, and uninstalled. The framework enforces the transitions between states, one cannot directly install a bundle and jump to an Active state without first passing through the resolved and starting states. The transitions between each state are illustrated in the following figure: Installed Bundles came into existence in an OSGi framework in the installed state. A bundle in this state cannot be immediately started, as the preceding diagram depicts that there is no direct transition from the installed state to the starting state. An installed bundle is also not active. There are three possible transitions: the bundle may become resolved, uninstalled, or refreshed. Apache Karaf command To install a bundle in Karaf, issue the osgi:install (bundle:install on Karaf 3.x) command, as follows: karaf@root> osgi:install URLs Having a bundle installed to the OSGi framework does not mean it is ready to be used; next we must resolve its dependencies. Resolved Entering the resolved state requires the framework to ensure that all the dependencies of a bundle have been met. Upon having its dependencies ensured, the bundle is now a candidate to be transitioned to the starting state. A resolved bundle may be refreshed, transitioning the bundle back to the installed state. A resolved bundle may also be transitioned to the uninstalled state. A resolved bundle is not active; however, it is ready to be activated. Apache Karaf command To resolve an installed bundle in Karaf, issue the osgi:resolve (bundle:resolve on Karaf 3.x) command, as follows: karaf@root> osgi:resolve BundleID Starting A resolved bundle may be started. The starting state is transitory; the framework is initializing the resolved bundle into a running active state. In fact, the transition from the starting to active state is implicit. Apache Karaf command To start a resolved bundle in Karaf, issue the osgi:start (bundle:start on Karaf 3.x) command, as follows: karaf@root> osgi:start BundleID Active The bundle is fully resolved, providing and consuming services in the OSGi environment. To perform any more transitions on an active bundle, it must first be stopped. Updating Bundle updates occur when the framework is instructed to re-evaluate a bundle's dependencies; this action is synonymous with refreshing a bundle. When this action occurs, all of the wiring to and from the bundle is broken, so care must be taken before refreshing to avoid starting a bundle storm (one bundle refreshing causes a domino effect of other bundles refreshing). Apache Karaf command To update a bundle in Karaf, issue the osgi:update (bundle:update on Karaf 3.x) command, as follows: karaf@root> osgi:update BundleID [location] The location option allows you to update the bundle via its predefined updated location or to specify a new location to find bundle updates. Stopping Stopping a bundle transitions it from the active to the resolved state. The bundle can be restarted while it remains in the resolved state. Apache Karaf command To stop an active bundle in Karaf, issue the osgi:stop (bundle:stop on Karaf 3.x) command, as follows: karaf@root> osgi:stop BundleID Uninstalled Uninstalling a bundle transitions an installed or resolved bundle out of the OSGi environment; however, the bundle is not removed from the environment! Why is this? While the bundle is no longer available for use, references to the bundle may still exist and used for introspection. To help leverage these states in your bundles, the OSGi specification provides a hook into your bundle state via the Activator interface. Apache Karaf command To uninstall a bundle in Karaf, issue the osgi:uninstall (bundle:uninstall on Karaf 3.x) command, as follows: karaf@root> osgi:uninstall BundleID BundleActivator bundle may optionally declare an Activator class implementing the org.osgi.framework.BundleActivator interface. This class must be referenced in the bundle manifest file via the BundleActivator header. Implementing the activator allows the bundle developer to specify actions to be performed upon starting or stopping a bundle. Generally, such operations include gaining access to or freeing resources, and registering and unregistering services. The entry in manifest.mf will appear as follows: Bundle-Activator: com.packt.osgi.starter.sample.Activator When building with maven-bundle-plugin, the following configuration instruction is added: <Bundle-Activator> com.packt.osgi.starter.sample.Activator </Bundle-Activator> The process can be seen in the following screenshot: Summary: This article covered the various states involved in the OSGi lifecycle. We also learnt about the transitions from one state to another. Resources for Article : Further resources on this subject: Koha's Web Installer, Crontab, and Other Server Configurations [Article] Using the OSGi Bundle Repository in OSGi and Apache Felix 3.0 [Article] Getting Started with Bookshelf Project in Apache Felix [Article]
Read more
  • 0
  • 0
  • 4975

article-image-getting-started-geoserver
Packt
07 Mar 2013
8 min read
Save for later

Getting Started with GeoServer

Packt
07 Mar 2013
8 min read
(For more resources related to this topic, see here.) Installing Java GeoServer is a Java application. So, we need to ensure that you have it installed and properly working on your machine, but you don't need to know how to write Java™ to install or to get started using GeoServer. There are two main packages of Java. Depending on what you are planning to do with Java, you may want to install a JDK (Java Development Kit) or JRE (Java Runtime Environment). The former enables you to compile Java™ code, while the latter has all you need to run most Java applications. Starting from release 2.0, GeoServer does not need a full JDK installation and you can go safely with JRE. It works well with Java 6 but as Java 7 is not deeply tested by developers, it should work but you may experience minor issues. Unless you have some strong reasons to use Java 7, you should use JRE 6. In the 90s, Java development was started by Sun Microsystems. Sun has developed each new release until it merged into Oracle Corporation. While Oracle did not change the Java license to a commercial one, there are some license issues (maybe it would be worthy to add some reference here) preventing Oracle Java™ from being available on an Ubuntu repository. On Ubuntu current releases, you will find OpenJDK already installed in the desktop edition; in the server, you need to choose it at setup. While there are a few users running GeoServer on OpenJDK with no issues, the developers community does not test it intensively and hence you can expect some performance loss. Oracle Java™ should be your first choice unless you have some specific issues. In the following steps, we will use Oracle Java™ JRE. If your installation machine is a new one, then chances are that there is no Java runtime pre-installed. Let's check. Time for action — checking the presence of Java on Windows We will verify the presence of a JRE/JDK installation on Windows, using the following steps: From the Start menu, select Control Panel. Then select Programs. If your system has a JRE/JDK installed, you should see an icon with the Java logo as shown in the following screenshot. It is a shortcut to the Java control panel. Open the Java control panel and select the Java tab. Here you will find settings for JRE. Press the Show Me button to visualize the installed release and the installation folder. What just happened? You checked for the presence of Java on your computer. In case you didn't find it, we are going to install it in the next section. (If you did find it, skip to the Installing Apache Tomcat section.) Time for action — checking the presence of Java on Ubuntu We will check JRE/JDK installation from the command line. Log in to your server and run this command: ~ $ sudo update-alternatives --config java If there is no Java properly configured you should see an output like the following: update-alternatives: error: no alternatives for java. In case there is one or more Java installed the output will be similar to: There is only one alternative in link group java: /usr/lib/jvm/ java-7-openjdk-amd64/jre/bin/java Nothing to configure. Or There are 2 choices for the alternative java (providing /usr/bin/ java). Selection Path Priority Status ———————————————————— * 0 /usr/lib/jvm/java-6-openjdk/jre/bin/java 1061 auto mode 1 /usr/lib/jvm/java-6-openjdk/jre/bin/java 1061 manual mode 2 /usr/lib/jvm/java-6-sun/jre/bin/java 63 manual mode Press enter to keep the current choice[*], or type selection number: What just happened? requirement for our installation. We had the opportunity to check if the installed release, in case we found it, is suitable for running GeoServer. Now we will go through the installation of JRE. Time for action — installing JRE on Windows We will install Oracle JRE 1.6. We are assuming that you didn't find any previous Java installation. Navigate to the Downloads tab at http://www.oracle.com/technetwork/java/javase/downloads/jre6u37-downloads-1859589.html. Select the installer for Windows 64-bit, that is, jre-6u37-windows-x64.exe, and save it in a convenient folder. Select the downloaded file and run it as an administrator; press the Yes button when asked from the User Account control. Go with the default settings and press the Install button. After it has been downloaded, you should see a window informing you about the success of installation. What just happened? We installed JRE on your Windows computer. The first requirement is now fulfilled and you can go over to the Tomcat installation. Time for action — installing JRE on Ubuntu We will install Oracle JRE 1.6. As mentioned previously, there is no Ubuntu package for Java 6; we are going to perform a manual installation. Visit the download area at http://www.oracle.com/technetwork/java/javase/downloads/jre6u37-downloads-1859589.html. Download the tar.gz archive, choosing the 32-bit or 64-bit archive, depending on the Ubuntu edition you are working with. You must accept the license agreement (reading it might be a nice idea) before you can select one of the tar.gz archives (be sure to avoid rpm archives as they are not for Debian-based Linux distribution). Save the archive to your home folder and extract it. ~ $ chmod a+x jre-6u37-linux-x64.bin ~ $ ./jre-6u37-linux-x64.bin The JRE 6 package is extracted into ./jre1.6.0_37 folder. Now move the JRE 6 directory to /opt and create a symbolic link to it in the default folder for libraries. ~ $ sudo mv ./jre1.6.0_37* /opt ~ $ sudo ln -s /opt/jre1.6.0_37 /usr/lib/jvm/ Let's check the installation: ~ $ /opt/jre1.6.0_37/bin/java -version java version "1.6.0_37" Java(TM) SE Runtime Environment (build 1.6.0_37-b06) Java HotSpot(TM) Client VM (build 20.12-b01, mixed mode) Although not strictly requested by the GeoServer installation, it is worth configuring the JRE as the primary Java alternative in your system: ~$ sudo update-alternatives --install /usr/bin/java java /usr/lib/ jvm/jre1.6.0_37/bin/java 0 Now you need to configure the Oracle JRE as default: ~ $ sudo update-alternatives --config java There are 2 choices for the alternative java (providing /usr/bin/ java). Selection Path Priority Status ------------------------------------------------------------ * 0 /usr/lib/jvm/java-6-openjdk-amd64/jre/bin/java 1061 auto mode 1 /usr/lib/jvm/java-6-openjdk-amd64/jre/bin/java 1061 manual mode 2 /usr/lib/jvm/jre1.6.0_37/bin/java 0 manual mode Press enter to keep the current choice[*], or type selection number: 2 update-alternatives: using /usr/lib/jvm/jre1.6.0_37/bin/java to provide /usr/bin/java (java) in manual mode. Clean your box by deleting the archive: ~$ rm jre-6u37-linux-x64.bin What just happened? We installed JRE. Now we can run a Java application on the JVM contained in the JRE. The JVM supports several different kinds of Java application; for example, a console-only application, an applet running in a browser, or a full desktop application. For GeoServer (a web application), we need another component on top of the JVM, that is, a servlet container. Installing Apache Tomcat Having correctly installed the JRE you can now pass on and install the servlet container. Servlet container, or web container, is the component server that interacts with the servlets. It is responsible for managing the lifecycle of servlets, mapping a URL to a particular servlet, and ensuring access security. It should implement Java servlet and JavaServer Pages technologies. As for JRE, you have a few choices here; a brief list is at http://en.wikipedia.org/wiki/Web_container. Apache Tomcat, GlassFish, and JBoss are most popular and are all available in an open source edition. You may wonder which one is the best choice for running GeoServer. In a production environment, usually the same container is shared among several web applications. You are not going to choose the container; the architects and system administrators made their choices and you have to conform to them. As a beginner, you have the opportunity of selecting it! Apache Tomcat should be your first choice as it is widely adopted in the Geoserver developer's community. If you run into any issues, the answer is probably waiting for you in the mailing list archive. We are going to install Apache Tomcat. It is an open source project of Apache foundation (http://tomcat.apache.org) and there are reasons for installing it such as it is widely used, well-documented, and relatively simple to configure. So let's start the Apache Tomcat installation.
Read more
  • 0
  • 0
  • 8203

article-image-thumping-moles-fun
Packt
07 Mar 2013
22 min read
Save for later

Thumping Moles for Fun

Packt
07 Mar 2013
22 min read
(For more resources related to this topic, see here.) The project is… In this article, we will be building a mole thumping game. Inspired by mechanical games of the past, we will build molehills on the screen and randomly cause animated moles to pop their heads out. The player taps them to score. Simple in concept, but there are a few challenging design considerations in this deceptively easy game. To make this game a little unusual, we will be using a penguin instead of a mole for the graphics, but we will continue to use the mole terminology throughout, since a molehill is easier to consider than a penguin-hill. Design approach Before diving into the code, let's start with a discussion of the design of the game. First, we will need to have molehills on the screen. To be aesthetically pleasing, the molehills will be in a 3 x 4 grid. Another approach would be to use random molehill positions, but that doesn't really work well on the limited screen space of the iPhone.Moles will randomly spawn from the molehills. Each mole will rise up, pause, and drop down. We will need touch handling to detect when a mole has been touched, and that mole will need to increase the player's score and then go away. How do we make the mole come up from underground? If we assume the ground is a big sprite with the molehills drawn on it, we would need to determine where to make the "slot" from which the mole emerges, and somehow make the mole disappear when it is below that slot. One approach is to adjust the size of the mole's displayed frame by clipping the bottom of the image so that the part below the ground is not visible. This needs to be done as a part of every update cycle for every mole for the entire game. From a programming standpoint this will work, but you may experience performance issues. Another consideration is that this usually means the hole in the molehill will always appear to be a straight-edged hole, if we trim the sprite with a straight line. This lacks the organic feel we want for this game. The approach we will take is to use Z-ordering to trick the eye into seeing a flat playfield when everything is really on staggered Z-orders. We will create a "stair step" board, with multiple "sandwiches" of graphics for every row of molehills on the board. For each "step" of the "stair step", we have a sandwich of Z-ordered elements in this order, from back to front: molehill top, mole, ground, and molehill bottom. We need to have everything aligned so that the molehill top graphic overlaps the ground of the next "step" further towards the top of the screen. This will visually contain the mole, so it appears to be emerging from inside the molehill. We intentionally skipped the Z value of 1, to provide an extra expansion space if we later decide that we need another element in the "sandwich". It is easier to leave little holes like this than to worry about changing everything later, if we enhance our design. So throughout our layout, we will consider it as a sandwich of five Z values, even though we only use four elements in the sandwich. As we said, we need this to be a "stair step" board. So for each row of molehills, from the top of the screen to the bottom, we will need to increase the Z-ordering between layers to complete the illusion. This is needed so that each mole will actually pass in front of the ground layer that is closer to the top of the screen, yet will hide completely behind the ground layer in its own sandwich of layers. Designing the spawn That covers the physical design of the game, but there is one additional design aspect we need to discuss: spawning moles. We need to spawn the moles whenever we need one to be put into the play. Just as we reviewed two approaches to the hiding mole problem earlier, we will touch on two approaches to mole spawning. The first approach (and most common) is to create a new mole from scratch each time you need one. When you are done with it, you destroy it. This works fine for games with a small number of objects or games of more limited complexity, but there is a performance penalty to create and destroy a lot of objects in a short amount of time. Strictly speaking, our mole thumping game would likely work fine with this approach. Even though we will be creating and destroying quite a few moles all the time, we only have a dozen possible moles, not hundreds. The other approach is to create a spawning pool. This is basically a set number of the objects that are created when you start up. When you need a mole, in our case, you ask the pool for an unused "blank mole", set any parameters that are needed, and use it. When you are done with it, you reset it back to the "blank mole" state, and it goes back into the pool. For our game the spawning pool might be a little more heavily coded than needed, as it is doubtful that we would run into any performance issues with this relatively simple game. Still, if you are willing to build the additional code as we are doing here, it does provide a strong foundation to add more performance-heavy effects later on. To clarify our design approach, we will actually implement a variation of the traditional spawning pool. Instead of a general pool of moles, we will build our "blank mole" objects attached to their molehills. A more traditional spawning pool might have six "blnk moles" in the pool, and they are assigned to a molehill when they are needed. Both approaches are perfectly valid. Portrait mode The default orientation supported by cocos2d is landscape mode, which is more commonly used in games. However, we want our game to be in portrait mode. The changes are very simple to make this work. If you click once on the project name (and blue icon) in the Project Navigator pane (where all your files are listed), and then click on the name of your game under TARGETS , you will see the Summary pane. Under the Supported Interface Orientations, select Portrait, and deselect Landscape Left and Landscape Right . That will change your project to portrait. The one adjustment to the cocos2d template code we need is in the IntroLayer.m. After it sets the background to Default.png, there is a command to rotate the background. Remove, or comment out this line, and everything will work correctly. Custom TTF fonts In this project we will be using a custom TTF font. In cocos2d 1.x, you could simply add the font to your project and use it. Under cocos2d 2.0, which we are using, we have to approach this a little differently. We add the font to our project (we are using anudrg.ttf). Then we edit the Info.plist for our project, and add a new key to the list, like this: This tells the project that we need to know about this font. To actually use the font, we need to call it by the proper name for the font, not the filename. To find out this name, in Finder, select the file and choose File Info . In the info box, there is an entry for Full Name . In our case, the file name is AnuDaw. Any time we create a label with CCLabelTTF, we simply need to use this as the font name, and everything works perfectly. Defining a molehill We have created a new subclass of CCNode to represent the MXMoleHill object. Yes, we will be using a subclass of CCNode, not a subclass of CCSprite . Even though we initially would consider the molehill to be a sprite, referring back to our design, it is actually made up of two sprites, one for the top of the hill and one for the bottom. We will use CCNode as a container that will then contain two CCSprite objects as variables inside the MXMoleHill class. Filename: MXMoleHill.h @interface MXMoleHill : CCNode { NSInteger moleHillID; CCSprite *moleHillTop; CCSprite *moleHillBottom; NSInteger moleHillBaseZ; MXMole *hillMole; BOOL isOccupied; } @property (nonatomic, assign) NSInteger moleHillID; @property (nonatomic, retain) CCSprite *moleHillTop; @property (nonatomic, retain) CCSprite *moleHillBottom; @property (nonatomic, assign) NSInteger moleHillBaseZ; @property (nonatomic, retain) MXMole *hillMole; @property (nonatomic, assign) BOOL isOccupied; @end If this seems rather sparse to you, it is. As we will be using this as a container for everything that defines the hill, we don't need to override any methods from the standard CCNode class. Likewise, the @implementation file contains nothing but the @synthesize statements for these variables. It is worth pointing out that we could have used a CCSprite object for the hillTop sprite, with the hillBottom object as a child of that sprite, and achieved the same effect. However, we prefer consistency in our object structure, so we have opted to use the structure noted previously. This allows us to refer to the two sprites in exactly the same fashion, as they are both children of the same parent. Building the mole When we start building the playfield, we will be creating "blank mole" objects for each hill, so we need to look at the MXMole class before we build the playfield. Following the same design decision as we did with the MXMoleHill class, the MXMole class is also a subclass of CCNode. Filename: MXMole.h #import #import "cocos2d.h" #import "MXDefinitions.h" #import "SimpleAudioEngine.h" // Forward declaration, since we don't want to import it here @class MXMoleHill; @interface MXMole : CCNode { CCSprite *moleSprite; // The sprite for the mole MXMoleHill *parentHill; // The hill for this mole float moleGroundY; // Where "ground" is MoleState _moleState; // Current state of the mole BOOL isSpecial; // Is this a "special" mole? } @property (nonatomic, retain) MXMoleHill *parentHill; @property (nonatomic, retain) CCSprite *moleSprite; @property (nonatomic, assign) float moleGroundY; @property (nonatomic, assign) MoleState moleState; @property (nonatomic, assign) BOOL isSpecial; -(void) destroyTouchDelegate; @end We see a forward declaration here (the @class statement). Use of forward declaration avoids creating a circular loop, because the MXMoleHill.h file needs to import MXMole.h . In our case, MXMole needs to know there is a valid class called MXMoleHill, so we can store a reference to an MXMoleHill object in the parentHill instance variable, but we don't actually need to import the class. The @class declaration is an instruction to the compiler that there is a valid class called MXMoleHill, but doesn't actually import the header while compiling the MXMole class. If we needed to call the methods of MXMoleHill from the MXMole class, we could then put the actual #import "MXMoleHill.h" line in the MXMole.m file. For our current project, we only need to know the class exists, so we don't need that additional line in the MXMole.m file. We have built a simple state machine for MoleState. Now that we have reviewed the MXMole.h file, we have a basic idea of what makes up a mole. It tracks the state of the mole (dead, alive, and so on), it keeps a reference to its parent hill, and it has CCSprite as a child where the actual mole sprite variable will be held. There are a couple of other variables (moleGroundY and isSpecial), but we will deal with these later. Filename: MXDefinitions.h typedef enum { kMoleDead = 0, kMoleHidden, kMoleMoving, kMoleHit, kMoleAlive } MoleState; #define SND_MOLE_NORMAL @"penguin_call.caf" #define SND_MOLE_SPECIAL @"penguin_call_echo.caf" #define SND_BUTTON @"button.caf" Unlike in the previous article, we do not have typedef enum that defines the MoleState type inside this header file. We have moved our definit ions to the MXDefinitions.h file, which helps to maintain slightly cleaner code. You can storethese "universal" definitions in a single header file, and include the header in any .h or .m files where they are needed, without needing to import classes just to gain access to these definitions. The MXDefinitions.h file only includes the definitions; there are no @interface or @implementation sections, nor a related .m file. Making a molehill We have our molehill class and we've seen the mole class, so now we can look at how we actually build the molehills in the MXPlayfieldLayer class: Filename: MXPlayfieldLayer.m -(void) drawHills { NSInteger hillCounter = 0; NSInteger newHillZ = 6; // We want to draw a grid of 12 hills for (NSInteger row = 1; row <= 4; row++) { // Each row reduces the Z order newHillZ--; for (NSInteger col = 1; col <= 3; col++) { hillCounter++; // Build a new MXMoleHill MXMoleHill *newHill = [[MXMoleHill alloc] init]; [newHill setPosition:[self hillPositionForRow:row andColumn:col]]; [newHill setMoleHillBaseZ:newHillZ]; [newHill setMoleHillTop:[CCSprite spriteWithSpriteFrameName:@"pileTop.png"]]; [newHill setMoleHillBottom:[CCSprite spriteWithSpriteFrameName:@"pileBottom.png"]]; [newHill setMoleHillID:hillCounter]; // We position the two moleHill sprites so // the "seam" is at the edge. We use the // size of the top to position both, // because the bottom image // has some overlap to add texture [[newHill moleHillTop] setPosition: ccp(newHill.position.x, newHill.position.y + [newHill moleHillTop].contentSize.height / 2)]; [[newHill moleHillBottom] setPosition: ccp(newHill.position.x, newHill.position.y - [newHill moleHillTop].contentSize.height / 2)]; //Add the sprites to the batch node [molesheet addChild:[newHill moleHillTop] z:(2 + (newHillZ * 5))]; [molesheet addChild:[newHill moleHillBottom] z:(5 + (newHillZ * 5))]; //Set up a mole in the hill MXMole *newMole = [[MXMole alloc] init]; [newHill setHillMole:newMole]; [[newHill hillMole] setParentHill:newHill]; [newMole release]; // This flatlines the values for the new mole [self resetMole:newHill]; [moleHillsInPlay addObject:newHill]; [newHill release]; } } } This is a pretty dense method, so we'll walk through it one section at a time. We start by creating two nested for loops so we can iterate over every possible row and column position. For clarity, we named our loop variables as row and column, so we know what each represents. If you recall from the design, we decided to use a 3 x 4 grid, so we will have three columns and four rows of molehills. We create a new hill using an alloc/init, and then we begin filling in the variables. We set an ID number (1 through 12), and we build CCSprite objects to fill in the moleHillTop and moleHillBottom variables. Filename: MXPlayfieldLayer.m -(CGPoint) hillPositionForRow:(NSInteger)row andColumn:(NSInteger)col { float rowPos = row * 82; float colPos = 54 + ((col - 1) * 104); return ccp(colPos,rowPos); } We also set the position using the helper method, hillPositionForRow:andColumn:, that returns a CGPoint for each molehill. (It is important to remember that ccp is a cocos2d shorthand term for a CGPoint. They are interchangeable in your code.) These calculations are based on experimentation with the layout, to create a grid that is both easy to draw as well as being visually appealing. The one variable that needs a little extra explaining is moleHillBaseZ . This represents which "step" of the Z-order stair-step design this hill belongs to. We use this to aid in the calculations to determine the proper Z-ordering across the entire playfield. If you recall, we used Z-orders from 2 to 5 in the illustration of the stack of elements. When we add the moleHillTop and moleHillBottom as children of the moleSheet (our CCSpriteBatchNode), we add the Z-order of the piece of the sandwich to the "base Z" times 5. We will use a "base Z" of 5 for the stack at the bottom of the screen, and a "base Z" of 2 at the top of the screen. This will be easier to understand the reason if we look at the following chart, which shows the calculations we use for each row of molehills: As we start building our molehills at the bottom of the screen, we start with a higher Z-order first. In the preceding chart, you will see that the mole in hole 4 (second row of molehills from the bottom) will have a Z-order of 23. This will put it behind its own ground layer, which is at a Z-order of 24, but in front of the ground higher on the screen, which would be at a Z-order of 19. It is worth calling out that since we have a grid of molehills in our design, all Z-ordering will be identical for all molehills in the same row. This is why the decrement of the baseHillZ variable occurs only when we are iterating through a new row. If we refer back to the drawHills method itself, we also see a big calculation for the actual position of the moleHillTop and moleHillBottom sprites. We want the "seam" between these two sprites to be at the top edge of the ground image of their stack, so we set the y position based on the position of the MXMoleHill object. At first it may look like an error, because both setPosition statements use contentSize of the moleHillTop sprite as a part of the calculation. This is intentional, because we have a little jagged overlap between those two sprites to give it a more organic feel. To wrap up the drawHills method, we allocate a new MXMole, assign it to the molehill that was just created, and set the cross-referencing hillMole and parentHill variables in the objects themselves. We add the molehill to our moleHillsInPlay array, and we clean everything up by releasing both the newHill and the newMole objects. Because the array retains a reference to the molehill, and the molehill retains a reference to the mole, we can safely release both the newHill and newMole objects in this method. Drawing the ground Now that we have gone over the Z-ordering "trickery", we should look at the drawGround method to see how we accomplish the Z-ordering in a similar fashion: Filename: MXPlayfieldLayer.m -(void) drawGround { // Randomly select a ground image NSString *groundName; NSInteger groundPick = CCRANDOM_0_1() * 2; switch (groundPick) { case 1: groundName = @"ground1.png"; break; default: // Case 2 also falls through here groundName = @"ground2.png"; break; } // Build the strips of ground from the selected image for (int i = 0; i < 5; i++) { CCSprite *groundStrip1 = [CCSprite spriteWithSpriteFrameName:groundName]; [groundStrip1 setAnchorPoint:ccp(0.5,0)]; [groundStrip1 setPosition:ccp(size.width/2,i*82)]; [molesheet addChild:groundStrip1 z:4+((5-i) * 5)]; } // Build a skybox skybox = [CCSprite spriteWithSpriteFrameName:@"skybox1.png"]; [skybox setPosition:ccp(size.width/2,5*82)]; [skybox setAnchorPoint:ccp(0.5,0)]; [molesheet addChild:skybox z:1]; } This format should look familiar to you. We create five CCSprite objects for the five stripes of ground, tile them from the bottom of the screen to the top, and assign the Z-order as z:4+((5-i) * 5). We do include a randomizer with two different background images, and we also include a skybox image at the top of the screen, because we want some sense of a horizon line above the mole-thumping area. anchorPoint is the point that is basically "center" for the sprite. The acceptable values are floats between 0 and 1. For the x axis, an anchorPoint of 0 is the left edge, and 1 is the right edge (0.5 is centered). For the y axis, an anchorPoint of 0 is the bottom edge, and 1 is the top edge. This anchorPoint is important here because that anchorPoint is the point on the object to which the setPosition method will refer. So in our code, the first groundStrip1 created will be anchored at the bottom center. When we call setPosition, the coordinate passed to setPosition needs to relate to that anchorPoint; the position set will be the bottom center of the sprite. If this is still fuzzy for you, it is a great exercise to change anchorPoint of your own CCSprite objects and see what happens on the screen. Mole spawning The only piece of the "sandwich" of elements we haven't seen in detail is the mole itself, so let's visit the mole spawning method to see how the mole fits in with our design: Filename: MXPlayfieldLayer.m -(void) spawnMole:(id)sender { // Spawn a new mole from a random, unoccupied hill NSInteger newMoleHill; BOOL isApprovedHole = FALSE; NSInteger rand; if (molesInPlay == [moleHillsInPlay count] || molesInPlay == maxMoles) { // Holes full, cannot spawn a new mole } else { // Loop until we pick a hill that isn't occupied do { rand = CCRANDOM_0_1() * maxHills; if (rand > maxHills) { rand = maxHills; } MXMoleHill *testHill = [moleHillsInPlay objectAtIndex:rand]; // Look for an unoccupied hill if ([testHill isOccupied] == NO) { newMoleHill = rand; isApprovedHole = YES; [testHill setIsOccupied:YES]; } } while (isApprovedHole == NO); // Mark that we have a new mole in play molesInPlay++; // Grab a handle on the mole Hill MXMoleHill *thisHill = [moleHillsInPlay objectAtIndex:newMoleHill]; NSInteger hillZ = [thisHill moleHillBaseZ]; // Set up the mole for this hill CCSprite *newMoleSprite = [CCSprite spriteWithSpriteFrameName:@"penguin_forward.png"]; [[thisHill hillMole] setMoleSprite:newMoleSprite]; [[thisHill hillMole] setMoleState:kMoleAlive]; // We keep track of where the ground level is [[thisHill hillMole] setMoleGroundY: thisHill.position.y]; // Set the position of the mole based on the hill float newMolePosX = thisHill.position.x; float newMolePosY = thisHill.position.y - (newMoleSprite.contentSize.height/2); [newMoleSprite setPosition:ccp(newMolePosX, newMolePosY)]; // See if we need this to be a "special" mole NSInteger moleRandomizer = CCRANDOM_0_1() * 100; // If we randomized under 5, make this special if (moleRandomizer < 5) { [[thisHill hillMole] setIsSpecial:YES]; } //Trigger the new mole to raise [molesheet addChild:newMoleSprite z:(3 + (hillZ * 5))]; [self raiseMole:thisHill]; } } The first thing we check is to make sure we don't have active moles in every molehill, and that we haven't reached the maximum number of simultaneous moles we want on screen at the same time (the maxMoles variable). If we have enough moles, we skip the rest of the loop. If we need a new mole, we enter a do…while loop that will randomly pick a molehill and check if it has the isOccupied variable set to NO (that is, no active mole in this molehill). If the randomizer picks a molehill that is already occupied, the do…while loop will pick another molehill and try again. When we find an unoccupied molehill, the code breaks out of the loop and starts to set up the mole. As we saw earlier, there is already a "blank mole" attached to every molehill. At this point we build a new sprite to attach to the moleSprite variable of MXMole, change the moleState to kMoleAlive, and set up the coordinates for the mole to start. We want the mole to start from underground (hidden by the ground image), so we set the mole's y position as the position of the molehill minus the height of the mole. Once we have set up the mole, we assign our calculated Z-order for this mole (based on the moleHillBaseZ variable we stored earlier for each molehill), and call the raiseMole method, which controls the animation and movement of the mole. Special moles We have seen two references to the isSpecial variable from the MXMole class, so now is a good time to explain how it is used. In order to break the repetitive nature of the game, we have added a "special mole" feature. When a new mole is requested to spawn in the spawnMole method, we generate a random number between 1 and 100. If the resulting number is less than five, then we set the isSpecial flag for that mole. This means that roughly 5 percent of the time the player will get a special mole. Our special moles use the same graphics as the standard mole, but we will make them flash a rainbow of colors when they are in the play. It is a small difference, but enough to set up the scoring to give extra points for the "special mole". To implement this special mole, we only need to adjust coding in three logic areas: When raiseMole is setting the mole's actions (to make it flashy) When we hit the mole (to play a different sound effect) When we score the mole (to score more points) This is a very small task, but it is the small variations in the gameplay that will draw the players in further. Let's see the game with a special mole in the play: Moving moles When we call the raiseMole method, we build all of the mole's behavior. The absolute minimum we need is to raise the mole from the hill and lower it again. For our game, we want to add a little randomness to the behavior, so that we don't see exactly the same motions for every mole. We use a combination of pre-built animations with actions to achieve our result. As we haven't used any CCAnimate calls before, we should talk about them first. The animation cache Cocos2d has many useful caches to store frequently used data. When we use a CCSpriteBatchNode, we are using the CCSpriteFrameCache to store all of the sprites we need by name. There is an equally useful CCAnimationCache as well. It is simple to use. You build your animation as a CCAnimation, and then load it to the CCAnimationCache by whatever name you would like. When you want to use your named animation, you can create a CCAnimate action that loads directly from CCAnimationCache. The only caution is that if you load two animations with the same name to the cache, they will collide in the cache, and the second one will replace the first. For our project, we preload the animation during the init method by calling the buildAnimations method. We only use one animation here, but you could preload as many as you need to the cache ahead of time. Filename: MXPlayfieldLayer.m -(void) buildAnimations { // Load the Animation to the CCSpriteFrameCache NSMutableArray *frameArray = [NSMutableArray array]; // Load the frames [frameArray addObject:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"penguin_forward.png"]]; [frameArray addObject:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"penguin_left.png"]]; [frameArray addObject:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"penguin_forward.png"]]; [frameArray addObject:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"penguin_right.png"]]; [frameArray addObject:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"penguin_forward.png"]]; [frameArray addObject:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"penguin_forward.png"]]; // Build the animation CCAnimation *newAnim = [CCAnimation animationWithSpriteFrames:frameArray delay:0.4]; // Store it in the cache [[CCAnimationCache sharedAnimationCache] addAnimation:newAnim name:@"penguinAnim"]; } We only have three unique frames of animation, but we load them multiple times into the frameArray to fit our desired animation. We create a CCAnimation object from the frameArray, and then commit it to CCAnimationCache under the name penguinAnim. Now that we have loaded it to the cache, we can reference it anywhere we want it, just by requesting it from CCAnimationCache, like this: [[CCAnimationCache sharedAnimationCache] animationByName:@"penguinAnim"]]
Read more
  • 0
  • 0
  • 2188

article-image-asynchrony-action
Packt
06 Mar 2013
17 min read
Save for later

Asynchrony in Action

Packt
06 Mar 2013
17 min read
(For more resources related to this topic, see here.) Asynchrony When we talk about C# 5.0, the primary topic of conversation is the new asynchronous programming features. What does asynchrony mean? Well, it can mean a few different things, but in our context, it is simply the opposite of synchronous. When you break up execution of a program into asynchronous blocks, you gain the ability to execute them side-by-side, in parallel. As you can see in the following diagram, executing multiple ac-tions concurrently can bring various positive qualities to your programs: Parallel execution can bring performance improvements to the execution of a program. The best way to put this into context is by way of an example, an example that has been experienced all too often in the world of desktop software. Let's say you have an application that you are developing, and this software should fulfill the following requirements: When the user clicks on a button, initiate a call to a web service. Upon completion of the web service call, store the results into a database. Finally, bind the results and display them to the user. There are a number of problems with the naïve way of implementing this solution. The first is that many developers write code in such a way that the user interface will be completely unresponsive while we are waiting to receive the results of these web service calls. Then, once the results finally arrive, we continue to make the user wait while we store the results in a database, an operation that the user does not care about in this case. The primary vehicle for mitigating these kinds of problems in the past has been writing multithreaded code. This is of course nothing new, as multi-threaded hardware has been around for many years, along with software capabilities to take advantage of this hardware. Most of the programming languages did not provide a very good abstraction layer on top of this hardware, often letting (or requiring) you program directly against the hardware threads. Thankfully, Microsoft introduced a new library to simplify the task of writing highly concurrent programs, which is explained in the next section. Task Parallel Library The Task Parallel Library (TPL) was introduced in .NET 4.0 (along with C# 4.0). Firstly, it is a huge topic and could not have been examined properly in such a small space. Secondly, it is highly relevant to the new asynchrony features in C# 5.0, so much so that they are the literal foundation upon which the new features are built. So, in this section, we will cover the basics of the TPL, along with some of the background information about how and why it works. TPL introduces a new type, the Task type, which abstracts away the concept of something that must be done into an object. At first glance, you might think that this abstraction already exists in the form of the Thread class. While there are some similarities between Task and Thread, the implementations have quite different implications. With a Thread class, you can program directly against the lowest level of parallelism supported by the operating system, as shown in the following code: Thread thread = new Thread(new ThreadStart(() => { Thread.Sleep(1000); Console.WriteLine("Hello, from the Thread"); })); thread.Start(); Console.WriteLine("Hello, from the main thread"); thread.Join(); In the previous example, we create a new Thread class, which when started will sleep for a second and then write out the text Hello, from the Thread. After we call thread.Start(), the code on the main thread immediately continues and writes Hello, from the main thread. After a second, we see the text from the background thread printed to the screen. In one sense, this example of using the Thread class shows how easy it is to branch off the execution to a background thread, while allowing execution of the main thread to continue, unimpeded. However, the problem with using the Thread class as your "concurrency primitive" is that the class itself is an indication of the implementation, which is to say, an operating system thread will be created. As far as abstractions go, it is not really an abstraction at all; your code must both manage the lifecycle of the thread, while at the same time dealing with the task the thread is executing. If you have multiple tasks to execute, spawning multiple threads can be disastrous, because the operating system can only spawn a finite number of them. For performance intensive applications, a thread should be considered a heavyweight resource, which means you should avoid using too many of them, and keep them alive for as long as possible. As you might imagine, the designers of the .NET Framework did not simply leave you to program against this without any help. The early versions of the frameworks had a mechanism to deal with this in the form of the ThreadPool, which lets you queue up a unit of work, and have the thread pool manage the lifecycle of a pool of threads. When a thread becomes available, your work item is then executed. The following is a simple example of using the thread pool: int[] numbers = { 1, 2, 3, 4 }; foreach (var number in numbers) { ThreadPool.QueueUserWorkItem(new WaitCallback(o => { Thread.Sleep(500); string tabs = new String('t', (int)o); Console.WriteLine("{0}processing #{1}", tabs, o); }), number); } This sample simulates multiple tasks, which should be executed in parallel. We start with an array of numbers, and for each number we want to queue a work item that will sleep for half a second, and then write to the console. This works much better than trying to manage multiple threads yourself because the pool will take care of spawning more threads if there is more work. When the configured limit of concurrent threads is reached, it will hold work items until a thread becomes available to process it. This is all work that you would have done yourself if you were using threads directly. However, the thread pool is not without its complications. First, it offers no way of synchronizing on completion of the work item. If you want to be notified when a job is completed, you have to code the notification yourself, whether by raising an event, or using a thread synchronization primitive, such as ManualResetEvent. You also have to be careful not to queue too many work items, or you may run into system limitations with the size of the thread pool. With the TPL, we now have a concurrency primitive called Task. Consider the following code: Task task = Task.Factory.StartNew(() => { Thread.Sleep(1000); Console.WriteLine("Hello, from the Task"); }); Console.WriteLine("Hello, from the main thread"); task.Wait(); Upon first glance, the code looks very similar to the sample using Thread, but they are very different. One big difference is that with Task, you are not committing to an implementation. The TPL uses some very interesting algorithms behind the scenes to manage the workload and system resources, and in fact, allows you customize those algorithms through the use of custom schedulers and synchronization contexts. This allows you to control the parallel execution of your programs with a high degree of control. Dealing with multiple tasks, as we did with the thread pool, is also easier because each task has synchronization features built-in. To demonstrate how simple it is to quickly parallelize an arbitrary number of tasks, we start with the same array of integers, as shown in the previous thread pool example: int[] numbers = { 1, 2, 3, 4 }; Because Task can be thought of as a primitive type that represents an asynchronous task, we can think of it as data. This means that we can use things such as Linq to project the numbers array to a list of tasks as follows: var tasks = numbers.Select(number => Task.Factory.StartNew(() => { Thread.Sleep(500); string tabs = new String('t', number); Console.WriteLine("{0}processing #{1}", tabs, number); })); And finally, if we wanted to wait until all of the tasks were done before continuing on, we could easily do that by calling the following method: Task.WaitAll(tasks.ToArray()); Once the code reaches this method, it will wait until every task in the array completes before continuing on. This level of control is very convenient, especially when you consider that, in the past, you would have had to depend on a number of different synchronization techniques to achieve the very same result that was accomplished in just a few lines of TPL code. With the usage patterns that we have discussed so far, there is still a big disconnect between the process that spawns a task, and the child process. It is very easy to pass values into a background task, but the tricky part comes when you want to retrieve a value and then do something with it. Consider the following requirements: Make a network call to retrieve some data. Query the database for some configuration data. Process the results of the network data, along with the configuration data. The following diagram shows the logic: Both the network call and query to the database can be done in parallel. With what we have learned so far about tasks, this is not a problem. However, acting on the results of those tasks would be slightly more complex, if it were not for the fact that the TPL provides support for exactly that scenario. There is an additional kind of Task, which is especially useful in cases like this called Task<T>. This generic version of a task expects the running task to ultimately return a value, whenever it is finished. Clients of the task can access the value through the .Result property of the task. When you call that property, it will return immediately if the task is completed and the result is available. If the task is not done, however, it will block execution in the current thread until it is. Using this kind of task, which promises you a result, you can write your programs such that you can plan for and initiate the parallelism that is required, and handle the response in a very logical manner. Look at the following code: varwebTask = Task.Factory.StartNew(() => { WebClient client = new WebClient(); return client.DownloadString("http://bing.com"); }); vardbTask = Task.Factory.StartNew(() => { // do a lengthy database query return new { WriteToConsole=true }; }); if (dbTask.Result.WriteToConsole) { Console.WriteLine(webTask.Result); } else { ProcessWebResult(webTask.Result); } In the previous example, we have two tasks, the webTask, and dbTask, which will execute at the same time. The webTask is simply downloading the HTML from http://bing.com Accessing things over the Internet can be notoriously flaky due to the dynamic nature of accessing the network so you never know how long that is going to take. With the dbTask task, we are simulating accessing a database to return some stored settings. Although in this simple example we are just returning a static anonymous type, database access will usually access a different server over the network; again, this is an I/O bound task just like downloading something over the Internet. Rather than waiting for both of them to execute like we did with Task.WaitAll, we can simply access the .Result property of the task. If the task is done, the result will be returned and execution can continue, and if not, the program will simply wait until it is. This ability to write your code without having to manually deal with task synchronization is great because the fewer concepts a programmer has to keep in his/her head, the more resources he/she can devote to the program. If you are curious about where this concept of a task that returns a value comes from, you can look for resources pertaining to "Futures", and "Promises" at: http://en.wikipedia.org/wiki/Promise_%28programming%29 At the simplest level, this is a construct that "promises" to give you a result in the "future", which is exactly what Task<T> does. Task composability Having a proper abstraction for asynchronous tasks makes it easier to coordinate multiple asynchronous activities. Once the first task has been initiated, the TPL allows you to compose a number of tasks together into a cohesive whole using what are called continuations. Look at the following code: Task<string> task = Task.Factory.StartNew(() => { WebClient client = new WebClient(); return client.DownloadString("http://bing.com"); }); task.ContinueWith(webTask => { Console.WriteLine(webTask.Result); }); Every task object has the .ContinueWith method, which lets you chain another task to it. This continuation task will begin execution once the first task is done. Unlike the previous example, where we relied on the .Result method to wait until the task was done—thus potentially holding up the main thread while it completed—the continuation will run asynchronously. This is a better approach for composing tasks because you can write tasks that will not block the UI thread, which results in very responsive applications. Task composability does not stop at providing continuations though, the TPL also provides considerations for scenarios, where a task must launch a number of subtasks. You have the ability to control how completion of those child tasks affects the parent task. In the following example, we will start a task, which will in turn launch a number of subtasks: int[] numbers = { 1, 2, 3, 4, 5, 6 }; varmainTask = Task.Factory.StartNew(() => { // create a new child task foreach (intnum in numbers) { int n = num; Task.Factory.StartNew(() => { Thread.SpinWait(1000); int multiplied = n * 2; Console.WriteLine("Child Task #{0}, result {1}", n, multiplied); }); } }); mainTask.Wait(); Console.WriteLine("done"); Each child task will write to the console, so that you can see how the child tasks behave along with the parent task. When you execute the previous program, it results in the following output: Child Task #1, result 2 Child Task #2, result 4 done Child Task #3, result 6 Child Task #6, result 12 Child Task #5, result 10 Child Task #4, result 8 Notice how even though you have called the .Wait() method on the outer task before writing done, the execution of the child task continues a bit longer after the task is concluded. This is because, by default, child tasks are detached, which means their execution is not tied to the task that launched it. An unrelated, but important bit in the previous example code, is you will notice that we assigned the loop variable to an intermediary variable before using it in the task. int n = num; Task.Factory.StartNew(() => { int multiplied = n * 2; This is actually related to the way closures work, and is a common misconception when trying to "pass in" values in a loop. Because the closure actually creates a reference to the value, rather than copying the value in, using the loop value will end up changing every time the loop iterates, and you will not get the behavior you expect. As you can see, an easy way to mitigate this is to set the value to a local variable before passing it into the lambda expression. That way, it will not be a reference to an integer that changes before it is used. You do however have the option to mark a child task as Attached, as follows: Task.Factory.StartNew( () =>DoSomething(), TaskCreationOptions.AttachedToParent); The TaskCreationOptions enumeration has a number of different options. Specifically in this case, the ability to attach a task to its parent task means that the parent task will not complete until all child tasks are complete. Other options in TaskCreationOptions let you give hints and instructions to the task scheduler. From the documentation, the following are the descriptions of all these options: None: This specifies that the default behavior should be used. PreferFairness: This is a hint to a TaskScheduler class to schedule a task in as fair a manner as possible, meaning that tasks scheduled sooner will be more likely to be run sooner, and tasks scheduled later will be more likely to be run later. LongRunning: This specifies that a task will be a long-running, coarsegrained operation. It provides a hint to the TaskScheduler class that oversubscription may be warranted. AttachedToParent: This specifies that a task is attached to a parent in the task hierarchy. DenyChildAttach: This specifies that an exception of the type InvalidOperationException will be thrown if an attempt is made to attach a child task to the created task. HideScheduler: This prevents the ambient scheduler from being seen as the current scheduler in the created task. This means that operations such as StartNew or ContinueWith that are performed in the created task, will see Default as the current scheduler. The best part about these options, and the way the TPL works, is that most of them are merely hints. So you can suggest that a task you are starting is long running, or that you would prefer tasks scheduled sooner to run first, but that does not guarantee this will be the case. The framework will take the responsibility of completing the tasks in the most efficient manner, so if you prefer fairness, but a task is taking too long, it will start executing other tasks to make sure it keeps using the available resources optimally. Error handling with tasks Error handling in the world of tasks needs special consideration. In summary, when an exception is thrown, the CLR will unwind the stack frames looking for an appropriate try/catch handler that wants to handle the error. If the exception reaches the top of the stack, the application crashes. With asynchronous programs, though, there is not a single linear stack of execution. So when your code launches a task, it is not immediately obvious what will happen to an exception that is thrown inside of the task. For example, look at the following code: Task t = Task.Factory.StartNew(() => { throw new Exception("fail"); }); This exception will not bubble up as an unhandled exception, and your application will not crash if you leave it unhandled in your code. It was in fact handled, but by the task machinery. However, if you call the .Wait() method, the exception will bubble up to the calling thread at that point. This is shown in the following example: try { t.Wait(); } catch (Exception ex) { Console.WriteLine(ex.Message); } When you execute that, it will print out the somewhat unhelpful message One or more errors occurred, rather than the fail message that is the actual message contained in the exception. This is because unhandled exceptions that occur in tasks will be wrapped in an AggregateException exception, which you can handle specifically when dealing with task exceptions. Look at the following code: catch (AggregateException ex) { foreach (var inner in ex.InnerExceptions) { Console.WriteLine(inner.Message); } } If you think about it, this makes sense, because of the way that tasks are composable with continuations and child tasks, this is a great way to represent all of the errors raised by this task. If you would rather handle exceptions on a more granular level, you can also pass a special TaskContinuationOptions parameter as follows: Task.Factory.StartNew(() => { throw new Exception("Fail"); }).ContinueWith(t => { // log the exception Console.WriteLine(t.Exception.ToString()); }, TaskContinuationOptions.OnlyOnFaulted); This continuation task will only run if the task that it was attached to is faulted (for example, if there was an unhandled exception). Error handling is, of course, something that is often overlooked when developers write code, so it is important to be familiar with the various methods of handling exceptions in an asynchronous world.
Read more
  • 0
  • 0
  • 3855
article-image-creating-virtual-landscapes
Packt
06 Mar 2013
9 min read
Save for later

Creating Virtual Landscapes

Packt
06 Mar 2013
9 min read
(For more resources related to this topic, see here.) Describing a world in data Just like modern games, early games like Ant Attack required data that described in some meaningful way how the landscape was to appear. The eerie city landscape of "Antchester" (shown in the following screenshot) was constructed in memory as a 128 x 128 byte grid, the first 128 bytes defined the upper-left wall, and the 128 byte row below that, and so on. Each of these bytes described the vertical arrangement of blocks in lower six bits, for game logic purposes the upper two bits were used for game sprites. Heightmaps are common ground The arrangement of numbers in a grid pattern is still extensively used to represent terrain. We call these grids "maps" and they are popular by virtue of being simple to use and manipulate. A long way from "Antchester", maps can now be measured in megabytes or Gigabytes (around 20GB is needed for the whole earth at 30 meter resolution). Each value in the map represents the height of the terrain at that location. These kinds of maps are known as heightmaps. However, any information that can be represented in the grid pattern can use maps. Additional maps can be used by 3D engines to tell it how to mix many textures together; this is a common terrain painting technique known as "splatting". Splats describe the amount of blending between texture layers. Another kind of map might be used for lighting, adding light, or shadows to an area of the map. We also find in some engines something called visibility maps which hide parts of the terrain; for example we might want to add holes or caves into a landscape. Coverage maps might be used to represent objects such as grasses, different vegetation layers might have some kind of map the engine uses to draw 3D objects onto the terrain surface. GROME allows us to create and edit all of these kinds of maps and export them, with a little bit of manipulation we can port this information into most game engines. Whatever the technique used by an engine to paint the terrain, height-maps are fairly universal in how they are used to describe topography. The following is an example of a heightmap loaded into an image viewer. It appears as a gray scale image, the intensity of each pixel represents a height value at that location on the map. This map represents a 100 square kilometer area of north-west Afghanistan used in a flight simulation. GROME like many other terrain editing tools uses heightmaps to transport terrain information. Typically importing the heightmap as a gray scale image using common file formats such as TIFF, PNG, or BMP. When it's time to export the terrain project you have similar options to save. This commonality is the basis of using GROME as a tool for many different engines. There's nothing to stop you from making changes to an exported heightmap using image editing software. The GROME plugin system and SDK permit you to make your own custom exporter for any unsupported formats. So long as we can deal with the material and texture format requirements for our host 3D engine we can integrate GROME into the art pipeline. Texture sizes Using textures for heightmap information does have limitations. The largest "safe" size for a texture is considered 4096 x 4096 although some of the older 3D cards would have problems with anything higher than 2048 x 2048. Also, host 3D engines often require texture dimensions to be a power of 2. A table of recommended dimensions for images follow: SafeTexture dimensions 64 x 64 128 x 128 256 x 256 512 x 512 1024 x 1024 2048 x 2048 4096 x 4096 512 x 512 provides efficient trade-off between resolution and performance and is the default value for GROME operations. If you're familiar with this already then great, you might see questions posted on forums about texture corruption or materials not looking correct. Sometimes these problems are the result of not conforming to this arrangement. Also, you'll see these numbers crop up a few times in GROME's drop-down property boxes. To avoid any potential problems it is wise to ensure any textures you use in your projects conform to these specifications. One exception is Unreal Development Kit ( UDK) in which you'll see numbers such as 257 x 257 used. If you have a huge amount of terrain data that you need to import for a project you can use the texture formats mentioned earlier but I recommend using RAW formats if possible. If your project is based on real-world topography then importing DTED or GeoTIFF data will extract geographical information such as latitude, longitude, and number of arc seconds represented by the terrain. Digital Terrain Elevation Data (DTED) A file format used by geographers and mappers to map the height of a terrain. Often used to import real-world topography into flight simulations. Shuttle Radar Topography Mission (SRTM) data is easily obtained and converted. The huge world problem Huge landscapes may require a lot of memory, potentially more than a 3D card can handle. In game consoles memory is a scarce resource, on mobile devices transferring the app and storing is a factor. Even on a cutting edge PC large datasets will eat into that onboard memory especially when we get down to designing and building them using high-resolution data. Requesting actions that eat up your system memory may cause the application to fail. We can use GROME to create vast worlds without worrying too much about memory. This is done by taking advantage of how GROME manages data through a process of splitting terrain into "zones" and swapping it out to disk. This swapping is similar to how operating systems move memory to disk and reload it on demand. By default whenever you import large DTED files GROME will break the region into multiple zones and hide them. Someone new to GROME might be confused by a lengthy file import operation only to be presen ted with a seemingly empty project space. When creating terrain for engines such as Unity, UDK, Ogre3D, and others you should keep in mind their own technical limitations of what they can reasonably import. Most of these engines are built for small scale scenes. While GROME doesn't impose any specific unit of measure on your designs, one unit equals one meter is a good rule of thumb. Many third-party models are made to this scale. However it's up to the artist to pick a unit of scale and importantly, be consistent. Keep in mind many 3D engines are limited by two factors: Floating point math precision Z-buffer (depth buffer) precision Floating point precision As a general rule anything larger than 20,000 units away from the world origin in any direction is going to exhibit precision errors. This manifests as vertex jitter whenever vertices are rotated and transformed by large values. The effects are not something you can easily work around. Changing the scale of the object shifts the error to another decimal point. Normally in engines that specialize in rendering large worlds they either use a camera-relative rendering or some kind of paging system. Unity and UDK are not inherently capable of camera-relative rendering but a method of paging is possible to employ. Depth buffer precision The other issue associated with large scene rendering is z-fighting. The depth buffer is a normally invisible part of a scene used to determine what part is hidden by another, depth-testing. Whenever a pixel is written to a scene buffer the z component is saved in the depth buffer. Typically this buffer has 16 bits of precision, meaning you have a linear depth of 0 to 65,536. This depth value is based on the 3D camera's view range (the difference between the camera near and far distance). Z-fighting occurs when objects appear to be co-planer polygons written into the z-buffer with similar depth values causing them to "fight" for visibility. This flickering is an indicator that the scene and camera settings need to be rethought. Often the easy fix is to increase the z-buffer precision by increasing the camera's near distance. The downside is that this can clip very near objects. GROME will let you create such large worlds. Its own Graphite engine handles them well. Most 3D engines are designed for smaller first and third-person games which will have a practical limit of around 10 to 25 square kilometers (1 meter = 1 unit). GROME can mix levels of detail quite easily, different regions of the terrain have their own mesh density. If for example you have a map on an island, you will want lots of detail for the land and less in the sea region. However, game engines such as Unity, UDK, and Ogre3 Dare are not easily adapted to deal with such variability in the terrain mesh since they are optimized to render a large triangular grid of uniform size. Instead, we use techniques to fake extra detail and bake it into our terrain textures, dramatically reducing the triangle count in the process. Using a combination of Normal Maps and Mesh Layers in GROME we can create the illusion of more detail than there is at a distance. Normal map A Normal is a unit vector (a vector with a total length of one) perpendicular to a surface. When a texture is used as a Normal map, the red, green, and blue channels represent the vector (x,y,z). These are used to generate the illusion of more detail by creating a bumpy looking surface. Also known as bump-maps. Summary In this article we looked at heightmaps and how they allow us to import and export to other programs and engines. We touched upon world sizes and limitations commonly found in 3D engines. Resources for Article : Further resources on this subject: Photo Manipulation with GIMP 2.6 [Article] Setting up a BizTalk Server Environment [Article] Creating and Warping 3D Text with Away3D 3.6 [Article]
Read more
  • 0
  • 0
  • 15794

article-image-implementing-data-model
Packt
06 Mar 2013
13 min read
Save for later

Implementing the data model

Packt
06 Mar 2013
13 min read
(For more resources related to this topic, see here.) Getting on with it Before we define our model, let's define a namespace where it will live. This is an important habit to establish since it relieves us of having to worry about whether or not we'll collide with another function, object, or variable of the same name. While there are various methods used to create a namespace, we're going to do it simply using the following code snippet: // quizQuestion.js var QQ = QQ || {}; Now that our namespace is defined, we can create our question object as follows: QQ.Question = function ( theQuestion ) { var self = this; Note the use of self: this will allow us to refer to the object using self rather than using this. (Javascript's this is a bit nuts, so it's always better to refer to a variable that we know will always refer to the object.) Next, we'll set up the properties based on the diagram we created from step two using the following code snippet: self.question = theQuestion; self.answers = Array(); self.correctAnswer = -1; We've set the self.correctAnswer value to -1 to indicate that, at the moment, any answer provided by the player is considered correct. This means you can ask questions where all of the answers are right. Our next step is to define the methods or interactions the object will have. Let's start with determining if an answer is correct. In the following code, we will take an incoming answer and compare it to the self.correctAnswer value. If it matches, or if the self.correctAnswer value is -1, we'll indicate that the answer is correct: self.testAnswer = function( theAnswerGiven ) { if ((theAnswerGiven == self.correctAnswer) || (self.correctAnswer == -1)) { return true; } else { return false; } } We're going to need a way to access a specific answer, so we'll define the answerAtIndex function as follows: self.answerAtIndex = function ( theIndex ) { return self.answers[ theIndex ]; } To be a well-defined model, we should always have a way of determining the number of items in the model as shown in the following code snippet: self.answerCount = function () { return self.answers.length; } Next, we need to define a method that allows an answer to be added to our object. Note that with the help of the return value, we return ourselves to permitting daisy-chaining in our code: self.addAnswer = function( theAnswer ) { self.answers.push ( theAnswer ); return self; } In theory we could display the answers to a question in the order they were given to the object. In practice, that would turn out to be a pretty boring game: the answers would always be in the same order, and chances would be pretty good that the first answer would be the correct answer. So let's give ourselves a randomized list using the following code snippet: self.getRandomizedAnswers = function () { var randomizedArray = Array(); var theRandomNumber; var theNumberExists; // go through each item in the answers array for (var i=0; i<self.answers.length; i++) { // always do this at least once do { // generate a random number less than the // count of answers theRandomNumber = Math.floor ( Math.random() * self.answers.length ); theNumberExists = false; // check to see if it is already in the array for (var j=0; j<randomizedArray.length; j++) { if (randomizedArray[j] == theRandomNumber) { theNumberExists = true; } } // If it exists, we repeat the loop. } while ( theNumberExists ); // We have a random number that is unique in the // array; add it to it. randomizedArray.push ( theRandomNumber ); } return randomizedArray; } The randomized list is just an array of numbers that indexes into the answers[] array. To get the actual answer, we'll have to use the answerAtIndex() method. Our model still needs a way to set the correct answer. Again, notice the return value in the following code snippet permitting us to daisy-chain later on: self.setCorrectAnswer = function ( theIndex ) { self.correctAnswer = theIndex; return self; } Now that we've properly set the correct answer, what if we need to ask the object what the correct answer is? For this let's define a getCorrectAnswer function using the following code snippet: self.getCorrectAnswer = function () { return self.correctAnswer; } Of course, our object also needs to return the question given to it whenever it was created; this can be done using the following code snippet: self.getQuestion = function() { return self.question; } } That's it for the question object. Next we'll create the container that will hold all of our questions using the following code line: QQ.questions = Array(); We could go the regular object-oriented approach and make the container an object as well, but in this game we have only one list of questions, so it's easier to do it this way. Next, we need to have the ability to add a question to the container, this can be done using the following code snippet: QQ.addQuestion = function (theQuestion) { QQ.questions.push ( theQuestion ); } Like any good data model, we need to know how many questions we have; we can know this using the following code snippet: QQ.count = function () { return QQ.questions.length; } Finally, we need to be able to get a random question out of the list so that we can show it to the player; this can be done using the following code snippet: QQ.getRandomQuestion = function () { var theQuestion = Math.floor (Math.random() * QQ.count()); return QQ.questions[theQuestion]; } Our data model is officially complete. Let's define some questions using the following code snippet: // quizQuestions.js // // QUESTION 1 // QQ.addQuestion ( new QQ.Question ( "WHAT_IS_THE_COLOR_OF_THE_SUN?" ) .addAnswer( "YELLOW" ) .addAnswer( "WHITE" ) .addAnswer( "GREEN" ) .setCorrectAnswer ( 0 ) ); Notice how we attach the addAnswer and setCorrectAnswer methods to the new question object. This is what is meant by daisy-chaining: it helps us write just a little bit less code. You may be wondering why we're using upper-case text for the questions and answers. This is due to how we'll localize the text, which is next: PKLOC.addTranslation ( "en", "WHAT_IS_THE_COLOR_OF_THE_SUN?", "What is the color of the Sun?" ); PKLOC.addTranslation ( "en", "YELLOW", "Yellow" ); PKLOC.addTranslation ( "en", "WHITE", "White" ); PKLOC.addTranslation ( "en", "GREEN", "Green" ); PKLOC.addTranslation ( "es", "WHAT_IS_THE_COLOR_OF_THE_SUN?", "¿Cuál es el color del Sol?" ); PKLOC.addTranslation ( "es", "YELLOW", "Amarillo" ); PKLOC.addTranslation ( "es", "WHITE", "Blanco" ); PKLOC.addTranslation ( "es", "GREEN", "Verde" ); The questions and answers themselves serve as keys to the actual translation. This serves two purposes: it makes the keys obvious in our code, so we know that the text will be replaced later on, and should we forget to include a translation for one of the keys, it'll show up in uppercase letters. PKLOC as used in the earlier code snippet is the namespace we're using for our localization library. It's defined in www/framework/localization.js. The addTranslation method is a method that adds a translation to a specific locale. The first parameter is the locale for which we're defining the translation, the second parameter is the key, and the third parameter is the translated text. The PKLOC.addTranslation function looks like the following code snippet: PKLOC.addTranslation = function (locale, key, value) { if (PKLOC.localizedText[locale]) { PKLOC.localizedText[locale][key] = value; } else { PKLOC.localizedText[locale] = {}; PKLOC.localizedText[locale][key] = value; } } The addTranslation method first checks to see if an array is defined under the PKLOC.localizedText array for the desired locale. If it is there, it just adds the key/value pair. If it isn't, it creates the array first and then adds the key/value pair. You may be wondering how the PKLOC.localizedText array gets defined in the first place. The answer is that it is defined when the script is loaded, a little higher in the file: PKLOC.localizedText = {}; Continue adding questions in this fashion until you've created all the questions you want. The quizQuestions.js file contains ten questions. You could, of course, add as many as you want. What did we do? In this task, we created our data model and created some data for the model. We also showed how translations are added to each locale. What else do I need to know? Before we move on to the next task, let's cover a little more of the localization library we'll be using. Our localization efforts are split into two parts: translation and data formatting . For the translation effort , we're using our own simple translation framework, literally just an array of keys and values based on locale. Whenever code asks for the translation for a key, we'll look it up in the array and return whatever translation we find, if any. But first, we need to determine the actual locale of the player, using the following code snippet: // www/framework/localization.js PKLOC.currentUserLocale = ""; PKLOC.getUserLocale = function() { Determining the locale isn't hard, but neither is it as easy as you would initially think. There is a property (navigator.language) under WebKit browsers that is technically supposed to return the locale, but it has a bug under Android, so we have to use the userAgent. For WP7, we have to use one of three properties to determine the value. Because that takes some work, we'll check to see if we've defined it before; if we have, we'll return that value instead: if (PKLOC.currentUserLocale) { return PKLOC.currentUserLocale; } Next, we determine the current device we're on by using the device object provided by Cordova. We'll check for it first, and if it doesn't exist, we'll assume we can access it using one of the four properties attached to the navigator object using the following code snippet: var currentPlatform = "unknown"; if (typeof device != 'undefined') { currentPlatform = device.platform; } We'll also provide a suitable default locale if we can't determine the user's locale at all as seen in the following code snippet: var userLocale = "en-US"; Next, we handle parsing the user agent if we're on an Android platform. The following code is heavily inspired by an answer given online at http://stackoverflow.com/a/7728507/741043. if (currentPlatform == "Android") { var userAgent = navigator.userAgent; var tempLocale = userAgent.match(/Android.*([a-zA-Z]{2}-[a-zA-Z] {2})/); if (tempLocale) { userLocale = tempLocale[1]; } } If we're on any other platform, we'll use the navigator object to retrieve the locale as follows: else { userLocale = navigator.language || navigator.browserLanguage || navigator.systemLanguage || navigator.userLanguage; } Once we have the locale, we return it as follows: PKLOC.currentUserLocale = userLocale; return PKLOC.currentUserLocale; } This method is called over and over by all of our translation codes, which means it needs to be efficient. This is why we've defined the PKLOC.currentUserLocale property. Once it is set, the preceding code won't try to calculate it out again. This also introduces another benefit: we can easily test our translation code by overwriting this property. While it is always important to test that the code properly localizes when the device is set to a specific language and region, it often takes considerable time to switch between these settings. Having the ability to set the specific locale helps us save time in the initial testing by bypassing the time it takes to switch device settings. It also permits us to focus on a specific locale, especially when testing. Translation of text is accomplished by a convenience function named __T() . The convenience functions are going to be our only functions outside of any specific namespace simply because we are aiming for easy-to-type and easy-to-remember names that aren't arduous to add to our code. This is especially important since they'll wrap every string, number, date, or percentage in our code. The __T() function depends on two functions: substituteVariables and lookupTranslation. The first function is de fined as follows: PKLOC.substituteVariables = function ( theString, theParms ) { var currentValue = theString; // handle replacement variables if (theParms) { for (var i=1; i<=theParms.length; i++) { currentValue = currentValue.replace("%" + i, theParms[i-1]); } } return currentValue; } All this function does is handle the substitution variables. This means we can define a translation with %1 in the text and we will be able to replace %1 with some value passed into the function. The next function, lookupTranslation, is defined as follows: PKLOC.lookupTranslation = function ( key, theLocale ) { var userLocale = theLocale || PKLOC.getUserLocale(); if ( PKLOC.localizedText[userLocale] ) { if ( PKLOC.localizedText[userLocale][key.toUpperCase()] ) { return PKLOC.localizedText[userLocale][key.toUpperCase()]; } } return null; } Essentially, we're checking to see if a specific translation exists for the given key and locale. If it does, we'll return the translation, but if it doesn't, we'll return null. Note that the key is always converted to uppercase, so case doesn't matter when looking up a translation. Our __T() function looks as follows: function __T(key, parms, locale) { var userLocale = locale || PKLOC.getUserLocale(); var currentValue = ""; First, we determine if the translation requested can be found in the locale, whatever that may be. Note that it can be passed in, therefore overriding the current locale. This can be done using the following code snippet: if (! (currentValue=PKLOC.lookupTranslation(key, userLocale)) ) { Locales are often of the form xx-YY, where xx is a two-character language code and YY is a two-character character code. My locale is defined as en-US. Another player's might be defined as es-ES. If you recall, we defined our translations only for the language. This presents a problem: the preceding code will not return any translation unless we defined the translation for the language and the country. Sometimes it is critical to define a translation specific to a language and a country. While various regions may speak the same language from a technical perspective, idioms often differ. If you use an idiom in your translation, you'll need to localize them to the specific region that uses them, or you could generate potential confusion. Therefore, we chop off the country code, and try again as follows: userLocale = userLocale.substr(0,2); if (! (currentValue=PKLOC.lookupTranslation(key, userLocale)) ) { But we've only defined translations for English (en) and Spanish(es)! What if the player's locale is fr-FR (French)? The preceding code will fail, because we've not defined any translation for the fr language (French). Therefore, we'll check for a suitable default, which we've defined to be en-US, American English: userLocale = "en-US"; if (! (currentValue=PKLOC.lookupTranslation(key, userLocale)) ) { Of course, we are now in the same boat as before: there are no translations defined for en-US in our game. So we need to fall back to en as follows: userLocale = "en"; if (! (currentValue=PKLOC.lookupTranslation(key, userLocale)) ) { But what happens if we can't find a translation at all? We could be mean and throw a nasty error, and perhaps you might want to do exactly that, but in our example, we're just returning the incoming key. If the convention of capitalizing the key is always followed, we'll still be able to see that something hasn't been translated. currentValue = key; } } } } Finally, we pass the currentValue parameter to the substituteVariables property in order to process any substitutions that we might need as follows: return PKLOC.substituteVariables( currentValue, parms ); } Summary In this article we saw the file quizQuestion.js which was the actual model: it specified how the data should be formatted and how we can interact with it. We also saw the quizQuestions.js file, which contained our actual question data. Resources for Article : Further resources on this subject: Configuring the ChildBrowser plugin [Article] Adding Geographic Capabilities via the GeoPlaces Theme [Article] iPhone: Issues Related to Calls, SMS, and Contacts [Article]
Read more
  • 0
  • 0
  • 2281

article-image-installing-removing-and-upgrading-software
Packt
04 Mar 2013
11 min read
Save for later

Installing, Removing, and Upgrading Software

Packt
04 Mar 2013
11 min read
(For more resources related to this topic, see here.) Installing software Sooner or later, system administrators need to install software in one or more machines. In fact, this is one of the most common tasks for the users of this kind of operating system. Linux Mint includes thousands of software applications that can be installed easily. Remember that when you install the operating system, a lot of software is installed by default. Also, it's possible to install additional software provided by Linux Mint. In order to facilitate the process of installing software in Linux Mint, this operating system includes a tool named Software Manager. Time for action – installing AbiWord word processor We're going to install a text processor named AbiWord (http://www.abisource.com) using the Software Manager application. Let us see how this is done: Click on the Menu button and then click on the All Applications button located on the right-hand side of the Favorites pane:     Look for the Administration menu option, and click on the Software Manager menu option; then a new window will be displayed:     Enter abiword in the textbox located on the right-hand side of the Software Manager main window, then hit Enter when you're ready. Now, you can see a list with a lot of software that is related to your search. Actually, the first result of the list is the text processor that we want to install, which is shown in this screenshot:     Double-clicking on an item will display information about the selected item:     Right now, you're ready to click on the Install button. By doing that, the installation process for AbiWord will be launched. You can see a progress bar indicating how the installation is progressing. In order to check whether AbiWord is installed in your system, you can open a shell and type AbiWord, and then press Enter. AbiWord will be executed, and you can start to use it. What just happened? Usually, GNU/Linux distributions include a lot of software that is ready to be installed in a specific format called a package. Software is organized in different packages and some applications need more than one package; this fact is called package dependency. Linux Mint uses packages in the Debian (DEB) format, same as the Ubuntu Linux and Debian operating systems. Packages are usually retrieved and installed from a storage location in a computer. This location is called a repository, and it contains many software packages ready to be downloaded and installed in your machine. Software Manager is an application developed by Linux Mint developers specifically for Linux Mint. This tool allows you to list, install, and remove software. Technically, Software Manager is a frontend for some commands, such as apt-get and apt-cache. Using a GUI, Software Manager provides an easy and intuitive way to list, install, and remove software, avoiding the use of the command-line interface. For learning how to install software, we've installed AbiWord, a simple but useful word processor. As you must have discovered, it's pretty easy to install software through packages provided by Linux Mint. You only need to look for a software and click on the Install button. Obviously, you can install a lot of different software on your machine following the same process. It's important to know that not only software distributed in packages can be installed in Linux Mint, but you can install software distributed in other formats, such as tarball and ZIP files as well. However, Mint includes a lot of software distributed in DEB packages, so this method of installation is advisable. Actually, it's good practice to use Software Manager to install software on your Linux Mint operating system. Have a go hero – discovering software provided by Linux Mint When you launch Software Manager, a window showing you different icons will be displayed. Each icon represents a different category of software included in Linux Mint. For example, you can see the Internet, Sound and Video, Graphics, and Office categories, among others. By clicking on each one, you'll have access to packages belonging to the selected category. You can try and click on a category, take a look at the list of packages for that category, and try to install one of the packages. This process allows you to discover interesting software that can be installed on your machine. Removing software Previously, we learned how to install software on your Linux Mint machine. Also, it's interesting and useful to know how to do the opposite step. You can remove installed software on your machine by following a simple process using the Software Manager tool. Remember, you can remove software installed by default during the installation process of the operating system or remove software that you have installed through the Software Manager application. Time for action – removing the AbiWord program Let's remove AbiWord from our system by following these steps: Click on the Menu button. On the left-hand side of the pane following the System label, you will find an option called Software Manager; click on it. Now, Software Manager will be launched. Type abiword in the search box, and press Enter. You will see a list displaying coincidences of your searching; click on the first one, and Software Manager will show you some information about AbiWord. In order to remove AbiWord from your system, you only need to click on the Remove button and the removing process will start automatically. What just happened? As we have commented before, Software Manager allows us to remove software from our system. We only need to look for the right package, select it, and then click on the Remove button. It's an easy and clean process, which helps the system administrators to a great extent. Also, it's very intuitive for newbie users who don't want to use the command-line interface. Before clicking on the Remove button, you can see a label informing you that the AbiWord software was installed on your system. When the software is not installed, this label is different and a Not Installed message is displayed instead. This is a simple and effective way to know if a specific software is installed or not on your Linux Mint machine. By now, you must have realized that we launched Software Manager through a different menu option than what was explained previously in the Installing software section. Both of them are valid methods for launching this useful tool. Upgrading software Between the release cycles, Linux Mint developers upgrade some software that is included in the operating system. This means that the system administrators should check what software has been upgraded and they should decide which of this software should be installed on the machine. Occasionally, upgraded software only applies minor changes, such as simple improvements or unimportant bug fixes. However, at times upgraded software also includes important security bug fixes. If we don't apply them, our machine can be at a serious risk. That's the reason we frequently check which software has been upgraded for our Linux Mint version. Time for action – upgrading software through the Update Manager tool We're going to discover which software is ready to be upgraded and we'll then proceed to install it. Here is how that is done: Click on the Menu button on the bottom panel, and then click on the All Applications button located at the right-hand side of the Favorites pane. Now you can see a menu option called Administration; click on it to display a new menu, where you can see the Update Manager menu option, as shown in the following screenshot:     After clicking on the Update Manager menu option, an application with the same name will be launched, as can been seen here:     Update Manager displays a list with only a single package that can be upgraded. Click on the Install Updates button. The upgrading process for the mintupdate package will be launched; when this process is finished, you will see a complete list of available software that can be upgraded. Right now, you're ready to select the packages that you want to upgrade. It's good practice to select all of them; you should install the packages listed as level 1 at least. Click on the Install Updates button when you're ready. When the upgrading process is finished, you'll see an empty list in Update Manager. What just happened? Update Manager is an effective application provided by Linux Mint to install new versions of installed software and to install security upgrades on your machine. When you launch that application the first time, you will see only a single item in a list for applying upgrades. This item indicates that you need to install an upgrade for mintupdate, which is the codename of Update Manager. Obviously, before applying other upgrades, we need to upgrade the tool itself. Once this process is finished, Update Manager will be launched again to check which software can be upgraded. The list of software ready to be upgraded displays different items providing information about the package name, installed version, and new version available. Also, a number for each item is displayed. This number ranges from 1 to 5, and it indicates a level based on the importance of the upgrade. Level 1 indicates that Linux Mint developers certify that a package and packages marked with this level should be upgraded as soon as possible. Usually, security updates use this level. The next level is for those packages whose upgrade is recommended by Linux Mint developers. Level 3 indicates that it is safe to apply an upgrade, but Linux Mint developers didn't test it. Packages marked with level 4 are unsafe, and if you upgrade it, the stability of the system can be affected. Finally, level 5 is for dangerous packages (in the unstable stage of development or with broken dependencies), and Linux Mint developers know that they can damage the operating system. In general terms, it's a good idea to launch Software Manager frequently and upgrade only those packages marked as level 1 and level 2. For levels 3, 4, and 5, system administrators and advanced users should decide about applying upgrades or not themselves. Also, you can mark all the packages listed or only a few of them. Usually, this is a task of the system administrator; it is he or she who takes these kinds of decisions. By default, only packages from level 1 to 3 are displayed in the list for upgrading. You can change this preference by going to Edit | Preferences. Have a go hero – finding out how to change preferences in Update Manager Some preferences of Update Manager can be changed. For example, you can change which level of upgrade is displayed by default. Also, you can select the frequency for refreshing information after clicking on the Refresh button. On the other hand, you can decide which packages will be ignored by Update Manager. Another important option offered by Update Manager is the option of choosing which kind of packages can be installed and from what server. By default, main, upstream, and imported packages are selected. Also, it's possible to add additional repositories instead of the official ones provided by Mint. For the server, the main server of Linux Mint is selected, but you can choose one of the many available mirrors. Summary After reading this article, we have discovered how to perform some important operations, such as installing, removing, and upgrading software. Linux Mint offers us two great applications to carry out the mentioned actions related to software, namely Update Manager and Software Manager. Specifically, we covered: How Linux Mint distributes software using packages Using Software Manager to add and remove software on Linux Mint How to upgrade software through the Update Manager tool Although Software Manager is the main tool for installing and removing software in Linux Mint, another tool called Synaptic Package Manager is included in this operating system. This tool offers a low-level control of packages, so you can use it when you're looking for a specific package instead of a specific application. Remember that a single application can be distributed in more than one package. Resources for Article : Further resources on this subject: Our First Project – A Basic Thermometer [Article] Working with Rails – Setting up and connecting to a database [Article] Fedora 8 — More than a Linux Distribution [Article]
Read more
  • 0
  • 0
  • 6444
article-image-planning-your-lessons-using-ipad
Packt
04 Mar 2013
5 min read
Save for later

Planning your lessons using iPad

Packt
04 Mar 2013
5 min read
(For more resources related to this topic, see here.) Getting ready We begin by downloading the Planbook app from the App Store. It costs $9.99 but is definitely worth the money spent. You may also like to download its Mac app from Mac App Store, if you wish to keep your iPad and Mac in sync. How to do it... The home screen of the app shows the available Planbooks, but as it is your first launch of the app there will be no available Planbooks. To start creating lesson plans you need to choose the option Create New Planbook or import one from your dropbox folder. Planbook provides excellent customization features to suit your exact needs. You can add as many courses as you want by clicking on Add Courses. You can specify the dates in the Dates you want included in your Planbook textbox as a date range, and even select the type of schedule you use as illustrated in the next screenshot: Now that you have specified the major features of your plan, you need to click on Continue to specify the time range of each course of a day. Planbook provides standard iOS date pickers to choose start and end times for each course. After putting in all details for a lesson plan, you need to click on Continue, which will take you back to the home screen, where you can now see the Planbook you just made in the list of available files. If you wish to, you can edit the name of your Planbook, delete it, or e-mail it. As we want to move forward to create your detailed plan, you should select Open Planbook File of your plan. You should now see your lesson plan for one entire week. Each course is represented by a different color for each day, and its contents are shown at the very same place minimizing unnecessary navigation. As you have created the complete outline of your lesson plan, it is time you should specify the content of each course. It's easy—just tap on the course you want to create content for, and a screen with many user-friendly customization features will appear. This is shown in the next screenshot: Here you can enter your course content divided into six different text fields in the Text Fields column at the right-hand side, with even the title of each field being editable! You can specify assignments in the textbox Assignments for the course, use attachments in Files and Web Links in the Attachments option related to the course content, and even have keywords in Keywords which might help you with easy look up. If you wish to see your lesson plan on the basis of a single day, simply click on Day tab at the bottom as shown in the following screenshot and you will see a nice, clean day plan. Tapping on the course still works to edit it! You can tap on a course with two fingers if you want to view it in detail. You will find this small feature very useful when you need to have a quick look at the course content while in class. One of the reasons I was so impressed with this app was the set of powerful editing and navigation options it provides. The gear icon at the bottom-right corner of each course pops up a box of tools. You can use this box to: Bump Lesson: You can shift the course further by one day using this option. The course content for the selected day becomes blank. Yank Lesson: This option removes the course for the selected day. The entire course shifts back by one day. Make Non-School Day: This option gets either all the courses of the day shifted further by one day or the content of all courses of the day is deleted, depending on the user's choice. You have a Courses button in the top-right corner, which lets you show/hide courses in the plan. You also have a Share button besides the Courses button that will let you print or e-mail the Planbook, in a day-wise or week-wise arrangement depending on the active tab. To navigate between weeks/days, swipe from the left- to right-hand side or from the right- to left-hand side to move to the previous or next week/day respectively. You can also click on the Week or Day options present at the bottom of the screen to navigate to that particular week/day. How it works... We have now created a Planbook as per your requirements. You can create multiple Planbooks in the same way. The Planbook app saves each Planbook as a file on the device that you can retrieve, modify, and delete at any later stage even when you are offline. There's more... The iPad App Store has many other apps apart from Planbook (which we chose due to its simplicity and powerful features) that facilitate planning lessons. There is an app My LessonPlan, which costs $2.99, but is more advantageous in classes where even students have access to personal devices. iPlanLessons, costing $6.99 is another powerful app and can be considered as an alternative to Planbook. Summary In this article we saw how the Planbook app makes it very easy to create, modify, and share plans for daily planning purposes. We also saw how it is a an easy-to-use app for a beginner who is not comfortable and proficient with iPad apps. Resources for Article : Further resources on this subject: iPhone User Interface: Starting, Stopping, and Multitasking Applications [Article] iPhone: Issues Related to Calls, SMS, and Contacts [Article] Create a Local Ubuntu Repository using Apt-Mirror and Apt-Cacher [Article]
Read more
  • 0
  • 0
  • 1660

article-image-2d-graphics
Packt
04 Mar 2013
8 min read
Save for later

2D Graphics

Packt
04 Mar 2013
8 min read
(For more resources related to this topic, see here.) Adding content Create a new project and call it Chapter2Demo. XNA Game Studio created a class called Game1. Rename it to MainGame so it has a proper name. When we take a look at our solution, we can see two projects. A game project called Chapter2Demo that contains all our code, and a content project called Chapter2DemoContent. This content project will hold all our assets, and compile them to an intermediate file format (xnb). This is often done in game development to make sure our games start faster. The resulting files are uncompressed, and thus larger, but can be read directly into memory without extra processing. Note that we can have more than one content project in a solution. We might add one per platform, but this is beyond the scope of this article. Navigate to the content project using Windows Explorer, and place our textures in there. The start files can be downloaded from the previously mentioned link. Then add the files to the content project by right-clicking on it in the Solution Explorer and choosing the Add | Existing Item.... Make sure to place the assets in a folder called Game2D. When we click on the hero texture in the content project, we can see several properties. First of all, our texture has a name, Hero. We can use that name to load our texture in code. Note that this has no extension, because the files will be compiled to an intermediate format anyway. We can also specify a Content Importer and Content Processor. Our .png file gets recognized as texture so XNA Game studio automatically selects the Texture importer and processor for us. An importer will convert our assets into the "Content Document Object Model", a format that can be read by the processor. The processor will compile the asset into a managed code object, which can then be serialized into the intermediate .xnb file. That file will then be loaded at runtime. Drawing sprites Everything is set up for us to begin. Let's start drawing some images. We'll draw a background, an enemy, and our hero. Adding fields At the top of our MainGame, we need to add a field for each of our objects.The type used here is Texture2D. Texture2D _background, _enemy, _hero; Loading textures In the LoadContent method, we need to load our textures using the content manager. // TODO: use this.Content to load your game content here _background = Content.Load<Texture2D>("Game2D/Background"); _enemy = Content.Load<Texture2D>("Game2D/Enemy"); _hero = Content.Load<Texture2D>("Game2D/Hero"); The content manager has a generic method called Load. Generic meaning we can specify a type, in this case Texture2D. It has one argument, being the asset name. Note that you do not specify an extension, the asset name corresponds with the folder structure and then the name of the asset that you specified in the properties. This is because the content is compiled to .xnb format by our content project anyway, so the files we load with the content manager all have the same extension. Also note that we do not specify the root directory of our content, because we've set it in the game's constructor. Drawing textures Before we start drawing textures, we need to make sure our game runs in full screen. This is because the emulator has a bug and our sprites wouldn't show up correctly. You can enable full screen by adding the following code to the constructor: graphics.IsFullScreen = true; Now we can go to the Draw method. Rendering textures is always done in a specific way: First we call the SpriteBatch.Begin() method. This will make sure all the correct states necessary for drawing 2D images are set properly. Next we draw all our sprites using the Draw method of the sprite batch. This method has several overloads. The first is the texture to draw. The second an object of type Vector2D that will store the position of the object. And the last argument is a color that will tint your texture. Specify Color.White if you don't want to tint your texture. Finally we call the SpriteBatch.End() method. This will sort all sprites we've rendered (according the the specified sort mode) and actually draw them. If we apply the previous steps, they result in the following code: // TODO: Add your drawing code here spriteBatch.Begin(); spriteBatch.Draw(_background, new Vector2(0, 0), Color.White); spriteBatch.Draw(_enemy, new Vector2(10, 10), Color.White); spriteBatch.Draw(_hero, new Vector2(10, 348), Color.White); spriteBatch.End(); Run the game by pressing F5. The result is shown in the following screenshot: Refactoring our code In the previous code, we've drawn three textures from our game class. We hardcoded the positions, something we shouldn't do. None of the textures were moving but if we want to add movement now, our game class would get cluttered, especially if we have many sprites. Therefore we will refactor our code and introduce some classes. We will create two classes: a GameObject2D class that is the base class for all 2D objects, and a GameSprite class, that will represent a sprite. We will also create a RenderContext class. This class will hold our graphics device, sprite batch, and game time objects. We will use all these classes even more extensively when we begin building our own framework. Render context Create a class called RenderContext. To create a new class, do the following: Right-click on your solution. Click on Add | New Item. Select the Code template on the left. Select Class and name it RenderContext. Click on OK. This class will contain three properties: SpriteBatch, GraphicsDevice, and GameTime. We will use an instance of this class to pass to the Update and Draw methods of all our objects. That way they can access the necessary information. Make sure the class has public as access specifier. The class is very simple: public class RenderContext { public SpriteBatch SpriteBatch { get; set; } public GraphicsDevice GraphicsDevice { get; set; } public GameTime GameTime { get; set; } } When you build this class, it will not recognize the terms SpriteBatch, GraphicsDevice, and GameTime. This is because they are stored in certain namespaces and we haven't told the compiler to look for them. Luckily, XNA Game Studio can find them for us automatically. If you hover over SpriteBatch, an icon like the one in the following screenshot will appear on the left-hand side. Click on it and choose the using Microsoft.Xna.Framework.Graphics; option. This will fix the using statement for you. Do it each time such a problem arises. The base class The base class is called GameObject2D. The only thing it does is store the position, scale, and rotation of the object and a Boolean that determines if the object should be drawn. It also contains four methods: Initialize, LoadContent, Draw, and Update. These methods currently have an empty body, but objects that will inherit from this base class later on will add an implementation. We will also use this base class for our scene graph, so don't worry if it still looks a bit empty. Properties We need to create four automatic properties. The Position and the Scale parameters are of type Vector2. The rotation is a float and the property that determines if the object should be drawn is a bool. public Vector2 Position { get; set; } public Vector2 Scale { get; set; } public float Rotation { get; set; } public bool CanDraw { get; set; } Constructor In the constructor, we will set the Scale parameter to one (no scaling) and set the CanDraw parameter to true. public GameObject2D() { Scale = Vector2.One; CanDraw = true; } Methods This class has four methods. Initialize: We will create all our new objects in this method. LoadContent: This method will be used for loading our content. It has one argument, being the content manager. Update: This method shall be used for updating our positions and game logic. It also has one argument, the render context. Draw: We will use this method to draw our 2D objects. It has one argument, the render context. public virtual void Initialize() { } public virtual void LoadContent(ContentManager contentManager) { } public virtual void Update(RenderContext renderContext) { } public virtual void Draw(RenderContext renderContext) { } Summary In this Article we have got used to the 2D coordinate system. Resources for Article : Further resources on this subject: 3D Animation Techniques with XNA Game Studio 4.0 [Article] Advanced Lighting in 3D Graphics with XNA Game Studio 4.0 [Article] Environmental Effects in 3D Graphics with XNA Game Studio 4.0 [Article]
Read more
  • 0
  • 0
  • 2431
Modal Close icon
Modal Close icon