Browse Source

Added network chat sample. Cleaned up UI creation code in SoundEffects sample.
Include kNet include dir when finding Urho3D library.
Reset the server connection pointer before sending disconnection event in the Network subsystem.

Lasse Öörni 12 years ago
parent
commit
426d664990

+ 3 - 1
Source/CMake/Modules/FindUrho3D.cmake

@@ -63,7 +63,9 @@ if (URHO3D_HOME)
             ${SOURCE_TREE_PATH}/UI
             ${SOURCE_TREE_PATH}/UI
             ${URHO3D_HOME}/Source/Extras/LuaScript
             ${URHO3D_HOME}/Source/Extras/LuaScript
             ${URHO3D_HOME}/Source/ThirdParty/SDL/include
             ${URHO3D_HOME}/Source/ThirdParty/SDL/include
-            ${URHO3D_HOME}/Source/ThirdParty/Bullet/src)
+            ${URHO3D_HOME}/Source/ThirdParty/Bullet/src
+            ${URHO3D_HOME}/Source/ThirdParty/kNet/include
+            )
 
 
         if (ANDROID)
         if (ANDROID)
             set (URHO3D_INCLUDE_DIR ${URHO3D_INCLUDE_DIR} ${URHO3D_HOME}/android-Build/Engine)
             set (URHO3D_INCLUDE_DIR ${URHO3D_INCLUDE_DIR} ${URHO3D_HOME}/android-Build/Engine)

+ 2 - 2
Source/Engine/Network/Network.cpp

@@ -470,6 +470,8 @@ void Network::OnServerDisconnected()
 {
 {
     // Differentiate between failed connection, and disconnection
     // Differentiate between failed connection, and disconnection
     bool failedConnect = serverConnection_ && serverConnection_->IsConnectPending();
     bool failedConnect = serverConnection_ && serverConnection_->IsConnectPending();
+    serverConnection_.Reset();
+    
     if (!failedConnect)
     if (!failedConnect)
     {
     {
         LOGINFO("Disconnected from server");
         LOGINFO("Disconnected from server");
@@ -480,8 +482,6 @@ void Network::OnServerDisconnected()
         LOGERROR("Failed to connect to server");
         LOGERROR("Failed to connect to server");
         SendEvent(E_CONNECTFAILED);
         SendEvent(E_CONNECTFAILED);
     }
     }
-    
-    serverConnection_.Reset();
 }
 }
 
 
 void RegisterNetworkLibrary(Context* context)
 void RegisterNetworkLibrary(Context* context)

+ 9 - 4
Source/Samples/14_SoundEffects/SoundEffects.cpp

@@ -77,6 +77,12 @@ void SoundEffects::CreateUI()
     // Create a scene which will not be actually rendered, but is used to hold SoundSource components while they play sounds
     // Create a scene which will not be actually rendered, but is used to hold SoundSource components while they play sounds
     scene_ = new Scene(context_);
     scene_ = new Scene(context_);
     
     
