From 7d49dd9759e136bd8b3a8fe5b39f1b69ae4d7683 Mon Sep 17 00:00:00 2001 From: Grant Postma Date: Fri, 26 Jun 2020 12:51:23 -0400 Subject: [PATCH 1/6] Auth full changes (#8) * Moving common functions into header files. Moving database & auth to MainMenuScene. Adding Auth node with fields and listeners. Set up user record management. Set up user sign-up, login and anonymous login. * auth full review by @DellaBitta * Private Class variables. Bug on disbanded room. --- demos/TicTacToe/Classes/AppDelegate.cpp | 15 + demos/TicTacToe/Classes/AppDelegate.h | 14 + demos/TicTacToe/Classes/MainMenuScene.cpp | 697 +++++++++++++++++++-- demos/TicTacToe/Classes/MainMenuScene.h | 79 ++- demos/TicTacToe/Classes/TicTacToeLayer.cpp | 485 ++++++-------- demos/TicTacToe/Classes/TicTacToeLayer.h | 104 +-- demos/TicTacToe/Classes/TicTacToeScene.cpp | 26 +- demos/TicTacToe/Classes/TicTacToeScene.h | 21 +- demos/TicTacToe/Resources/logout.png | Bin 0 -> 8006 bytes 9 files changed, 1068 insertions(+), 373 deletions(-) create mode 100644 demos/TicTacToe/Resources/logout.png diff --git a/demos/TicTacToe/Classes/AppDelegate.cpp b/demos/TicTacToe/Classes/AppDelegate.cpp index 15f1ce66..6fd2ab92 100644 --- a/demos/TicTacToe/Classes/AppDelegate.cpp +++ b/demos/TicTacToe/Classes/AppDelegate.cpp @@ -1,3 +1,17 @@ +// Copyright 2020 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include "AppDelegate.h" #include "MainMenuScene.h" @@ -6,6 +20,7 @@ USING_NS_CC; // Set based on the image width. const float kFrameWidth = 600; + // Set based on the image height plus 40 for windows bar. const float kFrameHeight = 640; diff --git a/demos/TicTacToe/Classes/AppDelegate.h b/demos/TicTacToe/Classes/AppDelegate.h index 49ad5f31..75e7b832 100644 --- a/demos/TicTacToe/Classes/AppDelegate.h +++ b/demos/TicTacToe/Classes/AppDelegate.h @@ -1,3 +1,17 @@ +// Copyright 2020 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #ifndef TICTACTOE_DEMO_CLASSES_APPDELEGATE_SCENE_H_ #define TICTACTOE_DEMO_CLASSES_APPDELEGATE_SCENE_H_ #include "cocos2d.h" diff --git a/demos/TicTacToe/Classes/MainMenuScene.cpp b/demos/TicTacToe/Classes/MainMenuScene.cpp index 297f3471..9fb2234e 100644 --- a/demos/TicTacToe/Classes/MainMenuScene.cpp +++ b/demos/TicTacToe/Classes/MainMenuScene.cpp @@ -1,13 +1,82 @@ +// Copyright 2020 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include "MainMenuScene.h" +#include + #include "TicTacToeScene.h" static const char* kCreateGameImage = "create_game.png"; static const char* kTextFieldBorderImage = "text_field_border.png"; static const char* kJoinButtonImage = "join_game.png"; +static const char* kLoginButtonImage = "login.png"; +static const char* kLogoutButtonImage = "logout.png"; +static const char* kSignUpButtonImage = "sign_up.png"; +// Regex that will validate if the email entered is a valid email pattern. +const std::regex email_pattern("(\\w+)(\\.|_)?(\\w*)@(\\w+)(\\.(\\w+))+"); USING_NS_CC; +void LogMessage(const char* format, ...) { + va_list list; + va_start(list, format); + vprintf(format, list); + va_end(list); + printf("\n"); + fflush(stdout); +} + +void ProcessEvents(int msec) { +#ifdef _WIN32 + Sleep(msec); +#else + usleep(msec * 1000); +#endif // _WIN32 +} + +// Wait for a Future to be completed. If the Future returns an error, it will +// be logged. +void WaitForCompletion(const firebase::FutureBase& future, const char* name) { + while (future.status() == firebase::kFutureStatusPending) { + ProcessEvents(100); + } + if (future.status() != firebase::kFutureStatusComplete) { + LogMessage("ERROR: %s returned an invalid result.", name); + } else if (future.error() != 0) { + LogMessage("ERROR: %s returned error %d: %s", name, future.error(), + future.error_message()); + } +} + +// Returns a random uid of a specified length. +std::string GenerateUid(std::size_t length) { + const std::string kCharacters = "0123456789abcdefghjkmnpqrstuvwxyz"; + + std::random_device random_device; + std::mt19937 generator(random_device()); + std::uniform_int_distribution<> distribution(0, kCharacters.size() - 1); + + std::string generate_uid; + + for (std::size_t i = 0; i < length; ++i) { + generate_uid += kCharacters[distribution(generator)]; + } + + return generate_uid; +} + Scene* MainMenuScene::createScene() { // Builds a simple scene that uses the bottom left cordinate point as (0,0) // and can have sprites, labels and layers added onto it. @@ -22,35 +91,316 @@ bool MainMenuScene::init() { if (!Layer::init()) { return false; } - // Creates a sprite for the create button and sets its position to the center - // of the screen. TODO(grantpostma): Dynamically choose the location. - auto create_button = Sprite::create(kCreateGameImage); - create_button->setPosition(25, 200); - create_button->setAnchorPoint(Vec2(0, 0)); + + // Call the function to initialize the firebase features. + this->InitializeFirebase(); + + // Create the background to add all of the authentication elements on. The + // visiblity of this node should match kAuthState, disable any + // touch_listeners when not in this state. + auth_background_ = DrawNode::create(); + auto auth_background_border = DrawNode::create(); + + const auto auth_background_size = Size(500, 550); + const auto auth_background_origin = Size(50, 50); + Vec2 auth_background_corners[4]; + auth_background_corners[0] = + Vec2(auth_background_origin.width, auth_background_origin.height); + auth_background_corners[1] = + Vec2(auth_background_origin.width + auth_background_size.width, + auth_background_origin.height); + auth_background_corners[2] = + Vec2(auth_background_origin.width + auth_background_size.width, + auth_background_origin.height + auth_background_size.height); + auth_background_corners[3] = + Vec2(auth_background_origin.width, + auth_background_origin.height + auth_background_size.height); + + Color4F white(1, 1, 1, 1); + auth_background_->drawPolygon(auth_background_corners, 4, Color4F::BLACK, 1, + Color4F::BLACK); + auth_background_border->drawPolygon(auth_background_corners, 4, + Color4F(0, 0, 0, 0), 1, Color4F::WHITE); + auth_background_->addChild(auth_background_border); + + this->addChild(auth_background_, 10); + + // Label the background as Authentication. + auto auth_label = Label::createWithSystemFont("authentication", "Arial", 48); + auth_label->setPosition(Vec2(300, 550)); + auth_background_->addChild(auth_label); + + // Label to print out all of the login errors. + invalid_login_label_ = Label::createWithSystemFont("", "Arial", 24); + invalid_login_label_->setTextColor(Color4B::RED); + invalid_login_label_->setPosition(Vec2(300, 220)); + auth_background_->addChild(invalid_login_label_); + + // Label to display the users record. + user_record_label_ = Label::createWithSystemFont("", "Arial", 24); + user_record_label_->setAlignment(TextHAlignment::RIGHT); + user_record_label_->setTextColor(Color4B::WHITE); + user_record_label_->setPosition(Vec2(500, 600)); + this->addChild(user_record_label_); + + // Label for anonymous sign in. + auto anonymous_login_label = + Label::createWithSystemFont("anonymous sign-in", "Arial", 18); + anonymous_login_label->setTextColor(Color4B::WHITE); + anonymous_login_label->setPosition(Vec2(460, 75)); + + auto anonymous_label_touch_listener = EventListenerTouchOneByOne::create(); + + anonymous_label_touch_listener->onTouchBegan = + [this](cocos2d::Touch* touch, cocos2d::Event* event) -> bool { + // Returns if the layer is not in the auth state or is switching states. + if (previous_state_ != current_state_ || current_state_ != kAuthState) + return true; + const auto bounds = event->getCurrentTarget()->getBoundingBox(); + const auto point = touch->getLocation(); + if (bounds.containsPoint(point)) { + // Use anonymous sign in for the user. + user_result_ = auth_->SignInAnonymously(); + current_state_ = kWaitingAnonymousState; + } + + return true; + }; + + // Attach the touch listener to the text field. + Director::getInstance() + ->getEventDispatcher() + ->addEventListenerWithSceneGraphPriority(anonymous_label_touch_listener, + anonymous_login_label); + auth_background_->addChild(anonymous_login_label); + + // Extract the origin, size and position of the text field so that the + // border can be created based on those values. + const auto email_text_field_origin = Vec2(0, 0); + const auto email_text_field_position = Size(110, 350); + const auto text_field_padding = 20; + const auto email_font_size = 36.0; + const auto email_text_field_size = Size(400, 2 * email_font_size); + + // Set up the constraints of the border so it surrounds the text box. + Vec2 email_border_corners[4] = { + Vec2(email_text_field_position.width - text_field_padding, + email_text_field_position.height), + Vec2(email_text_field_position.width + email_text_field_size.width, + email_text_field_position.height), + Vec2(email_text_field_position.width + email_text_field_size.width, + email_text_field_position.height + email_text_field_size.height), + Vec2(email_text_field_position.width - text_field_padding, + email_text_field_position.height + email_text_field_size.height)}; + + // Create a text field border and add it around the text field. + auto email_text_field_border = DrawNode::create(); + email_text_field_border->drawPolygon(email_border_corners, 4, + Color4F(0, 0, 0, 0), 1, Color4F::WHITE); + + // Create a text field to enter the user's email. + email_text_field_ = cocos2d::TextFieldTTF::textFieldWithPlaceHolder( + "enter an email address", email_text_field_size, TextHAlignment::LEFT, + "Arial", email_font_size); + email_text_field_->setPosition(email_text_field_position); + email_text_field_->setAnchorPoint(Vec2(0, 0)); + email_text_field_->setColorSpaceHolder(Color3B::GRAY); + email_text_field_->setDelegate(this); + + auto email_text_field_touch_listener = EventListenerTouchOneByOne::create(); + + email_text_field_touch_listener->onTouchBegan = + [this](cocos2d::Touch* touch, cocos2d::Event* event) -> bool { + // Returns if the layer is not in the auth state or is switching states. + if (previous_state_ != current_state_ || current_state_ != kAuthState) { + return true; + } + const auto bounds = event->getCurrentTarget()->getBoundingBox(); + const auto point = touch->getLocation(); + if (bounds.containsPoint(point)) { + // Show the on screen keyboard and places character inputs into the text + // field. + auto text_field = dynamic_cast(event->getCurrentTarget()); + text_field->setCursorEnabled(true); + text_field->attachWithIME(); + } else { + auto text_field = dynamic_cast(event->getCurrentTarget()); + text_field->setCursorEnabled(false); + text_field->detachWithIME(); + } + + return true; + }; + + // Attach the touch listener to the text field. + Director::getInstance() + ->getEventDispatcher() + ->addEventListenerWithSceneGraphPriority(email_text_field_touch_listener, + email_text_field_); + + auth_background_->addChild(email_text_field_, 1); + auth_background_->addChild(email_text_field_border, 1); + + // Extract the origin, size and position of the text field so that the + // border can be created based on those values. + const auto password_text_field_origin = Vec2(0, 0); + const auto password_text_field_position = Size(110, 250); + const auto password_font_size = 36.0; + const auto password_text_field_size = Size(400, 2 * password_font_size); + + // Set up the constraints of the border so it surronds the text box. + Vec2 password_border_corners[4] = { + Vec2(password_text_field_position.width - text_field_padding, + password_text_field_position.height), + Vec2(password_text_field_position.width + password_text_field_size.width, + password_text_field_position.height), + Vec2(password_text_field_position.width + password_text_field_size.width, + password_text_field_position.height + + password_text_field_size.height), + Vec2(password_text_field_position.width - text_field_padding, + password_text_field_position.height + + password_text_field_size.height)}; + + // Create a text field border and add it around the text field. + auto password_text_field_border = DrawNode::create(); + password_text_field_border->drawPolygon( + password_border_corners, 4, Color4F(0, 0, 0, 0), 1, Color4F::WHITE); + + // Create a text field to enter the user's password. + password_text_field_ = cocos2d::TextFieldTTF::textFieldWithPlaceHolder( + "enter a password", password_text_field_size, TextHAlignment::LEFT, + "Arial", password_font_size); + password_text_field_->setPosition(password_text_field_position); + password_text_field_->setAnchorPoint(Vec2(0, 0)); + password_text_field_->setColorSpaceHolder(Color3B::GRAY); + password_text_field_->setSecureTextEntry(true); + password_text_field_->setDelegate(this); + + auto password_text_field_touch_listener = + EventListenerTouchOneByOne::create(); + + password_text_field_touch_listener->onTouchBegan = + [this](cocos2d::Touch* touch, cocos2d::Event* event) -> bool { + // Returns if the layer is not in the auth state or is switching states. + if (previous_state_ != current_state_ || current_state_ != kAuthState) { + return true; + } + const auto bounds = event->getCurrentTarget()->getBoundingBox(); + const auto point = touch->getLocation(); + if (bounds.containsPoint(point)) { + // Shows the on screen keyboard and places character inputs into the text + // field. + auto text_field = dynamic_cast(event->getCurrentTarget()); + text_field->setCursorEnabled(true); + text_field->attachWithIME(); + } else { + auto text_field = dynamic_cast(event->getCurrentTarget()); + text_field->setCursorEnabled(false); + text_field->detachWithIME(); + } + + return true; + }; + + auth_background_->addChild(password_text_field_, 1); + auth_background_->addChild(password_text_field_border, 1); + + // Attach the touch listener to the text field. + Director::getInstance() + ->getEventDispatcher() + ->addEventListenerWithSceneGraphPriority( + password_text_field_touch_listener, password_text_field_); + + // Create the login button and give it a position, anchor point and + // touch_listener. + auto login_button = Sprite::create(kLoginButtonImage); + login_button->setPosition(90, 120); + login_button->setAnchorPoint(Vec2(0, 0)); + login_button->setContentSize(Size(200, 75)); + // Create a button listener to handle the touch event. - auto create_button_touch_listener = EventListenerTouchOneByOne::create(); - // Setting the onTouchBegan event up to a lambda tha will replace the MainMenu - // scene with a TicTacToe scene. - create_button_touch_listener->onTouchBegan = [](Touch* touch, - Event* event) -> bool { - auto bounds = event->getCurrentTarget()->getBoundingBox(); - auto point = touch->getLocation(); - // Replaces the scene with a new TicTacToe scene if the touched point is - // within the bounds of the button. + auto login_button_touch_listener = EventListenerTouchOneByOne::create(); + + // Transition from kAuthState to kWaitingLoginState on button press and set + // user_result_ to SignInWithEmailAndPassword future result. + login_button_touch_listener->onTouchBegan = [this](Touch* touch, + Event* event) -> bool { + // Returns if the layer is not in the auth state or is switching states. + if (previous_state_ != current_state_ || current_state_ != kAuthState) { + return true; + } + const auto bounds = event->getCurrentTarget()->getBoundingBox(); + const auto point = touch->getLocation(); if (bounds.containsPoint(point)) { - Director::getInstance()->replaceScene( - TicTacToe::createScene(std::string())); + // Check the string for a valid email pattern. + if (!std::regex_match(email_text_field_->getString(), email_pattern)) { + invalid_login_label_->setString("invalid email address"); + } else if (password_text_field_->getString().length() < 8) { + invalid_login_label_->setString( + "password must be at least 8 characters long"); + } else { + user_result_ = auth_->SignInWithEmailAndPassword( + email_text_field_->getString().c_str(), + password_text_field_->getString().c_str()); + current_state_ = kWaitingLoginState; + } } + return true; + }; + + // Attach the touch listener to the login button. + Director::getInstance() + ->getEventDispatcher() + ->addEventListenerWithSceneGraphPriority(login_button_touch_listener, + login_button); + auth_background_->addChild(login_button, 1); + // Create the sign_up button and give it a position, anchor point and + // touch_listener. + auto sign_up_button = Sprite::create(kSignUpButtonImage); + sign_up_button->setPosition(310, 120); + sign_up_button->setAnchorPoint(Vec2(0, 0)); + sign_up_button->setContentSize(Size(200, 75)); + + // Create a button listener to handle the touch event. + auto sign_up_button_touch_listener = EventListenerTouchOneByOne::create(); + + // Transition from kAuthState to kWaitingSignUpState on button press and set + // user_result_ to CreateUserWithEmailAndPassword future result. + sign_up_button_touch_listener->onTouchBegan = [this](Touch* touch, + Event* event) -> bool { + // Returns if the layer is not in the auth state or is switching states. + if (previous_state_ != current_state_ || current_state_ != kAuthState) { + return true; + } + const auto bounds = event->getCurrentTarget()->getBoundingBox(); + const auto point = touch->getLocation(); + if (bounds.containsPoint(point)) { + // Check the string for a valid email pattern. + if (!std::regex_match(email_text_field_->getString(), email_pattern)) { + invalid_login_label_->setString("invalid email address"); + } else if (password_text_field_->getString().length() < 8) { + invalid_login_label_->setString( + "password must be at least 8 characters long"); + } else { + user_result_ = auth_->CreateUserWithEmailAndPassword( + email_text_field_->getString().c_str(), + password_text_field_->getString().c_str()); + current_state_ = kWaitingSignUpState; + } + } return true; }; - // Attaching the touch listener to the create game button. + + // Attach the touch listener to the sign_up button. Director::getInstance() ->getEventDispatcher() - ->addEventListenerWithSceneGraphPriority(create_button_touch_listener, - create_button); - // Creating, setting the position and assigning a placeholder to the text - // field for entering the join game uuid. + ->addEventListenerWithSceneGraphPriority(sign_up_button_touch_listener, + sign_up_button); + auth_background_->addChild(sign_up_button, 1); + + // Create, set the position and assign a placeholder to the text + // field for the user to enter the join game uuid. TextFieldTTF* join_text_field = cocos2d::TextFieldTTF::textFieldWithPlaceHolder( "code", cocos2d::Size(200, 100), TextHAlignment::LEFT, "Arial", 55.0); @@ -59,19 +409,24 @@ bool MainMenuScene::init() { join_text_field->setColorSpaceHolder(Color3B::WHITE); join_text_field->setDelegate(this); - auto text_field_border = Sprite::create(kTextFieldBorderImage); - text_field_border->setPosition(390, 50); - text_field_border->setAnchorPoint(Vec2(0, 0)); - text_field_border->setScale(.53f); - this->addChild(text_field_border, 0); - // Create a touch listener to handle the touch event. TODO(grantpostma): add a - // focus bar when selecting inside the text field's bounding box. - auto text_field_touch_listener = EventListenerTouchOneByOne::create(); - - text_field_touch_listener->onTouchBegan = - [join_text_field](cocos2d::Touch* touch, cocos2d::Event* event) -> bool { - auto bounds = event->getCurrentTarget()->getBoundingBox(); - auto point = touch->getLocation(); + auto join_text_field_border = Sprite::create(kTextFieldBorderImage); + join_text_field_border->setPosition(390, 50); + join_text_field_border->setAnchorPoint(Vec2(0, 0)); + join_text_field_border->setScale(.53f); + this->addChild(join_text_field_border, 0); + + // Create a touch listener to handle the touch event. + auto join_text_field_touch_listener = EventListenerTouchOneByOne::create(); + + join_text_field_touch_listener->onTouchBegan = + [join_text_field, this](cocos2d::Touch* touch, + cocos2d::Event* event) -> bool { + // Returns if the layer is not in the game menu state or is switching + // states. + if (previous_state_ != current_state_ || current_state_ != kGameMenuState) + return true; + const auto bounds = event->getCurrentTarget()->getBoundingBox(); + const auto point = touch->getLocation(); if (bounds.containsPoint(point)) { // Show the on screen keyboard and places character inputs into the text // field. @@ -88,12 +443,89 @@ bool MainMenuScene::init() { return true; }; - // Attaching the touch listener to the text field. + // Attach the touch listener to the text field. Director::getInstance() ->getEventDispatcher() - ->addEventListenerWithSceneGraphPriority(text_field_touch_listener, + ->addEventListenerWithSceneGraphPriority(join_text_field_touch_listener, join_text_field); + // Creates a sprite for the create button and sets its position to the + // center of the screen. TODO(grantpostma): Dynamically choose the location. + auto create_button = Sprite::create(kCreateGameImage); + create_button->setPosition(25, 200); + create_button->setAnchorPoint(Vec2(0, 0)); + + // Create a button listener to handle the touch event. + auto create_button_touch_listener = EventListenerTouchOneByOne::create(); + + // Set the onTouchBegan event up to a lambda tha will replace the + // MainMenu scene with a TicTacToe scene. + create_button_touch_listener->onTouchBegan = + [this, join_text_field](Touch* touch, Event* event) -> bool { + if (current_state_ != kGameMenuState) return true; + const auto bounds = event->getCurrentTarget()->getBoundingBox(); + const auto point = touch->getLocation(); + + // Replaces the scene with a new TicTacToe scene if the touched point is + // within the bounds of the button. + if (bounds.containsPoint(point)) { + Director::getInstance()->pushScene( + TicTacToe::createScene(std::string(), database_, user_uid_)); + join_text_field->setString(""); + current_state_ = kWaitingGameOutcome; + } + + return true; + }; + + // Attach the touch listener to the create game button. + Director::getInstance() + ->getEventDispatcher() + ->addEventListenerWithSceneGraphPriority(create_button_touch_listener, + create_button); + + // Creates a sprite for the logout button and sets its position to the + auto logout_button = Sprite::create(kLogoutButtonImage); + logout_button->setPosition(25, 575); + logout_button->setAnchorPoint(Vec2(0, 0)); + logout_button->setContentSize(Size(125, 50)); + + // Create a button listener to handle the touch event. + auto logout_button_touch_listener = EventListenerTouchOneByOne::create(); + + // Set the onTouchBegan event up to a lambda tha will replace the + // MainMenu scene with a TicTacToe scene. + logout_button_touch_listener->onTouchBegan = [this](Touch* touch, + Event* event) -> bool { + // Returns if the layer is not in the game menu state or is switching + // states. + if (previous_state_ != current_state_ || current_state_ != kGameMenuState) { + return true; + } + const auto bounds = event->getCurrentTarget()->getBoundingBox(); + const auto point = touch->getLocation(); + + // Replaces the scene with a new TicTacToe scene if the touched point is + // within the bounds of the button. + if (bounds.containsPoint(point)) { + current_state_ = kAuthState; + user_uid_ = ""; + user_ = nullptr; + invalid_login_label_->setString(""); + email_text_field_->setString(""); + password_text_field_->setString(""); + user_record_label_->setString(""); + } + + return true; + }; + + // Attach the touch listener to the logout game button. + Director::getInstance() + ->getEventDispatcher() + ->addEventListenerWithSceneGraphPriority(logout_button_touch_listener, + logout_button); + // Creates a sprite for the join button and sets its position to the center // of the screen. TODO(grantpostma): Dynamically choose the location and set // size(). @@ -101,36 +533,209 @@ bool MainMenuScene::init() { join_button->setPosition(25, 50); join_button->setAnchorPoint(Vec2(0, 0)); join_button->setScale(1.3f); + // Create a button listener to handle the touch event. auto join_button_touch_listener = EventListenerTouchOneByOne::create(); - // Setting the onTouchBegan event up to a lambda tha will replace the MainMenu - // scene with a TicTacToe scene and pass in join_text_field string. + + // Set the onTouchBegan event up to a lambda tha will replace the + // MainMenu scene with a TicTacToe scene and pass in join_text_field string. join_button_touch_listener->onTouchBegan = - [join_text_field](Touch* touch, Event* event) -> bool { - auto bounds = event->getCurrentTarget()->getBoundingBox(); - auto point = touch->getLocation(); + [join_text_field, this](Touch* touch, Event* event) -> bool { + // Returns if the layer is not in the game menu state or is switching + // states. + if (previous_state_ != current_state_ || current_state_ != kGameMenuState) { + return true; + } + const auto bounds = event->getCurrentTarget()->getBoundingBox(); + const auto point = touch->getLocation(); if (bounds.containsPoint(point)) { - // Getting the string from join_text_field. + // Get the string from join_text_field. std::string join_text_field_string = join_text_field->getString(); if (join_text_field_string.length() == 4) { - Director::getInstance()->replaceScene( - TicTacToe::createScene(join_text_field_string)); + Director::getInstance()->pushScene(TicTacToe::createScene( + join_text_field_string, database_, user_uid_)); + current_state_ = kWaitingGameOutcome; + join_text_field->setString(""); } else { join_text_field->setString(""); } } return true; }; - // Attaching the touch listener to the join button. + + // Attach the touch listener to the join button. Director::getInstance() ->getEventDispatcher() ->addEventListenerWithSceneGraphPriority(join_button_touch_listener, join_button); - // Attaching the create button, join button and join text field to the + + // Attach the create button, join button and join text field to the // MainMenu scene. this->addChild(create_button); this->addChild(join_button); + this->addChild(logout_button); this->addChild(join_text_field, 1); + this->scheduleUpdate(); + return true; } +// Initialize the firebase auth and database while also ensuring no dependencies +// are missing. +void MainMenuScene::InitializeFirebase() { + LogMessage("Initialize Firebase App."); + ::firebase::App* app; + +#if defined(_ANDROID_) + app = ::firebase::App::Create(GetJniEnv(), GetActivity()); +#else + app = ::firebase::App::Create(); +#endif // defined(ANDROID) + + LogMessage("Initialize Firebase Auth and Firebase Database."); + + // Use ModuleInitializer to initialize both Auth and Database, ensuring no + // dependencies are missing. + database_ = nullptr; + auth_ = nullptr; + void* initialize_targets[] = {&auth_, &database_}; + + const firebase::ModuleInitializer::InitializerFn initializers[] = { + [](::firebase::App* app, void* data) { + LogMessage("Attempt to initialize Firebase Auth."); + void** targets = reinterpret_cast(data); + ::firebase::InitResult result; + *reinterpret_cast<::firebase::auth::Auth**>(targets[0]) = + ::firebase::auth::Auth::GetAuth(app, &result); + return result; + }, + [](::firebase::App* app, void* data) { + LogMessage("Attempt to initialize Firebase Database."); + void** targets = reinterpret_cast(data); + ::firebase::InitResult result; + *reinterpret_cast<::firebase::database::Database**>(targets[1]) = + ::firebase::database::Database::GetInstance(app, &result); + return result; + }}; + + ::firebase::ModuleInitializer initializer; + initializer.Initialize(app, initialize_targets, initializers, + sizeof(initializers) / sizeof(initializers[0])); + + WaitForCompletion(initializer.InitializeLastResult(), "Initialize"); + + if (initializer.InitializeLastResult().error() != 0) { + LogMessage("Failed to initialize Firebase libraries: %s", + initializer.InitializeLastResult().error_message()); + ProcessEvents(2000); + } + LogMessage("Successfully initialized Firebase Auth and Firebase Database."); + + database_->set_persistence_enabled(true); +} + +// Updates the user record variables to reflect what is in the database. +void MainMenuScene::UpdateUserRecord() { + ref_ = database_->GetReference("users").Child(user_uid_); + auto future_wins = ref_.Child("wins").GetValue(); + auto future_loses = ref_.Child("loses").GetValue(); + auto future_ties = ref_.Child("ties").GetValue(); + WaitForCompletion(future_wins, "getUserWinsData"); + WaitForCompletion(future_loses, "getUserLosesData"); + WaitForCompletion(future_ties, "getUserTiesData"); + user_wins_ = future_wins.result()->value().int64_value(); + user_loses_ = future_loses.result()->value().int64_value(); + user_ties_ = future_ties.result()->value().int64_value(); + user_record_label_->setString("W:" + to_string(user_wins_) + + " L:" + to_string(user_loses_) + + " T:" + to_string(user_ties_)); +} + +// Initialize the user records in the database. +void MainMenuScene::InitializeUserRecord() { + ref_ = database_->GetReference("users").Child(user_uid_); + auto future_wins = ref_.Child("wins").SetValue(0); + auto future_loses = ref_.Child("loses").SetValue(0); + auto future_ties = ref_.Child("ties").SetValue(0); + WaitForCompletion(future_wins, "setUserWinsData"); + WaitForCompletion(future_loses, "setUserLosesData"); + WaitForCompletion(future_ties, "setUserTiesData"); + user_wins_ = 0; + user_loses_ = 0; + user_ties_ = 0; + user_record_label_->setString("W:" + to_string(user_wins_) + + " L:" + to_string(user_loses_) + + " T:" + to_string(user_ties_)); +} + +// Overriding the onEnter method to udate the user_record on reenter. +void MainMenuScene::onEnter() { + // if the scene enter is from the game, updateUserRecords and change + // current_state_. + if (current_state_ == kWaitingGameOutcome) { + this->UpdateUserRecord(); + current_state_ = kGameMenuState; + } + Layer::onEnter(); +} + +// Update loop that gets called every frame and was set of by scheduleUpdate(). +// Acts as the state manager for this scene. +void MainMenuScene::update(float /*delta*/) { + if (current_state_ != previous_state_) { + if (current_state_ == kWaitingAnonymousState) { + if (user_result_.status() == firebase::kFutureStatusComplete) { + if (user_result_.error() == firebase::auth::kAuthErrorNone) { + user_ = *user_result_.result(); + user_uid_ = GenerateUid(10); + + this->InitializeUserRecord(); + + current_state_ = kGameMenuState; + } + } + } else if (current_state_ == kWaitingSignUpState) { + if (user_result_.status() == firebase::kFutureStatusComplete) { + if (user_result_.error() == firebase::auth::kAuthErrorNone) { + user_ = *user_result_.result(); + user_uid_ = user_->uid(); + + this->InitializeUserRecord(); + + current_state_ = kGameMenuState; + + } else { + // Change invalid_login_label_ to display the user_create failed. + invalid_login_label_->setString("invalid sign up"); + current_state_ = kAuthState; + } + } + } else if (current_state_ == kWaitingLoginState) { + if (user_result_.status() == firebase::kFutureStatusComplete) { + if (user_result_.error() == firebase::auth::kAuthErrorNone) { + user_ = *user_result_.result(); + user_uid_ = user_->uid(); + + this->UpdateUserRecord(); + + current_state_ = kGameMenuState; + } else { + // Change invalid_login_label_ to display the auth_result errored. + auto err = user_result_.error_message(); + invalid_login_label_->setString("invalid login"); + current_state_ = kAuthState; + } + } + } else if (current_state_ == kAuthState) { + // Sign out logic, adding auth screen. + auth_background_->setVisible(true); + user_ = nullptr; + previous_state_ = current_state_; + } else if (current_state_ == kGameMenuState) { + // Removes the authentication screen. + auth_background_->setVisible(false); + previous_state_ = current_state_; + } + } + return; +} diff --git a/demos/TicTacToe/Classes/MainMenuScene.h b/demos/TicTacToe/Classes/MainMenuScene.h index 28a78fb1..0dd56998 100644 --- a/demos/TicTacToe/Classes/MainMenuScene.h +++ b/demos/TicTacToe/Classes/MainMenuScene.h @@ -1,19 +1,92 @@ +// Copyright 2020 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #ifndef TICTACTOE_DEMO_CLASSES_MAINMENU_SCENE_H_ #define TICTACTOE_DEMO_CLASSES_MAINMENU_SCENE_H_ #include "cocos2d.h" +#include "firebase/auth.h" +#include "firebase/database.h" +#include "firebase/future.h" +#include "firebase/util.h" #include "ui/CocosGUI.h" +using std::to_string; + +void LogMessage(const char*, ...); +void ProcessEvents(int); +void WaitForCompletion(const firebase::FutureBase&, const char*); +std::string GenerateUid(std::size_t); class MainMenuScene : public cocos2d::Layer, public cocos2d::TextFieldDelegate { public: - // Builds a simple scene that uses the bottom left cordinate point as (0,0) + // Build a simple scene that uses the bottom left cordinate point as (0,0) // and can have sprites, labels and nodes added onto it. static cocos2d::Scene* createScene(); - // Initializes the instance of a Node and returns a boolean based on if it was + void MainMenuScene::update(float) override; + void MainMenuScene::onEnter() override; + + // Initialize the instance of a Node and returns a boolean based on if it was // successful in doing so. virtual bool init(); - // Defines a create type for a specific type, in this case a Layer. CREATE_FUNC(MainMenuScene); + + private: + // Defines the state the class is currently in, which updates the sprites in + // the MainMenuScene::update(float) method. + enum kSceneState { + kAuthState, + kGameMenuState, + kWaitingAnonymousState, + kWaitingSignUpState, + kWaitingLoginState, + kWaitingGameOutcome + }; + + // Updates the user record (wins,loses and ties) and displays it to the + // screen. + void MainMenuScene::UpdateUserRecord(); + + // Initializes the user record (wins,loses and ties) and displays it to the + // screen. + void MainMenuScene::InitializeUserRecord(); + void MainMenuScene::InitializeFirebase(); + + // Creates node to be used as a background for the authentication menu. + cocos2d::DrawNode* auth_background_; + + // Labels and textfields for the authentication menu. + cocos2d::Label* invalid_login_label_; + cocos2d::Label* user_record_label_; + cocos2d::TextFieldTTF* email_text_field_; + cocos2d::TextFieldTTF* password_text_field_; + + // Variable to track the current state and previous state to check against to + // see if the state change. + kSceneState current_state_ = kAuthState; + kSceneState previous_state_ = kAuthState; + + // User record variabales that are stored in firebase database. + int user_wins_; + int user_loses_; + int user_ties_; + + std::string user_uid_; + firebase::auth::User* user_; + firebase::Future user_result_; + firebase::database::Database* database_; + firebase::auth::Auth* auth_; + firebase::database::DatabaseReference ref_; }; #endif // TICTACTOE_DEMO_CLASSES_MAINMENU_SCENE_H_ diff --git a/demos/TicTacToe/Classes/TicTacToeLayer.cpp b/demos/TicTacToe/Classes/TicTacToeLayer.cpp index 03334b6e..94cb607d 100644 --- a/demos/TicTacToe/Classes/TicTacToeLayer.cpp +++ b/demos/TicTacToe/Classes/TicTacToeLayer.cpp @@ -1,30 +1,18 @@ -#include "TicTacToeLayer.h" +// Copyright 2020 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. -#include "MainMenuScene.h" -#include "TicTacToeScene.h" -#include "firebase/app.h" -#include "firebase/auth.h" -#include "firebase/database.h" -#include "firebase/future.h" -#include "firebase/util.h" - -// Thin OS abstraction layer. -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using firebase::database::DataSnapshot; -using firebase::database::MutableData; -using firebase::database::TransactionResult; -using std::string; +#include "TicTacToeLayer.h" USING_NS_CC; @@ -41,6 +29,11 @@ static const enum kGameOutcome { kGameTied, kGameDisbanded }; + +// create an array that has indicies of enum kGameOutcome and maps that to the +// database outcome key. +static const const char* kGameOutcomeStrings[] = {"wins", "loses", "ties", + "disbanded"}; static const std::array kGameOverStrings = { "you won!", "you lost.", "you tied.", "user left."}; @@ -48,37 +41,22 @@ static const std::array kGameOverStrings = { extern const int kTilesX; extern const int kTilesY; static const int kNumberOfTiles = kTilesX * kTilesY; + // Screen dimensions. static const double kScreenWidth = 600; static const double kScreenHeight = 600; static const double kTileWidth = (kScreenWidth / kTilesX); static const double kTileHeight = (kScreenHeight / kTilesY); + // The screen will display the end game text for 2 seconds (120frames/60fps). static const int kEndGameFramesMax = 120; + // Image file paths. static const char* kBoardImageFileName = "tic_tac_toe_board.png"; static const char* kLeaveButtonFileName = "leave_button.png"; static std::array kPlayerTokenFileNames = { "tic_tac_toe_x.png", "tic_tac_toe_o.png"}; -void LogMessage(const char* format, ...) { - va_list list; - va_start(list, format); - vprintf(format, list); - va_end(list); - printf("\n"); - fflush(stdout); -} - -bool ProcessEvents(int msec) { -#ifdef _WIN32 - Sleep(msec); -#else - usleep(msec * 1000); -#endif // _WIN32 - return false; -} - // An example of a ValueListener object. This specific version will // simply log every value it sees, and store them in a list so we can // confirm that all values were received. @@ -152,7 +130,7 @@ class SampleChildListener : public firebase::database::ChildListener { } public: - // Vector of strings defining the events we saw, in order. + // Vector of strings that define the events we saw, in order. std::vector events_; }; @@ -183,35 +161,6 @@ class ExpectValueListener : public firebase::database::ValueListener { bool got_value_; }; -// Wait for a Future to be completed. If the Future returns an error, it will -// be logged. -void WaitForCompletion(const firebase::FutureBase& future, const char* name) { - while (future.status() == firebase::kFutureStatusPending) { - ProcessEvents(100); - } - if (future.status() != firebase::kFutureStatusComplete) { - LogMessage("ERROR: %s returned an invalid result.", name); - } else if (future.error() != 0) { - LogMessage("ERROR: %s returned error %d: %s", name, future.error(), - future.error_message()); - } -} -std::string GenerateGameUuid(std::size_t length) { - const std::string kCharacters = "0123456789abcdefghjkmnpqrstuvwxyz"; - - std::random_device random_device; - std::mt19937 generator(random_device()); - std::uniform_int_distribution<> distribution(0, kCharacters.size() - 1); - - std::string GenerateGameUuid; - - for (std::size_t i = 0; i < length; ++i) { - GenerateGameUuid += kCharacters[distribution(generator)]; - } - - return GenerateGameUuid; -} - // A function that returns true if any of the row // is crossed with the same player's move static bool RowCrossed(int board[][kTilesY]) { @@ -254,106 +203,47 @@ static bool GameOver(int board[][kTilesY]) { return (RowCrossed(board) || ColumnCrossed(board) || DiagonalCrossed(board)); } -TicTacToeLayer::TicTacToeLayer(string game_uuid) { - join_game_uuid = game_uuid; - current_player_index = kPlayerOne; - game_outcome = kGameWon; - LogMessage("Initialized Firebase App."); - auto app = ::firebase::App::Create(); - LogMessage("Initialize Firebase Auth and Firebase Database."); - // Use ModuleInitializer to initialize both Auth and Database, ensuring no - // dependencies are missing. - firebase::ModuleInitializer initializer; - - database = nullptr; - auth = nullptr; - void* initialize_targets[] = {&auth, &database}; - - const firebase::ModuleInitializer::InitializerFn initializers[] = { - [](::firebase::App* app, void* data) { - LogMessage("Attempt to initialize Firebase Auth."); - void** targets = reinterpret_cast(data); - ::firebase::InitResult result; - *reinterpret_cast<::firebase::auth::Auth**>(targets[0]) = - ::firebase::auth::Auth::GetAuth(app, &result); - return result; - }, - [](::firebase::App* app, void* data) { - LogMessage("Attempt to initialize Firebase Database."); - void** targets = reinterpret_cast(data); - ::firebase::InitResult result; - *reinterpret_cast<::firebase::database::Database**>(targets[1]) = - ::firebase::database::Database::GetInstance(app, &result); - return result; - }}; - - initializer.Initialize(app, initialize_targets, initializers, - sizeof(initializers) / sizeof(initializers[0])); - - WaitForCompletion(initializer.InitializeLastResult(), "Initialize"); - - if (initializer.InitializeLastResult().error() != 0) { - LogMessage("Failed to initialize Firebase libraries: %s", - initializer.InitializeLastResult().error_message()); - ProcessEvents(2000); - return; - } - LogMessage("Successfully initialized Firebase Auth and Firebase Database."); - - database->set_persistence_enabled(true); - - // Sign in using Auth before accessing the database. - // The default Database permissions allow anonymous users access. This will - // work as long as your project's Authentication permissions allow anonymous - // sign-in. - { - firebase::Future sign_in_future = - auth->SignInAnonymously(); - WaitForCompletion(sign_in_future, "SignInAnonymously"); - if (sign_in_future.error() == firebase::auth::kAuthErrorNone) { - LogMessage("Auth: Signed in anonymously."); - } else { - LogMessage("ERROR: Could not sign in anonymously. Error %d: %s", - sign_in_future.error(), sign_in_future.error_message()); - LogMessage( - " Ensure your application has the Anonymous sign-in provider " - "enabled in Firebase Console."); - LogMessage( - " Attempting to connect to the database anyway. This may fail " - "depending on the security settings."); - } - } - // Splits on the if depending on if the player created or joined the game. - // Additionally sets the player_index and total_players based on joining or - // creating a game. - if (join_game_uuid.empty()) { - join_game_uuid = GenerateGameUuid(4); - ref = database->GetReference("game_data").Child(join_game_uuid); +TicTacToeLayer::TicTacToeLayer(string game_uuid, + firebase::database::Database* main_menu_database, + string main_menu_user) { + join_game_uuid_ = game_uuid; + current_player_index_ = kPlayerOne; + game_outcome_ = kGameWon; + database_ = main_menu_database; + user_uid_ = main_menu_user; + + // If the join_game_uuid_ is present, initialize game varibales, otherwise + // alter the game variables to signify a user joined. Additionally sets the + // player_index_ and total_players based on joining or creating a game. + if (join_game_uuid_.empty()) { + join_game_uuid_ = GenerateUid(4); + ref_ = database_->GetReference("game_data").Child(join_game_uuid_); firebase::Future future_create_game = - ref.Child("total_players").SetValue(1); - future_current_player_index = - ref.Child("current_player_index").SetValue(kPlayerOne); - future_game_over = ref.Child("game_over").SetValue(false); - WaitForCompletion(future_game_over, "setGameOver"); - WaitForCompletion(future_current_player_index, "setCurrentPlayerIndex"); + ref_.Child("total_players").SetValue(1); + future_current_player_index_ = + ref_.Child("current_player_index_").SetValue(kPlayerOne); + future_game_over_ = ref_.Child("game_over").SetValue(false); + WaitForCompletion(future_game_over_, "setGameOver"); + WaitForCompletion(future_current_player_index_, "setCurrentPlayerIndex"); WaitForCompletion(future_create_game, "createGame"); - player_index = kPlayerOne; - awaiting_opponenet_move = false; + player_index_ = kPlayerOne; + awaiting_opponenet_move_ = false; } else { // Checks whether the join_uuid map exists. If it does not it set the // initialization to failed. auto future_game_uuid = - database->GetReference("game_data").Child(join_game_uuid).GetValue(); + database_->GetReference("game_data").Child(join_game_uuid_).GetValue(); WaitForCompletion(future_game_uuid, "GetGameDataMap"); auto game_uuid_snapshot = future_game_uuid.result(); if (!game_uuid_snapshot->value().is_map()) { - initialization_failed = true; + initialization_failed_ = true; } else { - ref = database->GetReference("game_data").Child(join_game_uuid); + ref_ = database_->GetReference("game_data").Child(join_game_uuid_); auto future_increment_total_users = - ref.RunTransaction([](MutableData* data) { + ref_.RunTransaction([](MutableData* data) { auto total_players = data->Child("total_players").value(); - // Completing the transaction based on the returned mutable data + + // Complete the transaction based on the returned mutable data // value. if (total_players.is_null()) { // Must return this if the transaction was unsuccessful. @@ -365,157 +255,170 @@ TicTacToeLayer::TicTacToeLayer(string game_uuid) { return TransactionResult::kTransactionResultAbort; } data->Child("total_players").set_value(new_total_players); + // Must call this if the transaction was successful. return TransactionResult::kTransactionResultSuccess; }); WaitForCompletion(future_increment_total_users, "JoinGameTransaction"); - player_index = kPlayerTwo; - awaiting_opponenet_move = true; + player_index_ = kPlayerTwo; + awaiting_opponenet_move_ = true; } } - // Creating the board sprite , setting the position to the bottom left of the - // frame (0,0), and finally moving the anchor point from the center of the + // Create the board sprite , set the position to the bottom left of the + // frame (0,0), and finally move the anchor point from the center of the // image(default) to the bottom left, Vec2(0.0,0.0). - board_sprite = Sprite::create(kBoardImageFileName); - if (!board_sprite) { + board_sprite_ = Sprite::create(kBoardImageFileName); + if (!board_sprite_) { log("kBoardImageFileName: %s file not found.", kBoardImageFileName); exit(true); } - board_sprite->setPosition(0, 0); - board_sprite->setAnchorPoint(Vec2(0.0, 0.0)); + board_sprite_->setPosition(0, 0); + board_sprite_->setAnchorPoint(Vec2(0.0, 0.0)); - leave_button_sprite = Sprite::create(kLeaveButtonFileName); - if (!leave_button_sprite) { + leave_button_sprite_ = Sprite::create(kLeaveButtonFileName); + if (!leave_button_sprite_) { log("kLeaveButtonSprite: %s file not found.", kLeaveButtonFileName); exit(true); } - leave_button_sprite->setPosition(450, 585); - leave_button_sprite->setAnchorPoint(Vec2(0.0, 0.0)); - leave_button_sprite->setScale(.35); + leave_button_sprite_->setPosition(450, 585); + leave_button_sprite_->setAnchorPoint(Vec2(0.0, 0.0)); + leave_button_sprite_->setScale(.35); // Create a button listener to handle the touch event. auto leave_button_sprite_touch_listener = EventListenerTouchOneByOne::create(); - // Setting the onTouchBegan event up to a lambda will swap scenes and modify + + // Set the onTouchBegan event up to a lambda will swap scenes and modify // total_players leave_button_sprite_touch_listener->onTouchBegan = [this](Touch* touch, Event* event) -> bool { - auto bounds = event->getCurrentTarget()->getBoundingBox(); - auto point = touch->getLocation(); + const auto bounds = event->getCurrentTarget()->getBoundingBox(); + const auto point = touch->getLocation(); + // Replaces the scene with a new TicTacToe scene if the touched point is // within the bounds of the button. if (bounds.containsPoint(point)) { - // Update the game_outcome to reflect if the you rage quit or left + // Update the game_outcome_ to reflect if the you rage quit or left // pre-match. - if (remaining_tiles.size() == kNumberOfTiles) { - game_outcome = kGameDisbanded; + if (remaining_tiles_.size() == kNumberOfTiles) { + game_outcome_ = kGameDisbanded; } else { - game_outcome = kGameLost; + game_outcome_ = kGameLost; } - WaitForCompletion(ref.Child("game_over").SetValue(true), "setGameOver"); + WaitForCompletion(ref_.Child("game_over").SetValue(true), "setGameOver"); } return true; }; - // Attaching the touch listener to the create game button. + + // Attach the touch listener to the create game button. Director::getInstance() ->getEventDispatcher() ->addEventListenerWithSceneGraphPriority( - leave_button_sprite_touch_listener, leave_button_sprite); + leave_button_sprite_touch_listener, leave_button_sprite_); - board_sprite->addChild(leave_button_sprite, 1); + board_sprite_->addChild(leave_button_sprite_, /*layer_index=*/1); // TODO(grantpostma@): Modify these numbers to be based on the extern window // size & label size dimensions. cocos2d::Label* game_uuid_label = - Label::createWithSystemFont(join_game_uuid, "Arial", 30); + Label::createWithSystemFont(join_game_uuid_, "Arial", 30); game_uuid_label->setPosition(Vec2(40, 20)); - board_sprite->addChild(game_uuid_label, 1); - waiting_label = Label::createWithSystemFont("waiting", "Arial", 30); - waiting_label->setPosition(Vec2(530, 20)); + board_sprite_->addChild(game_uuid_label, /*layer_index=*/1); + waiting_label_ = Label::createWithSystemFont("waiting", "Arial", 30); + waiting_label_->setPosition(Vec2(530, 20)); - board_sprite->addChild(waiting_label, 1); - game_over_label = Label::createWithSystemFont("", "Arial", 80); - game_over_label->setPosition(Vec2(300, 300)); - board_sprite->addChild(game_over_label, 1); + board_sprite_->addChild(waiting_label_, /*layer_index=*/1); + game_over_label_ = Label::createWithSystemFont("", "Arial", 80); + game_over_label_->setPosition(Vec2(300, 300)); + board_sprite_->addChild(game_over_label_, /*layer_index=*/1); - // total_player_listener and CurrentPlayerIndexListener listener is set up + // total_player_listener_ and CurrentPlayerIndexListener listener is set up // to recognise when the desired players have connected & when turns // alternate - LogMessage("total_player_listener"); - total_player_listener = + LogMessage("total_player_listener_"); + total_player_listener_ = std::make_unique(kNumberOfPlayers); - game_over_listener = std::make_unique(true); - - current_player_index_listener = std::make_unique(); - last_move_listener = std::make_unique(); - LogMessage("%i", total_player_listener->got_value()); - ref.Child("total_players").AddValueListener(total_player_listener.get()); - ref.Child("game_over").AddValueListener(game_over_listener.get()); - ref.Child("current_player_index") - .AddValueListener(current_player_index_listener.get()); - ref.Child("last_move").AddValueListener(last_move_listener.get()); - - // A 3*3 Tic-Tac-Toe board for playing + game_over_listener_ = std::make_unique(true); + + current_player_index_listener_ = std::make_unique(); + last_move_listener_ = std::make_unique(); + LogMessage("%i", total_player_listener_->got_value()); + ref_.Child("total_players").AddValueListener(total_player_listener_.get()); + ref_.Child("game_over").AddValueListener(game_over_listener_.get()); + ref_.Child("current_player_index_") + .AddValueListener(current_player_index_listener_.get()); + ref_.Child("last_move").AddValueListener(last_move_listener_.get()); + + // Set up a 3*3 Tic-Tac-Toe board for tracking results. for (int i = 0; i < kTilesY; i++) { for (int j = 0; j < kTilesX; j++) { board[i][j] = kEmptyTile; - remaining_tiles.insert((i * kTilesX) + j); + remaining_tiles_.insert((i * kTilesX) + j); }; } - // Adding a function to determine which tile was selected to the onTouchBegan + + // Add a function to determine which tile was selected to the onTouchBegan // listener. auto touch_listener = EventListenerTouchOneByOne::create(); touch_listener->onTouchBegan = [this](Touch* touch, Event* event) mutable -> bool { - if (!total_player_listener->got_value()) return true; - if (current_player_index_listener->last_seen_value() != player_index) + if (!total_player_listener_->got_value()) return true; + if (current_player_index_listener_->last_seen_value() != player_index_) return true; - auto bounds = event->getCurrentTarget()->getBoundingBox(); - // Checking to make sure the touch location is within the bounds of the + const auto bounds = event->getCurrentTarget()->getBoundingBox(); + + // Check to make sure the touch location is within the bounds of the // board. if (bounds.containsPoint(touch->getLocation())) { // Calculates the tile number [0-8] which corresponds to the touch // location. int selected_tile = floor(touch->getLocation().x / kTileWidth) + kTilesX * floor(touch->getLocation().y / kTileHeight); - if (remaining_tiles.find(selected_tile) == remaining_tiles.end()) + if (remaining_tiles_.find(selected_tile) == remaining_tiles_.end()) return true; - auto sprite = Sprite::create(kPlayerTokenFileNames[current_player_index]); + auto sprite = + Sprite::create(kPlayerTokenFileNames[current_player_index_]); if (sprite == NULL) { log("kPlayerTokenFileNames: %s file not found.", - kPlayerTokenFileNames[current_player_index]); + kPlayerTokenFileNames[current_player_index_]); exit(true); } + // Calculates and sets the position of the sprite based on the // move_tile and the constant screen variables. sprite->setPosition((.5 + selected_tile % kTilesX) * kTileWidth, (.5 + selected_tile / kTilesY) * kTileHeight); - board_sprite->addChild(sprite); - // Modifying local game state variables to reflect this most recent move + board_sprite_->addChild(sprite); + + // Modify local game state variables to reflect this most recent move board[selected_tile / kTilesX][selected_tile % kTilesX] = - current_player_index; - remaining_tiles.erase(selected_tile); - current_player_index = (current_player_index + 1) % kNumberOfPlayers; - future_last_move = ref.Child("last_move").SetValue(selected_tile); - future_current_player_index = - ref.Child("current_player_index").SetValue(current_player_index); - WaitForCompletion(future_last_move, "setLastMove"); - WaitForCompletion(future_current_player_index, "setCurrentPlayerIndex"); - awaiting_opponenet_move = true; - waiting_label->setString("waiting"); + current_player_index_; + remaining_tiles_.erase(selected_tile); + current_player_index_ = (current_player_index_ + 1) % kNumberOfPlayers; + future_last_move_ = ref_.Child("last_move").SetValue(selected_tile); + future_current_player_index_ = + ref_.Child("current_player_index_").SetValue(current_player_index_); + WaitForCompletion(future_last_move_, "setLastMove"); + WaitForCompletion(future_current_player_index_, "setCurrentPlayerIndex"); + awaiting_opponenet_move_ = true; + waiting_label_->setString("waiting"); if (GameOver(board)) { - // Set game_over_label to reflect the use won. - game_over_label->setString("you won!"); - } else if (remaining_tiles.size() == 0) { - // Update game_outcome to reflect the user tied. - WaitForCompletion(ref.Child("game_over").SetValue(true), "setGameOver"); - game_outcome = kGameTied; + // Set game_over_label_ to reflect the use won. + game_over_label_->setString("you won!"); + game_outcome_ = kGameWon; + WaitForCompletion(ref_.Child("game_over").SetValue(true), + "setGameOver"); + } else if (remaining_tiles_.size() == 0) { + // Update game_outcome_ to reflect the user tied. + game_outcome_ = kGameTied; + WaitForCompletion(ref_.Child("game_over").SetValue(true), + "setGameOver"); } } return true; @@ -523,81 +426,105 @@ TicTacToeLayer::TicTacToeLayer(string game_uuid) { Director::getInstance() ->getEventDispatcher() - ->addEventListenerWithSceneGraphPriority(touch_listener, board_sprite); + ->addEventListenerWithSceneGraphPriority(touch_listener, board_sprite_); + + this->addChild(board_sprite_); - this->addChild(board_sprite); // Schedule the update method for this scene. this->scheduleUpdate(); } // Called automatically every frame. The update is scheduled in constructor. void TicTacToeLayer::update(float /*delta*/) { - // Replacing the scene with MainMenuScene if the initialization fails. - if (initialization_failed == true) { - Director::getInstance()->replaceScene(MainMenuScene::createScene()); + // Replace the scene with MainMenuScene if the initialization fails. + if (initialization_failed_ == true) { + Director::getInstance()->popScene(); } + // Performs the actions of the other player when the - // current_player_index_listener is equal to the player index. - else if (current_player_index_listener->last_seen_value() == player_index && - awaiting_opponenet_move == true) { + // current_player_index_listener_ is equal to the player index. + else if (current_player_index_listener_->last_seen_value() == player_index_ && + awaiting_opponenet_move_ == true) { int last_move = - last_move_listener->last_seen_value().AsInt64().int64_value(); - // Placing the players move on the board. - board[last_move / kTilesX][last_move % kTilesX] = current_player_index; - // Removing the tile from the tile unordered set. - remaining_tiles.erase(last_move); - auto sprite = Sprite::create(kPlayerTokenFileNames[current_player_index]); + last_move_listener_->last_seen_value().AsInt64().int64_value(); + + // Place the players move on the board. + board[last_move / kTilesX][last_move % kTilesX] = current_player_index_; + + // Remove the tile from the tile unordered set. + remaining_tiles_.erase(last_move); + auto sprite = Sprite::create(kPlayerTokenFileNames[current_player_index_]); if (sprite == NULL) { log("kPlayerTokenFileNames: %s file not found.", - kPlayerTokenFileNames[current_player_index]); + kPlayerTokenFileNames[current_player_index_]); exit(true); } + // Calculates and sets the position of the sprite based on the // move_tile and the constant screen variables. sprite->setPosition((.5 + last_move % kTilesX) * kTileWidth, (.5 + last_move / kTilesY) * kTileHeight); - board_sprite->addChild(sprite); - // Modifying local game state variables to reflect this most recent move. - board[last_move / kTilesX][last_move % kTilesX] = current_player_index; - remaining_tiles.erase(last_move); - awaiting_opponenet_move = false; - current_player_index = player_index; + board_sprite_->addChild(sprite); + + // Modify local game state variables to reflect this most recent move. + board[last_move / kTilesX][last_move % kTilesX] = current_player_index_; + remaining_tiles_.erase(last_move); + awaiting_opponenet_move_ = false; + current_player_index_ = player_index_; if (GameOver(board)) { - // Set game_outcome to reflect the use lost. - game_outcome = kGameLost; - } else if (remaining_tiles.size() == 0) { - // Set game_outcome to reflect the game ended in a tie. - game_outcome = kGameTied; + // Set game_outcome_ to reflect the use lost. + game_outcome_ = kGameLost; + WaitForCompletion(ref_.Child("game_over").SetValue(true), "setGameOver"); + } else if (remaining_tiles_.size() == 0) { + // Set game_outcome_ to reflect the game ended in a tie. + game_outcome_ = kGameTied; + WaitForCompletion(ref_.Child("game_over").SetValue(true), "setGameOver"); } } + // Shows the end game label for kEndGameFramesMax to show the result of the // game. - else if (game_over_listener->got_value()) { - if (game_outcome == kGameDisbanded && - remaining_tiles.size() != kNumberOfTiles) { - game_outcome = kGameWon; + else if (game_over_listener_->got_value()) { + if (game_outcome_ == kGameDisbanded && + remaining_tiles_.size() != kNumberOfTiles) { + game_outcome_ = kGameWon; } - game_over_label->setString(kGameOverStrings[game_outcome]); - end_game_frames++; - if (end_game_frames > kEndGameFramesMax) { - // TODO(grantpostma): Update authenticated users record. - WaitForCompletion(database->GetReference("game_data") - .Child(join_game_uuid) + game_over_label_->setString(kGameOverStrings[game_outcome_]); + end_game_frames_++; + if (end_game_frames_ > kEndGameFramesMax) { + // Remove the game from existence and update the user's record before + // swap back scenes. + WaitForCompletion(database_->GetReference("game_data") + .Child(join_game_uuid_) .RemoveValue(), "removeGameUuid"); - Director::getInstance()->replaceScene(MainMenuScene::createScene()); + ref_ = database_->GetReference("users").Child(user_uid_); + + // Updates user record unless the game was disbanded. + if (game_outcome_ != kGameDisbanded) { + auto future_record = + ref_.Child(kGameOutcomeStrings[game_outcome_]).GetValue(); + WaitForCompletion(future_record, "getPreviousOutcomeRecord"); + WaitForCompletion( + ref_.Child(kGameOutcomeStrings[game_outcome_]) + .SetValue(future_record.result()->value().int64_value() + 1), + "setGameOutcomeRecord"); + } + + Director::getInstance()->popScene(); } } + // Updates the waiting label to signify it is this players move. - else if (total_player_listener->got_value() && - awaiting_opponenet_move == false) { - waiting_label->setString("your move"); + else if (total_player_listener_->got_value() && + awaiting_opponenet_move_ == false) { + waiting_label_->setString("your move"); } } TicTacToeLayer::~TicTacToeLayer() { // release our sprite and layer so that it gets deallocated - CC_SAFE_RELEASE_NULL(this->board_sprite); - CC_SAFE_RELEASE_NULL(this->game_over_label); - CC_SAFE_RELEASE_NULL(this->waiting_label); + CC_SAFE_RELEASE_NULL(this->board_sprite_); + CC_SAFE_RELEASE_NULL(this->game_over_label_); + CC_SAFE_RELEASE_NULL(this->waiting_label_); } diff --git a/demos/TicTacToe/Classes/TicTacToeLayer.h b/demos/TicTacToe/Classes/TicTacToeLayer.h index 1fa708b0..6092733c 100644 --- a/demos/TicTacToe/Classes/TicTacToeLayer.h +++ b/demos/TicTacToe/Classes/TicTacToeLayer.h @@ -1,15 +1,35 @@ +// Copyright 2020 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #ifndef TICTACTOE_DEMO_CLASSES_TICTACTOELAYER_SCENE_H_ #define TICTACTOE_DEMO_CLASSES_TICTACTOELAYER_SCENE_H_ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include +#include "MainMenuScene.h" #include "TicTacToeScene.h" #include "cocos2d.h" -#include "firebase/app.h" -#include "firebase/auth.h" -#include "firebase/database.h" -#include "firebase/future.h" -#include "firebase/util.h" using cocos2d::Director; using cocos2d::Event; @@ -22,57 +42,65 @@ using firebase::Future; using firebase::database::DataSnapshot; using firebase::database::MutableData; using firebase::database::TransactionResult; +using std::string; static const int kTilesX = 3; static const int kTilesY = 3; class SampleValueListener; class ExpectValueListener; class TicTacToeLayer : public Layer { + public: + TicTacToeLayer(std::string, firebase::database::Database*, std::string); + ~TicTacToeLayer(); + private: typedef TicTacToeLayer self; typedef Layer super; + void TicTacToeLayer::update(float) override; - public: - TicTacToeLayer(std::string); - ~TicTacToeLayer(); - virtual void TicTacToeLayer::update(float); // Tracks whether the board was unable to build. - bool initialization_failed = false; + bool initialization_failed_ = false; + // Tracks the game outcome. - int game_outcome; - // Creating a string for the join game code and initializing the database + int game_outcome_; + + // Create a string for the join game code and initialize the database // reference. - std::string join_game_uuid; - /// Firebase Auth, used for logging into Firebase. - firebase::auth::Auth* auth; + std::string join_game_uuid_; + + // User uid to update the user's record after the game is over. + std::string user_uid_; /// Firebase Realtime Database, the entry point to all database operations. - firebase::database::Database* database; + firebase::database::Database* database_; + firebase::database::DatabaseReference ref_; - firebase::database::DatabaseReference ref; - // Creating listeners for database values. + // Create listeners for database values. // The database schema has a top level game_uuid object which includes - // last_move, total_players and current_player_index fields. - std::unique_ptr current_player_index_listener; - std::unique_ptr last_move_listener; - std::unique_ptr total_player_listener; - std::unique_ptr game_over_listener; - // Creating lables and a sprites. - Sprite* board_sprite; - Sprite* leave_button_sprite; - cocos2d::Label* game_over_label; - cocos2d::Label* waiting_label; - // Creating firebase futures for last_move and current_player_index - Future future_last_move; - Future future_current_player_index; - Future future_game_over; - // Creating the board, remaining available tile set and player index + // last_move, total_players and current_player_index_ fields. + std::unique_ptr current_player_index_listener_; + std::unique_ptr last_move_listener_; + std::unique_ptr total_player_listener_; + std::unique_ptr game_over_listener_; + + // Create lables and a sprites. + Sprite* board_sprite_; + Sprite* leave_button_sprite_; + cocos2d::Label* game_over_label_; + cocos2d::Label* waiting_label_; + + // Create firebase futures for last_move and current_player_index_ + Future future_last_move_; + Future future_current_player_index_; + Future future_game_over_; + + // Create the board, remain available tile set and player index // variables. - int current_player_index; - int player_index; - bool awaiting_opponenet_move; + int current_player_index_; + int player_index_; + bool awaiting_opponenet_move_; int board[kTilesX][kTilesY]; - std::unordered_set remaining_tiles; - int end_game_frames = 0; + std::unordered_set remaining_tiles_; + int end_game_frames_ = 0; }; #endif // TICTACTOE_DEMO_CLASSES_TICTACTOELAYER_SCENE_H_ diff --git a/demos/TicTacToe/Classes/TicTacToeScene.cpp b/demos/TicTacToe/Classes/TicTacToeScene.cpp index 127b25d3..054b8abf 100644 --- a/demos/TicTacToe/Classes/TicTacToeScene.cpp +++ b/demos/TicTacToe/Classes/TicTacToeScene.cpp @@ -1,19 +1,33 @@ +// Copyright 2020 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include "TicTacToeScene.h" -#include "MainMenuScene.h" -#include "TicTacToeLayer.h" -#include "cocos2d.h" using cocos2d::Scene; -Scene* TicTacToe::createScene(const std::string& game_uuid) { +Scene* TicTacToe::createScene(const std::string& game_uuid, + firebase::database::Database* main_menu_database, + const std::string& main_menu_user_uid) { // Sets the join_game_uuid to the passed in game_uuid. // Builds a simple scene that uses the bottom left cordinate point as (0,0) // and can have sprites, labels and layers added onto it. Scene* scene = Scene::create(); // Builds a layer to be placed onto the scene which has access to TouchEvents. - // This TicTacToe layer being created is owned by scene. - auto tic_tac_toe_layer = new TicTacToeLayer(game_uuid); + // This TicTacToe layer created is owned by the scene. + auto tic_tac_toe_layer = + new TicTacToeLayer(game_uuid, main_menu_database, main_menu_user_uid); scene->addChild(tic_tac_toe_layer); return scene; diff --git a/demos/TicTacToe/Classes/TicTacToeScene.h b/demos/TicTacToe/Classes/TicTacToeScene.h index ffdc8651..6593dd3f 100644 --- a/demos/TicTacToe/Classes/TicTacToeScene.h +++ b/demos/TicTacToe/Classes/TicTacToeScene.h @@ -1,13 +1,32 @@ +// Copyright 2020 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #ifndef TICTACTOE_DEMO_CLASSES_TICTACTOE_SCENE_H_ #define TICTACTOE_DEMO_CLASSES_TICTACTOE_SCENE_H_ +#include "MainMenuScene.h" +#include "TicTacToeLayer.h" #include "cocos2d.h" class TicTacToe : public cocos2d::Layer { public: // Builds a simple scene that uses the bottom left cordinate point as (0,0) // and can have sprites, labels and nodes added onto it. - static cocos2d::Scene* createScene(const std::string&); + static cocos2d::Scene* createScene(const std::string&, + firebase::database::Database*, + const std::string&); + // Defines a create type for a specific type, in this case a Layer. CREATE_FUNC(TicTacToe); }; diff --git a/demos/TicTacToe/Resources/logout.png b/demos/TicTacToe/Resources/logout.png new file mode 100644 index 0000000000000000000000000000000000000000..9f0531e882437013bf0f7768fcbd283d415286c3 GIT binary patch literal 8006 zcmeHs_fwNy6eb`b5s(%@1q3va1dtBWq)1Ju2_OQY2@*l1_a;)MV+g&6j`ZFH1f&Qe zReA?$f`}BQxi5Y@JF~NY!0rz_lVmdYz4zSro^$Rg&l9GtsX~42)-@s`B5H)HBASSZ zI1l(=MnMkzCOoB|A|hg^MkvbbdYJ5_gNCBVPkUmX;lQI$5yDW78AzUx8@F02I2@1L zvl_||rXQgxvd)IgGa)$%@EWvGsjg}sI(sBvccdfeug{l(mA_u^zxb9pXL?IyoO)Xg zh`c|Zl9})vJm#H{C`fzCK}`SeqeX$|;pU!RLKr-u`Zw~LiUG#Q$ESk&5#maSqwDCgoL{EG#XYYj(DfMx^sNjXjkw9V{NuGs|fJjXph%7X;c#f=RpdEY5vs7Uu6FlZkIJ{YNUw3?csK9=We1d8$)2VzLHB1;gBE! z=dC{Zk-(q?SNc7WLi&`W;#oga?aLXl+AlJvU9nzbxLjD?b8I*|d@orPq^N4v6U}Bb zRrSo~_ui7t-r|R1gK8_f{EX6E7!p36O2FUovC<%+qnoTS4{Tb`@E!{SF^pxtx+t3W z`K1w#yJ7@fR^SjF*sl=U03szgsYpKz3{^8j`2i1c_eSr${(U(Ws}4*R#3JEBR=`9kaX1<; z(ytjo0LxkcN{YNx62hMVhH8YP&qVG!FYc#91^hHg;0#%?kM=}^fPjJI|7{*HRA8Na z1&d5*|1AN^RRv0YNh_2c_%{fDC54LElDE<9Bf%Y($m_@Z)Kpt4gqDRgO3YM|mO)q%8|?)Gbl;T(i5ragz&5)Ml(A?AD)pz#XbnB%JfP z`S>35=$F+X+1WfZWqfrPT76xyCWg;nogeDE_CXU16HWaZ2V7Qsnr=U;c|Lr{xICDW z`P+Lck-`o@5t%k~O|{(I+~|S~ui|1%V-+L&X3q^$5&L=RAM3RJ1(|+^Y`zD>BaqTo z@2#Q&7w-Y)LGcx;VEgORd+jX?Nh1~9+C?$LoAd4qzLh`S;i)u^a3tK2vvf`B%`l67 zOZINdufY~J+Z((a2Yd~SB%-#H0(u3S=>mwL=s<(H$x4gyt}ur2?kE<8g1|S0Ds6t} zV}}!_<;D8tt-_@uiksza7igV)brcP-AD{h^xSlrre$xcTIf|*U5UT~wGuzh25BMPc zP#6rR41+=qN8|oF1ChF{jkudXQ0^9?#%;&&-0~NLmNZ?ehQLb0;a4Mz%{?UZEt1pa z2!0!4_@5y|aD3;R+x+1fekYq=U-7xHY?Jc$M=4Ef2}TF>#u#EX?;MqwudckNtv-+` zV7qiaEb`Ay$9!<#7Xi#2L^%CXWJO7^X4z^hvH{oswD!e>Cg4(?KAm;0<^Xw&I#vvCA}w5-RT*_@cR>4Y4L z0Bfw_498JE>}f!R(eKOm_|Cknmq?hDkvyWVC<$>rgQas;l!#jP(%$!Syy`Nh5PDrN z(bz4SsX+V~FJQK@(AJhB`^s;2eER-*G@FzSara1uS{4k2$4t{G^#x~G%t91F>Bkxm z=5p_;CoMji-Wwk7oAdth{#v>TxC5`8&Y^x+IZ~UyWiP<6!c0C{0K>-ixYRJu>*#yY z`>l~{Nps73<9B1Eg}OByX{}nj5G3Jy z#q6q)10D2x=;7A5T61{;+o04B>0n>WhLcbb7#faFa*K;l_)mZ)YP!-Q)NA#vN=@}^ z4S#QUz+-B*9p;)`^?-ZoX6J7|*Q9rTG-H24HTvs|YzD)2o$+9E{ojjR_hR%J*<1f8@8pLNp6>Iz#8X#e{5*?Btk8V@p6 z>?K;?*fl=>7h>=|OI^UJXbb7_oclt0iJ1L-$(Q6MYrF4If}I%geCU&X>Xl>_z(7+q zwId5?JZ3|pJ4y#XB)F&^Rr5=^=i&1qj`136ol0gV$EBH0=h>mRZO(49`~3R+pvg7~8swNpaThsL_i|J%^~uxtAc4aU zBC?&u4_Dc1idhB~4|nHgy!vWs72%opJv<(dAm>_vr0~-@oGlSH71^@XO3hy$!Qc8s zuUHqZ>+5&+Q)S1+U!eg{3qUREddWdrrv+eBZe6eyjue@SKQ2rkr}BL|IU^C zsM_jt&1i2QSElWKuqunI_jDZtcwPSj9BI!z8P3THL|AdV zw_V|Ccn2wk^}+P0XNv1l!@^c7W!EW;>THKI9PX+_iwp|w1^Vvyag|=5T=rqq+)O>f zR4BfA*6($=b#t5j-~(5t4*_eKe}>FA=Rul<+#ESSUa;rg&Py~YP8@ak8292yo8SAU z-KOoa5`*eUgnXJx)U9WtX4!x*M^f`FUit5mIKH7j@~JSlGh7bfcz7_RP>hJX7hU`N z?}nHr#f_h)zK7#a1$_>NRSwZ;?Ke-q6kDFu%{$Z)YApNcT`&Huzhn3N%~@eW3lVq6 zw-|SfKbJi}h+~=qGBlq6T)a^F*MRUAUPqG-ivSEC#-etU{eK!>0q?+n3sW9=q*%c( z=(?p##{aG`aLl+_BUQ}H;_qT0jTn>soti=hNv#GEm7BJ#JhqA;Z-j&3--W*y%ui+o zs*%>`d4vdI!v?b4N5R(!xo{*Hic;wNMfcw=EmQ)ru}RX-lGB16oY>i(ehg=1d!g;- zvWaF<-ei+>5lA3TpEbayGs0@j6Otou^5nIH`>o8Zu89#207bq~0b{CC_`9Gr&|iVn z0t}S5y39P?r)#X26>5QNhuf^DKXCs~gQy5xZcfB$$TwE{J|~tV(eB8b48Jj?V(XO! z#Fd|!JM4PZKG$1)zBOKI^k60XF$>D;a9lsCx(UL!+cn_U?oWI&GLgl>CdWY?POpSG znc?1+`d@-AmCc*M40Yj?T(3{vTW4ru`7*TH8Hj;cIhablo7!nYpYUy;?7N1!7{gcZ zAzhVCbgC{M@+iW9{)o)cgm;(2VE_G}Z>fjq97|9PK@sI-T@8jZdqq3uFhOUH#2gme z+t)Z|3)6#|i)&VV*W&fTx33Q{FA7Di3+%lyZZ#7Qr+Fb1rW0@})cad9cFVUd@VX?- z_U*@?et*`O-v=F!sPV5wIS)##Z|r{uYK3l>OFLNH1^xCXtUmf8Xon{p{Al~TRV^y- zJ`0^1UQ2LP_m*$)q7W|jA%37jDq#t!Wrc4x2wt|?;G^b?Y6%dd{6uqP6ySzN1*i#Klv=F6d$)%(Vj11>cGi_=I{ z2YRS+J%h!AR!4HgwdCvI#uKK1ZHbFDO8K0}ee;yNQJqu*VP+{Bs#s+Hd)c(507^G} z=rau;hjNaxlxt4vZ@*p|Q>gX<;3(NQR+Qn${Ai+3T_BXa`=OyGlw`#$Pk>i2M}Z}uNX`mlNc=%~9u=HQ4pg4gVn~!w)bh$M z#lE@7eLb>xwPo7aTv&2~DwF_`SWN5slRI*Rt5LFUgdx-5jV|y0^Cc+T>)ad#L5$?9 z9vf0+cX%R#G*)n)64~*Q%`^Ga-OBi`ZzF@U$Yv!2zHqeDOQBCRcG;WVCZNS-jb-2OG;98EA*S)GPr(}Izw@DI7emP^tKZGqo!7@s1kw* zUZw@f`Bo?yP=o2vcp%i!6r}b?(Y*6x1oJx}xvyy;4Vl=DJAPU*?zW}MzG`(oA7O$r zn1-XlZ6Uu$fC9beYmyV5u)4Bj&{sml#+hN+Q#zR!7^H>M=So&yM5Fe9?v<<}3&R+= zwkB*eUK12muIjLWPNe?DG7O@CXJ4wTE0MqJm36`r;)DX z+mTJ87vj^M+IZ_PE4_DP#npz`2`aemOphnor4S%DDID`HMa+u< zCr5X&=fMzmvZ_vxiA%P>mX7LAv;Y-Cc4ClvbzJUlSRBR^!U)wVpnifd?X_u&VtavFTc?y0l;BoPfW~dK zLxn*_rH}`DkUuWLmTu-NX=e0Lmem7;049=ck22CLHrMD#4MelQsIU>1@z3 zX9$xQ@sUYTZ#Ks^Y05g>`dl6XlI#kV@Q-bwx2;xB$%i4xX#h`>dMN)D3?aV1=^5epwHL>)YBiKwa?mQ34Zmxvgfj?ELw?{;$zbJAY7_nZgS$epT)rl?aT2( z$b`yZ2U*7(o!8r&rljfa&_PG3BuY4;{juP@3YWMr6%;bW`Fp>6ubj}kH_ca6B!)JZ zsq+W~f?U(c!!n>dC1h|VKiT3tN2|Rda{}FnwVNH4mu&8!3wm%5lx%LMj<)>_#|6QW zZ_TI@RmX&(eI+E&kJXzw%28;b3A(25pily2gW}z}=4}PMyu74g0xv(C+obumm>cBj zcdzLfSR2RIMFD!EO}f7X56fC#>>MkyWPL+DyB|opU?mH(R+E-rX{aSpbN8_F8!y%a zLnlJsKJ~e5%z-4Kt0eSJ4tw?3fwz3kJ4Bi{q3z;CE#@(odJ=7tBJdJp2b-hgOQGy` z14zkVk5b~ev>qo>E7J|3Xl{w9Ue*(YRa-?w)q03}PMJ<$c-F67pt4(}=V`5x1zx2QLj#$+ z@Rp2EL$P*tz)vF|j`T`1h1;QyDA`0a9@RQws1Gpr2aoi|)rmh@{>k*W)qvV3UoGA( z>~vI1cGE2&8LdrGT@Q6!mCM)@P&9HhcKVn_c7;`1k6mP?e_YrOh09nEA4nogpap=7 zz-~@tVYDTJe8Jp+oXrEUS2|YxMo~1-r9`nxj7$oe;Un40J}+r++JX){PLUQp(c|Uc zLy|#$=Z6zpAuM(Rl45u}Nikt)SMAL?i?E%t=3O*UiaXg=Zcmg?X!WL&^Yw02_4DZf zeVTu86kq_{!ZHO7SczR`DU&E+P!)Vqi!mVgVFRXb}$#0W#O3JWeCNNaPCC z+1mFHbq z!;ASR+{ze-#DQ1>Ewm5%K`bNuYy6-bDc~f^(Q0^Y=;nxa*~!jK{e-vt25W9Ve4`8d zC8HGRVm044-UpSbq3m0JhO`n-*R00#PgE28z1U(|Q7o-S=WXb#8WCc7?~QajW%X0uD@dt5L5uF^1vjj{ zVFrGEZ~bSFZ`_yzkcMd^on`AcH@^jtQP$M20q6mA((O<8vpHi&!=56Yc-+lev`!%v@?9hO@-e+r@q@7fA`K8unG1IfR%V6w;V->29kxx zTReZ2jJUB$tFlQTnYbs824zldFMIj)!>Lx^<_?*0VBPp^qw`^v#SllKR;G+sK2`m% zs(=U>V%J$QLxtV9hH2XQ%R~LYAx^|?o@db&tlT%XpWG~!lgS4Vv|e!~_{7wQyCCm} z)}gS67rD3n5EBw5uVw1+Il}9t?ogqqmNLsKscJT;b zK|FY0yhk5h(vh$C<|hDC8H(DWK7r!8l%zDNP}k69Y33~}m@Nv&(7<*)Txb8@${nPt z?iOJOO`|hgNDBI6Ru?pNZQ7JRdsSU}Lj#(aOuzLSs_k(SOpQxb@5nc%_$O2l1PC47 z7|vn5fU*stvMf5Hs8Ia+a!DLad)Fg)^pZ40EngmJxznDiuY|+KGH4+(xpE zo83;xLDK1TdKHeEj(yb!?Od8D!z#-#NdVYmyzUwbvX<7M&rh@dm<`KQGJ(hAVcFr6 ztQlf2Q`}tLrUvRi1Z!T`q~*%+AZUoZvtKD;J|bH*jD9O>XWrAQHA@KkG~*>~NZkKR zaGI>8d(nl~wLKYzc?NNmr-^enxRNQZ1Zp#)(&kt+zryNfSZ8}v%uBu}2T=SxLu9)@uDO2bR6#poj9j?V`cXdxX@Cxyxk-C7%j%vDU{vvEn zu3cW6^<+hM2IvwwkzzCK$h8O$G`QdLN8nw&*Wcukt_74I73-p?q0_zF1u#z2iM+>{ zu;8f&ox7xff$G9@rJ68$#>O_kI0J8(i@m1y-ki#U{Zw#S3Xi1YwQEgiidz5Vzu##@ zH?g&Nl7mZ6>w4_Sni530ZxQn_U+MscZP*w9?xXS!pD6&0VV+#%{SN`;VkK4NOD!#ky3F|lV*2CALAIj zyJ)L<(Gzyt^>pi9d5xOLg?!fHCzGmZ0OcFP`Z|QYh+w)8#^9W>NytPECmHB<)Tj6> z*p8vM3Rw6W45QfWlACeD;v?J2Y`-y-%BDhE19I!l-RNGP zrb=c>E%J@AM1IpU0!J4yix?yX5hAm zal@+p)kA*`hg#+f7Gb?JHzzF7%YE(xdpI)K&ipGsJqa$gT<|WXGgJ#gW_bsq%>kH` z?5XPGyCx(6u94jO(0uJv{4*=d({I`>dC#^W73=g_u${&_i#`U3nHRhcXpN`WWY$X) z&4!a0L0~WdBgiLL3-AO=oh_n5u`Cq@+Rbh+-^&JwdfEE)_{Q95>E1*0&-;I3_B!4z zEq9*AX8K+mr6^$pK@|N7c-rSy3>id^_l+S=;#&g&q}Ni3l!_{L zjA&~+J6$|TI5x@zXZ?nXbP9U6c{~%ZVg2@)7_QF>0R&D1T^3M~TYM*_P@S#RlG+gw z1rPw=5bk0QUrmw+a*LvK75vA_dM5b_B*Ak21YL^9ynB+W3l)uf9h8Ss56U`%pi-=N zy0^>36sd9vuE5zGRbIh3RoT=E$J#IYc(V z!0!E1vpk_!swdStM8&B9YHOG_3#Qchwg@6;2DJOdg@wIaBwrfB8GBwdImX9t;@UF+ z4cBlP@T@$GT6U~0q+N!CTI%s%HD*xbft#`Z_JHYI4T9D{5dQ)$a+=#ExBY%E>8h?} zsQj3z=O=?RQVNVd;*vvgyHGNlo$p-?#&Rkg@Z=-N#Zi&066nm(<+v*d9M;6(jvVR} zu9mW}%%jWxYl(qP*BiP@8RiTHaxTi%%Pd1e=q--*e+6uJF%<-@;_&Y|&R0(+U^VK# zWU5uYKAvxXedX}3XBr#g%}*A3hr4nB57IN4LEG-Ug$x zqXF01gO9xocdKtCa{xOBC0FYqJT{lYIcAd+y{COUL#UgjtEumZHwRTKi@x}(dkOO& zZcj}7-BTnFh8evKsXYt@2tszF3dlc#&=a7E1|ATT{9_(pQE21OD#_FykpRzyK>tDj`geqv7ko+oS^_fQsczWPf26Y- c$=|CR)+!WBbwY&zKTd>D(o`&aWESv00MP??$p8QV literal 0 HcmV?d00001 From 1926ade4430058e19789818f55a8e9855461fcb2 Mon Sep 17 00:00:00 2001 From: Grant Postma Date: Mon, 6 Jul 2020 16:56:44 -0400 Subject: [PATCH 2/6] Add util files (#10) * Moving common functions to util.h and util.cpp. Adding more comments on util functions. * Added missing period. * removing double new line. modifying includes in util.h and util.cpp --- demos/TicTacToe/Classes/MainMenuScene.cpp | 1065 +++++++++----------- demos/TicTacToe/Classes/MainMenuScene.h | 15 +- demos/TicTacToe/Classes/TicTacToeLayer.cpp | 4 +- demos/TicTacToe/Classes/TicTacToeLayer.h | 2 +- demos/TicTacToe/Classes/TicTacToeScene.h | 1 + demos/TicTacToe/Classes/util.cpp | 67 ++ demos/TicTacToe/Classes/util.h | 43 + 7 files changed, 621 insertions(+), 576 deletions(-) create mode 100644 demos/TicTacToe/Classes/util.cpp create mode 100644 demos/TicTacToe/Classes/util.h diff --git a/demos/TicTacToe/Classes/MainMenuScene.cpp b/demos/TicTacToe/Classes/MainMenuScene.cpp index 0f6e2dab..3a3267b0 100644 --- a/demos/TicTacToe/Classes/MainMenuScene.cpp +++ b/demos/TicTacToe/Classes/MainMenuScene.cpp @@ -16,6 +16,8 @@ #include +#include "cocos2d.h" +#include "util.h" #include "TicTacToeScene.h" static const char* kCreateGameImage = "create_game.png"; @@ -29,54 +31,6 @@ static const char* kSignUpButtonImage = "sign_up.png"; const std::regex email_pattern("(\\w+)(\\.|_)?(\\w*)@(\\w+)(\\.(\\w+))+"); USING_NS_CC; -void LogMessage(const char* format, ...) { - va_list list; - va_start(list, format); - vprintf(format, list); - va_end(list); - printf("\n"); - fflush(stdout); -} - -void ProcessEvents(int msec) { -#ifdef _WIN32 - Sleep(msec); -#else - usleep(msec * 1000); -#endif // _WIN32 -} - -// Wait for a Future to be completed. If the Future returns an error, it will -// be logged. -void WaitForCompletion(const firebase::FutureBase& future, const char* name) { - while (future.status() == firebase::kFutureStatusPending) { - ProcessEvents(100); - } - if (future.status() != firebase::kFutureStatusComplete) { - LogMessage("ERROR: %s returned an invalid result.", name); - } else if (future.error() != 0) { - LogMessage("ERROR: %s returned error %d: %s", name, future.error(), - future.error_message()); - } -} - -// Returns a random uid of a specified length. -std::string GenerateUid(std::size_t length) { - const std::string kCharacters = "0123456789abcdefghjkmnpqrstuvwxyz"; - - std::random_device random_device; - std::mt19937 generator(random_device()); - std::uniform_int_distribution<> distribution(0, kCharacters.size() - 1); - - std::string generate_uid; - - for (std::size_t i = 0; i < length; ++i) { - generate_uid += kCharacters[distribution(generator)]; - } - - return generate_uid; -} - Scene* MainMenuScene::createScene() { // Builds a simple scene that uses the bottom left cordinate point as (0,0) // and can have sprites, labels and layers added onto it. @@ -213,566 +167,549 @@ bool MainMenuScene::init() { email_text_field_touch_listener->onTouchBegan = [this](cocos2d::Touch* touch, cocos2d::Event* event) -> bool { - // Returns if the layer is not in the auth state or is switching states. + // Returns false, not consuming the event, to exit the layer if + // current_state_ is not in the kAuthState or is switching states. if (previous_state_ != current_state_ || current_state_ != kAuthState) { - return true; - // Returns false, not consuming the event, to exit the layer if - // current_state_ is not in the kAuthState or is switching states. - if (previous_state_ != current_state_ || current_state_ != kAuthState) { - return false; - } - const auto bounds = event->getCurrentTarget()->getBoundingBox(); - const auto point = touch->getLocation(); - if (bounds.containsPoint(point)) { - // Show the on screen keyboard and places character inputs into the text - // field. - auto text_field = - dynamic_cast(event->getCurrentTarget()); - text_field->setCursorEnabled(true); - text_field->attachWithIME(); - } else { - auto text_field = - dynamic_cast(event->getCurrentTarget()); - text_field->setCursorEnabled(false); - text_field->detachWithIME(); - } + return false; + } + const auto bounds = event->getCurrentTarget()->getBoundingBox(); + const auto point = touch->getLocation(); + if (bounds.containsPoint(point)) { + // Show the on screen keyboard and places character inputs into the text + // field. + auto text_field = dynamic_cast(event->getCurrentTarget()); + text_field->setCursorEnabled(true); + text_field->attachWithIME(); + } else { + auto text_field = dynamic_cast(event->getCurrentTarget()); + text_field->setCursorEnabled(false); + text_field->detachWithIME(); + } - return true; - }; + return true; + }; - // Attach the touch listener to the text field. - Director::getInstance() - ->getEventDispatcher() - ->addEventListenerWithSceneGraphPriority( - email_text_field_touch_listener, email_text_field_); - - auth_background_->addChild(email_text_field_, /*layer_index=*/1); - auth_background_->addChild(email_text_field_border, /*layer_index=*/1); - - // Extract the origin, size and position of the text field so that the - // border can be created based on those values. - const auto password_text_field_origin = Vec2(0, 0); - const auto password_text_field_position = Size(110, 250); - const auto password_font_size = 36.0; - const auto password_text_field_size = Size(400, 2 * password_font_size); - - // Set up the constraints of the border so it surrounds the text box. - Vec2 password_border_corners[4] = { - Vec2(password_text_field_position.width - text_field_padding, - password_text_field_position.height), - Vec2( - password_text_field_position.width + password_text_field_size.width, - password_text_field_position.height), - Vec2( - password_text_field_position.width + password_text_field_size.width, - password_text_field_position.height + - password_text_field_size.height), - Vec2(password_text_field_position.width - text_field_padding, - password_text_field_position.height + - password_text_field_size.height)}; - - // Create a text field border and add it around the text field. - auto password_text_field_border = DrawNode::create(); - password_text_field_border->drawPolygon( - password_border_corners, 4, Color4F(0, 0, 0, 0), 1, Color4F::WHITE); - - // Create a text field to enter the user's password. - password_text_field_ = cocos2d::TextFieldTTF::textFieldWithPlaceHolder( - "enter a password", password_text_field_size, TextHAlignment::LEFT, - "Arial", password_font_size); - password_text_field_->setPosition(password_text_field_position); - password_text_field_->setAnchorPoint(Vec2(0, 0)); - password_text_field_->setColorSpaceHolder(Color3B::GRAY); - password_text_field_->setSecureTextEntry(true); - password_text_field_->setDelegate(this); - - auto password_text_field_touch_listener = - EventListenerTouchOneByOne::create(); - - password_text_field_touch_listener->onTouchBegan = - [this](cocos2d::Touch* touch, cocos2d::Event* event) -> bool { - // Returns false, not consuming the event, to exit the layer if - // current_state_ is not in the kAuthState or is switching states. - if (previous_state_ != current_state_ || current_state_ != kAuthState) { - return false; - } - const auto bounds = event->getCurrentTarget()->getBoundingBox(); - const auto point = touch->getLocation(); - if (bounds.containsPoint(point)) { - // Shows the on screen keyboard and places character inputs into the - // text field. - auto text_field = - dynamic_cast(event->getCurrentTarget()); - text_field->setCursorEnabled(true); - text_field->attachWithIME(); - } else { - auto text_field = - dynamic_cast(event->getCurrentTarget()); - text_field->setCursorEnabled(false); - text_field->detachWithIME(); - } + // Attach the touch listener to the text field. + Director::getInstance() + ->getEventDispatcher() + ->addEventListenerWithSceneGraphPriority(email_text_field_touch_listener, + email_text_field_); - return true; - }; + auth_background_->addChild(email_text_field_, /*layer_index=*/1); + auth_background_->addChild(email_text_field_border, /*layer_index=*/1); - auth_background_->addChild(password_text_field_, /*layer_index=*/1); - auth_background_->addChild(password_text_field_border, /*layer_index=*/1); + // Extract the origin, size and position of the text field so that the + // border can be created based on those values. + const auto password_text_field_origin = Vec2(0, 0); + const auto password_text_field_position = Size(110, 250); + const auto password_font_size = 36.0; + const auto password_text_field_size = Size(400, 2 * password_font_size); - // Attach the touch listener to the text field. - Director::getInstance() - ->getEventDispatcher() - ->addEventListenerWithSceneGraphPriority( - password_text_field_touch_listener, password_text_field_); + // Set up the constraints of the border so it surrounds the text box. + Vec2 password_border_corners[4] = { + Vec2(password_text_field_position.width - text_field_padding, + password_text_field_position.height), + Vec2(password_text_field_position.width + password_text_field_size.width, + password_text_field_position.height), + Vec2(password_text_field_position.width + password_text_field_size.width, + password_text_field_position.height + + password_text_field_size.height), + Vec2(password_text_field_position.width - text_field_padding, + password_text_field_position.height + + password_text_field_size.height)}; - // Create the login button and give it a position, anchor point and - // touch_listener. - auto login_button = Sprite::create(kLoginButtonImage); - login_button->setPosition(90, 120); - login_button->setAnchorPoint(Vec2(0, 0)); - login_button->setContentSize(Size(200, 75)); + // Create a text field border and add it around the text field. + auto password_text_field_border = DrawNode::create(); + password_text_field_border->drawPolygon( + password_border_corners, 4, Color4F(0, 0, 0, 0), 1, Color4F::WHITE); + + // Create a text field to enter the user's password. + password_text_field_ = cocos2d::TextFieldTTF::textFieldWithPlaceHolder( + "enter a password", password_text_field_size, TextHAlignment::LEFT, + "Arial", password_font_size); + password_text_field_->setPosition(password_text_field_position); + password_text_field_->setAnchorPoint(Vec2(0, 0)); + password_text_field_->setColorSpaceHolder(Color3B::GRAY); + password_text_field_->setSecureTextEntry(true); + password_text_field_->setDelegate(this); + + auto password_text_field_touch_listener = + EventListenerTouchOneByOne::create(); + + password_text_field_touch_listener->onTouchBegan = + [this](cocos2d::Touch* touch, cocos2d::Event* event) -> bool { + // Returns false, not consuming the event, to exit the layer if + // current_state_ is not in the kAuthState or is switching states. + if (previous_state_ != current_state_ || current_state_ != kAuthState) { + return false; + } + const auto bounds = event->getCurrentTarget()->getBoundingBox(); + const auto point = touch->getLocation(); + if (bounds.containsPoint(point)) { + // Shows the on screen keyboard and places character inputs into the text + // field. + auto text_field = dynamic_cast(event->getCurrentTarget()); + text_field->setCursorEnabled(true); + text_field->attachWithIME(); + } else { + auto text_field = dynamic_cast(event->getCurrentTarget()); + text_field->setCursorEnabled(false); + text_field->detachWithIME(); + } - // Create a button listener to handle the touch event. - auto login_button_touch_listener = EventListenerTouchOneByOne::create(); + return true; + }; - // Transition from kAuthState to kWaitingLoginState on button press and set - // user_result_ to SignInWithEmailAndPassword future result. - login_button_touch_listener->onTouchBegan = [this](Touch* touch, - Event* event) -> bool { - // Returns false, not consuming the event, to exit the layer if - // current_state_ is not in the kAuthState or is switching states. - if (previous_state_ != current_state_ || current_state_ != kAuthState) { - return false; - } - const auto bounds = event->getCurrentTarget()->getBoundingBox(); - const auto point = touch->getLocation(); - if (bounds.containsPoint(point)) { - // Check the string for a valid email pattern. - if (!std::regex_match(email_text_field_->getString(), email_pattern)) { - invalid_login_label_->setString("invalid email address"); - } else if (password_text_field_->getString().length() < 8) { - invalid_login_label_->setString( - "password must be at least 8 characters long"); - } else { - user_result_ = auth_->SignInWithEmailAndPassword( - email_text_field_->getString().c_str(), - password_text_field_->getString().c_str()); - current_state_ = kWaitingLoginState; - } - } - return true; - }; + auth_background_->addChild(password_text_field_, /*layer_index=*/1); + auth_background_->addChild(password_text_field_border, /*layer_index=*/1); - // Attach the touch listener to the login button. - Director::getInstance() - ->getEventDispatcher() - ->addEventListenerWithSceneGraphPriority(login_button_touch_listener, - login_button); - auth_background_->addChild(login_button, /*layer_index=*/1); - - // Create the sign_up button and give it a position, anchor point and - // touch_listener. - auto sign_up_button = Sprite::create(kSignUpButtonImage); - sign_up_button->setPosition(310, 120); - sign_up_button->setAnchorPoint(Vec2(0, 0)); - sign_up_button->setContentSize(Size(200, 75)); - - // Create a button listener to handle the touch event. - auto sign_up_button_touch_listener = EventListenerTouchOneByOne::create(); - - // Transition from kAuthState to kWaitingSignUpState on button press and set - // user_result_ to CreateUserWithEmailAndPassword future result. - sign_up_button_touch_listener->onTouchBegan = [this](Touch* touch, - Event* event) -> bool { - // Returns false, not consuming the event, to exit the layer if - // current_state_ is not in the kAuthState or is switching states. - if (previous_state_ != current_state_ || current_state_ != kAuthState) { - return false; - } - const auto bounds = event->getCurrentTarget()->getBoundingBox(); - const auto point = touch->getLocation(); - if (bounds.containsPoint(point)) { - // Check the string for a valid email pattern. - if (!std::regex_match(email_text_field_->getString(), email_pattern)) { - invalid_login_label_->setString("invalid email address"); - } else if (password_text_field_->getString().length() < 8) { - invalid_login_label_->setString( - "password must be at least 8 characters long"); - } else { - user_result_ = auth_->CreateUserWithEmailAndPassword( - email_text_field_->getString().c_str(), - password_text_field_->getString().c_str()); - current_state_ = kWaitingSignUpState; - } + // Attach the touch listener to the text field. + Director::getInstance() + ->getEventDispatcher() + ->addEventListenerWithSceneGraphPriority( + password_text_field_touch_listener, password_text_field_); + + // Create the login button and give it a position, anchor point and + // touch_listener. + auto login_button = Sprite::create(kLoginButtonImage); + login_button->setPosition(90, 120); + login_button->setAnchorPoint(Vec2(0, 0)); + login_button->setContentSize(Size(200, 75)); + + // Create a button listener to handle the touch event. + auto login_button_touch_listener = EventListenerTouchOneByOne::create(); + + // Transition from kAuthState to kWaitingLoginState on button press and set + // user_result_ to SignInWithEmailAndPassword future result. + login_button_touch_listener->onTouchBegan = [this](Touch* touch, + Event* event) -> bool { + // Returns false, not consuming the event, to exit the layer if + // current_state_ is not in the kAuthState or is switching states. + if (previous_state_ != current_state_ || current_state_ != kAuthState) { + return false; + } + const auto bounds = event->getCurrentTarget()->getBoundingBox(); + const auto point = touch->getLocation(); + if (bounds.containsPoint(point)) { + // Check the string for a valid email pattern. + if (!std::regex_match(email_text_field_->getString(), email_pattern)) { + invalid_login_label_->setString("invalid email address"); + } else if (password_text_field_->getString().length() < 8) { + invalid_login_label_->setString( + "password must be at least 8 characters long"); + } else { + user_result_ = auth_->SignInWithEmailAndPassword( + email_text_field_->getString().c_str(), + password_text_field_->getString().c_str()); + current_state_ = kWaitingLoginState; } - return true; - }; + } + return true; + }; - // Attach the touch listener to the sign_up button. - Director::getInstance() - ->getEventDispatcher() - ->addEventListenerWithSceneGraphPriority(sign_up_button_touch_listener, - sign_up_button); - auth_background_->addChild(sign_up_button, /*layer_index=*/1); - - // Create, set the position and assign a placeholder to the text - // field for the user to enter the join game uuid. - TextFieldTTF* join_text_field = - cocos2d::TextFieldTTF::textFieldWithPlaceHolder( - "code", cocos2d::Size(200, 100), TextHAlignment::LEFT, "Arial", - 55.0); - join_text_field->setPosition(420, 45); - join_text_field->setAnchorPoint(Vec2(0, 0)); - join_text_field->setColorSpaceHolder(Color3B::WHITE); - join_text_field->setDelegate(this); - - auto join_text_field_border = Sprite::create(kTextFieldBorderImage); - join_text_field_border->setPosition(390, 50); - join_text_field_border->setAnchorPoint(Vec2(0, 0)); - join_text_field_border->setScale(.53f); - this->addChild(join_text_field_border, /*layer_index=*/0); - - // Create a touch listener to handle the touch event. - auto join_text_field_touch_listener = EventListenerTouchOneByOne::create(); - - join_text_field_touch_listener->onTouchBegan = - [join_text_field, this](cocos2d::Touch* touch, - cocos2d::Event* event) -> bool { - // Returns false, not consuming the event, to exit the layer if - // current_state_ is not in the kGameMenuState or is switching states. - if (previous_state_ != current_state_ || - current_state_ != kGameMenuState) { - return false; - } - const auto bounds = event->getCurrentTarget()->getBoundingBox(); - const auto point = touch->getLocation(); - if (bounds.containsPoint(point)) { - // Show the on screen keyboard and places character inputs into the text - // field. - auto str = join_text_field->getString(); - auto text_field = - dynamic_cast(event->getCurrentTarget()); - text_field->setCursorEnabled(true); - text_field->attachWithIME(); + // Attach the touch listener to the login button. + Director::getInstance() + ->getEventDispatcher() + ->addEventListenerWithSceneGraphPriority(login_button_touch_listener, + login_button); + auth_background_->addChild(login_button, /*layer_index=*/1); + + // Create the sign_up button and give it a position, anchor point and + // touch_listener. + auto sign_up_button = Sprite::create(kSignUpButtonImage); + sign_up_button->setPosition(310, 120); + sign_up_button->setAnchorPoint(Vec2(0, 0)); + sign_up_button->setContentSize(Size(200, 75)); + + // Create a button listener to handle the touch event. + auto sign_up_button_touch_listener = EventListenerTouchOneByOne::create(); + + // Transition from kAuthState to kWaitingSignUpState on button press and set + // user_result_ to CreateUserWithEmailAndPassword future result. + sign_up_button_touch_listener->onTouchBegan = [this](Touch* touch, + Event* event) -> bool { + // Returns false, not consuming the event, to exit the layer if + // current_state_ is not in the kAuthState or is switching states. + if (previous_state_ != current_state_ || current_state_ != kAuthState) { + return false; + } + const auto bounds = event->getCurrentTarget()->getBoundingBox(); + const auto point = touch->getLocation(); + if (bounds.containsPoint(point)) { + // Check the string for a valid email pattern. + if (!std::regex_match(email_text_field_->getString(), email_pattern)) { + invalid_login_label_->setString("invalid email address"); + } else if (password_text_field_->getString().length() < 8) { + invalid_login_label_->setString( + "password must be at least 8 characters long"); } else { - auto text_field = - dynamic_cast(event->getCurrentTarget()); - text_field->setCursorEnabled(false); - text_field->detachWithIME(); + user_result_ = auth_->CreateUserWithEmailAndPassword( + email_text_field_->getString().c_str(), + password_text_field_->getString().c_str()); + current_state_ = kWaitingSignUpState; } + } + return true; + }; - return true; - }; + // Attach the touch listener to the sign_up button. + Director::getInstance() + ->getEventDispatcher() + ->addEventListenerWithSceneGraphPriority(sign_up_button_touch_listener, + sign_up_button); + auth_background_->addChild(sign_up_button, /*layer_index=*/1); + + // Create, set the position and assign a placeholder to the text + // field for the user to enter the join game uuid. + TextFieldTTF* join_text_field = + cocos2d::TextFieldTTF::textFieldWithPlaceHolder( + "code", cocos2d::Size(200, 100), TextHAlignment::LEFT, "Arial", 55.0); + join_text_field->setPosition(420, 45); + join_text_field->setAnchorPoint(Vec2(0, 0)); + join_text_field->setColorSpaceHolder(Color3B::WHITE); + join_text_field->setDelegate(this); + + auto join_text_field_border = Sprite::create(kTextFieldBorderImage); + join_text_field_border->setPosition(390, 50); + join_text_field_border->setAnchorPoint(Vec2(0, 0)); + join_text_field_border->setScale(.53f); + this->addChild(join_text_field_border, /*layer_index=*/0); + + // Create a touch listener to handle the touch event. + auto join_text_field_touch_listener = EventListenerTouchOneByOne::create(); + + join_text_field_touch_listener->onTouchBegan = + [join_text_field, this](cocos2d::Touch* touch, + cocos2d::Event* event) -> bool { + // Returns false, not consuming the event, to exit the layer if + // current_state_ is not in the kGameMenuState or is switching states. + if (previous_state_ != current_state_ || current_state_ != kGameMenuState) { + return false; + } + const auto bounds = event->getCurrentTarget()->getBoundingBox(); + const auto point = touch->getLocation(); + if (bounds.containsPoint(point)) { + // Show the on screen keyboard and places character inputs into the text + // field. + auto str = join_text_field->getString(); + auto text_field = dynamic_cast(event->getCurrentTarget()); + text_field->setCursorEnabled(true); + text_field->attachWithIME(); + } else { + auto text_field = dynamic_cast(event->getCurrentTarget()); + text_field->setCursorEnabled(false); + text_field->detachWithIME(); + } - // Attach the touch listener to the text field. - Director::getInstance() - ->getEventDispatcher() - ->addEventListenerWithSceneGraphPriority(join_text_field_touch_listener, - join_text_field); - - // Creates a sprite for the create button and sets its position to the - // center of the screen. TODO(grantpostma): Dynamically choose the location. - auto create_button = Sprite::create(kCreateGameImage); - create_button->setPosition(25, 200); - create_button->setAnchorPoint(Vec2(0, 0)); - - // Create a button listener to handle the touch event. - auto create_button_touch_listener = EventListenerTouchOneByOne::create(); - - // Set the onTouchBegan event up to a lambda tha will replace the - // MainMenu scene with a TicTacToe scene. - create_button_touch_listener->onTouchBegan = - [this, join_text_field](Touch* touch, Event* event) -> bool { - // Returns false, not consuming the event, to exit the layer if - // current_state_ is not in the kGameMenuState or is switching states. - if (previous_state_ != current_state_ || - current_state_ != kGameMenuState) { - return false; - }; - const auto bounds = event->getCurrentTarget()->getBoundingBox(); - const auto point = touch->getLocation(); - - // Replaces the scene with a new TicTacToe scene if the touched point is - // within the bounds of the button. - if (bounds.containsPoint(point)) { - Director::getInstance()->pushScene( - TicTacToe::createScene(std::string(), database_, user_uid_)); - join_text_field->setString(""); - current_state_ = kWaitingGameOutcome; - } + return true; + }; - return true; + // Attach the touch listener to the text field. + Director::getInstance() + ->getEventDispatcher() + ->addEventListenerWithSceneGraphPriority(join_text_field_touch_listener, + join_text_field); + + // Creates a sprite for the create button and sets its position to the + // center of the screen. TODO(grantpostma): Dynamically choose the location. + auto create_button = Sprite::create(kCreateGameImage); + create_button->setPosition(25, 200); + create_button->setAnchorPoint(Vec2(0, 0)); + + // Create a button listener to handle the touch event. + auto create_button_touch_listener = EventListenerTouchOneByOne::create(); + + // Set the onTouchBegan event up to a lambda tha will replace the + // MainMenu scene with a TicTacToe scene. + create_button_touch_listener->onTouchBegan = + [this, join_text_field](Touch* touch, Event* event) -> bool { + // Returns false, not consuming the event, to exit the layer if + // current_state_ is not in the kGameMenuState or is switching states. + if (previous_state_ != current_state_ || current_state_ != kGameMenuState) { + return false; }; + const auto bounds = event->getCurrentTarget()->getBoundingBox(); + const auto point = touch->getLocation(); - // Attach the touch listener to the create game button. - Director::getInstance() - ->getEventDispatcher() - ->addEventListenerWithSceneGraphPriority(create_button_touch_listener, - create_button); - - // Creates a sprite for the logout button and sets its position to the - auto logout_button = Sprite::create(kLogoutButtonImage); - logout_button->setPosition(25, 575); - logout_button->setAnchorPoint(Vec2(0, 0)); - logout_button->setContentSize(Size(125, 50)); - - // Create a button listener to handle the touch event. - auto logout_button_touch_listener = EventListenerTouchOneByOne::create(); - - // Set the onTouchBegan event up to a lambda tha will replace the - // MainMenu scene with a TicTacToe scene. - logout_button_touch_listener->onTouchBegan = [this](Touch* touch, - Event* event) -> bool { - // Returns false, not consuming the event, to exit the layer if - // current_state_ is not in the kGameMenuState or is switching states. - if (previous_state_ != current_state_ || - current_state_ != kGameMenuState) { - return false; - } - const auto bounds = event->getCurrentTarget()->getBoundingBox(); - const auto point = touch->getLocation(); - - // Replaces the scene with a new TicTacToe scene if the touched point is - // within the bounds of the button. - if (bounds.containsPoint(point)) { - current_state_ = kAuthState; - user_uid_ = ""; - user_ = nullptr; - invalid_login_label_->setString(""); - email_text_field_->setString(""); - password_text_field_->setString(""); - user_record_label_->setString(""); - } + // Replaces the scene with a new TicTacToe scene if the touched point is + // within the bounds of the button. + if (bounds.containsPoint(point)) { + Director::getInstance()->pushScene( + TicTacToe::createScene(std::string(), database_, user_uid_)); + join_text_field->setString(""); + current_state_ = kWaitingGameOutcome; + } - return true; - }; + return true; + }; - // Attach the touch listener to the logout game button. - Director::getInstance() - ->getEventDispatcher() - ->addEventListenerWithSceneGraphPriority(logout_button_touch_listener, - logout_button); - - // Creates a sprite for the join button and sets its position to the center - // of the screen. TODO(grantpostma): Dynamically choose the location and set - // size(). - auto join_button = Sprite::create(kJoinButtonImage); - join_button->setPosition(25, 50); - join_button->setAnchorPoint(Vec2(0, 0)); - join_button->setScale(1.3f); - - // Create a button listener to handle the touch event. - auto join_button_touch_listener = EventListenerTouchOneByOne::create(); - - // Set the onTouchBegan event up to a lambda tha will replace the - // MainMenu scene with a TicTacToe scene and pass in join_text_field string. - join_button_touch_listener->onTouchBegan = - [join_text_field, this](Touch* touch, Event* event) -> bool { - // Returns false, not consuming the event, to exit the layer if - // current_state_ is not in the kGameMenuState or is switching states. - if (previous_state_ != current_state_ || - current_state_ != kGameMenuState) { - return false; - } - const auto bounds = event->getCurrentTarget()->getBoundingBox(); - const auto point = touch->getLocation(); - if (bounds.containsPoint(point)) { - // Get the string from join_text_field. - std::string join_text_field_string = join_text_field->getString(); - if (join_text_field_string.length() == 4) { - Director::getInstance()->pushScene(TicTacToe::createScene( - join_text_field_string, database_, user_uid_)); - current_state_ = kWaitingGameOutcome; - join_text_field->setString(""); - } else { - join_text_field->setString(""); - } + // Attach the touch listener to the create game button. + Director::getInstance() + ->getEventDispatcher() + ->addEventListenerWithSceneGraphPriority(create_button_touch_listener, + create_button); + + // Creates a sprite for the logout button and sets its position to the + auto logout_button = Sprite::create(kLogoutButtonImage); + logout_button->setPosition(25, 575); + logout_button->setAnchorPoint(Vec2(0, 0)); + logout_button->setContentSize(Size(125, 50)); + + // Create a button listener to handle the touch event. + auto logout_button_touch_listener = EventListenerTouchOneByOne::create(); + + // Set the onTouchBegan event up to a lambda tha will replace the + // MainMenu scene with a TicTacToe scene. + logout_button_touch_listener->onTouchBegan = [this](Touch* touch, + Event* event) -> bool { + // Returns false, not consuming the event, to exit the layer if + // current_state_ is not in the kGameMenuState or is switching states. + if (previous_state_ != current_state_ || current_state_ != kGameMenuState) { + return false; + } + const auto bounds = event->getCurrentTarget()->getBoundingBox(); + const auto point = touch->getLocation(); + + // Replaces the scene with a new TicTacToe scene if the touched point is + // within the bounds of the button. + if (bounds.containsPoint(point)) { + current_state_ = kAuthState; + user_uid_ = ""; + user_ = nullptr; + invalid_login_label_->setString(""); + email_text_field_->setString(""); + password_text_field_->setString(""); + user_record_label_->setString(""); + } + + return true; + }; + + // Attach the touch listener to the logout game button. + Director::getInstance() + ->getEventDispatcher() + ->addEventListenerWithSceneGraphPriority(logout_button_touch_listener, + logout_button); + + // Creates a sprite for the join button and sets its position to the center + // of the screen. TODO(grantpostma): Dynamically choose the location and set + // size(). + auto join_button = Sprite::create(kJoinButtonImage); + join_button->setPosition(25, 50); + join_button->setAnchorPoint(Vec2(0, 0)); + join_button->setScale(1.3f); + + // Create a button listener to handle the touch event. + auto join_button_touch_listener = EventListenerTouchOneByOne::create(); + + // Set the onTouchBegan event up to a lambda tha will replace the + // MainMenu scene with a TicTacToe scene and pass in join_text_field string. + join_button_touch_listener->onTouchBegan = + [join_text_field, this](Touch* touch, Event* event) -> bool { + // Returns false, not consuming the event, to exit the layer if + // current_state_ is not in the kGameMenuState or is switching states. + if (previous_state_ != current_state_ || current_state_ != kGameMenuState) { + return false; + } + const auto bounds = event->getCurrentTarget()->getBoundingBox(); + const auto point = touch->getLocation(); + if (bounds.containsPoint(point)) { + // Get the string from join_text_field. + std::string join_text_field_string = join_text_field->getString(); + if (join_text_field_string.length() == 4) { + Director::getInstance()->pushScene(TicTacToe::createScene( + join_text_field_string, database_, user_uid_)); + current_state_ = kWaitingGameOutcome; + join_text_field->setString(""); + } else { + join_text_field->setString(""); } - return true; - }; + } + return true; + }; - // Attach the touch listener to the join button. - Director::getInstance() - ->getEventDispatcher() - ->addEventListenerWithSceneGraphPriority(join_button_touch_listener, - join_button); + // Attach the touch listener to the join button. + Director::getInstance() + ->getEventDispatcher() + ->addEventListenerWithSceneGraphPriority(join_button_touch_listener, + join_button); - // Attach the create button, join button and join text field to the - // MainMenu scene. - this->addChild(create_button); - this->addChild(join_button); - this->addChild(logout_button); - this->addChild(join_text_field, /*layer_index=*/1); + // Attach the create button, join button and join text field to the + // MainMenu scene. + this->addChild(create_button); + this->addChild(join_button); + this->addChild(logout_button); + this->addChild(join_text_field, /*layer_index=*/1); - this->scheduleUpdate(); + this->scheduleUpdate(); - return true; - } - // Initialize the firebase auth and database while also ensuring no - // dependencies are missing. - void - MainMenuScene::InitializeFirebase() { - LogMessage("Initialize Firebase App."); - ::firebase::App* app; + return true; +} +// Initialize the firebase auth and database while also ensuring no dependencies +// are missing. +void MainMenuScene::InitializeFirebase() { + LogMessage("Initialize Firebase App."); + ::firebase::App* app; #if defined(_ANDROID_) - app = ::firebase::App::Create(GetJniEnv(), GetActivity()); + app = ::firebase::App::Create(GetJniEnv(), GetActivity()); #else - app = ::firebase::App::Create(); + app = ::firebase::App::Create(); #endif // defined(ANDROID) - LogMessage("Initialize Firebase Auth and Firebase Database."); - - // Use ModuleInitializer to initialize both Auth and Database, ensuring no - // dependencies are missing. - database_ = nullptr; - auth_ = nullptr; - void* initialize_targets[] = {&auth_, &database_}; - - const firebase::ModuleInitializer::InitializerFn initializers[] = { - [](::firebase::App* app, void* data) { - LogMessage("Attempt to initialize Firebase Auth."); - void** targets = reinterpret_cast(data); - ::firebase::InitResult result; - *reinterpret_cast<::firebase::auth::Auth**>(targets[0]) = - ::firebase::auth::Auth::GetAuth(app, &result); - return result; - }, - [](::firebase::App* app, void* data) { - LogMessage("Attempt to initialize Firebase Database."); - void** targets = reinterpret_cast(data); - ::firebase::InitResult result; - *reinterpret_cast<::firebase::database::Database**>(targets[1]) = - ::firebase::database::Database::GetInstance(app, &result); - return result; - }}; - - ::firebase::ModuleInitializer initializer; - initializer.Initialize(app, initialize_targets, initializers, - sizeof(initializers) / sizeof(initializers[0])); - - WaitForCompletion(initializer.InitializeLastResult(), "Initialize"); - - if (initializer.InitializeLastResult().error() != 0) { - LogMessage("Failed to initialize Firebase libraries: %s", - initializer.InitializeLastResult().error_message()); - ProcessEvents(2000); - } - LogMessage("Successfully initialized Firebase Auth and Firebase Database."); + LogMessage("Initialize Firebase Auth and Firebase Database."); - database_->set_persistence_enabled(true); + // Use ModuleInitializer to initialize both Auth and Database, ensuring no + // dependencies are missing. + database_ = nullptr; + auth_ = nullptr; + void* initialize_targets[] = {&auth_, &database_}; + + const firebase::ModuleInitializer::InitializerFn initializers[] = { + [](::firebase::App* app, void* data) { + LogMessage("Attempt to initialize Firebase Auth."); + void** targets = reinterpret_cast(data); + ::firebase::InitResult result; + *reinterpret_cast<::firebase::auth::Auth**>(targets[0]) = + ::firebase::auth::Auth::GetAuth(app, &result); + return result; + }, + [](::firebase::App* app, void* data) { + LogMessage("Attempt to initialize Firebase Database."); + void** targets = reinterpret_cast(data); + ::firebase::InitResult result; + *reinterpret_cast<::firebase::database::Database**>(targets[1]) = + ::firebase::database::Database::GetInstance(app, &result); + return result; + }}; + + ::firebase::ModuleInitializer initializer; + initializer.Initialize(app, initialize_targets, initializers, + sizeof(initializers) / sizeof(initializers[0])); + + WaitForCompletion(initializer.InitializeLastResult(), "Initialize"); + + if (initializer.InitializeLastResult().error() != 0) { + LogMessage("Failed to initialize Firebase libraries: %s", + initializer.InitializeLastResult().error_message()); + ProcessEvents(2000); } + LogMessage("Successfully initialized Firebase Auth and Firebase Database."); - // Updates the user record variables to reflect what is in the database. - void MainMenuScene::UpdateUserRecord() { - ref_ = database_->GetReference("users").Child(user_uid_); - auto future_wins = ref_.Child("wins").GetValue(); - auto future_loses = ref_.Child("loses").GetValue(); - auto future_ties = ref_.Child("ties").GetValue(); - WaitForCompletion(future_wins, "getUserWinsData"); - WaitForCompletion(future_loses, "getUserLosesData"); - WaitForCompletion(future_ties, "getUserTiesData"); - user_wins_ = future_wins.result()->value().int64_value(); - user_loses_ = future_loses.result()->value().int64_value(); - user_ties_ = future_ties.result()->value().int64_value(); - user_record_label_->setString("W:" + to_string(user_wins_) + - " L:" + to_string(user_loses_) + - " T:" + to_string(user_ties_)); - } + database_->set_persistence_enabled(true); +} - // Initialize the user records in the database. - void MainMenuScene::InitializeUserRecord() { - ref_ = database_->GetReference("users").Child(user_uid_); - auto future_wins = ref_.Child("wins").SetValue(0); - auto future_loses = ref_.Child("loses").SetValue(0); - auto future_ties = ref_.Child("ties").SetValue(0); - WaitForCompletion(future_wins, "setUserWinsData"); - WaitForCompletion(future_loses, "setUserLosesData"); - WaitForCompletion(future_ties, "setUserTiesData"); - user_wins_ = 0; - user_loses_ = 0; - user_ties_ = 0; - user_record_label_->setString("W:" + to_string(user_wins_) + - " L:" + to_string(user_loses_) + - " T:" + to_string(user_ties_)); - } +// Updates the user record variables to reflect what is in the database. +void MainMenuScene::UpdateUserRecord() { + ref_ = database_->GetReference("users").Child(user_uid_); + auto future_wins = ref_.Child("wins").GetValue(); + auto future_loses = ref_.Child("loses").GetValue(); + auto future_ties = ref_.Child("ties").GetValue(); + WaitForCompletion(future_wins, "getUserWinsData"); + WaitForCompletion(future_loses, "getUserLosesData"); + WaitForCompletion(future_ties, "getUserTiesData"); + user_wins_ = future_wins.result()->value().int64_value(); + user_loses_ = future_loses.result()->value().int64_value(); + user_ties_ = future_ties.result()->value().int64_value(); + user_record_label_->setString("W:" + to_string(user_wins_) + + " L:" + to_string(user_loses_) + + " T:" + to_string(user_ties_)); +} - // Overriding the onEnter method to update the user_record on reenter. - void MainMenuScene::onEnter() { - // if the scene enter is from the game, updateUserRecords and change - // current_state_. - if (current_state_ == kWaitingGameOutcome) { - this->UpdateUserRecord(); - current_state_ = kGameMenuState; - } - Layer::onEnter(); +// Initialize the user records in the database. +void MainMenuScene::InitializeUserRecord() { + ref_ = database_->GetReference("users").Child(user_uid_); + auto future_wins = ref_.Child("wins").SetValue(0); + auto future_loses = ref_.Child("loses").SetValue(0); + auto future_ties = ref_.Child("ties").SetValue(0); + WaitForCompletion(future_wins, "setUserWinsData"); + WaitForCompletion(future_loses, "setUserLosesData"); + WaitForCompletion(future_ties, "setUserTiesData"); + user_wins_ = 0; + user_loses_ = 0; + user_ties_ = 0; + user_record_label_->setString("W:" + to_string(user_wins_) + + " L:" + to_string(user_loses_) + + " T:" + to_string(user_ties_)); +} + +// Overriding the onEnter method to update the user_record on reenter. +void MainMenuScene::onEnter() { + // if the scene enter is from the game, updateUserRecords and change + // current_state_. + if (current_state_ == kWaitingGameOutcome) { + this->UpdateUserRecord(); + current_state_ = kGameMenuState; } + Layer::onEnter(); +} - // Updates the previous_state_ when current_state_ != previous_state_: - // - // switch (current_state_) - // (1) kAuthState: makes the auth_background_ visable. - // (2) kGameMenuState: makes the auth_background_ invisable. - // (3) kWaitingAnonymousState: waits for anonymous sign in then swaps to (1). - // (4) kWaitingSignUpState: waits for sign up future completion, - // updates user variables, and swaps to (2). - // (5) kWaitingLoginState: waits for login future completion, - // updates user variables, and swaps to (2). - // (6) kWaitingGameOutcome: waits for director to pop the TicTacToeScene. - void MainMenuScene::update(float /*delta*/) { - if (current_state_ != previous_state_) { - if (current_state_ == kWaitingAnonymousState) { - if (user_result_.status() == firebase::kFutureStatusComplete) { - if (user_result_.error() == firebase::auth::kAuthErrorNone) { - user_ = *user_result_.result(); - user_uid_ = GenerateUid(10); - - this->InitializeUserRecord(); - - current_state_ = kGameMenuState; - } +// Updates the previous_state_ when current_state_ != previous_state_: +// +// switch (current_state_) +// (1) kAuthState: makes the auth_background_ visable. +// (2) kGameMenuState: makes the auth_background_ invisable. +// (3) kWaitingAnonymousState: waits for anonymous sign in then swaps to (1). +// (4) kWaitingSignUpState: waits for sign up future completion, +// updates user variables, and swaps to (2). +// (5) kWaitingLoginState: waits for login future completion, +// updates user variables, and swaps to (2). +// (6) kWaitingGameOutcome: waits for director to pop the TicTacToeScene. +void MainMenuScene::update(float /*delta*/) { + if (current_state_ != previous_state_) { + if (current_state_ == kWaitingAnonymousState) { + if (user_result_.status() == firebase::kFutureStatusComplete) { + if (user_result_.error() == firebase::auth::kAuthErrorNone) { + user_ = *user_result_.result(); + user_uid_ = GenerateUid(10); + + this->InitializeUserRecord(); + + current_state_ = kGameMenuState; } - } else if (current_state_ == kWaitingSignUpState) { - if (user_result_.status() == firebase::kFutureStatusComplete) { - if (user_result_.error() == firebase::auth::kAuthErrorNone) { - user_ = *user_result_.result(); - user_uid_ = user_->uid(); + } + } else if (current_state_ == kWaitingSignUpState) { + if (user_result_.status() == firebase::kFutureStatusComplete) { + if (user_result_.error() == firebase::auth::kAuthErrorNone) { + user_ = *user_result_.result(); + user_uid_ = user_->uid(); - this->InitializeUserRecord(); + this->InitializeUserRecord(); - current_state_ = kGameMenuState; + current_state_ = kGameMenuState; - } else { - // Change invalid_login_label_ to display the user_create failed. - invalid_login_label_->setString("invalid sign up"); - current_state_ = kAuthState; - } + } else { + // Change invalid_login_label_ to display the user_create failed. + invalid_login_label_->setString("invalid sign up"); + current_state_ = kAuthState; } - } else if (current_state_ == kWaitingLoginState) { - if (user_result_.status() == firebase::kFutureStatusComplete) { - if (user_result_.error() == firebase::auth::kAuthErrorNone) { - user_ = *user_result_.result(); - user_uid_ = user_->uid(); - - this->UpdateUserRecord(); - - current_state_ = kGameMenuState; - } else { - // Change invalid_login_label_ to display the auth_result errored. - auto err = user_result_.error_message(); - invalid_login_label_->setString("invalid login"); - current_state_ = kAuthState; - } + } + } else if (current_state_ == kWaitingLoginState) { + if (user_result_.status() == firebase::kFutureStatusComplete) { + if (user_result_.error() == firebase::auth::kAuthErrorNone) { + user_ = *user_result_.result(); + user_uid_ = user_->uid(); + + this->UpdateUserRecord(); + + current_state_ = kGameMenuState; + } else { + // Change invalid_login_label_ to display the auth_result errored. + auto err = user_result_.error_message(); + invalid_login_label_->setString("invalid login"); + current_state_ = kAuthState; } - } else if (current_state_ == kAuthState) { - // Sign out logic, adding auth screen. - auth_background_->setVisible(true); - user_ = nullptr; - previous_state_ = current_state_; - } else if (current_state_ == kGameMenuState) { - // Removes the authentication screen. - auth_background_->setVisible(false); - previous_state_ = current_state_; } + } else if (current_state_ == kAuthState) { + // Sign out logic, adding auth screen. + auth_background_->setVisible(true); + user_ = nullptr; + previous_state_ = current_state_; + } else if (current_state_ == kGameMenuState) { + // Removes the authentication screen. + auth_background_->setVisible(false); + previous_state_ = current_state_; } - return; } + return; +} diff --git a/demos/TicTacToe/Classes/MainMenuScene.h b/demos/TicTacToe/Classes/MainMenuScene.h index fd411d28..2759fbe8 100644 --- a/demos/TicTacToe/Classes/MainMenuScene.h +++ b/demos/TicTacToe/Classes/MainMenuScene.h @@ -15,19 +15,14 @@ #ifndef TICTACTOE_DEMO_CLASSES_MAINMENU_SCENE_H_ #define TICTACTOE_DEMO_CLASSES_MAINMENU_SCENE_H_ -#include "cocos2d.h" +#include + #include "firebase/auth.h" -#include "firebase/database.h" #include "firebase/future.h" -#include "firebase/util.h" -#include "ui/CocosGUI.h" -using std::to_string; +#include "firebase/database.h" +#include "cocos2d.h" -// TODO(grantpostma): Create a common util.h & util.cpp file. -void LogMessage(const char*, ...); -void ProcessEvents(int); -void WaitForCompletion(const firebase::FutureBase&, const char*); -std::string GenerateUid(std::size_t); +using std::to_string; class MainMenuScene : public cocos2d::Layer, public cocos2d::TextFieldDelegate { public: diff --git a/demos/TicTacToe/Classes/TicTacToeLayer.cpp b/demos/TicTacToe/Classes/TicTacToeLayer.cpp index 41e19d13..f903966f 100644 --- a/demos/TicTacToe/Classes/TicTacToeLayer.cpp +++ b/demos/TicTacToe/Classes/TicTacToeLayer.cpp @@ -130,7 +130,8 @@ class SampleChildListener : public firebase::database::ChildListener { } public: - // Vector of strings that define the events we saw, in order. + // Vector of strings that contains the events in the order in which they + // occurred. std::vector events_; }; @@ -182,6 +183,7 @@ static bool ColumnCrossed(int board[][kTilesY]) { } return (false); } + // A function that returns true if any of the diagonal // is crossed with the same player's move static bool DiagonalCrossed(int board[][kTilesY]) { diff --git a/demos/TicTacToe/Classes/TicTacToeLayer.h b/demos/TicTacToe/Classes/TicTacToeLayer.h index b8b645d2..81581e3e 100644 --- a/demos/TicTacToe/Classes/TicTacToeLayer.h +++ b/demos/TicTacToe/Classes/TicTacToeLayer.h @@ -27,9 +27,9 @@ #include #include -#include "MainMenuScene.h" #include "TicTacToeScene.h" #include "cocos2d.h" +#include "util.h" using cocos2d::Director; using cocos2d::Event; diff --git a/demos/TicTacToe/Classes/TicTacToeScene.h b/demos/TicTacToe/Classes/TicTacToeScene.h index 6593dd3f..031e184b 100644 --- a/demos/TicTacToe/Classes/TicTacToeScene.h +++ b/demos/TicTacToe/Classes/TicTacToeScene.h @@ -18,6 +18,7 @@ #include "MainMenuScene.h" #include "TicTacToeLayer.h" #include "cocos2d.h" +#include "util.h" class TicTacToe : public cocos2d::Layer { public: diff --git a/demos/TicTacToe/Classes/util.cpp b/demos/TicTacToe/Classes/util.cpp new file mode 100644 index 00000000..32838260 --- /dev/null +++ b/demos/TicTacToe/Classes/util.cpp @@ -0,0 +1,67 @@ +// Copyright 2020 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "util.h" + +#include + +// Logs the message passed in to the console. +void LogMessage(const char* format, ...) { + va_list list; + va_start(list, format); + vprintf(format, list); + va_end(list); + printf("\n"); + fflush(stdout); +} + +// Based on operating system, waits for the specified amount of miliseconds. +void ProcessEvents(int msec) { +#ifdef _WIN32 + Sleep(msec); +#else + usleep(msec * 1000); +#endif // _WIN32 +} + +// Wait for a Future to be completed. If the Future returns an error, it will +// be logged. +void WaitForCompletion(const firebase::FutureBase& future, const char* name) { + while (future.status() == firebase::kFutureStatusPending) { + ProcessEvents(100); + } + if (future.status() != firebase::kFutureStatusComplete) { + LogMessage("ERROR: %s returned an invalid result.", name); + } else if (future.error() != 0) { + LogMessage("ERROR: %s returned error %d: %s", name, future.error(), + future.error_message()); + } +} + +// Generates a random uid of a specified length. +std::string GenerateUid(std::size_t length) { + const std::string kCharacters = "0123456789abcdefghjkmnpqrstuvwxyz"; + + std::random_device random_device; + std::mt19937 generator(random_device()); + std::uniform_int_distribution<> distribution(0, kCharacters.size() - 1); + + std::string generate_uid; + + for (std::size_t i = 0; i < length; ++i) { + generate_uid += kCharacters[distribution(generator)]; + } + + return generate_uid; +} diff --git a/demos/TicTacToe/Classes/util.h b/demos/TicTacToe/Classes/util.h new file mode 100644 index 00000000..60ea2ccb --- /dev/null +++ b/demos/TicTacToe/Classes/util.h @@ -0,0 +1,43 @@ +// Copyright 2020 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#ifndef TICTACTOE_DEMO_CLASSES_UTIL_H_ +#define TICTACTOE_DEMO_CLASSES_UTIL_H_ + +#include "firebase/future.h" + +// inputs: const char* as the message that will be displayed. +// +// Logs the message to the user through the console. +void LogMessage(const char*, ...); + +// inputs: integer for number of miliseconds. +// +// Acts as a blocking statement to wait for the specified time. +void ProcessEvents(int); + +// inputs: FutureBase waiting to be completed and message describing the future +// action. +// +// Spins on ProcessEvents() while the FutureBase's status is pending, which it +// will then break out and LogMessage() notifying the FutureBase's status is +// completed. +void WaitForCompletion(const firebase::FutureBase&, const char*); + +// inputs: size_t integer specifying the length of the uid. +// +// Generates a unique string based on the length passed in, grabbing the allowed +// characters from the character string defined inside the function. +std::string GenerateUid(std::size_t); + +#endif // TICTACTOE_DEMO_CLASSES_UTIL_H_ From 2df0761a891658fc31f500eabac8478b8051f144 Mon Sep 17 00:00:00 2001 From: Grant Postma Date: Tue, 7 Jul 2020 11:10:53 -0400 Subject: [PATCH 3/6] Add util files (#11) * Moving common functions to util.h and util.cpp. Adding more comments on util functions. * Added missing period. * removing double new line. modifying includes in util.h and util.cpp From 184127e83a4357eb9c405caa70ae90fc36d8b9ac Mon Sep 17 00:00:00 2001 From: Grant Postma Date: Tue, 7 Jul 2020 11:15:52 -0400 Subject: [PATCH 4/6] Refactor file names (#12) * refactor file names * reverting cmake unnessecary changes. --- demos/TicTacToe/CMakeLists.txt | 9 ++++----- .../Classes/{AppDelegate.cpp => app_delegate.cc} | 7 ++++--- .../TicTacToe/Classes/{AppDelegate.h => app_delegate.h} | 0 .../Classes/{MainMenuScene.cpp => main_menu_scene.cc} | 5 +++-- .../Classes/{MainMenuScene.h => main_menu_scene.h} | 4 ++-- .../Classes/{TicTacToeLayer.cpp => tic_tac_toe_layer.cc} | 2 +- .../Classes/{TicTacToeLayer.h => tic_tac_toe_layer.h} | 2 +- .../Classes/{TicTacToeScene.cpp => tic_tac_toe_scene.cc} | 2 +- .../Classes/{TicTacToeScene.h => tic_tac_toe_scene.h} | 4 ++-- demos/TicTacToe/Classes/{util.cpp => util.cc} | 3 +++ demos/TicTacToe/Classes/util.h | 2 ++ 11 files changed, 23 insertions(+), 17 deletions(-) rename demos/TicTacToe/Classes/{AppDelegate.cpp => app_delegate.cc} (93%) rename demos/TicTacToe/Classes/{AppDelegate.h => app_delegate.h} (100%) rename demos/TicTacToe/Classes/{MainMenuScene.cpp => main_menu_scene.cc} (99%) rename demos/TicTacToe/Classes/{MainMenuScene.h => main_menu_scene.h} (100%) rename demos/TicTacToe/Classes/{TicTacToeLayer.cpp => tic_tac_toe_layer.cc} (99%) rename demos/TicTacToe/Classes/{TicTacToeLayer.h => tic_tac_toe_layer.h} (99%) rename demos/TicTacToe/Classes/{TicTacToeScene.cpp => tic_tac_toe_scene.cc} (97%) rename demos/TicTacToe/Classes/{TicTacToeScene.h => tic_tac_toe_scene.h} (95%) rename demos/TicTacToe/Classes/{util.cpp => util.cc} (97%) diff --git a/demos/TicTacToe/CMakeLists.txt b/demos/TicTacToe/CMakeLists.txt index 630ad65e..b209edcc 100644 --- a/demos/TicTacToe/CMakeLists.txt +++ b/demos/TicTacToe/CMakeLists.txt @@ -10,10 +10,10 @@ # 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 @@ -90,7 +90,6 @@ endif() if(NOT DEFINED BUILD_ENGINE_DONE) # to test install_test into root project set(COCOS2DX_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cocos2d) set(CMAKE_MODULE_PATH ${COCOS2DX_ROOT_PATH}/cmake/Modules/) - include(CocosBuildSet) add_subdirectory(${COCOS2DX_ROOT_PATH}/cocos ${ENGINE_BINARY_PATH}/cocos/core) endif() @@ -105,8 +104,8 @@ if(APPLE OR WINDOWS) endif() # add cross-platforms source files and header files -list(APPEND GAME_SOURCE Classes/AppDelegate.cpp Classes/TicTacToeScene.cpp) -list(APPEND GAME_HEADER Classes/AppDelegate.h Classes/TicTacToeScene.h) +list(APPEND GAME_SOURCE Classes/app_delegate.cc ) +list(APPEND GAME_HEADER Classes/app_delegate.h ) if(ANDROID) # change APP_NAME to the share library name for Android, it's value depend on AndroidManifest.xml diff --git a/demos/TicTacToe/Classes/AppDelegate.cpp b/demos/TicTacToe/Classes/app_delegate.cc similarity index 93% rename from demos/TicTacToe/Classes/AppDelegate.cpp rename to demos/TicTacToe/Classes/app_delegate.cc index 6fd2ab92..8849dcc5 100644 --- a/demos/TicTacToe/Classes/AppDelegate.cpp +++ b/demos/TicTacToe/Classes/app_delegate.cc @@ -12,10 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "AppDelegate.h" +#include "app_delegate.h" + +#include "main_menu_scene.h" +#include "tic_tac_toe_scene.h" -#include "MainMenuScene.h" -#include "TicTacToeScene.h" USING_NS_CC; // Set based on the image width. diff --git a/demos/TicTacToe/Classes/AppDelegate.h b/demos/TicTacToe/Classes/app_delegate.h similarity index 100% rename from demos/TicTacToe/Classes/AppDelegate.h rename to demos/TicTacToe/Classes/app_delegate.h diff --git a/demos/TicTacToe/Classes/MainMenuScene.cpp b/demos/TicTacToe/Classes/main_menu_scene.cc similarity index 99% rename from demos/TicTacToe/Classes/MainMenuScene.cpp rename to demos/TicTacToe/Classes/main_menu_scene.cc index 3a3267b0..3ad84af9 100644 --- a/demos/TicTacToe/Classes/MainMenuScene.cpp +++ b/demos/TicTacToe/Classes/main_menu_scene.cc @@ -12,13 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "MainMenuScene.h" +#include "main_menu_scene.h" #include #include "cocos2d.h" +#include "firebase/util.h" +#include "tic_tac_toe_scene.h" #include "util.h" -#include "TicTacToeScene.h" static const char* kCreateGameImage = "create_game.png"; static const char* kTextFieldBorderImage = "text_field_border.png"; diff --git a/demos/TicTacToe/Classes/MainMenuScene.h b/demos/TicTacToe/Classes/main_menu_scene.h similarity index 100% rename from demos/TicTacToe/Classes/MainMenuScene.h rename to demos/TicTacToe/Classes/main_menu_scene.h index 2759fbe8..f5181339 100644 --- a/demos/TicTacToe/Classes/MainMenuScene.h +++ b/demos/TicTacToe/Classes/main_menu_scene.h @@ -17,10 +17,10 @@ #include +#include "cocos2d.h" #include "firebase/auth.h" -#include "firebase/future.h" #include "firebase/database.h" -#include "cocos2d.h" +#include "firebase/future.h" using std::to_string; diff --git a/demos/TicTacToe/Classes/TicTacToeLayer.cpp b/demos/TicTacToe/Classes/tic_tac_toe_layer.cc similarity index 99% rename from demos/TicTacToe/Classes/TicTacToeLayer.cpp rename to demos/TicTacToe/Classes/tic_tac_toe_layer.cc index f903966f..45266dc8 100644 --- a/demos/TicTacToe/Classes/TicTacToeLayer.cpp +++ b/demos/TicTacToe/Classes/tic_tac_toe_layer.cc @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "TicTacToeLayer.h" +#include "tic_tac_toe_layer.h" USING_NS_CC; diff --git a/demos/TicTacToe/Classes/TicTacToeLayer.h b/demos/TicTacToe/Classes/tic_tac_toe_layer.h similarity index 99% rename from demos/TicTacToe/Classes/TicTacToeLayer.h rename to demos/TicTacToe/Classes/tic_tac_toe_layer.h index 81581e3e..deb4624f 100644 --- a/demos/TicTacToe/Classes/TicTacToeLayer.h +++ b/demos/TicTacToe/Classes/tic_tac_toe_layer.h @@ -27,8 +27,8 @@ #include #include -#include "TicTacToeScene.h" #include "cocos2d.h" +#include "tic_tac_toe_scene.h" #include "util.h" using cocos2d::Director; diff --git a/demos/TicTacToe/Classes/TicTacToeScene.cpp b/demos/TicTacToe/Classes/tic_tac_toe_scene.cc similarity index 97% rename from demos/TicTacToe/Classes/TicTacToeScene.cpp rename to demos/TicTacToe/Classes/tic_tac_toe_scene.cc index 054b8abf..de3f907d 100644 --- a/demos/TicTacToe/Classes/TicTacToeScene.cpp +++ b/demos/TicTacToe/Classes/tic_tac_toe_scene.cc @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "TicTacToeScene.h" +#include "tic_tac_toe_scene.h" using cocos2d::Scene; diff --git a/demos/TicTacToe/Classes/TicTacToeScene.h b/demos/TicTacToe/Classes/tic_tac_toe_scene.h similarity index 95% rename from demos/TicTacToe/Classes/TicTacToeScene.h rename to demos/TicTacToe/Classes/tic_tac_toe_scene.h index 031e184b..3232b1ca 100644 --- a/demos/TicTacToe/Classes/TicTacToeScene.h +++ b/demos/TicTacToe/Classes/tic_tac_toe_scene.h @@ -15,9 +15,9 @@ #ifndef TICTACTOE_DEMO_CLASSES_TICTACTOE_SCENE_H_ #define TICTACTOE_DEMO_CLASSES_TICTACTOE_SCENE_H_ -#include "MainMenuScene.h" -#include "TicTacToeLayer.h" #include "cocos2d.h" +#include "main_menu_scene.h" +#include "tic_tac_toe_layer.h" #include "util.h" class TicTacToe : public cocos2d::Layer { diff --git a/demos/TicTacToe/Classes/util.cpp b/demos/TicTacToe/Classes/util.cc similarity index 97% rename from demos/TicTacToe/Classes/util.cpp rename to demos/TicTacToe/Classes/util.cc index 32838260..de43d63f 100644 --- a/demos/TicTacToe/Classes/util.cpp +++ b/demos/TicTacToe/Classes/util.cc @@ -14,6 +14,9 @@ #include "util.h" +#include +#include + #include // Logs the message passed in to the console. diff --git a/demos/TicTacToe/Classes/util.h b/demos/TicTacToe/Classes/util.h index 60ea2ccb..c43906ad 100644 --- a/demos/TicTacToe/Classes/util.h +++ b/demos/TicTacToe/Classes/util.h @@ -14,6 +14,8 @@ #ifndef TICTACTOE_DEMO_CLASSES_UTIL_H_ #define TICTACTOE_DEMO_CLASSES_UTIL_H_ +#include + #include "firebase/future.h" // inputs: const char* as the message that will be displayed. From cbf442d236a637bdad8c470a68a6f817e21af1ae Mon Sep 17 00:00:00 2001 From: Grant Postma Date: Tue, 7 Jul 2020 12:35:29 -0400 Subject: [PATCH 5/6] Refactor file names (#14) * Update for API changes * Change logging from verbose to warning Otherwise test results get lost in the crowd. * Add spaces around equals signs * Integrate Latest @ 314842997 CL: 314842997 * refactor file names * Adding util.cpp and util.h files (#54) * Auth full changes (#8) * Moving common functions into header files. Moving database & auth to MainMenuScene. Adding Auth node with fields and listeners. Set up user record management. Set up user sign-up, login and anonymous login. * auth full review by @DellaBitta * Private Class variables. Bug on disbanded room. * Moving common functions to util.h and util.cpp. Adding more comments on util functions. * Added missing period. * removing double new line. modifying includes in util.h and util.cpp * reverting cmake unnessecary changes. * deleteing util.cpp. * Removing old include header. Co-authored-by: Mark Grimes Co-authored-by: Morgan Chen Co-authored-by: Anthony Co-authored-by: Alex Ames --- admob/testapp/Podfile | 2 +- analytics/testapp/Podfile | 2 +- auth/testapp/Podfile | 2 +- database/testapp/CMakeLists.txt | 2 +- database/testapp/Podfile | 4 +- dynamic_links/testapp/Podfile | 2 +- firestore/testapp/Podfile | 6 +- firestore/testapp/src/common_main.cc | 114 ++++++++---------- firestore/testapp/src/desktop/desktop_main.cc | 2 +- functions/testapp/Podfile | 4 +- messaging/testapp/Podfile | 2 +- remote_config/testapp/Podfile | 2 +- storage/testapp/Podfile | 4 +- 13 files changed, 65 insertions(+), 83 deletions(-) diff --git a/admob/testapp/Podfile b/admob/testapp/Podfile index 724dcaab..269cc6be 100644 --- a/admob/testapp/Podfile +++ b/admob/testapp/Podfile @@ -2,5 +2,5 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # AdMob test application. target 'testapp' do - pod 'Firebase/AdMob', '6.17.0' + pod 'Firebase/AdMob', '6.24.0' end diff --git a/analytics/testapp/Podfile b/analytics/testapp/Podfile index da8d3b53..63db963c 100644 --- a/analytics/testapp/Podfile +++ b/analytics/testapp/Podfile @@ -2,5 +2,5 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # Analytics test application. target 'testapp' do - pod 'Firebase/Analytics', '6.17.0' + pod 'Firebase/Analytics', '6.24.0' end diff --git a/auth/testapp/Podfile b/auth/testapp/Podfile index 0f2f4278..a71424d3 100644 --- a/auth/testapp/Podfile +++ b/auth/testapp/Podfile @@ -2,5 +2,5 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # Auth test application. target 'testapp' do - pod 'Firebase/Auth', '6.17.0' + pod 'Firebase/Auth', '6.24.0' end diff --git a/database/testapp/CMakeLists.txt b/database/testapp/CMakeLists.txt index e2aa427b..d505b0c9 100644 --- a/database/testapp/CMakeLists.txt +++ b/database/testapp/CMakeLists.txt @@ -95,7 +95,7 @@ else() "-framework Security" ) elseif(MSVC) - set(ADDITIONAL_LIBS advapi32 ws2_32 crypt32 iphlpapi psapi userenv) + set(ADDITIONAL_LIBS advapi32 ws2_32 crypt32 iphlpapi psapi userenv shell32) else() set(ADDITIONAL_LIBS pthread) endif() diff --git a/database/testapp/Podfile b/database/testapp/Podfile index ca0a839e..3509b28c 100644 --- a/database/testapp/Podfile +++ b/database/testapp/Podfile @@ -2,6 +2,6 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # Firebase Realtime Database test application. target 'testapp' do - pod 'Firebase/Database', '6.17.0' - pod 'Firebase/Auth', '6.17.0' + pod 'Firebase/Database', '6.24.0' + pod 'Firebase/Auth', '6.24.0' end diff --git a/dynamic_links/testapp/Podfile b/dynamic_links/testapp/Podfile index 8d4eb2c1..be1c85ab 100644 --- a/dynamic_links/testapp/Podfile +++ b/dynamic_links/testapp/Podfile @@ -2,5 +2,5 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # Dynamic Links test application. target 'testapp' do - pod 'Firebase/DynamicLinks', '6.17.0' + pod 'Firebase/DynamicLinks', '6.24.0' end diff --git a/firestore/testapp/Podfile b/firestore/testapp/Podfile index 4ded7f10..eb725652 100644 --- a/firestore/testapp/Podfile +++ b/firestore/testapp/Podfile @@ -2,7 +2,7 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # Firebase Firestore test application. target 'testapp' do - pod 'Firebase/Analytics', '6.18.0' - pod 'Firebase/Firestore', '6.18.0' - pod 'Firebase/Auth', '6.18.0' + pod 'Firebase/Analytics', '6.24.0' + pod 'Firebase/Firestore', '6.24.0' + pod 'Firebase/Auth', '6.24.0' end diff --git a/firestore/testapp/src/common_main.cc b/firestore/testapp/src/common_main.cc index e13abe14..3fd6757e 100644 --- a/firestore/testapp/src/common_main.cc +++ b/firestore/testapp/src/common_main.cc @@ -30,9 +30,9 @@ const int kTimeoutMs = 5000; const int kSleepMs = 100; -// Wait for a Future to be completed. If the Future returns an error, it will -// be logged. -void Await(const firebase::FutureBase& future, const char* name) { +// Waits for a Future to be completed and returns whether the future has +// completed successfully. If the Future returns an error, it will be logged. +bool Await(const firebase::FutureBase& future, const char* name) { int remaining_timeout = kTimeoutMs; while (future.status() == firebase::kFutureStatusPending && remaining_timeout > 0) { @@ -42,10 +42,13 @@ void Await(const firebase::FutureBase& future, const char* name) { if (future.status() != firebase::kFutureStatusComplete) { LogMessage("ERROR: %s returned an invalid result.", name); + return false; } else if (future.error() != 0) { LogMessage("ERROR: %s returned error %d: %s", name, future.error(), future.error_message()); + return false; } + return true; } class Countable { @@ -65,7 +68,7 @@ class TestEventListener : public Countable, void OnEvent(const T& value, const firebase::firestore::Error error) override { event_count_++; - if (error != firebase::firestore::Ok) { + if (error != firebase::firestore::kOk) { LogMessage("ERROR: EventListener %s got %d.", name_.c_str(), error); } } @@ -124,11 +127,11 @@ extern "C" int common_main(int argc, const char* argv[]) { // Auth caches the previously signed-in user, which can be annoying when // trying to test for sign-in failures. auth->SignOut(); - auto future_login = auth->SignInAnonymously(); - Await(future_login, "Auth sign-in"); - auto* future_login_result = future_login.result(); - if (future_login_result && *future_login_result) { - const firebase::auth::User* user = *future_login_result; + auto login_future = auth->SignInAnonymously(); + Await(login_future, "Auth sign-in"); + auto* login_result = login_future.result(); + if (login_result && *login_result) { + const firebase::auth::User* user = *login_result; LogMessage("Signed in as %s user, uid: %s, email: %s.\n", user->is_anonymous() ? "an anonymous" : "a non-anonymous", user->uid().c_str(), user->email().c_str()); @@ -138,7 +141,7 @@ extern "C" int common_main(int argc, const char* argv[]) { // Note: Auth cannot be deleted while any of the futures issued by it are // still valid. - future_login.Release(); + login_future.Release(); LogMessage("Initialize Firebase Firestore."); @@ -170,7 +173,7 @@ extern "C" int common_main(int argc, const char* argv[]) { } LogMessage("Successfully initialized Firebase Firestore."); - firestore->set_logging_enabled(true); + firestore->set_log_level(firebase::kLogLevelDebug); if (firestore->app() != app) { LogMessage("ERROR: failed to get App the Firestore was created with."); @@ -219,28 +222,20 @@ extern "C" int common_main(int argc, const char* argv[]) { } LogMessage("Testing Set()."); - document.Set(firebase::firestore::MapFieldValue{ - {"str", firebase::firestore::FieldValue::FromString("foo")}, - {"int", firebase::firestore::FieldValue::FromInteger(123LL)}}); - Await(document.SetLastResult(), "document.Set"); - if (document.SetLastResult().status() != firebase::kFutureStatusComplete) { - LogMessage("ERROR: failed to write document."); - } + Await(document.Set(firebase::firestore::MapFieldValue{ + {"str", firebase::firestore::FieldValue::String("foo")}, + {"int", firebase::firestore::FieldValue::Integer(123)}}), + "document.Set"); LogMessage("Testing Update()."); - document.Update(firebase::firestore::MapFieldValue{ - {"int", firebase::firestore::FieldValue::FromInteger(321LL)}}); - Await(document.UpdateLastResult(), "document.Update"); - if (document.UpdateLastResult().status() != firebase::kFutureStatusComplete) { - LogMessage("ERROR: failed to write document."); - } + Await(document.Update(firebase::firestore::MapFieldValue{ + {"int", firebase::firestore::FieldValue::Integer(321)}}), + "document.Update"); LogMessage("Testing Get()."); - document.Get(); - Await(document.GetLastResult(), "document.Get"); - if (document.GetLastResult().status() == firebase::kFutureStatusComplete) { - const firebase::firestore::DocumentSnapshot* snapshot = - document.GetLastResult().result(); + auto doc_future = document.Get(); + if (Await(doc_future, "document.Get")) { + const firebase::firestore::DocumentSnapshot* snapshot = doc_future.result(); if (snapshot == nullptr) { LogMessage("ERROR: failed to read document."); } else { @@ -263,11 +258,7 @@ extern "C" int common_main(int argc, const char* argv[]) { } LogMessage("Testing Delete()."); - document.Delete(); - Await(document.DeleteLastResult(), "document.Delete"); - if (document.DeleteLastResult().status() != firebase::kFutureStatusComplete) { - LogMessage("ERROR: failed to delete document."); - } + Await(document.Delete(), "document.Delete"); LogMessage("Tested document operations."); TestEventListener @@ -282,50 +273,41 @@ extern "C" int common_main(int argc, const char* argv[]) { firebase::firestore::WriteBatch batch = firestore->batch(); batch.Set(collection.Document("one"), firebase::firestore::MapFieldValue{ - {"str", firebase::firestore::FieldValue::FromString("foo")}}); + {"str", firebase::firestore::FieldValue::String("foo")}}); batch.Set(collection.Document("two"), firebase::firestore::MapFieldValue{ - {"int", firebase::firestore::FieldValue::FromInteger(123LL)}}); - batch.Commit(); - Await(batch.CommitLastResult(), "batch.Commit"); - if (batch.CommitLastResult().status() != firebase::kFutureStatusComplete) { - LogMessage("ERROR: failed to write batch."); - } + {"int", firebase::firestore::FieldValue::Integer(123)}}); + Await(batch.Commit(), "batch.Commit"); LogMessage("Tested batch write."); LogMessage("Testing transaction."); - firestore->RunTransaction( - [collection](firebase::firestore::Transaction* transaction, - std::string* error_message) -> firebase::firestore::Error { - transaction->Update( - collection.Document("one"), - firebase::firestore::MapFieldValue{ - {"int", firebase::firestore::FieldValue::FromInteger(123LL)}}); - transaction->Delete(collection.Document("two")); - transaction->Set( - collection.Document("three"), - firebase::firestore::MapFieldValue{ - {"int", firebase::firestore::FieldValue::FromInteger(321LL)}}); - return firebase::firestore::Ok; - }); - Await(firestore->RunTransactionLastResult(), "firestore.RunTransaction"); - if (firestore->RunTransactionLastResult().status() != - firebase::kFutureStatusComplete) { - LogMessage("ERROR: failed to run transaction."); - } + Await( + firestore->RunTransaction( + [collection](firebase::firestore::Transaction& transaction, + std::string&) -> firebase::firestore::Error { + transaction.Update( + collection.Document("one"), + firebase::firestore::MapFieldValue{ + {"int", firebase::firestore::FieldValue::Integer(123)}}); + transaction.Delete(collection.Document("two")); + transaction.Set( + collection.Document("three"), + firebase::firestore::MapFieldValue{ + {"int", firebase::firestore::FieldValue::Integer(321)}}); + return firebase::firestore::kOk; + }), + "firestore.RunTransaction"); LogMessage("Tested transaction."); LogMessage("Testing query."); firebase::firestore::Query query = collection .WhereGreaterThan("int", - firebase::firestore::FieldValue::FromBoolean(true)) + firebase::firestore::FieldValue::Boolean(true)) .Limit(3); - query.Get(); - Await(query.GetLastResult(), "query.Get"); - if (query.GetLastResult().status() == firebase::kFutureStatusComplete) { - const firebase::firestore::QuerySnapshot* snapshot = - query.GetLastResult().result(); + auto query_future = query.Get(); + if (Await(query_future, "query.Get")) { + const firebase::firestore::QuerySnapshot* snapshot = query_future.result(); if (snapshot == nullptr) { LogMessage("ERROR: failed to fetch query result."); } else { diff --git a/firestore/testapp/src/desktop/desktop_main.cc b/firestore/testapp/src/desktop/desktop_main.cc index 74f03896..3a027f82 100644 --- a/firestore/testapp/src/desktop/desktop_main.cc +++ b/firestore/testapp/src/desktop/desktop_main.cc @@ -118,6 +118,6 @@ int64_t WinGetCurrentTimeInMicroseconds() { // Windows file time is expressed in 100s of nanoseconds. // To convert to microseconds, multiply x10. - return now.QuadPart * 10LL; + return now.QuadPart * 10; } #endif diff --git a/functions/testapp/Podfile b/functions/testapp/Podfile index 5c0c906a..eb5c0fc8 100644 --- a/functions/testapp/Podfile +++ b/functions/testapp/Podfile @@ -2,6 +2,6 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # Cloud Functions for Firebase test application. target 'testapp' do - pod 'Firebase/Functions', '6.17.0' - pod 'Firebase/Auth', '6.17.0' + pod 'Firebase/Functions', '6.24.0' + pod 'Firebase/Auth', '6.24.0' end diff --git a/messaging/testapp/Podfile b/messaging/testapp/Podfile index 425161a9..b0f6333b 100644 --- a/messaging/testapp/Podfile +++ b/messaging/testapp/Podfile @@ -2,5 +2,5 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # FCM test application. target 'testapp' do - pod 'Firebase/Messaging', '6.17.0' + pod 'Firebase/Messaging', '6.24.0' end diff --git a/remote_config/testapp/Podfile b/remote_config/testapp/Podfile index 14afba59..d44ec81f 100644 --- a/remote_config/testapp/Podfile +++ b/remote_config/testapp/Podfile @@ -2,5 +2,5 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # Firebase Remote Config test application. target 'testapp' do - pod 'Firebase/RemoteConfig', '6.17.0' + pod 'Firebase/RemoteConfig', '6.24.0' end diff --git a/storage/testapp/Podfile b/storage/testapp/Podfile index d7be90c8..5dca5c0b 100644 --- a/storage/testapp/Podfile +++ b/storage/testapp/Podfile @@ -2,6 +2,6 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # Cloud Storage for Firebase test application. target 'testapp' do - pod 'Firebase/Storage', '6.17.0' - pod 'Firebase/Auth', '6.17.0' + pod 'Firebase/Storage', '6.24.0' + pod 'Firebase/Auth', '6.24.0' end From eb5378b4350c3225b0ca75c34a6050e33c1011c8 Mon Sep 17 00:00:00 2001 From: Grant-Postma Date: Tue, 7 Jul 2020 17:54:03 -0400 Subject: [PATCH 6/6] Removing unnecessary headers. Duplicate libaray statements replaced with using statement. --- demos/TicTacToe/Classes/app_delegate.cc | 1 - demos/TicTacToe/Classes/app_delegate.h | 1 + demos/TicTacToe/Classes/main_menu_scene.cc | 80 +++++++++------- demos/TicTacToe/Classes/main_menu_scene.h | 31 ++++--- demos/TicTacToe/Classes/tic_tac_toe_layer.cc | 97 ++++++++++++-------- demos/TicTacToe/Classes/tic_tac_toe_layer.h | 45 ++++----- demos/TicTacToe/Classes/tic_tac_toe_scene.cc | 6 ++ demos/TicTacToe/Classes/tic_tac_toe_scene.h | 4 +- 8 files changed, 151 insertions(+), 114 deletions(-) diff --git a/demos/TicTacToe/Classes/app_delegate.cc b/demos/TicTacToe/Classes/app_delegate.cc index 8849dcc5..15ea2a52 100644 --- a/demos/TicTacToe/Classes/app_delegate.cc +++ b/demos/TicTacToe/Classes/app_delegate.cc @@ -15,7 +15,6 @@ #include "app_delegate.h" #include "main_menu_scene.h" -#include "tic_tac_toe_scene.h" USING_NS_CC; diff --git a/demos/TicTacToe/Classes/app_delegate.h b/demos/TicTacToe/Classes/app_delegate.h index 75e7b832..ca4887a2 100644 --- a/demos/TicTacToe/Classes/app_delegate.h +++ b/demos/TicTacToe/Classes/app_delegate.h @@ -14,6 +14,7 @@ #ifndef TICTACTOE_DEMO_CLASSES_APPDELEGATE_SCENE_H_ #define TICTACTOE_DEMO_CLASSES_APPDELEGATE_SCENE_H_ + #include "cocos2d.h" class AppDelegate : private cocos2d::Application { diff --git a/demos/TicTacToe/Classes/main_menu_scene.cc b/demos/TicTacToe/Classes/main_menu_scene.cc index 3ad84af9..65cbe8d4 100644 --- a/demos/TicTacToe/Classes/main_menu_scene.cc +++ b/demos/TicTacToe/Classes/main_menu_scene.cc @@ -15,12 +15,33 @@ #include "main_menu_scene.h" #include +#include #include "cocos2d.h" +#include "firebase/auth.h" +#include "firebase/database.h" +#include "firebase/future.h" #include "firebase/util.h" #include "tic_tac_toe_scene.h" #include "util.h" +using cocos2d::Event; +using cocos2d::Label; +using cocos2d::Scene; +using cocos2d::Size; +using cocos2d::Sprite; +using cocos2d::TextFieldTTF; +using cocos2d::Touch; +using cocos2d::Vec2; +using firebase::App; +using firebase::InitResult; +using firebase::kFutureStatusComplete; +using firebase::ModuleInitializer; +using firebase::auth::Auth; +using firebase::auth::kAuthErrorNone; +using firebase::database::Database; +using std::to_string; + static const char* kCreateGameImage = "create_game.png"; static const char* kTextFieldBorderImage = "text_field_border.png"; static const char* kJoinButtonImage = "join_game.png"; @@ -106,8 +127,8 @@ bool MainMenuScene::init() { auto anonymous_label_touch_listener = EventListenerTouchOneByOne::create(); - anonymous_label_touch_listener->onTouchBegan = - [this](cocos2d::Touch* touch, cocos2d::Event* event) -> bool { + anonymous_label_touch_listener->onTouchBegan = [this](Touch* touch, + Event* event) -> bool { // Returns false, not consuming the event, to exit the layer if // current_state_ is not in the kAuthState or is switching states. if (previous_state_ != current_state_ || current_state_ != kAuthState) { @@ -156,7 +177,7 @@ bool MainMenuScene::init() { Color4F(0, 0, 0, 0), 1, Color4F::WHITE); // Create a text field to enter the user's email. - email_text_field_ = cocos2d::TextFieldTTF::textFieldWithPlaceHolder( + email_text_field_ = TextFieldTTF::textFieldWithPlaceHolder( "enter an email address", email_text_field_size, TextHAlignment::LEFT, "Arial", email_font_size); email_text_field_->setPosition(email_text_field_position); @@ -166,8 +187,8 @@ bool MainMenuScene::init() { auto email_text_field_touch_listener = EventListenerTouchOneByOne::create(); - email_text_field_touch_listener->onTouchBegan = - [this](cocos2d::Touch* touch, cocos2d::Event* event) -> bool { + email_text_field_touch_listener->onTouchBegan = [this](Touch* touch, + Event* event) -> bool { // Returns false, not consuming the event, to exit the layer if // current_state_ is not in the kAuthState or is switching states. if (previous_state_ != current_state_ || current_state_ != kAuthState) { @@ -225,7 +246,7 @@ bool MainMenuScene::init() { password_border_corners, 4, Color4F(0, 0, 0, 0), 1, Color4F::WHITE); // Create a text field to enter the user's password. - password_text_field_ = cocos2d::TextFieldTTF::textFieldWithPlaceHolder( + password_text_field_ = TextFieldTTF::textFieldWithPlaceHolder( "enter a password", password_text_field_size, TextHAlignment::LEFT, "Arial", password_font_size); password_text_field_->setPosition(password_text_field_position); @@ -238,7 +259,7 @@ bool MainMenuScene::init() { EventListenerTouchOneByOne::create(); password_text_field_touch_listener->onTouchBegan = - [this](cocos2d::Touch* touch, cocos2d::Event* event) -> bool { + [this](Touch* touch, Event* event) -> bool { // Returns false, not consuming the event, to exit the layer if // current_state_ is not in the kAuthState or is switching states. if (previous_state_ != current_state_ || current_state_ != kAuthState) { @@ -362,9 +383,8 @@ bool MainMenuScene::init() { // Create, set the position and assign a placeholder to the text // field for the user to enter the join game uuid. - TextFieldTTF* join_text_field = - cocos2d::TextFieldTTF::textFieldWithPlaceHolder( - "code", cocos2d::Size(200, 100), TextHAlignment::LEFT, "Arial", 55.0); + TextFieldTTF* join_text_field = TextFieldTTF::textFieldWithPlaceHolder( + "code", Size(200, 100), TextHAlignment::LEFT, "Arial", 55.0); join_text_field->setPosition(420, 45); join_text_field->setAnchorPoint(Vec2(0, 0)); join_text_field->setColorSpaceHolder(Color3B::WHITE); @@ -380,8 +400,7 @@ bool MainMenuScene::init() { auto join_text_field_touch_listener = EventListenerTouchOneByOne::create(); join_text_field_touch_listener->onTouchBegan = - [join_text_field, this](cocos2d::Touch* touch, - cocos2d::Event* event) -> bool { + [join_text_field, this](Touch* touch, Event* event) -> bool { // Returns false, not consuming the event, to exit the layer if // current_state_ is not in the kGameMenuState or is switching states. if (previous_state_ != current_state_ || current_state_ != kGameMenuState) { @@ -550,12 +569,12 @@ bool MainMenuScene::init() { // are missing. void MainMenuScene::InitializeFirebase() { LogMessage("Initialize Firebase App."); - ::firebase::App* app; + App* app; #if defined(_ANDROID_) - app = ::firebase::App::Create(GetJniEnv(), GetActivity()); + app = App::Create(GetJniEnv(), GetActivity()); #else - app = ::firebase::App::Create(); + app = App::Create(); #endif // defined(ANDROID) LogMessage("Initialize Firebase Auth and Firebase Database."); @@ -566,25 +585,24 @@ void MainMenuScene::InitializeFirebase() { auth_ = nullptr; void* initialize_targets[] = {&auth_, &database_}; - const firebase::ModuleInitializer::InitializerFn initializers[] = { - [](::firebase::App* app, void* data) { + const ModuleInitializer::InitializerFn initializers[] = { + [](::App* app, void* data) { LogMessage("Attempt to initialize Firebase Auth."); void** targets = reinterpret_cast(data); - ::firebase::InitResult result; - *reinterpret_cast<::firebase::auth::Auth**>(targets[0]) = - ::firebase::auth::Auth::GetAuth(app, &result); + InitResult result; + *reinterpret_cast<::Auth**>(targets[0]) = Auth::GetAuth(app, &result); return result; }, - [](::firebase::App* app, void* data) { + [](::App* app, void* data) { LogMessage("Attempt to initialize Firebase Database."); void** targets = reinterpret_cast(data); - ::firebase::InitResult result; - *reinterpret_cast<::firebase::database::Database**>(targets[1]) = - ::firebase::database::Database::GetInstance(app, &result); + InitResult result; + *reinterpret_cast<::Database**>(targets[1]) = + Database::GetInstance(app, &result); return result; }}; - ::firebase::ModuleInitializer initializer; + ModuleInitializer initializer; initializer.Initialize(app, initialize_targets, initializers, sizeof(initializers) / sizeof(initializers[0])); @@ -659,8 +677,8 @@ void MainMenuScene::onEnter() { void MainMenuScene::update(float /*delta*/) { if (current_state_ != previous_state_) { if (current_state_ == kWaitingAnonymousState) { - if (user_result_.status() == firebase::kFutureStatusComplete) { - if (user_result_.error() == firebase::auth::kAuthErrorNone) { + if (user_result_.status() == kFutureStatusComplete) { + if (user_result_.error() == kAuthErrorNone) { user_ = *user_result_.result(); user_uid_ = GenerateUid(10); @@ -670,8 +688,8 @@ void MainMenuScene::update(float /*delta*/) { } } } else if (current_state_ == kWaitingSignUpState) { - if (user_result_.status() == firebase::kFutureStatusComplete) { - if (user_result_.error() == firebase::auth::kAuthErrorNone) { + if (user_result_.status() == kFutureStatusComplete) { + if (user_result_.error() == kAuthErrorNone) { user_ = *user_result_.result(); user_uid_ = user_->uid(); @@ -686,8 +704,8 @@ void MainMenuScene::update(float /*delta*/) { } } } else if (current_state_ == kWaitingLoginState) { - if (user_result_.status() == firebase::kFutureStatusComplete) { - if (user_result_.error() == firebase::auth::kAuthErrorNone) { + if (user_result_.status() == kFutureStatusComplete) { + if (user_result_.error() == kAuthErrorNone) { user_ = *user_result_.result(); user_uid_ = user_->uid(); diff --git a/demos/TicTacToe/Classes/main_menu_scene.h b/demos/TicTacToe/Classes/main_menu_scene.h index f5181339..8f8c9914 100644 --- a/demos/TicTacToe/Classes/main_menu_scene.h +++ b/demos/TicTacToe/Classes/main_menu_scene.h @@ -15,14 +15,21 @@ #ifndef TICTACTOE_DEMO_CLASSES_MAINMENU_SCENE_H_ #define TICTACTOE_DEMO_CLASSES_MAINMENU_SCENE_H_ -#include +#include #include "cocos2d.h" #include "firebase/auth.h" #include "firebase/database.h" #include "firebase/future.h" -using std::to_string; +using cocos2d::Label; +using cocos2d::TextFieldTTF; +using firebase::Future; +using firebase::auth::Auth; +using firebase::auth::User; +using firebase::database::Database; +using firebase::database::DatabaseReference; +using std::string; class MainMenuScene : public cocos2d::Layer, public cocos2d::TextFieldDelegate { public: @@ -69,10 +76,10 @@ class MainMenuScene : public cocos2d::Layer, public cocos2d::TextFieldDelegate { cocos2d::DrawNode* auth_background_; // Labels and textfields for the authentication menu. - cocos2d::Label* invalid_login_label_; - cocos2d::Label* user_record_label_; - cocos2d::TextFieldTTF* email_text_field_; - cocos2d::TextFieldTTF* password_text_field_; + Label* invalid_login_label_; + Label* user_record_label_; + TextFieldTTF* email_text_field_; + TextFieldTTF* password_text_field_; // Variable to track the current state and previous state to check against to // see if the state changed. @@ -84,12 +91,12 @@ class MainMenuScene : public cocos2d::Layer, public cocos2d::TextFieldDelegate { int user_loses_; int user_ties_; - std::string user_uid_; - firebase::auth::User* user_; - firebase::Future user_result_; - firebase::database::Database* database_; - firebase::auth::Auth* auth_; - firebase::database::DatabaseReference ref_; + string user_uid_; + Auth* auth_; + User* user_; + Future user_result_; + Database* database_; + DatabaseReference ref_; }; #endif // TICTACTOE_DEMO_CLASSES_MAINMENU_SCENE_H_ diff --git a/demos/TicTacToe/Classes/tic_tac_toe_layer.cc b/demos/TicTacToe/Classes/tic_tac_toe_layer.cc index 45266dc8..b3f6410a 100644 --- a/demos/TicTacToe/Classes/tic_tac_toe_layer.cc +++ b/demos/TicTacToe/Classes/tic_tac_toe_layer.cc @@ -14,6 +14,32 @@ #include "tic_tac_toe_layer.h" +#include +#include +#include +#include +#include + +#include "cocos2d.h" +#include "firebase/database.h" +#include "firebase/variant.h" +#include "util.h" + +using cocos2d::Director; +using cocos2d::Event; +using cocos2d::Label; +using cocos2d::Sprite; +using cocos2d::Touch; +using firebase::Variant; +using firebase::database::DataSnapshot; +using firebase::database::Error; +using firebase::database::TransactionResult; +using firebase::database::ValueListener; +using std::array; +using std::make_unique; +using std::string; +using std::vector; + USING_NS_CC; // Player constants. @@ -34,8 +60,8 @@ static const enum kGameOutcome { // database outcome key. static const const char* kGameOutcomeStrings[] = {"wins", "loses", "ties", "disbanded"}; -static const std::array kGameOverStrings = { - "you won!", "you lost.", "you tied.", "user left."}; +static const array kGameOverStrings = {"you won!", "you lost.", + "you tied.", "user left."}; // Game board dimensions. extern const int kTilesX; @@ -54,62 +80,60 @@ static const int kEndGameFramesMax = 120; // Image file paths. static const char* kBoardImageFileName = "tic_tac_toe_board.png"; static const char* kLeaveButtonFileName = "leave_button.png"; -static std::array kPlayerTokenFileNames = { +static array kPlayerTokenFileNames = { "tic_tac_toe_x.png", "tic_tac_toe_o.png"}; // An example of a ValueListener object. This specific version will // simply log every value it sees, and store them in a list so we can // confirm that all values were received. -class SampleValueListener : public firebase::database::ValueListener { +class SampleValueListener : public ValueListener { public: - void OnValueChanged( - const firebase::database::DataSnapshot& snapshot) override { + void OnValueChanged(const DataSnapshot& snapshot) override { LogMessage(" ValueListener.OnValueChanged(%s)", snapshot.value().AsString().string_value()); last_seen_value_ = snapshot.value(); seen_values_.push_back(snapshot.value()); } - void OnCancelled(const firebase::database::Error& error_code, + void OnCancelled(const Error& error_code, const char* error_message) override { LogMessage("ERROR: SampleValueListener canceled: %d: %s", error_code, error_message); } - const firebase::Variant& last_seen_value() { return last_seen_value_; } - bool seen_value(const firebase::Variant& value) { + const Variant& last_seen_value() { return last_seen_value_; } + bool seen_value(const Variant& value) { return std::find(seen_values_.begin(), seen_values_.end(), value) != seen_values_.end(); } size_t num_seen_values() { return seen_values_.size(); } private: - firebase::Variant last_seen_value_; - std::vector seen_values_; + Variant last_seen_value_; + vector seen_values_; }; // An example ChildListener class. class SampleChildListener : public firebase::database::ChildListener { public: - void OnChildAdded(const firebase::database::DataSnapshot& snapshot, + void OnChildAdded(const DataSnapshot& snapshot, const char* previous_sibling) override { LogMessage(" ChildListener.OnChildAdded(%s)", snapshot.key()); - events_.push_back(std::string("added ") + snapshot.key()); + events_.push_back(string("added ") + snapshot.key()); } - void OnChildChanged(const firebase::database::DataSnapshot& snapshot, + void OnChildChanged(const DataSnapshot& snapshot, const char* previous_sibling) override { LogMessage(" ChildListener.OnChildChanged(%s)", snapshot.key()); - events_.push_back(std::string("changed ") + snapshot.key()); + events_.push_back(string("changed ") + snapshot.key()); } - void OnChildMoved(const firebase::database::DataSnapshot& snapshot, + void OnChildMoved(const DataSnapshot& snapshot, const char* previous_sibling) override { LogMessage(" ChildListener.OnChildMoved(%s)", snapshot.key()); - events_.push_back(std::string("moved ") + snapshot.key()); + events_.push_back(string("moved ") + snapshot.key()); } - void OnChildRemoved( - const firebase::database::DataSnapshot& snapshot) override { + void OnChildRemoved(const DataSnapshot& snapshot) override { LogMessage(" ChildListener.OnChildRemoved(%s)", snapshot.key()); - events_.push_back(std::string("removed ") + snapshot.key()); + events_.push_back(string("removed ") + snapshot.key()); } - void OnCancelled(const firebase::database::Error& error_code, + void OnCancelled(const Error& error_code, const char* error_message) override { LogMessage("ERROR: SampleChildListener canceled: %d: %s", error_code, error_message); @@ -119,7 +143,7 @@ class SampleChildListener : public firebase::database::ChildListener { size_t total_events() { return events_.size(); } // Get the number of times this event was seen. - int num_events(const std::string& event) { + int num_events(const string& event) { int count = 0; for (int i = 0; i < events_.size(); i++) { if (events_[i] == event) { @@ -132,16 +156,15 @@ class SampleChildListener : public firebase::database::ChildListener { public: // Vector of strings that contains the events in the order in which they // occurred. - std::vector events_; + vector events_; }; // A ValueListener that expects a specific value to be set. -class ExpectValueListener : public firebase::database::ValueListener { +class ExpectValueListener : public ValueListener { public: - explicit ExpectValueListener(firebase::Variant wait_value) + explicit ExpectValueListener(Variant wait_value) : wait_value_(wait_value.AsString()), got_value_(false) {} - void OnValueChanged( - const firebase::database::DataSnapshot& snapshot) override { + void OnValueChanged(const DataSnapshot& snapshot) override { if (snapshot.value().AsString() == wait_value_) { got_value_ = true; } else { @@ -149,7 +172,7 @@ class ExpectValueListener : public firebase::database::ValueListener { "FAILURE: ExpectValueListener did not receive the expected result."); } } - void OnCancelled(const firebase::database::Error& error_code, + void OnCancelled(const Error& error_code, const char* error_message) override { LogMessage("ERROR: ExpectValueListener canceled: %d: %s", error_code, error_message); @@ -158,7 +181,7 @@ class ExpectValueListener : public firebase::database::ValueListener { bool got_value() { return got_value_; } private: - firebase::Variant wait_value_; + Variant wait_value_; bool got_value_; }; @@ -219,14 +242,13 @@ TicTacToeLayer::TicTacToeLayer(string game_uuid, if (join_game_uuid_.empty()) { join_game_uuid_ = GenerateUid(4); ref_ = database_->GetReference("game_data").Child(join_game_uuid_); - firebase::Future future_create_game = - ref_.Child("total_players").SetValue(1); + future_create_game_ = ref_.Child("total_players").SetValue(1); future_current_player_index_ = ref_.Child("current_player_index_").SetValue(kPlayerOne); future_game_over_ = ref_.Child("game_over").SetValue(false); WaitForCompletion(future_game_over_, "setGameOver"); WaitForCompletion(future_current_player_index_, "setCurrentPlayerIndex"); - WaitForCompletion(future_create_game, "createGame"); + WaitForCompletion(future_create_game_, "createGame"); player_index_ = kPlayerOne; awaiting_opponenet_move_ = false; } else { @@ -241,7 +263,7 @@ TicTacToeLayer::TicTacToeLayer(string game_uuid, } else { ref_ = database_->GetReference("game_data").Child(join_game_uuid_); auto future_increment_total_users = - ref_.RunTransaction([](MutableData* data) { + ref_.RunTransaction([](firebase::database::MutableData* data) { auto total_players = data->Child("total_players").value(); // Complete the transaction based on the returned mutable data @@ -340,12 +362,11 @@ TicTacToeLayer::TicTacToeLayer(string game_uuid, // total_player_listener_ and CurrentPlayerIndexListener listener is set up // to recognise when the desired players have connected & when turns // alternate. - total_player_listener_ = - std::make_unique(kNumberOfPlayers); - game_over_listener_ = std::make_unique(true); + total_player_listener_ = make_unique(kNumberOfPlayers); + game_over_listener_ = make_unique(true); - current_player_index_listener_ = std::make_unique(); - last_move_listener_ = std::make_unique(); + current_player_index_listener_ = make_unique(); + last_move_listener_ = make_unique(); ref_.Child("total_players").AddValueListener(total_player_listener_.get()); ref_.Child("game_over").AddValueListener(game_over_listener_.get()); ref_.Child("current_player_index_") diff --git a/demos/TicTacToe/Classes/tic_tac_toe_layer.h b/demos/TicTacToe/Classes/tic_tac_toe_layer.h index deb4624f..9e535908 100644 --- a/demos/TicTacToe/Classes/tic_tac_toe_layer.h +++ b/demos/TicTacToe/Classes/tic_tac_toe_layer.h @@ -15,34 +15,20 @@ #ifndef TICTACTOE_DEMO_CLASSES_TICTACTOELAYER_SCENE_H_ #define TICTACTOE_DEMO_CLASSES_TICTACTOELAYER_SCENE_H_ -#include -#include -#include -#include -#include -#include -#include -#include -#include #include #include #include "cocos2d.h" -#include "tic_tac_toe_scene.h" -#include "util.h" +#include "firebase/database.h" +#include "firebase/future.h" -using cocos2d::Director; -using cocos2d::Event; +using cocos2d::Label; using cocos2d::Layer; -using cocos2d::LayerColor; -using cocos2d::Point; using cocos2d::Sprite; -using cocos2d::Touch; using firebase::Future; -using firebase::database::DataSnapshot; -using firebase::database::MutableData; -using firebase::database::TransactionResult; +using firebase::database::Database; using std::string; +using std::unique_ptr; // Tile Constants. static const int kTilesX = 3; @@ -56,7 +42,7 @@ class TicTacToeLayer : public Layer { public: // Derived from Layer class with input paramters for the game_uid, database // and user_uid and overrides Layer::update(). - TicTacToeLayer(std::string, firebase::database::Database*, std::string); + TicTacToeLayer(string, Database*, string); ~TicTacToeLayer(); private: @@ -73,34 +59,35 @@ class TicTacToeLayer : public Layer { // String for the join game code and initialize the database // reference. - std::string join_game_uuid_; + string join_game_uuid_; // User uid to update the user's record after the game is over. - std::string user_uid_; + string user_uid_; // Firebase Realtime Database, the entry point to all database operations. - firebase::database::Database* database_; + Database* database_; firebase::database::DatabaseReference ref_; // The database schema has a top level game_uuid object which includes // last_move, total_players and current_player_index_ fields. // Listeners for database values. - std::unique_ptr current_player_index_listener_; - std::unique_ptr last_move_listener_; - std::unique_ptr total_player_listener_; - std::unique_ptr game_over_listener_; + unique_ptr current_player_index_listener_; + unique_ptr last_move_listener_; + unique_ptr total_player_listener_; + unique_ptr game_over_listener_; // Lables and a sprites. Sprite* board_sprite_; Sprite* leave_button_sprite_; - cocos2d::Label* game_over_label_; - cocos2d::Label* waiting_label_; + Label* game_over_label_; + Label* waiting_label_; // Firebase futures for last_move and current_player_index_. Future future_last_move_; Future future_current_player_index_; Future future_game_over_; + Future future_create_game_; int current_player_index_; int player_index_; diff --git a/demos/TicTacToe/Classes/tic_tac_toe_scene.cc b/demos/TicTacToe/Classes/tic_tac_toe_scene.cc index de3f907d..2e63d7d3 100644 --- a/demos/TicTacToe/Classes/tic_tac_toe_scene.cc +++ b/demos/TicTacToe/Classes/tic_tac_toe_scene.cc @@ -14,6 +14,12 @@ #include "tic_tac_toe_scene.h" +#include + +#include "cocos2d.h" +#include "firebase/database.h" +#include "tic_tac_toe_layer.h" + using cocos2d::Scene; Scene* TicTacToe::createScene(const std::string& game_uuid, diff --git a/demos/TicTacToe/Classes/tic_tac_toe_scene.h b/demos/TicTacToe/Classes/tic_tac_toe_scene.h index 3232b1ca..f8f3073a 100644 --- a/demos/TicTacToe/Classes/tic_tac_toe_scene.h +++ b/demos/TicTacToe/Classes/tic_tac_toe_scene.h @@ -16,9 +16,7 @@ #define TICTACTOE_DEMO_CLASSES_TICTACTOE_SCENE_H_ #include "cocos2d.h" -#include "main_menu_scene.h" -#include "tic_tac_toe_layer.h" -#include "util.h" +#include "firebase/database.h" class TicTacToe : public cocos2d::Layer { public: