This commit is contained in:
watrabi
2025-09-18 17:55:52 -04:00
commit 977f1ff4b8
15030 changed files with 17324420 additions and 0 deletions
@@ -0,0 +1,29 @@
//
// ControlComponent.h
// IOSClient
//
// Created by Ben Tkacheff on 9/6/12.
// Copyright (c) 2012 tolkring@gmail.com. All rights reserved.
//
#pragma once
#import <UIKit/UIKit.h>
#include "v8datamodel/UserInputService.h"
#include "v8datamodel/GamepadService.h"
#include "v8datamodel/TouchInputService.h"
@class ControlView;
@interface ControlComponent : UIImageView
{
}
- (RBX::Game*) getGameFromControlView;
- (RBX::GamepadService*) getGamepadServiceForGameDataModel;
- (RBX::UserInputService*) getUserInputServiceForGameDataModel;
- (RBX::TouchInputService*) getTouchInputServiceForGameDataModel;
- (ControlView*) findControlView;
@end
@@ -0,0 +1,84 @@
//
// ControlComponent.m
// IOSClient
//
// Created by Ben Tkacheff on 9/6/12.
// Copyright (c) 2012 tolkring@gmail.com. All rights reserved.
//
#import "ControlComponent.h"
#import "ControlView.h"
#include "v8datamodel/TouchInputService.h"
@implementation ControlComponent
- (id) init
{
if (self = [super init])
{
self.userInteractionEnabled = true;
}
return self;
}
- (ControlView*) findControlView
{
if ([self isKindOfClass:[ControlView class]])
return (ControlView*)self;
id nextSuperView = self.superview;
while (nextSuperView != nil)
{
if ([nextSuperView isKindOfClass:[ControlView class]])
return nextSuperView;
if ([nextSuperView isKindOfClass:[UIView class]])
{
UIView* theUIView = nextSuperView;
nextSuperView = theUIView.superview;
}
else
nextSuperView = nil;
}
return nil;
}
-(RBX::Game*) getGameFromControlView
{
ControlView* controlView = [self findControlView];
if (controlView == nil)
return nil;
return [controlView getGame].get();
}
- (RBX::GamepadService*) getGamepadServiceForGameDataModel
{
if (RBX::Game* game = [self getGameFromControlView])
if (shared_ptr<RBX::DataModel> currDataModel = game->getDataModel())
return RBX::ServiceProvider::create<RBX::GamepadService>(currDataModel.get());
return nil;
}
- (RBX::UserInputService*) getUserInputServiceForGameDataModel
{
if (RBX::Game* game = [self getGameFromControlView])
if (shared_ptr<RBX::DataModel> currDataModel = game->getDataModel())
return RBX::ServiceProvider::find<RBX::UserInputService>(currDataModel.get());
return nil;
}
- (RBX::TouchInputService*) getTouchInputServiceForGameDataModel
{
if (RBX::Game* game = [self getGameFromControlView])
if (shared_ptr<RBX::DataModel> currDataModel = game->getDataModel())
return RBX::ServiceProvider::create<RBX::TouchInputService>(currDataModel.get());
return nil;
}
@end
@@ -0,0 +1,65 @@
//
// ControlView.h
// IOSClient
//
// Created by Ben Tkacheff on 8/21/12.
// Copyright (c) 2012 tolkring@gmail.com. All rights reserved.
//
#pragma once
#include "v8datamodel/UserInputService.h"
#include "V8DataModel/TextBox.h"
#include "v8datamodel/Game.h"
#import <UIKit/UIKit.h>
#import "RbxInputView.h"
@interface ControlView : ControlComponent <UIGestureRecognizerDelegate>
{
RbxInputView* rbxInputView;
RBX::Vector2int16 frameSize;
UITouch* tapTouch;
G3D::Vector2 tapTouchBeginPos;
double tapSensitivity;
int tapTouchMoveTolerance;
boost::weak_ptr<RBX::Game> game;
rbx::signals::scoped_connection dmUserInputTextBoxFocusCon;
rbx::signals::scoped_connection dmUserInputTextBoxReleaseFocusCon;
rbx::signals::scoped_connection dmUserInputProcessMouseEventCon;
// fake mouse events (for backwards compatibility
shared_ptr<RBX::InputObject> mouseButton1Event;
shared_ptr<RBX::InputObject> mouseMoveEvent;
}
- (id) init:(CGRect)frame withGame:(boost::shared_ptr<RBX::Game>) newGame;
- (void) dealloc;
- (RbxInputView*) getRbxInputView;
- (void) disconnectEvents;
- (void) setupEvents;
- (void) setGame:(boost::shared_ptr<RBX::Game>) newGame;
- (boost::shared_ptr<RBX::Game>) getGame;
- (void) textBoxFocusGained:(boost::shared_ptr<RBX::TextBox>) textBoxFocused;
- (void) textBoxFocusLost:(boost::shared_ptr<RBX::TextBox>) textBoxUnfocused;
// Gesture Recognizers
// Tap
- (void) oneFingerSingleTap;
- (UITouch*) checkTouchesForTap:(NSSet *)touches withEvent:(UIEvent *)event;
- (void) checkTapTouchMove:(NSSet*) touchesSet;
- (void) invalidateTapGesture:(id) oldTapTouch;
- (void) postMouseEventProcessed:(bool) processedEvent inputObject: (void*) uiTouch event:(const shared_ptr<RBX::InputObject>&) event;
@end
@@ -0,0 +1,312 @@
//
// ControlView.m
// IOSClient
//
// Created by Ben Tkacheff on 8/21/12.
// Copyright (c) 2012 tolkring@gmail.com. All rights reserved.
//
#import "ControlView.h"
#import "GameKeyboard.h"
#import "PlaceLauncher.h"
#include "ObjectiveCUtilities.h"
#import "RobloxNotifications.h"
@implementation ControlView
- (id) init:(CGRect)frame withGame:(boost::shared_ptr<RBX::Game>) newGame
{
self = [super init];
if (self)
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(gotStartLeaveGameNotification:)
name:RBX_NOTIFY_GAME_START_LEAVING
object:nil ];
game = newGame;
[self setupEvents];
// initial size has coordinates for height/width flipped, as it always returns portait mode, no matter our current orientation :(
CGRect correctRect = CGRectMake(0,0,frame.size.height,frame.size.width);
frameSize = RBX::Vector2int16(frame.size.height, frame.size.width);
// self initialization
self.multipleTouchEnabled = YES;
self.frame = correctRect;
[self setupInputControls];
mouseMoveEvent = RBX::Creatable<RBX::Instance>::create<RBX::InputObject>(RBX::InputObject::TYPE_MOUSEMOVEMENT,
RBX::InputObject::INPUT_STATE_CHANGE,
RBX::Vector3(-1,-1,0),
RBX::Vector3(0,0,0),
[self getGame]->getDataModel().get());
mouseButton1Event = RBX::Creatable<RBX::Instance>::create<RBX::InputObject>(RBX::InputObject::TYPE_MOUSEBUTTON1,
RBX::InputObject::INPUT_STATE_BEGIN,
RBX::Vector3(-1,-1,0),
RBX::Vector3(0,0,0),
[self getGame]->getDataModel().get());
}
return self;
}
-(void) dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
[[GameKeyboard sharedInstance] setParentView:nil];
rbxInputView = nil;
[NSObject cancelPreviousPerformRequestsWithTarget:self];
}
- (RbxInputView*) getRbxInputView
{
return rbxInputView;
}
- (void) setGame:(boost::shared_ptr<RBX::Game>) newGame
{
game = newGame;
if(boost::shared_ptr<RBX::Game> sharedGame = game.lock())
{
[self dataModelChanged:sharedGame->getDataModel().get()];
}
}
- (void) gotStartLeaveGameNotification:(NSNotification *)aNotification
{
[self disconnectEvents];
}
-(void) dataModelChanged:(RBX::DataModel*) dataModel
{
if(dataModel)
{
[self setupEvents];
[self setupInputControls];
}
else // we have a null datamodel, tear down connections
[self disconnectEvents];
}
- (void) postMouseEventProcessed:(bool) processedEvent inputObject: (void*) uiTouch event: (const shared_ptr<RBX::InputObject>&) event
{
if (uiTouch && processedEvent)
{
void* tapTouchPtr = (__bridge void*) tapTouch;
if( uiTouch && (uiTouch == tapTouchPtr) )
[self invalidateTapGesture:nil];
}
}
- (void) textBoxFocusGained:(boost::shared_ptr<RBX::TextBox>) textBoxFocused
{
if(textBoxFocused != NULL && textBoxFocused != boost::shared_ptr<RBX::TextBox>())
[[GameKeyboard sharedInstance] showKeyboardWithTextBox: textBoxFocused];
else
[[GameKeyboard sharedInstance] showKeyboard: ""];
}
- (void) textBoxFocusLost:(boost::shared_ptr<RBX::TextBox>) textBoxUnfocused
{
dispatch_async(dispatch_get_main_queue(), ^{
[[GameKeyboard sharedInstance] hideKeyboard];
});
}
- (boost::shared_ptr<RBX::Game>) getGame
{
return game.lock();
}
- (void) setupEvents
{
if(boost::shared_ptr<RBX::Game> sharedGame = game.lock())
{
[self bindToUserInputService:sharedGame->getDataModel()];
}
}
- (void) disconnectEvents
{
dmUserInputTextBoxFocusCon.disconnect();
dmUserInputTextBoxReleaseFocusCon.disconnect();
dmUserInputProcessMouseEventCon.disconnect();
}
- (void) bindToUserInputService:(shared_ptr<RBX::DataModel>) dataModel
{
if( RBX::UserInputService* userInputService = RBX::ServiceProvider::find<RBX::UserInputService>(dataModel.get()) )
{
// code below will listen to UserInputService and detect when a textbox is in focus (so we can show the virtual keyboard)
dmUserInputTextBoxFocusCon = userInputService->textBoxGainFocus.connect( boostFuncFromSelector_1< boost::shared_ptr<RBX::Instance> >
(@selector(textBoxFocusGained:),self) );
dmUserInputTextBoxReleaseFocusCon = userInputService->textBoxReleaseFocus.connect( boostFuncFromSelector_1< boost::shared_ptr<RBX::Instance> >
(@selector(textBoxFocusLost:),self) );
// code below will listen to UserInputService when it fires a mouse event post event (bool tells whether the mouse event was used by app)
dmUserInputProcessMouseEventCon = userInputService->processedMouseEvent.connect( boostFuncFromSelector_3<bool, void*, const shared_ptr<RBX::InputObject>& >
(@selector(postMouseEventProcessed:inputObject:event:),self) );
}
}
- (void) setupInputControls
{
// how quickly (in seconds) a user has to tap the screen to have a mouse down/up gesture sent
tapSensitivity = 0.19f;
// how much a tap can move in pixels on screen
tapTouchMoveTolerance = 20;
// Subview initialization
CGRect correctRect = self.frame;
if(rbxInputView != nil)
{
[rbxInputView removeFromSuperview];
rbxInputView = nil;
}
rbxInputView = [[RbxInputView alloc] init:correctRect];
[self addSubview:rbxInputView];
[rbxInputView datamodelInit];
[[GameKeyboard sharedInstance] setParentView:self];
}
- (void) invalidateTapGesture:(id) oldTapTouch
{
if(oldTapTouch)
{
if( oldTapTouch == tapTouch )
tapTouch = nil;
}
else
tapTouch = nil;
}
-(UITouch*) checkTouchesForTap:(NSSet *)touches withEvent:(UIEvent *)event
{
if(tapTouch)
{
for(UITouch* touch in touches)
{
if(touch == tapTouch)
{
UITouch* retainTapTouch = tapTouch;
[self oneFingerSingleTap];
return retainTapTouch;
}
}
}
return nil;
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
if (!tapTouch && [touches count] == 1)
{
tapTouch = [touches anyObject];
CGPoint startPoint = [tapTouch locationInView:self];
tapTouchBeginPos = G3D::Vector2(startPoint.x,startPoint.y);
[self performSelector:@selector(invalidateTapGesture:) withObject:[touches anyObject] afterDelay:tapSensitivity];
}
for (UITouch* touch in touches)
{
CGPoint currPoint = [touch locationInView:self];
mouseButton1Event->setInputState(RBX::InputObject::INPUT_STATE_BEGIN);
mouseButton1Event->setPosition(RBX::Vector3(currPoint.x,currPoint.y,0));
}
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch* theTapTouch = [self checkTouchesForTap:touches withEvent:event];
for(UITouch* touch in touches)
{
CGPoint currPoint = [touch locationInView:self];
if(touch == theTapTouch)
[self invalidateTapGesture:nil];
else
{
mouseButton1Event->setInputState(RBX::InputObject::INPUT_STATE_END);
mouseButton1Event->setPosition(RBX::Vector3(currPoint.x,currPoint.y,0));
}
}
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
[self checkTapTouchMove:touches];
for(UITouch* touch in touches)
{
CGPoint currPoint = [touch locationInView:self];
mouseMoveEvent->setPosition(G3D::Vector3(currPoint.x, currPoint.y, 0));
}
}
-(void) checkTapTouchMove:(NSSet*) touchesSet
{
for(UITouch* touch in touchesSet)
{
if(tapTouch == touch)
{
CGPoint tapTouchLocationCGPoint = [tapTouch locationInView:self];
RBX::Vector2 tapTouchLocation = RBX::Vector2(tapTouchLocationCGPoint.x,tapTouchLocationCGPoint.y);
if((tapTouchLocation - tapTouchBeginPos).length() > tapTouchMoveTolerance)
[self invalidateTapGesture:nil];
break;
}
}
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
for(UITouch* touch in touches)
{
if(touch == tapTouch)
{
tapTouch = nil;
return;
}
}
}
- (void) oneFingerSingleTap
{
if(RBX::UserInputService* userInputService = [self getUserInputServiceForGameDataModel])
{
CGPoint tapPoint = [tapTouch locationInView:self];
tapTouch = nil;
shared_ptr<RBX::InputObject> eventDown = RBX::Creatable<RBX::Instance>::create<RBX::InputObject>(RBX::InputObject::TYPE_MOUSEBUTTON1,
RBX::InputObject::INPUT_STATE_BEGIN,
RBX::Vector3(tapPoint.x,tapPoint.y,0),
RBX::Vector3(0,0,0),
[self getGame]->getDataModel().get());
userInputService->processToolEvent(eventDown);
shared_ptr<RBX::InputObject> eventUp = RBX::Creatable<RBX::Instance>::create<RBX::InputObject>(RBX::InputObject::TYPE_MOUSEBUTTON1,
RBX::InputObject::INPUT_STATE_END,
RBX::Vector3(tapPoint.x,tapPoint.y,0),
RBX::Vector3(0,0,0),
[self getGame]->getDataModel().get());
userInputService->processToolEvent(eventUp);
}
}
@end
@@ -0,0 +1,35 @@
//
// GameKeyboard.h
// RobloxMobile
//
// Created by Ben Tkacheff on 10/23/12.
// Copyright (c) 2012 ROBLOX. All rights reserved.
//
#pragma once
#import "ControlComponent.h"
#include "v8datamodel/Textbox.h"
@interface GameKeyboard : ControlComponent <UITextFieldDelegate>
{
UITextField* textView;
boost::shared_ptr<RBX::TextBox> currentTextBox;
}
- (id) init;
- (void) dealloc;
+(id) sharedInstance;
-(void) hideKeyboard;
-(bool) showKeyboard:(const char*) stringToShow;
-(bool) showKeyboardWithTextBox:(boost::shared_ptr<RBX::TextBox>) newTextBox;
-(void)keyboardWillChangeFrame:(NSNotification *) notification;
-(void)keyboardWillHide:(NSNotification *) notification;
-(NSString*) getText;
-(void) setDefaultString:(NSString*) defaultString;
-(void) setParentView:(UIView*) parentView;
@end
@@ -0,0 +1,191 @@
//
// GameKeyboard.m
// RobloxMobile
//
// Created by Ben Tkacheff on 10/23/12.
// Copyright (c) 2012 ROBLOX. All rights reserved.
//
#import "GameKeyboard.h"
#import "UIScreen+PortraitBounds.h"
#include "v8datamodel/InputObject.h"
#import "RobloxInfo.h"
#define TEXTVIEWHEIGHT 28
static void runExternalReleaseFocus(shared_ptr<RBX::TextBox> currentTextbox) {
if (currentTextbox.get())
currentTextbox->externalReleaseFocus(currentTextbox->getText().c_str(), false, shared_ptr<RBX::InputObject>());
}
@implementation GameKeyboard
+ (id)sharedInstance
{
static dispatch_once_t pred = 0;
static GameKeyboard *shared = nil;
dispatch_once(&pred, ^{ // Need to use GCD for thread-safe allocation
shared = [[GameKeyboard alloc] init];
});
return shared;
}
- (id) init
{
if (self = [super init])
{
currentTextBox = boost::shared_ptr<RBX::TextBox>();
CGRect bounds = [[UIScreen mainScreen] portraitBounds];
bounds = CGRectMake(0,0,bounds.size.height,bounds.size.width);
self.frame = bounds;
[self setUserInteractionEnabled:NO];
textView = [[UITextField alloc] initWithFrame:CGRectMake(5,bounds.size.height/2,bounds.size.width - 10,TEXTVIEWHEIGHT)];
textView.borderStyle = UITextBorderStyleRoundedRect;
textView.delegate = self;
textView.autocorrectionType = UITextAutocorrectionTypeNo;
textView.hidden = YES;
[self addSubview:textView];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nil];
}
return self;
}
-(void) dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
-(void) hideKeyboard
{
currentTextBox = boost::shared_ptr<RBX::TextBox>();
textView.text = @"";
textView.hidden = YES;
[self setUserInteractionEnabled:NO];
[textView resignFirstResponder];
}
-(void)keyboardWillHide:(NSNotification *) notification
{
if (RBX::Game* game = [self getGameFromControlView])
if (shared_ptr<RBX::DataModel> currDataModel = game->getDataModel())
if(currentTextBox && currentTextBox.get())
currDataModel->submitTask(boost::bind(runExternalReleaseFocus, currentTextBox), RBX::DataModelJob::TaskType::Write);
[self hideKeyboard];
}
-(void)keyboardWillChangeFrame:(NSNotification *) notification
{
if (![RobloxInfo isDeviceOSVersionPreiOS8])
{
dispatch_async(dispatch_get_main_queue(),^{
//use information pulled from the notification to reposition the keyboard
CGRect endFrame = [[notification.userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
CGRect textFrame = CGRectMake(0, endFrame.origin.y - TEXTVIEWHEIGHT, endFrame.size.width, TEXTVIEWHEIGHT);
[textView setFrame:textFrame];
});
}
}
-(void) setDefaultString:(NSString*) defaultString
{
textView.placeholder = defaultString;
}
-(void) setParentView:(UIView*) parentView
{
if(!parentView)
[self hideKeyboard];
[parentView addSubview:self];
}
- (bool) showKeyboard:(const char*) stringToShow
{
if(textView.hidden)
{
dispatch_async(dispatch_get_main_queue(), ^{
textView.text = [NSString stringWithUTF8String:stringToShow];
CGRect bounds = [[UIScreen mainScreen] portraitBounds];
bounds = CGRectMake(0,0,bounds.size.height,bounds.size.width);
int margin = 5;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
textView.frame = CGRectMake(margin,bounds.size.height/2.5f,bounds.size.width - (margin + margin),TEXTVIEWHEIGHT);
else
textView.frame = CGRectMake(margin,bounds.size.height/2,bounds.size.width - (margin + margin),TEXTVIEWHEIGHT);
textView.hidden = NO;
[self setUserInteractionEnabled:YES];
[textView becomeFirstResponder];
});
return YES;
}
return NO;
}
- (bool) showKeyboardWithTextBox:(boost::shared_ptr<RBX::TextBox>) newTextBox
{
if(textView.hidden && newTextBox)
{
currentTextBox = newTextBox;
return [self showKeyboard:currentTextBox->getBufferedText().c_str()];
}
return NO;
}
-(NSString*) getText
{
return textView.text;
}
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
if (currentTextBox)
{
currentTextBox->setBufferedText([textView.text stringByReplacingCharactersInRange:range withString:string].UTF8String, range.location + string.length);
}
return YES;
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
if(RBX::UserInputService* userInputService = RBX::ServiceProvider::find<RBX::UserInputService>(currentTextBox.get()))
if (!userInputService->showStatsBasedOnInputString([textView.text UTF8String]))
{
userInputService->textboxDidFinishEditing([textView.text UTF8String], true);
// make sure our ui calls happen on main thread
dispatch_async(dispatch_get_main_queue(), ^(void) {
[self hideKeyboard];
});
}
return YES;
}
- (void) textFieldDidEndEditing:(UITextField *)textField
{
if(![textView isFirstResponder])
return;
if(RBX::UserInputService* userInputService = RBX::ServiceProvider::find<RBX::UserInputService>(currentTextBox.get()))
userInputService->textboxDidFinishEditing([textView.text UTF8String], false);
// make sure our ui calls happen on main thread
dispatch_async(dispatch_get_main_queue(), ^(void) {
[self hideKeyboard];
});
}
@end
@@ -0,0 +1,13 @@
//
// GameView.h
// RobloxMobile
//
// Created by Ben Tkacheff on 11/21/12.
// Copyright (c) 2012 ROBLOX. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface GameView : UIView
@end
@@ -0,0 +1,50 @@
//
// GameView.m
// RobloxMobile
//
// Created by Ben Tkacheff on 11/21/12.
// Copyright (c) 2012 ROBLOX. All rights reserved.
//
#import "GameView.h"
@implementation GameView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
self.userInteractionEnabled = YES;
// Initialization code
}
return self;
}
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesBegan:touches withEvent:event];
}
-(void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesMoved:touches withEvent:event];
}
-(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesEnded:touches withEvent:event];
}
-(void) touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesCancelled:touches withEvent:event];
}
+ (Class)layerClass
{
return [CAEAGLLayer class];
}
@end
@@ -0,0 +1,37 @@
//
// GameViewController.h
// RobloxMobile
//
// Created by Ben Tkacheff on 11/20/12.
// Copyright (c) 2012 ROBLOX. All rights reserved.
//
#import "GameView.h"
#include <string>
#import "ControlView.h"
#import <AdColony/AdColony.h>
@interface GameViewController : UIViewController <UIWebViewDelegate, AdColonyAdDelegate>
{
GameView* gameView;
UIWebView* externalWebView;
UIButton* closeWebviewButton;
UIActivityIndicatorView* webViewActivityIndicator;
CGFloat webviewTweenTime;
}
-(void) playVideoAd;
-(void) resizeGameView;
-(void) openUrlWindow:(std::string) url;
-(void) closeUrlWindow;
-(void) closeUrlWindow:(id) sender;
- (void) addControlView:(ControlView*)controlView;
-(ControlView*) getControlView;
- (void) onAdColonyAdStartedInZone:(NSString *)zoneID;
- (void) onAdColonyAdAttemptFinished:(BOOL)shown inZone:(NSString *)zoneID;
@end
@@ -0,0 +1,308 @@
//
// GameViewController.m
// RobloxMobile
//
// Created by Ben Tkacheff on 11/20/12.
// Copyright (c) 2012 ROBLOX. All rights reserved.
//
#import "GameViewController.h"
#import "LoginManager.h"
#import "RobloxInfo.h"
#import "StoreManager.h"
#import "AppDelegate.h"
#import "UIScreen+PortraitBounds.h"
#import "PlaceLauncher.h"
#import "RobloxNotifications.h"
#include "v8datamodel/LoginService.h"
#include "v8datamodel/GuiService.h"
#include "v8datamodel/AdService.h"
#include "util/SoundService.h"
DYNAMIC_FASTSTRINGVARIABLE(AdColonyAppId, "app02d1db3451cc4b6b97");
DYNAMIC_FASTSTRINGVARIABLE(AdColonyZoneId, "vz073fd9a8cf0c447cbc");
@implementation GameViewController
{
ControlView* _controlView;
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self)
{
webviewTweenTime = 0.3;
CGSize screenSize = [[UIScreen mainScreen] portraitBounds].size;
gameView = [[GameView alloc] initWithFrame:CGRectMake(0, 0, screenSize.height, screenSize.width)];
self.view = gameView;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleGoingBackgroundNotification:)
name:RBX_NOTIFY_GAME_START_LEAVING
object:nil ];
}
return self;
}
-(void) dealloc
{
if(externalWebView)
{
[externalWebView removeFromSuperview];
externalWebView = nil;
}
_controlView = nil;
[[NSNotificationCenter defaultCenter] removeObserver:self];
gameView = nil;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
NSDictionary* dictionary = [NSDictionary dictionaryWithObjectsAndKeys: [RobloxInfo getUserAgentString], @"UserAgent", nil];
[[NSUserDefaults standardUserDefaults] registerDefaults:dictionary];
}
-(void) resizeGameView
{
[gameView layoutSubviews];
}
- (BOOL)prefersStatusBarHidden
{
return YES;
}
-(BOOL)shouldAutorotate
{
return YES;
}
#ifdef __IPHONE_9_0
-(UIInterfaceOrientationMask) supportedInterfaceOrientations
#else
-(NSUInteger)supportedInterfaceOrientations
#endif
{
return UIInterfaceOrientationMaskLandscape;
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
return (orientation == UIInterfaceOrientationLandscapeRight) ? UIInterfaceOrientationLandscapeRight : UIInterfaceOrientationLandscapeLeft;
}
- (void) addControlView:(ControlView*)controlView
{
_controlView = controlView;
[self.view addSubview:controlView];
}
-(ControlView*) getControlView
{
return _controlView;
}
-(void) playVideoAd
{
NSString* adColonyZoneId = [NSString stringWithUTF8String:DFString::AdColonyZoneId.c_str()];
if(ControlView* controlView = [self getControlView])
{
if(shared_ptr<RBX::Game> game = [controlView getGame])
{
if(RBX::DataModel* dm = game->getDataModel().get())
{
if(RBX::Soundscape::SoundService* soundService = RBX::ServiceProvider::find<RBX::Soundscape::SoundService>(dm))
{
soundService->muteAllChannels(true);
}
}
}
}
[AdColony playVideoAdForZone:adColonyZoneId withDelegate:self];
}
- (void) onAdColonyAdStartedInZone:(NSString *)zoneID
{
if (ControlView* controlView = [self getControlView])
{
[controlView setUserInteractionEnabled:false];
if (RbxInputView* inputView = [controlView getRbxInputView])
{
[inputView setUserInteractionEnabled:false];
[inputView cancelAllTouches];
}
}
}
- (void) onAdColonyAdAttemptFinished:(BOOL)shown inZone:(NSString *)zoneID
{
if(ControlView* controlView = [self getControlView])
{
[controlView setUserInteractionEnabled:true];
if (RbxInputView* inputView = [controlView getRbxInputView])
{
[inputView setUserInteractionEnabled:true];
}
if(shared_ptr<RBX::Game> game = [controlView getGame])
{
if(RBX::DataModel* dm = game->getDataModel().get())
{
if(RBX::Soundscape::SoundService* soundService = RBX::ServiceProvider::find<RBX::Soundscape::SoundService>(dm))
{
soundService->muteAllChannels(false);
}
if(RBX::AdService* adService = RBX::ServiceProvider::find<RBX::AdService>(dm))
{
adService->videoAdClosed(shown);
}
}
}
if (shown)
{
//todo: do some webcall here for revenue sharing/stat tracking
}
}
}
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
id storeManager = GetStoreMgr;
if ([storeManager isKindOfClass:[StoreManager class]])
{
if([storeManager checkForInAppPurchases:request navigationType:navigationType])
return NO;
}
return YES;
}
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
if (webView == externalWebView && webViewActivityIndicator)
{
[webViewActivityIndicator setHidden:YES];
}
}
-(void) signalGuiServiceUrlWindowClosedOnDataModel:(RBX::DataModel*) dataModel
{
if(dataModel)
if(RBX::GuiService* guiService = RBX::ServiceProvider::find<RBX::GuiService>(dataModel))
guiService->urlWindowClosed();
}
-(void) closeUrlWindow:(id) sender
{
if(externalWebView)
{
UIWebView* tempWebView = externalWebView;
externalWebView = nil;
if(ControlView* controlView = [self getControlView])
if(shared_ptr<RBX::Game> game = [controlView getGame])
{
[self signalGuiServiceUrlWindowClosedOnDataModel:game->getDataModel().get()];
}
CGSize screenSize = [[UIScreen mainScreen] portraitBounds].size;
screenSize = CGSizeMake(screenSize.height, screenSize.width);
dispatch_async(dispatch_get_main_queue(), ^{
[UIView animateWithDuration:webviewTweenTime
delay:0
options:UIViewAnimationOptionTransitionNone
animations:^{
tempWebView.frame = CGRectMake(screenSize.width/2 - tempWebView.frame.size.width/2, screenSize.height, tempWebView.frame.size.width, tempWebView.frame.size.height);
}
completion:^(BOOL finished) {
[tempWebView removeFromSuperview];
}
];
});
}
}
-(void) closeUrlWindow
{
[self closeUrlWindow:nil];
}
-(void) openUrlWindow:(std::string) url
{
if(externalWebView)
return;
CGSize screenSize = [[UIScreen mainScreen] portraitBounds].size;
// switch so dimensions are in landscape
screenSize = CGSizeMake(screenSize.height, screenSize.width);
int webviewWidth = 660;
int webviewHeight = 400;
if(UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
{
webviewWidth = screenSize.width;
webviewHeight = screenSize.height;
}
if (externalWebView == nil)
{
int closeButtonSize = 22;
int closeButtonOffset = 5;
dispatch_async(dispatch_get_main_queue(), ^{
externalWebView = [[UIWebView alloc] initWithFrame:CGRectMake(screenSize.width/2 - webviewWidth/2, screenSize.height, webviewWidth, webviewHeight)];
externalWebView.delegate = self;
externalWebView.userInteractionEnabled = YES;
externalWebView.scalesPageToFit = NO;
webViewActivityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
[webViewActivityIndicator setHidden:NO];
[webViewActivityIndicator startAnimating];
[webViewActivityIndicator setFrame:CGRectMake(externalWebView.frame.size.width/2 - webViewActivityIndicator.frame.size.width/2,
externalWebView.frame.size.height/2 - webViewActivityIndicator.frame.size.height/2,
webViewActivityIndicator.frame.size.width,
webViewActivityIndicator.frame.size.height)];
closeWebviewButton = [[UIButton alloc] initWithFrame:CGRectMake(externalWebView.frame.size.width - closeButtonSize - closeButtonOffset, closeButtonOffset, closeButtonSize, closeButtonSize)];
[closeWebviewButton setImage:[UIImage imageNamed:@"Clear.png"] forState:UIControlStateNormal];
[closeWebviewButton addTarget:self action:@selector(closeUrlWindow:) forControlEvents:UIControlEventTouchUpInside];
[externalWebView addSubview:closeWebviewButton];
[externalWebView addSubview:webViewActivityIndicator];
if(ControlView* controlView = [self getControlView])
[controlView addSubview:externalWebView];
});
}
dispatch_async(dispatch_get_main_queue(), ^{
[externalWebView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithUTF8String:url.c_str()]]]];
[UIView animateWithDuration:webviewTweenTime animations:^{
externalWebView.frame = CGRectMake(screenSize.width/2 - webviewWidth/2, screenSize.height/2 - webviewHeight/2, externalWebView.frame.size.width, externalWebView.frame.size.height);
}];
});
}
-(void) handleGoingBackgroundNotification:(NSNotification*) leaveGameNotification
{
if ([AdColony videoAdCurrentlyRunning])
[AdColony cancelAd];
}
@end
@@ -0,0 +1,93 @@
//
// RbxInputView.h
// Roblox iOS Shared Code
//
// Created by Ben Tkacheff on 10/25/13.
// Copyright (c) 2013 ROBLOX. All rights reserved.
//
#pragma once
#import <GameController/GameController.h>
#import <CoreMotion/CoreMotion.h>
#import "ControlComponent.h"
#include "v8datamodel/TouchInputService.h"
#include "v8datamodel/UserInputService.h"
@class RbxInputView;
typedef std::vector<G3D::Vector3> KeycodeInputs;
typedef boost::unordered_map<RBX::KeyCode, KeycodeInputs> BufferedGamepadState;
typedef boost::unordered_map<RBX::InputObject::UserInputType, BufferedGamepadState> BufferedGamepadStates;
static boost::mutex ControllerBufferMutex;
@interface RbxInputView : ControlComponent <UIGestureRecognizerDelegate>
{
G3D::Vector2int16 windowSize;
// Gesture Input
// each direction needs its own recognizer, nice...
UISwipeGestureRecognizer *swipeRightRecognizer;
UISwipeGestureRecognizer *swipeLeftRecognizer;
UISwipeGestureRecognizer *swipeUpRecognizer;
UISwipeGestureRecognizer *swipeDownRecognizer;
UITapGestureRecognizer *tapRecognizer;
UITapGestureRecognizer *twoFingerTapRecognizer;
UITapGestureRecognizer *threeFingerTapRecognizer;
UIRotationGestureRecognizer* rotationRecognizer;
UILongPressGestureRecognizer* longPressRecognizer;
UIPinchGestureRecognizer *pinchRecognizer;
UIPanGestureRecognizer *panRecognizer;
// Touch Input
boost::unordered_map<void*, shared_ptr<RBX::InputObject> > touchInputMap;
NSMutableArray* storedTouches;
// Motion Input
CMMotionManager *motionManager;
NSOperationQueue *motionQueue;
CMAttitude* refAttitude;
// Controller Input
bool paused;
std::map<int, bool> controllersConnectedMap;
BufferedGamepadStates controllerBufferMap;
// DataModel service references
weak_ptr<RBX::TouchInputService> weakTouchInputService;
}
- (id) init: (CGRect)frame;
// any init that requires use of a datamodel or its services, put in this function
- (void) datamodelInit;
// Basic Input handling
-(void) sendTouchEvent:(UITouch*) touch;
-(void) sendTouchEvent:(UITouch*) touch shouldOverride:(BOOL) shouldOverride overrideState:(UITouchPhase) overrideState;
-(void) cancelAllTouches;
-(void) basicGestureConfig:(UIGestureRecognizer*) gesture;
// Gesture handlers
-(void) twoFingerPinch:(UIPinchGestureRecognizer *)recognizer;
-(void) tapGesture:(UITapGestureRecognizer*) recognizer;
-(void) swipeGesture:(UISwipeGestureRecognizer*)recognizer;
-(void) longPressGesture:(UILongPressGestureRecognizer*) recognizer;
-(void) panGesture:(UIPanGestureRecognizer*) recognizer;
-(RBX::UserInputService::SwipeDirection) getRbxSwipeDirection:(UISwipeGestureRecognizerDirection) uiSwipeDirection;
// Motion Stuff
-(void) startMotionUpdates;
+(BOOL) isGyroscopeAvailable;
// Controller Stuff
-(void) gcControllerConnected:(NSNotification*) notification;
-(void) gcControllerDisconnected:(NSNotification*) notification;
@end
File diff suppressed because it is too large Load Diff