+    UIElement* root = GetSubsystem<UI>()->GetRoot();
+    ResourceCache* cache = GetSubsystem<ResourceCache>();
+    XMLFile* uiStyle = cache->GetResource<XMLFile>("UI/DefaultStyle.xml");
+    // Set style to the UI root so that elements will inherit it
+    root->SetDefaultStyle(uiStyle);
+    
     // Create buttons for playing back sounds
     // Create buttons for playing back sounds
     for (unsigned i = 0; i < NUM_SOUNDS; ++i)
     for (unsigned i = 0; i < NUM_SOUNDS; ++i)
     {
     {
@@ -109,14 +115,14 @@ Button* SoundEffects::CreateButton(int x, int y, int xSize, int ySize, const Str
 {
 {
     UIElement* root = GetSubsystem<UI>()->GetRoot();
     UIElement* root = GetSubsystem<UI>()->GetRoot();
     ResourceCache* cache = GetSubsystem<ResourceCache>();
     ResourceCache* cache = GetSubsystem<ResourceCache>();
-    XMLFile* defaultStyle = cache->GetResource<XMLFile>("UI/DefaultStyle.xml");
     Font* font = cache->GetResource<Font>("Fonts/Anonymous Pro.ttf");
     Font* font = cache->GetResource<Font>("Fonts/Anonymous Pro.ttf");
     
     
     // Create the button and center the text onto it
     // Create the button and center the text onto it
     Button* button = root->CreateChild<Button>();
     Button* button = root->CreateChild<Button>();
-    button->SetStyleAuto(defaultStyle);
+    button->SetStyleAuto();
     button->SetPosition(x, y);
     button->SetPosition(x, y);
     button->SetSize(xSize, ySize);
     button->SetSize(xSize, ySize);
+    
     Text* buttonText = button->CreateChild<Text>();
     Text* buttonText = button->CreateChild<Text>();
     buttonText->SetAlignment(HA_CENTER, VA_CENTER);
     buttonText->SetAlignment(HA_CENTER, VA_CENTER);
     buttonText->SetFont(font, 12);
     buttonText->SetFont(font, 12);
@@ -129,7 +135,6 @@ Slider* SoundEffects::CreateSlider(int x, int y, int xSize, int ySize, const Str
 {
 {
     UIElement* root = GetSubsystem<UI>()->GetRoot();
     UIElement* root = GetSubsystem<UI>()->GetRoot();
     ResourceCache* cache = GetSubsystem<ResourceCache>();
     ResourceCache* cache = GetSubsystem<ResourceCache>();
-    XMLFile* defaultStyle = cache->GetResource<XMLFile>("UI/DefaultStyle.xml");
     Font* font = cache->GetResource<Font>("Fonts/Anonymous Pro.ttf");
     Font* font = cache->GetResource<Font>("Fonts/Anonymous Pro.ttf");
     
     
     // Create text and slider below it
     // Create text and slider below it
@@ -139,7 +144,7 @@ Slider* SoundEffects::CreateSlider(int x, int y, int xSize, int ySize, const Str
     sliderText->SetText(text);
     sliderText->SetText(text);
     
     
     Slider* slider = root->CreateChild<Slider>();
     Slider* slider = root->CreateChild<Slider>();
-    slider->SetStyleAuto(defaultStyle);
+    slider->SetStyleAuto();
     slider->SetPosition(x, y + 20);
     slider->SetPosition(x, y + 20);
     slider->SetSize(xSize, ySize);
     slider->SetSize(xSize, ySize);
     // Use 0-1 range for controlling sound/music master volume
     // Use 0-1 range for controlling sound/music master volume

+ 10 - 0
Source/Samples/16_Chat/CMakeLists.txt

@@ -0,0 +1,10 @@
+# Define target name
+set (TARGET_NAME 16_Chat)
+
+# Define source files
+file (GLOB CPP_FILES *.cpp)
+file (GLOB H_FILES *.h)
+set (SOURCE_FILES ${CPP_FILES} ${H_FILES} ${COMMON_SAMPLE_H_FILES})
+
+# Setup target with resource copying
+setup_main_executable ()

+ 270 - 0
Source/Samples/16_Chat/Chat.cpp

@@ -0,0 +1,270 @@
+//
+// Copyright (c) 2008-2013 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#include "Audio.h"
+#include "Button.h"
+#include "Engine.h"
+#include "Font.h"
+#include "Graphics.h"
+#include "Input.h"
+#include "IOEvents.h"
+#include "LineEdit.h"
+#include "Log.h"
+#include "MemoryBuffer.h"
+#include "Network.h"
+#include "NetworkEvents.h"
+#include "ResourceCache.h"
+#include "Scene.h"
+#include "Sound.h"
+#include "Text.h"
+#include "UI.h"
+#include "UIEvents.h"
+#include "VectorBuffer.h"
+#include "Zone.h"
+
+#include "Chat.h"
+
+#include "DebugNew.h"
+
+// Undefine Windows macro, as our Connection class has a function called SendMessage
+#ifdef SendMessage
+#undef SendMessage
+#endif
+
+// Identifier for the chat network messages
+const int MSG_CHAT = 32;
+// UDP port we will use
+const unsigned short CHAT_SERVER_PORT = 2345;
+
+// Expands to this example's entry-point
+DEFINE_APPLICATION_MAIN(Chat)
+
+Chat::Chat(Context* context) :
+    Sample(context)
+{
+}
+
+void Chat::Start()
+{
+    // Execute base class startup
+    Sample::Start();
+
+    // Enable OS cursor
+    GetSubsystem<Input>()->SetMouseVisible(true);
+
+    // Create the user interface
+    CreateUI();
+    
+    // Subscribe to UI and network events
+    SubscribeToEvents();
+}
+
+void Chat::CreateUI()
+{
+    SetLogoVisible(false); // We need the full rendering window
+
+    Graphics* graphics = GetSubsystem<Graphics>();
+    UIElement* root = GetSubsystem<UI>()->GetRoot();
+    ResourceCache* cache = GetSubsystem<ResourceCache>();
+    XMLFile* uiStyle = cache->GetResource<XMLFile>("UI/DefaultStyle.xml");
+    // Set style to the UI root so that elements will inherit it
+    root->SetDefaultStyle(uiStyle);
+    
+    Font* font = cache->GetResource<Font>("Fonts/Anonymous Pro.ttf");
+    chatHistoryText_ = root->CreateChild<Text>();
+    chatHistoryText_->SetFont(font, 12);
+    
+    buttonContainer_ = root->CreateChild<UIElement>();
+    buttonContainer_->SetFixedSize(graphics->GetWidth(), 20);
+    buttonContainer_->SetPosition(0, graphics->GetHeight() - 20);
+    buttonContainer_->SetLayoutMode(LM_HORIZONTAL);
+    
+    textEdit_ = buttonContainer_->CreateChild<LineEdit>();
+    textEdit_->SetStyleAuto();
+    
+    sendButton_ = CreateButton("Send", 70);
+    connectButton_ = CreateButton("Connect", 90);
+    disconnectButton_ = CreateButton("Disconnect", 100);
+    startServerButton_ = CreateButton("Start Server", 110);
+
+    UpdateButtons();
+
+    chatHistory_.Resize((graphics->GetHeight() - 20) / chatHistoryText_->GetRowHeight());
+
+    // No viewports or scene is defined. However, the default zone's fog color controls the fill color
+    GetSubsystem<Renderer>()->GetDefaultZone()->SetFogColor(Color(0.0f, 0.0f, 0.1f));
+}
+
+void Chat::SubscribeToEvents()
+{
+    SubscribeToEvent(textEdit_, E_TEXTFINISHED, HANDLER(Chat, HandleSend));
+    SubscribeToEvent(sendButton_, E_RELEASED, HANDLER(Chat, HandleSend));
+    SubscribeToEvent(connectButton_, E_RELEASED, HANDLER(Chat, HandleConnect));
+    SubscribeToEvent(disconnectButton_, E_RELEASED, HANDLER(Chat, HandleDisconnect));
+    SubscribeToEvent(startServerButton_, E_RELEASED, HANDLER(Chat, HandleStartServer));
+    SubscribeToEvent(E_LOGMESSAGE, HANDLER(Chat, HandleLogMessage));
+    SubscribeToEvent(E_NETWORKMESSAGE, HANDLER(Chat, HandleNetworkMessage));
+    SubscribeToEvent(E_SERVERCONNECTED, HANDLER(Chat, HandleConnectionStatus));
+    SubscribeToEvent(E_SERVERDISCONNECTED, HANDLER(Chat, HandleConnectionStatus));
+    SubscribeToEvent(E_CONNECTFAILED, HANDLER(Chat, HandleConnectionStatus));
+}
+
+Button* Chat::CreateButton(const String& text, int width)
+{
+    ResourceCache* cache = GetSubsystem<ResourceCache>();
+    Font* font = cache->GetResource<Font>("Fonts/Anonymous Pro.ttf");
+    
+    Button* button = buttonContainer_->CreateChild<Button>();
+    button->SetStyleAuto();
+    button->SetFixedWidth(width);
+    
+    Text* buttonText = button->CreateChild<Text>();
+    buttonText->SetFont(font, 12);
+    buttonText->SetAlignment(HA_CENTER, VA_CENTER);
+    buttonText->SetText(text);
+    
+    return button;
+}
+
+void Chat::ShowChatText(const String& row)
+{
+    chatHistory_.Erase(0);
+    chatHistory_.Push(row);
+
+    // Concatenate all the rows in history
+    String allRows;
+    for (unsigned i = 0; i < chatHistory_.Size(); ++i)
+        allRows += chatHistory_[i] + "\n";
+    
+    chatHistoryText_->SetText(allRows);
+}
+
+void Chat::UpdateButtons()
+{
+    Network* network = GetSubsystem<Network>();
+    Connection* serverConnection = network->GetServerConnection();
+    bool serverRunning = network->IsServerRunning();
+    
+    sendButton_->SetVisible(serverConnection != 0);
+    connectButton_->SetVisible(!serverConnection && !serverRunning);
+    disconnectButton_->SetVisible(serverConnection || serverRunning);
+    startServerButton_->SetVisible(!serverConnection && !serverRunning);
+}
+
+void Chat::HandleLogMessage(StringHash eventType, VariantMap& eventData)
+{
+    using namespace LogMessage;
+    
+    ShowChatText(eventData[P_MESSAGE].GetString());
+}
+
+void Chat::HandleSend(StringHash eventType, VariantMap& eventData)
+{
+    String text = textEdit_->GetText();
+    if (text.Empty())
+        return; // Do not send an empty message
+    
+    Network* network = GetSubsystem<Network>();
+    Connection* serverConnection = network->GetServerConnection();
+    
+    if (serverConnection)
+    {
+        // A VectorBuffer object is convenient for constructing a message to send
+        VectorBuffer msg;
+        msg.WriteString(text);
+        // Send the chat message as in-order and reliable
+        serverConnection->SendMessage(MSG_CHAT, true, true, msg);
+        
+        // Empty the editor after sending
+        textEdit_->SetText(String::EMPTY);
+    }
+}
+
+void Chat::HandleConnect(StringHash eventType, VariantMap& eventData)
+{
+    Network* network = GetSubsystem<Network>();
+    String address = textEdit_->GetText().Trimmed();
+    if (address.Empty())
+        address = "localhost"; // Use localhost to connect if nothing else specified
+    
+    // Connect to server, do not specify a client scene as we are not using scene replication, just messages
+    network->Connect(address, CHAT_SERVER_PORT, 0);
+    
+    UpdateButtons();
+}
+
+void Chat::HandleDisconnect(StringHash eventType, VariantMap& eventData)
+{
+    Network* network = GetSubsystem<Network>();
+    Connection* serverConnection = network->GetServerConnection();
+    if (serverConnection)
+        serverConnection->Disconnect();
+    else if (network->IsServerRunning())
+        network->StopServer();
+    
+    UpdateButtons();
+}
+
+void Chat::HandleStartServer(StringHash eventType, VariantMap& eventData)
+{
+    Network* network = GetSubsystem<Network>();
+    network->StartServer(CHAT_SERVER_PORT);
+    
+    UpdateButtons();
+}
+
+void Chat::HandleNetworkMessage(StringHash eventType, VariantMap& eventData)
+{
+    Network* network = GetSubsystem<Network>();
+    
+    using namespace NetworkMessage;
+    
+    int msgID = eventData[P_MESSAGEID].GetInt();
+    if (msgID == MSG_CHAT)
+    {
+        const PODVector<unsigned char>& data = eventData[P_DATA].GetBuffer();
+        // Use a MemoryBuffer to read the message data so that there is no unnecessary copying
+        MemoryBuffer msg(data);
+        String text = msg.ReadString();
+        
+        // If we are the server, prepend the sender's IP address and port and echo to everyone
+        // If we are a client, just display the message
+        if (network->IsServerRunning())
+        {
+            Connection* sender = (Connection*)eventData[P_CONNECTION].GetPtr();
+            
+            text = sender->ToString() + " " + text;
+            
+            VectorBuffer sendMsg;
+            sendMsg.WriteString(text);
+            // Broadcast as in-order and reliable
+            network->BroadcastMessage(MSG_CHAT, true, true, sendMsg);
+        }
+        
+        ShowChatText(text);
+    }
+}
+
+void Chat::HandleConnectionStatus(StringHash eventType, VariantMap& eventData)
+{
+    UpdateButtons();
+}

+ 95 - 0
Source/Samples/16_Chat/Chat.h

@@ -0,0 +1,95 @@
+//
+// Copyright (c) 2008-2013 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#pragma once
+
+#include "Sample.h"
+
+namespace Urho3D
+{
+    class Button;
+    class LineEdit;
+    class Text;
+    class UIElement;
+}
+
+using namespace Urho3D;
+
+/// Chat example
+/// This sample demonstrates:
+///     - Starting up a network server or connecting to it;
+///     - Implementing simple chat functionality with network messages;
+class Chat : public Sample
+{
+    OBJECT(Chat);
+
+public:
+    /// Construct.
+    Chat(Context* context);
+
+    /// Setup after engine initialization and before running the main loop.
+    virtual void Start();
+
+private:
+    /// Create the UI.
+    void CreateUI();
+    /// Subscribe to UI and network events.
+    void SubscribeToEvents();
+    /// Create a button.
+    Button* CreateButton(const String& text, int width);
+    /// Print chat text.
+    void ShowChatText(const String& row);
+    /// Update visibility of buttons according to connection and server status.
+    void UpdateButtons();
+    /// Handle log message event; pipe it also to the chat display.
+    void HandleLogMessage(StringHash eventType, VariantMap& eventData);
+    /// Handle pressing the send button.
+    void HandleSend(StringHash eventType, VariantMap& eventData);
+    /// Handle pressing the connect button.
+    void HandleConnect(StringHash eventType, VariantMap& eventData);
+    /// Handle pressing the disconnect button.
+    void HandleDisconnect(StringHash eventType, VariantMap& eventData);
+    /// Handle pressing the start server button.
+    void HandleStartServer(StringHash eventType, VariantMap& eventData);
+    /// Handle a network message.
+    void HandleNetworkMessage(StringHash eventType, VariantMap& eventData);
+    /// Handle connection status change (just update the buttons that should be shown.)
+    void HandleConnectionStatus(StringHash eventType, VariantMap& eventData);
+    /// Strings printed so far.
+    Vector<String> chatHistory_;
+    /// Chat text element.
+    SharedPtr<Text> chatHistoryText_;
+    /// Button container element.
+    SharedPtr<UIElement> buttonContainer_;
+    /// Line editor element.
+    SharedPtr<LineEdit> textEdit_;
+    /// Send button.
+    SharedPtr<Button> sendButton_;
+    /// Connect button.
+    SharedPtr<Button> connectButton_;
+    /// Disconnect button.
+    SharedPtr<Button> disconnectButton_;
+    /// Start server button.
+    SharedPtr<Button> startServerButton_;
+};
+
+

+ 2 - 1
Source/Samples/CMakeLists.txt

@@ -39,7 +39,7 @@ set (LIBS ../../Engine/Audio
           ../../Engine/Scene
           ../../Engine/Scene
           ../../Engine/UI)
           ../../Engine/UI)
 
 
-set (INCLUDE_DIRS_ONLY ${CMAKE_CURRENT_SOURCE_DIR} ../../ThirdParty/Bullet/src)
+set (INCLUDE_DIRS_ONLY ${CMAKE_CURRENT_SOURCE_DIR} ../../ThirdParty/Bullet/src ../../ThirdParty/kNet/include)
 
 
 # Add samples
 # Add samples
 add_subdirectory (01_HelloWorld)
 add_subdirectory (01_HelloWorld)
@@ -57,3 +57,4 @@ add_subdirectory (12_PhysicsStressTest)
 add_subdirectory (13_Ragdolls)
 add_subdirectory (13_Ragdolls)
 add_subdirectory (14_SoundEffects)
 add_subdirectory (14_SoundEffects)
 add_subdirectory (15_Navigation)
 add_subdirectory (15_Navigation)
+add_subdirectory (16_Chat)