From d94737b4fbb477830631b6f5f4ad308193c97667 Mon Sep 17 00:00:00 2001 From: Grant-Postma Date: Wed, 24 Jun 2020 02:16:33 -0400 Subject: [PATCH 1/5] 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. --- demos/TicTacToe/Classes/MainMenuScene.cpp | 615 +++++++++++++++++++-- demos/TicTacToe/Classes/MainMenuScene.h | 42 ++ demos/TicTacToe/Classes/TicTacToeLayer.cpp | 171 +----- demos/TicTacToe/Classes/TicTacToeLayer.h | 26 +- demos/TicTacToe/Classes/TicTacToeScene.cpp | 10 +- demos/TicTacToe/Classes/TicTacToeScene.h | 6 +- demos/TicTacToe/Resources/logout.png | Bin 0 -> 8006 bytes 7 files changed, 678 insertions(+), 192 deletions(-) create mode 100644 demos/TicTacToe/Resources/logout.png diff --git a/demos/TicTacToe/Classes/MainMenuScene.cpp b/demos/TicTacToe/Classes/MainMenuScene.cpp index 297f3471..8e4411f0 100644 --- a/demos/TicTacToe/Classes/MainMenuScene.cpp +++ b/demos/TicTacToe/Classes/MainMenuScene.cpp @@ -1,13 +1,67 @@ #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"; +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); +} + +bool ProcessEvents(int msec) { +#ifdef _WIN32 + Sleep(msec); +#else + usleep(msec * 1000); +#endif // _WIN32 + return false; +} + +// 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 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 GenerateUid; + + for (std::size_t i = 0; i < length; ++i) { + GenerateUid += kCharacters[distribution(generator)]; + } + + return GenerateUid; +} + 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,33 +76,345 @@ 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)); + ::firebase::App* app; + +#if defined(__ANDROID__) + app = ::firebase::App::Create(GetJniEnv(), GetActivity()); +#else + app = ::firebase::App::Create(); +#endif // defined(ANDROID) + + LogMessage("Initialized Firebase App."); + + 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); + return 1; + } + LogMessage("Successfully initialized Firebase Auth and Firebase Database."); + + database->set_persistence_enabled(true); + // Creating the background to add all of the authentication elements on. The + // visiblity of this node should match kAuthState, disabling any + // touch_listeners when not in this state. + auth_background = DrawNode::create(); + auto auth_background_border = DrawNode::create(); + + auto auth_background_size = Size(500, 550); + 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); + + // Labeling 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 { + if (previous_state != current_state || current_state != kAuthState) + return true; + auto bounds = event->getCurrentTarget()->getBoundingBox(); + auto point = touch->getLocation(); + if (bounds.containsPoint(point)) { + // Using anonymous sign in for the user. + user_result = auth->SignInAnonymously(); + current_state = kWaitingAnonymousState; + } + + return true; + }; + // Attaching the touch listener to the text field. + Director::getInstance() + ->getEventDispatcher() + ->addEventListenerWithSceneGraphPriority(anonymous_label_touch_listener, + anonymous_login_label); + auth_background->addChild(anonymous_login_label); + + // Extracting the origin, size and position of the text field so that the + // border can be created based on those values. + auto email_text_field_origin = Vec2(0, 0); + auto email_text_field_position = Size(110, 350); + auto text_field_padding = 20; + auto email_font_size = 36.0; + auto email_text_field_size = Size(400, 2 * email_font_size); + + // Setting 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)}; + + // Creating a text field border and adding 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); + // Creating 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 { + if (previous_state != current_state || current_state != kAuthState) + return true; + auto bounds = event->getCurrentTarget()->getBoundingBox(); + auto point = touch->getLocation(); + if (bounds.containsPoint(point)) { + // Show the on screen keyboard and places character inputs into the text + // field. + auto str = email_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(); + } + + return true; + }; + + // Attaching 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); + // Extracting the origin, size and position of the text field so that the + // border can be created based on those values. + auto password_text_field_origin = Vec2(0, 0); + auto password_text_field_position = Size(110, 250); + auto password_font_size = 36.0; + auto password_text_field_size = Size(400, 2 * password_font_size); + + // Setting 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)}; + + // Creating a text field border and adding 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); + // Creating a text field to enter the user's password. + password_text_field = cocos2d::TextFieldTTF::textFieldWithPlaceHolder( + "enter an 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 { + if (previous_state != current_state || current_state != kAuthState) { + return true; + } + auto bounds = event->getCurrentTarget()->getBoundingBox(); + auto point = touch->getLocation(); + if (bounds.containsPoint(point)) { + // Show the on screen keyboard and places character inputs into the text + // field. + auto str = password_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(); + } + + return true; + }; + + auth_background->addChild(password_text_field, 1); + auth_background->addChild(password_text_field_border, 1); + + // Attaching the touch listener to the text field. + Director::getInstance() + ->getEventDispatcher() + ->addEventListenerWithSceneGraphPriority( + password_text_field_touch_listener, password_text_field); + // Creating the login button and giving 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 login_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 login_text_field + // string. + login_button_touch_listener->onTouchBegan = [this](Touch* touch, + Event* event) -> bool { + if (previous_state != current_state || current_state != kAuthState) { + return true; + } 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. if (bounds.containsPoint(point)) { - Director::getInstance()->replaceScene( - TicTacToe::createScene(std::string())); + 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; + }; + // Attaching the touch listener to the login button. + Director::getInstance() + ->getEventDispatcher() + ->addEventListenerWithSceneGraphPriority(login_button_touch_listener, + login_button); + auth_background->addChild(login_button, 1); + // Creating the sign_up button and giving 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(); + // Setting the onTouchBegan event up to a lambda tha will replace the + // MainMenu scene with a TicTacToe scene and pass in sign_up_text_field + // string. + sign_up_button_touch_listener->onTouchBegan = [this](Touch* touch, + Event* event) -> bool { + if (previous_state != current_state || current_state != kAuthState) + return true; + auto bounds = event->getCurrentTarget()->getBoundingBox(); + auto point = touch->getLocation(); + if (bounds.containsPoint(point)) { + 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. + // Attaching the touch listener to the sign_up button. Director::getInstance() ->getEventDispatcher() - ->addEventListenerWithSceneGraphPriority(create_button_touch_listener, - create_button); + ->addEventListenerWithSceneGraphPriority(sign_up_button_touch_listener, + sign_up_button); + auth_background->addChild(sign_up_button, 1); + // Creating, setting the position and assigning a placeholder to the text // field for entering the join game uuid. TextFieldTTF* join_text_field = @@ -59,17 +425,20 @@ 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 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. TODO(grantpostma): add + // a focus bar when selecting inside the text field's bounding box. + 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 { + if (previous_state != current_state || current_state != kGameMenuState) + return true; auto bounds = event->getCurrentTarget()->getBoundingBox(); auto point = touch->getLocation(); if (bounds.containsPoint(point)) { @@ -91,8 +460,73 @@ bool MainMenuScene::init() { // Attaching 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(); + // Setting 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; + 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. + if (bounds.containsPoint(point)) { + Director::getInstance()->pushScene( + TicTacToe::createScene(std::string(), database, user_uid)); + join_text_field->setString(""); + current_state = kWaitingGameOutcome; + } + + return true; + }; + // Attaching 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(); + // Setting 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 { + if (previous_state != current_state || current_state != kGameMenuState) { + return true; + } + 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. + 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; + }; + // Attaching 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 @@ -103,18 +537,23 @@ bool MainMenuScene::init() { 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. + // 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. join_button_touch_listener->onTouchBegan = - [join_text_field](Touch* touch, Event* event) -> bool { + [join_text_field, this](Touch* touch, Event* event) -> bool { + if (previous_state != current_state || current_state != kGameMenuState) { + return true; + } auto bounds = event->getCurrentTarget()->getBoundingBox(); auto point = touch->getLocation(); if (bounds.containsPoint(point)) { // Getting 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(""); } @@ -130,7 +569,115 @@ bool MainMenuScene::init() { // MainMenu scene. this->addChild(create_button); this->addChild(join_button); + this->addChild(logout_button); this->addChild(join_text_field, 1); + this->scheduleUpdate(); + return true; } + +void MainMenuScene::onEnter() { + // if the scene enter is from the game, updateUserRecords and change + // current_state. + if (current_state == kWaitingGameOutcome) { + this->updateUserRecord(); + user_record_label->setString("W:" + to_string(user_wins) + + " L:" + to_string(user_loses) + + " T:" + to_string(user_ties)); + current_state = kGameMenuState; + } + Layer::onEnter(); +} + +// 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(); +} + +// Initialized 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; +} + +// 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; + user_record_label->setString("W:" + to_string(user_wins) + + " L:" + to_string(user_loses) + + " T:" + to_string(user_ties)); + } + } + } 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. + auto err = user_result.error_message(); + 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; + user_record_label->setString("W:" + to_string(user_wins) + + " L:" + to_string(user_loses) + + " T:" + to_string(user_ties)); + } 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..87c841a2 100644 --- a/demos/TicTacToe/Classes/MainMenuScene.h +++ b/demos/TicTacToe/Classes/MainMenuScene.h @@ -2,18 +2,60 @@ #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*, ...); +bool 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) // and can have sprites, labels and nodes added onto it. static cocos2d::Scene* createScene(); + virtual void MainMenuScene::update(float); + virtual void MainMenuScene::onEnter(); + void MainMenuScene::updateUserRecord(); + void MainMenuScene::initializeUserRecord(); // Initializes 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); + cocos2d::DrawNode* auth_background; + cocos2d::Node auth_node; + cocos2d::Label* invalid_login_label; + cocos2d::Label* user_record_label; + cocos2d::TextFieldTTF* email_text_field; + cocos2d::TextFieldTTF* password_text_field; + // Defines the stage that the class is in. + enum stageEnum { + kAuthState, + kGameMenuState, + kWaitingAnonymousState, + kWaitingSignUpState, + kWaitingLoginState, + kWaitingGameOutcome + }; + // Defines the current stage + stageEnum current_state = kAuthState; + stageEnum previous_state = kAuthState; + firebase::Future user_result; + std::string user_uid; + int user_wins; + int user_loses; + int user_ties; + firebase::auth::User* user; + 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..35dc51ae 100644 --- a/demos/TicTacToe/Classes/TicTacToeLayer.cpp +++ b/demos/TicTacToe/Classes/TicTacToeLayer.cpp @@ -1,31 +1,5 @@ #include "TicTacToeLayer.h" -#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; - USING_NS_CC; // Player constants. @@ -41,6 +15,10 @@ static const enum kGameOutcome { kGameTied, kGameDisbanded }; +// creating 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."}; @@ -61,24 +39,6 @@ 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. @@ -183,35 +143,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,80 +185,19 @@ static bool GameOver(int board[][kTilesY]) { return (RowCrossed(board) || ColumnCrossed(board) || DiagonalCrossed(board)); } -TicTacToeLayer::TicTacToeLayer(string 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; - 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."); - } - } + database = main_menu_database; + user_uid = main_menu_user; // 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); + 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); @@ -512,10 +382,12 @@ TicTacToeLayer::TicTacToeLayer(string game_uuid) { if (GameOver(board)) { // 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. - WaitForCompletion(ref.Child("game_over").SetValue(true), "setGameOver"); game_outcome = kGameTied; + WaitForCompletion(ref.Child("game_over").SetValue(true), "setGameOver"); } } return true; @@ -534,7 +406,7 @@ TicTacToeLayer::TicTacToeLayer(string game_uuid) { void TicTacToeLayer::update(float /*delta*/) { // Replacing the scene with MainMenuScene if the initialization fails. if (initialization_failed == true) { - Director::getInstance()->replaceScene(MainMenuScene::createScene()); + Director::getInstance()->popScene(); } // Performs the actions of the other player when the // current_player_index_listener is equal to the player index. @@ -565,9 +437,11 @@ void TicTacToeLayer::update(float /*delta*/) { if (GameOver(board)) { // 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 @@ -580,12 +454,21 @@ void TicTacToeLayer::update(float /*delta*/) { game_over_label->setString(kGameOverStrings[game_outcome]); end_game_frames++; if (end_game_frames > kEndGameFramesMax) { - // TODO(grantpostma): Update authenticated users record. + // Removing the game from existence and updating the user's record before + // swapping 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); + 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. diff --git a/demos/TicTacToe/Classes/TicTacToeLayer.h b/demos/TicTacToe/Classes/TicTacToeLayer.h index 1fa708b0..4672ca37 100644 --- a/demos/TicTacToe/Classes/TicTacToeLayer.h +++ b/demos/TicTacToe/Classes/TicTacToeLayer.h @@ -3,13 +3,22 @@ #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" + +// Thin OS abstraction layer. +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include using cocos2d::Director; using cocos2d::Event; @@ -22,6 +31,7 @@ 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; @@ -33,7 +43,7 @@ class TicTacToeLayer : public Layer { typedef Layer super; public: - TicTacToeLayer(std::string); + TicTacToeLayer(std::string, firebase::database::Database*, std::string); ~TicTacToeLayer(); virtual void TicTacToeLayer::update(float); // Tracks whether the board was unable to build. @@ -43,8 +53,8 @@ class TicTacToeLayer : public Layer { // Creating a string for the join game code and initializing the database // reference. std::string join_game_uuid; - /// Firebase Auth, used for logging into Firebase. - firebase::auth::Auth* auth; + // User uid for updating 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; diff --git a/demos/TicTacToe/Classes/TicTacToeScene.cpp b/demos/TicTacToe/Classes/TicTacToeScene.cpp index 127b25d3..1c7976ca 100644 --- a/demos/TicTacToe/Classes/TicTacToeScene.cpp +++ b/demos/TicTacToe/Classes/TicTacToeScene.cpp @@ -1,11 +1,10 @@ #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. @@ -13,7 +12,8 @@ Scene* TicTacToe::createScene(const std::string& game_uuid) { // 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); + 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..578ebf2a 100644 --- a/demos/TicTacToe/Classes/TicTacToeScene.h +++ b/demos/TicTacToe/Classes/TicTacToeScene.h @@ -1,13 +1,17 @@ #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 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 literal 0 HcmV?d00001 From b432d3d31136f1a3c84103e7ae01e31400772808 Mon Sep 17 00:00:00 2001 From: Grant-Postma Date: Thu, 25 Jun 2020 02:42:46 -0400 Subject: [PATCH 2/5] auth full review by @DellaBitta --- demos/TicTacToe/Classes/AppDelegate.cpp | 15 + demos/TicTacToe/Classes/AppDelegate.h | 14 + demos/TicTacToe/Classes/MainMenuScene.cpp | 360 ++++++++++++--------- demos/TicTacToe/Classes/MainMenuScene.h | 79 +++-- demos/TicTacToe/Classes/TicTacToeLayer.cpp | 90 ++++-- demos/TicTacToe/Classes/TicTacToeLayer.h | 48 ++- demos/TicTacToe/Classes/TicTacToeScene.cpp | 16 +- demos/TicTacToe/Classes/TicTacToeScene.h | 15 + 8 files changed, 419 insertions(+), 218 deletions(-) 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 8e4411f0..7eb79a5d 100644 --- a/demos/TicTacToe/Classes/MainMenuScene.cpp +++ b/demos/TicTacToe/Classes/MainMenuScene.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 "MainMenuScene.h" #include @@ -11,6 +25,7 @@ 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; @@ -23,13 +38,12 @@ void LogMessage(const char* format, ...) { fflush(stdout); } -bool ProcessEvents(int msec) { +void ProcessEvents(int msec) { #ifdef _WIN32 Sleep(msec); #else usleep(msec * 1000); #endif // _WIN32 - return false; } // Wait for a Future to be completed. If the Future returns an error, it will @@ -46,6 +60,7 @@ void WaitForCompletion(const firebase::FutureBase& future, const char* name) { } } +// Returns a random uid of a specified length. std::string GenerateUid(std::size_t length) { const std::string kCharacters = "0123456789abcdefghjkmnpqrstuvwxyz"; @@ -53,13 +68,13 @@ std::string GenerateUid(std::size_t length) { std::mt19937 generator(random_device()); std::uniform_int_distribution<> distribution(0, kCharacters.size() - 1); - std::string GenerateUid; + std::string generate_uid; for (std::size_t i = 0; i < length; ++i) { - GenerateUid += kCharacters[distribution(generator)]; + generate_uid += kCharacters[distribution(generator)]; } - return GenerateUid; + return generate_uid; } Scene* MainMenuScene::createScene() { @@ -76,65 +91,18 @@ bool MainMenuScene::init() { if (!Layer::init()) { return false; } - ::firebase::App* app; - -#if defined(__ANDROID__) - app = ::firebase::App::Create(GetJniEnv(), GetActivity()); -#else - app = ::firebase::App::Create(); -#endif // defined(ANDROID) - - LogMessage("Initialized Firebase App."); - - 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); - return 1; - } - LogMessage("Successfully initialized Firebase Auth and Firebase Database."); + // Call the function to initialize the firebase features. + this->InitializeFirebase(); - database->set_persistence_enabled(true); - // Creating the background to add all of the authentication elements on. The - // visiblity of this node should match kAuthState, disabling any + // 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(); - auto auth_background_size = Size(500, 550); - auto auth_background_origin = Size(50, 50); + 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); @@ -157,7 +125,7 @@ bool MainMenuScene::init() { this->addChild(auth_background, 10); - // Labeling the background as Authentication. + // Label the background as Authentication. auto auth_label = Label::createWithSystemFont("authentication", "Arial", 48); auth_label->setPosition(Vec2(300, 550)); auth_background->addChild(auth_label); @@ -185,34 +153,36 @@ bool MainMenuScene::init() { 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; - auto bounds = event->getCurrentTarget()->getBoundingBox(); - auto point = touch->getLocation(); + const auto bounds = event->getCurrentTarget()->getBoundingBox(); + const auto point = touch->getLocation(); if (bounds.containsPoint(point)) { - // Using anonymous sign in for the user. + // Use anonymous sign in for the user. user_result = auth->SignInAnonymously(); current_state = kWaitingAnonymousState; } return true; }; - // Attaching the touch listener to the text field. + + // 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); - // Extracting the origin, size and position of the text field so that the + // Extract the origin, size and position of the text field so that the // border can be created based on those values. - auto email_text_field_origin = Vec2(0, 0); - auto email_text_field_position = Size(110, 350); - auto text_field_padding = 20; - auto email_font_size = 36.0; - auto email_text_field_size = Size(400, 2 * email_font_size); + 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); - // Setting up the constraints of the border so it surrounds the text box. + // 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), @@ -223,11 +193,12 @@ bool MainMenuScene::init() { Vec2(email_text_field_position.width - text_field_padding, email_text_field_position.height + email_text_field_size.height)}; - // Creating a text field border and adding it around the text field. + // 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); - // Creating a text field to enter the user's email. + + // 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); @@ -240,14 +211,15 @@ bool MainMenuScene::init() { email_text_field_touch_listener->onTouchBegan = [this](cocos2d::Touch* touch, cocos2d::Event* event) -> bool { - if (previous_state != current_state || current_state != kAuthState) + // Returns if the layer is not in the auth state or is switching states. + if (previous_state != current_state || current_state != kAuthState) { return true; - auto bounds = event->getCurrentTarget()->getBoundingBox(); - auto point = touch->getLocation(); + } + 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 = email_text_field->getString(); auto text_field = dynamic_cast(event->getCurrentTarget()); text_field->setCursorEnabled(true); text_field->attachWithIME(); @@ -260,7 +232,7 @@ 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(email_text_field_touch_listener, @@ -268,14 +240,15 @@ bool MainMenuScene::init() { auth_background->addChild(email_text_field, 1); auth_background->addChild(email_text_field_border, 1); - // Extracting the origin, size and position of the text field so that the + + // Extract the origin, size and position of the text field so that the // border can be created based on those values. - auto password_text_field_origin = Vec2(0, 0); - auto password_text_field_position = Size(110, 250); - auto password_font_size = 36.0; - auto password_text_field_size = Size(400, 2 * password_font_size); + 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); - // Setting up the constraints of the border so it surronds the text box. + // 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), @@ -288,13 +261,14 @@ bool MainMenuScene::init() { password_text_field_position.height + password_text_field_size.height)}; - // Creating a text field border and adding it around the text field. + // 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); - // Creating a text field to enter the user's password. + + // Create a text field to enter the user's password. password_text_field = cocos2d::TextFieldTTF::textFieldWithPlaceHolder( - "enter an password", password_text_field_size, TextHAlignment::LEFT, + "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)); @@ -307,15 +281,15 @@ bool MainMenuScene::init() { 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; } - auto bounds = event->getCurrentTarget()->getBoundingBox(); - auto point = touch->getLocation(); + 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 + // Shows the on screen keyboard and places character inputs into the text // field. - auto str = password_text_field->getString(); auto text_field = dynamic_cast(event->getCurrentTarget()); text_field->setCursorEnabled(true); text_field->attachWithIME(); @@ -331,30 +305,34 @@ bool MainMenuScene::init() { auth_background->addChild(password_text_field, 1); auth_background->addChild(password_text_field_border, 1); - // Attaching the touch listener to the text field. + // Attach the touch listener to the text field. Director::getInstance() ->getEventDispatcher() ->addEventListenerWithSceneGraphPriority( password_text_field_touch_listener, password_text_field); - // Creating the login button and giving it a position, anchor point and + + // 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(); - // Setting the onTouchBegan event up to a lambda tha will replace the - // MainMenu scene with a TicTacToe scene and pass in login_text_field - // string. + + // 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; } - auto bounds = event->getCurrentTarget()->getBoundingBox(); - auto point = touch->getLocation(); + 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) { @@ -369,31 +347,36 @@ bool MainMenuScene::init() { } return true; }; - // Attaching the touch listener to the login button. + + // Attach the touch listener to the login button. Director::getInstance() ->getEventDispatcher() ->addEventListenerWithSceneGraphPriority(login_button_touch_listener, login_button); auth_background->addChild(login_button, 1); - // Creating the sign_up button and giving it a position, anchor point and + // 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(); - // Setting the onTouchBegan event up to a lambda tha will replace the - // MainMenu scene with a TicTacToe scene and pass in sign_up_text_field - // string. + + // 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 { - if (previous_state != current_state || current_state != kAuthState) + // Returns if the layer is not in the auth state or is switching states. + if (previous_state != current_state || current_state != kAuthState) { return true; - auto bounds = event->getCurrentTarget()->getBoundingBox(); - auto point = touch->getLocation(); + } + 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) { @@ -408,15 +391,16 @@ bool MainMenuScene::init() { } return true; }; - // Attaching the touch listener to the sign_up button. + + // 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, 1); - // Creating, setting the position and assigning a placeholder to the text - // field for entering the join game uuid. + // 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); @@ -430,17 +414,19 @@ bool MainMenuScene::init() { 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. TODO(grantpostma): add - // a focus bar when selecting inside the text field's bounding box. + + // 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; - auto bounds = event->getCurrentTarget()->getBoundingBox(); - auto point = touch->getLocation(); + 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. @@ -457,25 +443,29 @@ 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(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(); - // Setting the onTouchBegan event up to a lambda tha will replace the + + // 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; - 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)) { @@ -487,27 +477,34 @@ bool MainMenuScene::init() { return true; }; - // Attaching the touch listener to the create game button. + + // 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(); - // Setting the onTouchBegan event up to a lambda tha will replace the + + // 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; } - 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)) { @@ -522,7 +519,8 @@ bool MainMenuScene::init() { return true; }; - // Attaching the touch listener to the logout game button. + + // Attach the touch listener to the logout game button. Director::getInstance() ->getEventDispatcher() ->addEventListenerWithSceneGraphPriority(logout_button_touch_listener, @@ -535,19 +533,23 @@ 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 + + // 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 if the layer is not in the game menu state or is switching + // states. if (previous_state != current_state || current_state != kGameMenuState) { return true; } - auto bounds = event->getCurrentTarget()->getBoundingBox(); - auto point = touch->getLocation(); + 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()->pushScene( @@ -560,12 +562,14 @@ bool MainMenuScene::init() { } 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); @@ -576,22 +580,62 @@ bool MainMenuScene::init() { 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; -void MainMenuScene::onEnter() { - // if the scene enter is from the game, updateUserRecords and change - // current_state. - if (current_state == kWaitingGameOutcome) { - this->updateUserRecord(); - user_record_label->setString("W:" + to_string(user_wins) + - " L:" + to_string(user_loses) + - " T:" + to_string(user_ties)); - current_state = kGameMenuState; +#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); } - Layer::onEnter(); + 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() { +void MainMenuScene::UpdateUserRecord() { ref = database->GetReference("users").Child(user_uid); auto future_wins = ref.Child("wins").GetValue(); auto future_loses = ref.Child("loses").GetValue(); @@ -602,10 +646,13 @@ void MainMenuScene::updateUserRecord() { 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)); } -// Initialized the user records in the database. -void MainMenuScene::initializeUserRecord() { +// 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); @@ -616,6 +663,20 @@ void MainMenuScene::initializeUserRecord() { 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(). @@ -627,11 +688,10 @@ void MainMenuScene::update(float /*delta*/) { if (user_result.error() == firebase::auth::kAuthErrorNone) { user = *user_result.result(); user_uid = GenerateUid(10); - this->initializeUserRecord(); + + this->InitializeUserRecord(); + current_state = kGameMenuState; - user_record_label->setString("W:" + to_string(user_wins) + - " L:" + to_string(user_loses) + - " T:" + to_string(user_ties)); } } } else if (current_state == kWaitingSignUpState) { @@ -639,12 +699,13 @@ void MainMenuScene::update(float /*delta*/) { if (user_result.error() == firebase::auth::kAuthErrorNone) { user = *user_result.result(); user_uid = user->uid(); - this->initializeUserRecord(); + + this->InitializeUserRecord(); current_state = kGameMenuState; + } else { // Change invalid_login_label to display the user create failed. - auto err = user_result.error_message(); invalid_login_label->setString("invalid sign up"); current_state = kAuthState; } @@ -655,12 +716,9 @@ void MainMenuScene::update(float /*delta*/) { user = *user_result.result(); user_uid = user->uid(); - this->updateUserRecord(); + this->UpdateUserRecord(); current_state = kGameMenuState; - user_record_label->setString("W:" + to_string(user_wins) + - " L:" + to_string(user_loses) + - " T:" + to_string(user_ties)); } else { // Change invalid_login_label to display the auth_result errored. auto err = user_result.error_message(); @@ -669,7 +727,7 @@ void MainMenuScene::update(float /*delta*/) { } } } else if (current_state == kAuthState) { - // Sign out logic, adding auth screen + // Sign out logic, adding auth screen. auth_background->setVisible(true); user = nullptr; previous_state = current_state; diff --git a/demos/TicTacToe/Classes/MainMenuScene.h b/demos/TicTacToe/Classes/MainMenuScene.h index 87c841a2..2bf5d756 100644 --- a/demos/TicTacToe/Classes/MainMenuScene.h +++ b/demos/TicTacToe/Classes/MainMenuScene.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_MAINMENU_SCENE_H_ #define TICTACTOE_DEMO_CLASSES_MAINMENU_SCENE_H_ @@ -10,51 +24,68 @@ using std::to_string; void LogMessage(const char*, ...); -bool ProcessEvents(int); +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) + // 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 + }; + + // 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(); - virtual void MainMenuScene::update(float); - virtual void MainMenuScene::onEnter(); - void MainMenuScene::updateUserRecord(); - void MainMenuScene::initializeUserRecord(); - // Initializes the instance of a Node and returns a boolean based on if it was + void MainMenuScene::update(float) override; + void MainMenuScene::onEnter() override; + + // 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(); + + // 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); + + // Creates node to be used as a background for the authentication menu. cocos2d::DrawNode* auth_background; cocos2d::Node auth_node; + + // 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; - // Defines the stage that the class is in. - enum stageEnum { - kAuthState, - kGameMenuState, - kWaitingAnonymousState, - kWaitingSignUpState, - kWaitingLoginState, - kWaitingGameOutcome - }; - // Defines the current stage - stageEnum current_state = kAuthState; - stageEnum previous_state = kAuthState; - firebase::Future user_result; - std::string user_uid; + + // 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 varibales 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::auth::Auth* auth; firebase::database::DatabaseReference ref; }; diff --git a/demos/TicTacToe/Classes/TicTacToeLayer.cpp b/demos/TicTacToe/Classes/TicTacToeLayer.cpp index 35dc51ae..3fc62a9e 100644 --- a/demos/TicTacToe/Classes/TicTacToeLayer.cpp +++ b/demos/TicTacToe/Classes/TicTacToeLayer.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 "TicTacToeLayer.h" USING_NS_CC; @@ -15,7 +29,8 @@ static const enum kGameOutcome { kGameTied, kGameDisbanded }; -// creating an array that has indicies of enum kGameOutcome and maps that to the + +// 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"}; @@ -26,13 +41,16 @@ 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"; @@ -112,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_; }; @@ -193,9 +211,10 @@ TicTacToeLayer::TicTacToeLayer(string game_uuid, game_outcome = kGameWon; database = main_menu_database; user_uid = main_menu_user; - // 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 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); @@ -223,7 +242,8 @@ TicTacToeLayer::TicTacToeLayer(string game_uuid, auto future_increment_total_users = 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. @@ -235,6 +255,7 @@ 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; }); @@ -245,8 +266,8 @@ TicTacToeLayer::TicTacToeLayer(string game_uuid, } } - // 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) { @@ -268,12 +289,14 @@ TicTacToeLayer::TicTacToeLayer(string game_uuid, // 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)) { @@ -290,27 +313,28 @@ TicTacToeLayer::TicTacToeLayer(string game_uuid, 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); - 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); game_uuid_label->setPosition(Vec2(40, 20)); - board_sprite->addChild(game_uuid_label, 1); + 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); + 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, 1); + board_sprite->addChild(game_over_label, /*layer_index=*/1); // total_player_listener and CurrentPlayerIndexListener listener is set up // to recognise when the desired players have connected & when turns @@ -329,14 +353,15 @@ TicTacToeLayer::TicTacToeLayer(string game_uuid, .AddValueListener(current_player_index_listener.get()); ref.Child("last_move").AddValueListener(last_move_listener.get()); - // A 3*3 Tic-Tac-Toe board for playing + // 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); }; } - // 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, @@ -345,8 +370,9 @@ TicTacToeLayer::TicTacToeLayer(string game_uuid, 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 @@ -362,12 +388,14 @@ TicTacToeLayer::TicTacToeLayer(string game_uuid, 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 + + // 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); @@ -398,25 +426,29 @@ TicTacToeLayer::TicTacToeLayer(string game_uuid, ->addEventListenerWithSceneGraphPriority(touch_listener, 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. + // 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) { int last_move = last_move_listener->last_seen_value().AsInt64().int64_value(); - // Placing the players move on the board. + + // Place the player's move on the board. board[last_move / kTilesX][last_move % kTilesX] = current_player_index; - // Removing the tile from the tile unordered set. + + // Remove the tile from the tile unordered set. remaining_tiles.erase(last_move); auto sprite = Sprite::create(kPlayerTokenFileNames[current_player_index]); if (sprite == NULL) { @@ -424,12 +456,14 @@ void TicTacToeLayer::update(float /*delta*/) { 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. + + // 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; @@ -444,6 +478,7 @@ void TicTacToeLayer::update(float /*delta*/) { 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()) { @@ -454,8 +489,8 @@ void TicTacToeLayer::update(float /*delta*/) { game_over_label->setString(kGameOverStrings[game_outcome]); end_game_frames++; if (end_game_frames > kEndGameFramesMax) { - // Removing the game from existence and updating the user's record before - // swapping back scenes. + // 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(), @@ -471,6 +506,7 @@ void TicTacToeLayer::update(float /*delta*/) { 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) { diff --git a/demos/TicTacToe/Classes/TicTacToeLayer.h b/demos/TicTacToe/Classes/TicTacToeLayer.h index 4672ca37..ada660ae 100644 --- a/demos/TicTacToe/Classes/TicTacToeLayer.h +++ b/demos/TicTacToe/Classes/TicTacToeLayer.h @@ -1,13 +1,20 @@ +// 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 "MainMenuScene.h" -#include "TicTacToeScene.h" -#include "cocos2d.h" - -// Thin OS abstraction layer. #include #include #include @@ -20,6 +27,10 @@ #include #include +#include "MainMenuScene.h" +#include "TicTacToeScene.h" +#include "cocos2d.h" + using cocos2d::Director; using cocos2d::Event; using cocos2d::Layer; @@ -45,38 +56,45 @@ class TicTacToeLayer : public Layer { public: TicTacToeLayer(std::string, firebase::database::Database*, std::string); ~TicTacToeLayer(); - virtual void TicTacToeLayer::update(float); + void TicTacToeLayer::update(float) override; + // Tracks whether the board was unable to build. bool initialization_failed = false; + // Tracks the game outcome. int game_outcome; - // Creating a string for the join game code and initializing the database + + // Create a string for the join game code and initialize the database // reference. std::string join_game_uuid; - // User uid for updating the user's record after the game is over. + + // 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::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. + + // Create 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 + + // Create 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 + + // Create the board, remain available tile set and player index // variables. int current_player_index; int player_index; diff --git a/demos/TicTacToe/Classes/TicTacToeScene.cpp b/demos/TicTacToe/Classes/TicTacToeScene.cpp index 1c7976ca..054b8abf 100644 --- a/demos/TicTacToe/Classes/TicTacToeScene.cpp +++ b/demos/TicTacToe/Classes/TicTacToeScene.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 "TicTacToeScene.h" using cocos2d::Scene; @@ -11,7 +25,7 @@ Scene* TicTacToe::createScene(const std::string& game_uuid, 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. + // 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); diff --git a/demos/TicTacToe/Classes/TicTacToeScene.h b/demos/TicTacToe/Classes/TicTacToeScene.h index 578ebf2a..6593dd3f 100644 --- a/demos/TicTacToe/Classes/TicTacToeScene.h +++ b/demos/TicTacToe/Classes/TicTacToeScene.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_TICTACTOE_SCENE_H_ #define TICTACTOE_DEMO_CLASSES_TICTACTOE_SCENE_H_ @@ -12,6 +26,7 @@ class TicTacToe : public cocos2d::Layer { 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); }; From c44068fb4189cb624c1014e4a835d0d1cf362f97 Mon Sep 17 00:00:00 2001 From: Grant-Postma Date: Fri, 26 Jun 2020 00:33:28 -0400 Subject: [PATCH 3/5] Private Class variables. Bug on disbanded room. --- demos/TicTacToe/Classes/MainMenuScene.cpp | 276 ++++++++++----------- demos/TicTacToe/Classes/MainMenuScene.h | 58 ++--- demos/TicTacToe/Classes/TicTacToeLayer.cpp | 272 ++++++++++---------- demos/TicTacToe/Classes/TicTacToeLayer.h | 56 ++--- 4 files changed, 335 insertions(+), 327 deletions(-) diff --git a/demos/TicTacToe/Classes/MainMenuScene.cpp b/demos/TicTacToe/Classes/MainMenuScene.cpp index 7eb79a5d..9fb2234e 100644 --- a/demos/TicTacToe/Classes/MainMenuScene.cpp +++ b/demos/TicTacToe/Classes/MainMenuScene.cpp @@ -98,7 +98,7 @@ bool MainMenuScene::init() { // 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(); + auth_background_ = DrawNode::create(); auto auth_background_border = DrawNode::create(); const auto auth_background_size = Size(500, 550); @@ -117,31 +117,31 @@ bool MainMenuScene::init() { 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_->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); + auth_background_->addChild(auth_background_border); - this->addChild(auth_background, 10); + 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); + 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); + 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); + 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 = @@ -154,14 +154,14 @@ bool MainMenuScene::init() { 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) + 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; + user_result_ = auth_->SignInAnonymously(); + current_state_ = kWaitingAnonymousState; } return true; @@ -172,7 +172,7 @@ bool MainMenuScene::init() { ->getEventDispatcher() ->addEventListenerWithSceneGraphPriority(anonymous_label_touch_listener, anonymous_login_label); - auth_background->addChild(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. @@ -199,20 +199,20 @@ 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_ = 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); + 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) { + if (previous_state_ != current_state_ || current_state_ != kAuthState) { return true; } const auto bounds = event->getCurrentTarget()->getBoundingBox(); @@ -236,10 +236,10 @@ bool MainMenuScene::init() { Director::getInstance() ->getEventDispatcher() ->addEventListenerWithSceneGraphPriority(email_text_field_touch_listener, - email_text_field); + email_text_field_); - auth_background->addChild(email_text_field, 1); - auth_background->addChild(email_text_field_border, 1); + 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. @@ -267,14 +267,14 @@ 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_ = 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); + 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(); @@ -282,7 +282,7 @@ bool MainMenuScene::init() { 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) { + if (previous_state_ != current_state_ || current_state_ != kAuthState) { return true; } const auto bounds = event->getCurrentTarget()->getBoundingBox(); @@ -302,14 +302,14 @@ bool MainMenuScene::init() { return true; }; - auth_background->addChild(password_text_field, 1); - auth_background->addChild(password_text_field_border, 1); + 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); + password_text_field_touch_listener, password_text_field_); // Create the login button and give it a position, anchor point and // touch_listener. @@ -322,27 +322,27 @@ bool MainMenuScene::init() { auto login_button_touch_listener = EventListenerTouchOneByOne::create(); // Transition from kAuthState to kWaitingLoginState on button press and set - // user_result to SignInWithEmailAndPassword future result. + // 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) { + 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( + 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; + user_result_ = auth_->SignInWithEmailAndPassword( + email_text_field_->getString().c_str(), + password_text_field_->getString().c_str()); + current_state_ = kWaitingLoginState; } } return true; @@ -353,7 +353,7 @@ bool MainMenuScene::init() { ->getEventDispatcher() ->addEventListenerWithSceneGraphPriority(login_button_touch_listener, login_button); - auth_background->addChild(login_button, 1); + auth_background_->addChild(login_button, 1); // Create the sign_up button and give it a position, anchor point and // touch_listener. @@ -366,27 +366,27 @@ bool MainMenuScene::init() { auto sign_up_button_touch_listener = EventListenerTouchOneByOne::create(); // Transition from kAuthState to kWaitingSignUpState on button press and set - // user_result to CreateUserWithEmailAndPassword future result. + // 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) { + 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( + 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; + user_result_ = auth_->CreateUserWithEmailAndPassword( + email_text_field_->getString().c_str(), + password_text_field_->getString().c_str()); + current_state_ = kWaitingSignUpState; } } return true; @@ -397,7 +397,7 @@ bool MainMenuScene::init() { ->getEventDispatcher() ->addEventListenerWithSceneGraphPriority(sign_up_button_touch_listener, sign_up_button); - auth_background->addChild(sign_up_button, 1); + 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. @@ -423,7 +423,7 @@ bool MainMenuScene::init() { 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) + if (previous_state_ != current_state_ || current_state_ != kGameMenuState) return true; const auto bounds = event->getCurrentTarget()->getBoundingBox(); const auto point = touch->getLocation(); @@ -462,7 +462,7 @@ bool MainMenuScene::init() { // 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; + if (current_state_ != kGameMenuState) return true; const auto bounds = event->getCurrentTarget()->getBoundingBox(); const auto point = touch->getLocation(); @@ -470,9 +470,9 @@ bool MainMenuScene::init() { // within the bounds of the button. if (bounds.containsPoint(point)) { Director::getInstance()->pushScene( - TicTacToe::createScene(std::string(), database, user_uid)); + TicTacToe::createScene(std::string(), database_, user_uid_)); join_text_field->setString(""); - current_state = kWaitingGameOutcome; + current_state_ = kWaitingGameOutcome; } return true; @@ -499,7 +499,7 @@ bool MainMenuScene::init() { 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) { + if (previous_state_ != current_state_ || current_state_ != kGameMenuState) { return true; } const auto bounds = event->getCurrentTarget()->getBoundingBox(); @@ -508,13 +508,13 @@ bool MainMenuScene::init() { // 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(""); + 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; @@ -543,7 +543,7 @@ bool MainMenuScene::init() { [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) { + if (previous_state_ != current_state_ || current_state_ != kGameMenuState) { return true; } const auto bounds = event->getCurrentTarget()->getBoundingBox(); @@ -552,9 +552,9 @@ bool MainMenuScene::init() { // 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; + Director::getInstance()->pushScene(TicTacToe::createScene( + join_text_field_string, database_, user_uid_)); + current_state_ = kWaitingGameOutcome; join_text_field->setString(""); } else { join_text_field->setString(""); @@ -586,7 +586,7 @@ void MainMenuScene::InitializeFirebase() { LogMessage("Initialize Firebase App."); ::firebase::App* app; -#if defined(__ANDROID__) +#if defined(_ANDROID_) app = ::firebase::App::Create(GetJniEnv(), GetActivity()); #else app = ::firebase::App::Create(); @@ -596,9 +596,9 @@ void MainMenuScene::InitializeFirebase() { // Use ModuleInitializer to initialize both Auth and Database, ensuring no // dependencies are missing. - database = nullptr; - auth = nullptr; - void* initialize_targets[] = {&auth, &database}; + database_ = nullptr; + auth_ = nullptr; + void* initialize_targets[] = {&auth_, &database_}; const firebase::ModuleInitializer::InitializerFn initializers[] = { [](::firebase::App* app, void* data) { @@ -631,50 +631,50 @@ void MainMenuScene::InitializeFirebase() { } LogMessage("Successfully initialized Firebase Auth and Firebase Database."); - database->set_persistence_enabled(true); + 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(); + 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)); + 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); + 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)); + 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. +// 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) { + // current_state_. + if (current_state_ == kWaitingGameOutcome) { this->UpdateUserRecord(); - current_state = kGameMenuState; + current_state_ = kGameMenuState; } Layer::onEnter(); } @@ -682,59 +682,59 @@ void MainMenuScene::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); + 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; + 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(); - 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; + // 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(); + } 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; + 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; + // 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) { + } 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) { + 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; + auth_background_->setVisible(false); + previous_state_ = current_state_; } } return; diff --git a/demos/TicTacToe/Classes/MainMenuScene.h b/demos/TicTacToe/Classes/MainMenuScene.h index 2bf5d756..0dd56998 100644 --- a/demos/TicTacToe/Classes/MainMenuScene.h +++ b/demos/TicTacToe/Classes/MainMenuScene.h @@ -30,6 +30,18 @@ std::string GenerateUid(std::size_t); class MainMenuScene : public cocos2d::Layer, public cocos2d::TextFieldDelegate { public: + // 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(); + 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(); + CREATE_FUNC(MainMenuScene); + + private: // Defines the state the class is currently in, which updates the sprites in // the MainMenuScene::update(float) method. enum kSceneState { @@ -41,12 +53,6 @@ class MainMenuScene : public cocos2d::Layer, public cocos2d::TextFieldDelegate { kWaitingGameOutcome }; - // 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(); - void MainMenuScene::update(float) override; - void MainMenuScene::onEnter() override; - // Updates the user record (wins,loses and ties) and displays it to the // screen. void MainMenuScene::UpdateUserRecord(); @@ -56,37 +62,31 @@ class MainMenuScene : public cocos2d::Layer, public cocos2d::TextFieldDelegate { void MainMenuScene::InitializeUserRecord(); void MainMenuScene::InitializeFirebase(); - // Initialize the instance of a Node and returns a boolean based on if it was - // successful in doing so. - virtual bool init(); - CREATE_FUNC(MainMenuScene); - // Creates node to be used as a background for the authentication menu. - cocos2d::DrawNode* auth_background; - cocos2d::Node auth_node; + 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; + 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; + kSceneState current_state_ = kAuthState; + kSceneState previous_state_ = kAuthState; - // User record varibales that are stored in firebase database. - int user_wins; - int user_loses; - int user_ties; + // 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; + 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 3fc62a9e..94cb607d 100644 --- a/demos/TicTacToe/Classes/TicTacToeLayer.cpp +++ b/demos/TicTacToe/Classes/TicTacToeLayer.cpp @@ -206,41 +206,41 @@ static bool GameOver(int board[][kTilesY]) { 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; + 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 + // 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); + // 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(); // Complete the transaction based on the returned mutable data @@ -261,30 +261,30 @@ TicTacToeLayer::TicTacToeLayer(string game_uuid, }); WaitForCompletion(future_increment_total_users, "JoinGameTransaction"); - player_index = kPlayerTwo; - awaiting_opponenet_move = true; + player_index_ = kPlayerTwo; + awaiting_opponenet_move_ = true; } } // 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 = @@ -300,15 +300,15 @@ TicTacToeLayer::TicTacToeLayer(string game_uuid, // 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; @@ -318,46 +318,46 @@ TicTacToeLayer::TicTacToeLayer(string game_uuid, 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, /*layer_index=*/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, /*layer_index=*/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, /*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); + 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); + 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()); + 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); }; } @@ -366,8 +366,8 @@ TicTacToeLayer::TicTacToeLayer(string game_uuid, 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; const auto bounds = event->getCurrentTarget()->getBoundingBox(); @@ -379,13 +379,14 @@ TicTacToeLayer::TicTacToeLayer(string game_uuid, // 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); } @@ -393,29 +394,31 @@ TicTacToeLayer::TicTacToeLayer(string game_uuid, // move_tile and the constant screen variables. sprite->setPosition((.5 + selected_tile % kTilesX) * kTileWidth, (.5 + selected_tile / kTilesY) * kTileHeight); - board_sprite->addChild(sprite); + 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!"); - 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"); + // 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; @@ -423,9 +426,9 @@ 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(); @@ -434,26 +437,26 @@ TicTacToeLayer::TicTacToeLayer(string game_uuid, // Called automatically every frame. The update is scheduled in constructor. void TicTacToeLayer::update(float /*delta*/) { // Replace the scene with MainMenuScene if the initialization fails. - if (initialization_failed == true) { + 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(); + last_move_listener_->last_seen_value().AsInt64().int64_value(); - // Place the player's move on the board. - board[last_move / kTilesX][last_move % kTilesX] = current_player_index; + // 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]); + 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); } @@ -461,62 +464,67 @@ void TicTacToeLayer::update(float /*delta*/) { // move_tile and the constant screen variables. sprite->setPosition((.5 + last_move % kTilesX) * kTileWidth, (.5 + last_move / kTilesY) * kTileHeight); - board_sprite->addChild(sprite); + 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; + 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; - 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"); + // 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) { + 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) + WaitForCompletion(database_->GetReference("game_data") + .Child(join_game_uuid_) .RemoveValue(), "removeGameUuid"); - ref = database->GetReference("users").Child(user_uid); - 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"); + 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 ada660ae..6092733c 100644 --- a/demos/TicTacToe/Classes/TicTacToeLayer.h +++ b/demos/TicTacToe/Classes/TicTacToeLayer.h @@ -49,58 +49,58 @@ static const int kTilesY = 3; class SampleValueListener; class ExpectValueListener; class TicTacToeLayer : public Layer { - private: - typedef TicTacToeLayer self; - typedef Layer super; - public: TicTacToeLayer(std::string, firebase::database::Database*, std::string); ~TicTacToeLayer(); + + private: + typedef TicTacToeLayer self; + typedef Layer super; void TicTacToeLayer::update(float) override; // Tracks whether the board was unable to build. - bool initialization_failed = false; + bool initialization_failed_ = false; // Tracks the game outcome. - int game_outcome; + int game_outcome_; // Create a string for the join game code and initialize the database // reference. - std::string join_game_uuid; + std::string join_game_uuid_; // User uid to update the user's record after the game is over. - std::string user_uid; + std::string user_uid_; /// Firebase Realtime Database, the entry point to all database operations. - firebase::database::Database* database; - firebase::database::DatabaseReference ref; + firebase::database::Database* database_; + firebase::database::DatabaseReference ref_; // 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; + // 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; + 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 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_ From 168816ccff162a7b64d31522ba4244c9fcebb736 Mon Sep 17 00:00:00 2001 From: Grant-Postma Date: Mon, 29 Jun 2020 20:40:45 -0400 Subject: [PATCH 4/5] Comment typos, clarifications, and fixes. --- demos/TicTacToe/Classes/MainMenuScene.cpp | 17 ++++++++++--- demos/TicTacToe/Classes/MainMenuScene.h | 16 +++++++++--- demos/TicTacToe/Classes/TicTacToeLayer.cpp | 15 ++++++----- demos/TicTacToe/Classes/TicTacToeLayer.h | 29 +++++++++++++++------- 4 files changed, 52 insertions(+), 25 deletions(-) diff --git a/demos/TicTacToe/Classes/MainMenuScene.cpp b/demos/TicTacToe/Classes/MainMenuScene.cpp index 9fb2234e..7f35e6ec 100644 --- a/demos/TicTacToe/Classes/MainMenuScene.cpp +++ b/demos/TicTacToe/Classes/MainMenuScene.cpp @@ -248,7 +248,7 @@ bool MainMenuScene::init() { 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. + // 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), @@ -668,7 +668,7 @@ void MainMenuScene::InitializeUserRecord() { " T:" + to_string(user_ties_)); } -// Overriding the onEnter method to udate the user_record on reenter. +// 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_. @@ -679,8 +679,17 @@ void MainMenuScene::onEnter() { Layer::onEnter(); } -// Update loop that gets called every frame and was set of by scheduleUpdate(). -// Acts as the state manager for this scene. +// 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) { diff --git a/demos/TicTacToe/Classes/MainMenuScene.h b/demos/TicTacToe/Classes/MainMenuScene.h index 0dd56998..fd411d28 100644 --- a/demos/TicTacToe/Classes/MainMenuScene.h +++ b/demos/TicTacToe/Classes/MainMenuScene.h @@ -23,6 +23,7 @@ #include "ui/CocosGUI.h" using std::to_string; +// TODO(grantpostma): Create a common util.h & util.cpp file. void LogMessage(const char*, ...); void ProcessEvents(int); void WaitForCompletion(const firebase::FutureBase&, const char*); @@ -33,12 +34,18 @@ class MainMenuScene : public cocos2d::Layer, public cocos2d::TextFieldDelegate { // 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(); + + // The game loop method for this layer which runs every frame once scheduled + // using this->scheduleUpdate(). Acts as the state manager for this scene. void MainMenuScene::update(float) override; + + // If the scene is re-entered from TicTacToeScene, then call + // UpdateUserRecord() and swap current_state_ to kGameMenuState. void MainMenuScene::onEnter() override; - // Initialize the instance of a Node and returns a boolean based on if it was + // Initializes the instance of a Node and returns a boolean based on if it was // successful in doing so. - virtual bool init(); + bool init() override; CREATE_FUNC(MainMenuScene); private: @@ -60,9 +67,10 @@ class MainMenuScene : public cocos2d::Layer, public cocos2d::TextFieldDelegate { // Initializes the user record (wins,loses and ties) and displays it to the // screen. void MainMenuScene::InitializeUserRecord(); + // Initializes the the firebase app, auth, and database. void MainMenuScene::InitializeFirebase(); - // Creates node to be used as a background for the authentication menu. + // Node to be used as a background for the authentication menu. cocos2d::DrawNode* auth_background_; // Labels and textfields for the authentication menu. @@ -72,7 +80,7 @@ class MainMenuScene : public cocos2d::Layer, public cocos2d::TextFieldDelegate { cocos2d::TextFieldTTF* password_text_field_; // Variable to track the current state and previous state to check against to - // see if the state change. + // see if the state changed. kSceneState current_state_ = kAuthState; kSceneState previous_state_ = kAuthState; diff --git a/demos/TicTacToe/Classes/TicTacToeLayer.cpp b/demos/TicTacToe/Classes/TicTacToeLayer.cpp index 94cb607d..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_; }; @@ -212,7 +213,7 @@ TicTacToeLayer::TicTacToeLayer(string game_uuid, database_ = main_menu_database; user_uid_ = main_menu_user; - // If the join_game_uuid_ is present, initialize game varibales, otherwise + // If the join_game_uuid_ is present, initialize game variables, 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()) { @@ -229,8 +230,8 @@ TicTacToeLayer::TicTacToeLayer(string game_uuid, 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. + // Checks whether the join_uuid map exists. If it does not then set + // the initialization to failed. auto future_game_uuid = database_->GetReference("game_data").Child(join_game_uuid_).GetValue(); WaitForCompletion(future_game_uuid, "GetGameDataMap"); @@ -338,15 +339,13 @@ 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 - LogMessage("total_player_listener_"); + // alternate. 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_") @@ -409,7 +408,7 @@ TicTacToeLayer::TicTacToeLayer(string game_uuid, awaiting_opponenet_move_ = true; waiting_label_->setString("waiting"); if (GameOver(board)) { - // Set game_over_label_ to reflect the use won. + // Set game_over_label_ to reflect the user won. game_over_label_->setString("you won!"); game_outcome_ = kGameWon; WaitForCompletion(ref_.Child("game_over").SetValue(true), diff --git a/demos/TicTacToe/Classes/TicTacToeLayer.h b/demos/TicTacToe/Classes/TicTacToeLayer.h index 6092733c..e2b24544 100644 --- a/demos/TicTacToe/Classes/TicTacToeLayer.h +++ b/demos/TicTacToe/Classes/TicTacToeLayer.h @@ -44,18 +44,25 @@ using firebase::database::MutableData; using firebase::database::TransactionResult; using std::string; +// Tile Constants. static const int kTilesX = 3; static const int kTilesY = 3; + +// Firebase listeners. class SampleValueListener; class ExpectValueListener; + 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(); private: - typedef TicTacToeLayer self; - typedef Layer super; + // The game loop for this layer which runs every frame once scheduled using + // this->scheduleUpdate(). It constantly checks current_player_index_listener_ + // and game_over_listener so it can take action accordingly. void TicTacToeLayer::update(float) override; // Tracks whether the board was unable to build. @@ -64,7 +71,7 @@ class TicTacToeLayer : public Layer { // Tracks the game outcome. int game_outcome_; - // Create a string for the join game code and initialize the database + // String for the join game code and initialize the database // reference. std::string join_game_uuid_; @@ -75,32 +82,36 @@ class TicTacToeLayer : public Layer { firebase::database::Database* database_; firebase::database::DatabaseReference ref_; - // 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. + + // 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_; - // Create lables and a sprites. + // 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_ + // 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 board[kTilesX][kTilesY]; + + bool awaiting_opponenet_move_; + + // Unordered set of remaining tiles available for player moves. std::unordered_set remaining_tiles_; + + // Amount of frames the screen has been in the end game state. int end_game_frames_ = 0; }; #endif // TICTACTOE_DEMO_CLASSES_TICTACTOELAYER_SCENE_H_ From 25fa1d7fdd890030df854389775be0cfb22b2744 Mon Sep 17 00:00:00 2001 From: Grant-Postma Date: Tue, 30 Jun 2020 14:27:45 -0400 Subject: [PATCH 5/5] Layer index update. Consuming event onTouchBegan. --- demos/TicTacToe/Classes/MainMenuScene.cpp | 73 +++++++++++++---------- demos/TicTacToe/Classes/TicTacToeLayer.h | 2 +- 2 files changed, 43 insertions(+), 32 deletions(-) diff --git a/demos/TicTacToe/Classes/MainMenuScene.cpp b/demos/TicTacToe/Classes/MainMenuScene.cpp index 7f35e6ec..515ea26e 100644 --- a/demos/TicTacToe/Classes/MainMenuScene.cpp +++ b/demos/TicTacToe/Classes/MainMenuScene.cpp @@ -123,7 +123,7 @@ bool MainMenuScene::init() { Color4F(0, 0, 0, 0), 1, Color4F::WHITE); auth_background_->addChild(auth_background_border); - this->addChild(auth_background_, 10); + this->addChild(auth_background_, /*layer_index=*/10); // Label the background as Authentication. auto auth_label = Label::createWithSystemFont("authentication", "Arial", 48); @@ -153,9 +153,11 @@ bool MainMenuScene::init() { 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; + // 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)) { @@ -211,9 +213,10 @@ 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; + return false; } const auto bounds = event->getCurrentTarget()->getBoundingBox(); const auto point = touch->getLocation(); @@ -238,8 +241,8 @@ bool MainMenuScene::init() { ->addEventListenerWithSceneGraphPriority(email_text_field_touch_listener, email_text_field_); - auth_background_->addChild(email_text_field_, 1); - auth_background_->addChild(email_text_field_border, 1); + 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. @@ -281,9 +284,10 @@ bool MainMenuScene::init() { 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. + // 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; + return false; } const auto bounds = event->getCurrentTarget()->getBoundingBox(); const auto point = touch->getLocation(); @@ -302,8 +306,8 @@ bool MainMenuScene::init() { return true; }; - auth_background_->addChild(password_text_field_, 1); - auth_background_->addChild(password_text_field_border, 1); + 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 text field. Director::getInstance() @@ -325,9 +329,10 @@ bool MainMenuScene::init() { // 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. + // 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; + return false; } const auto bounds = event->getCurrentTarget()->getBoundingBox(); const auto point = touch->getLocation(); @@ -353,7 +358,7 @@ bool MainMenuScene::init() { ->getEventDispatcher() ->addEventListenerWithSceneGraphPriority(login_button_touch_listener, login_button); - auth_background_->addChild(login_button, 1); + auth_background_->addChild(login_button, /*layer_index=*/1); // Create the sign_up button and give it a position, anchor point and // touch_listener. @@ -369,9 +374,10 @@ bool MainMenuScene::init() { // 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. + // 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; + return false; } const auto bounds = event->getCurrentTarget()->getBoundingBox(); const auto point = touch->getLocation(); @@ -397,7 +403,7 @@ bool MainMenuScene::init() { ->getEventDispatcher() ->addEventListenerWithSceneGraphPriority(sign_up_button_touch_listener, sign_up_button); - auth_background_->addChild(sign_up_button, 1); + 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. @@ -413,7 +419,7 @@ bool MainMenuScene::init() { 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); + 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(); @@ -421,10 +427,11 @@ bool MainMenuScene::init() { 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; + // 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)) { @@ -462,7 +469,11 @@ bool MainMenuScene::init() { // 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; + // 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(); @@ -497,10 +508,10 @@ bool MainMenuScene::init() { // 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. + // 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 true; + return false; } const auto bounds = event->getCurrentTarget()->getBoundingBox(); const auto point = touch->getLocation(); @@ -541,10 +552,10 @@ bool MainMenuScene::init() { // 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 if the layer is not in the game menu state or is switching - // states. + // 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 true; + return false; } const auto bounds = event->getCurrentTarget()->getBoundingBox(); const auto point = touch->getLocation(); @@ -574,7 +585,7 @@ bool MainMenuScene::init() { this->addChild(create_button); this->addChild(join_button); this->addChild(logout_button); - this->addChild(join_text_field, 1); + this->addChild(join_text_field, /*layer_index=*/1); this->scheduleUpdate(); diff --git a/demos/TicTacToe/Classes/TicTacToeLayer.h b/demos/TicTacToe/Classes/TicTacToeLayer.h index e2b24544..b8b645d2 100644 --- a/demos/TicTacToe/Classes/TicTacToeLayer.h +++ b/demos/TicTacToe/Classes/TicTacToeLayer.h @@ -78,7 +78,7 @@ class TicTacToeLayer : public Layer { // 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 Realtime Database, the entry point to all database operations. firebase::database::Database* database_; firebase::database::DatabaseReference ref_;