I've been developing a Social Strategy City-Builder RPG
game of awesomeness.
Planning
Inventory Design Info
Locate, Categorize, Analyze.
Planning
Conceptualize Development
Planning
Initial Database Design
Resources
Goods
Buildings
Items
Actions
Districts
Classes
Attributes
Units
Planning
Learn the Framework
harvesting, processing, crafting, training, building, actions, leveling
Core Systems
Initial Database Content
Install Doctrine Fixtures Bundle
Create Entities
> composer require doctrine/doctrine-fixtures-bundle
// src/Group/NameBundle/Entity/Resources.php
/**
* @ORM\Entity
* @ORM\Table(name="resources")
*/
> php app/console doctrine:schema:update --force
> php app/console doctrine:generate:entities Group/NameBundle/Entity/Resources
Core Systems
Initial Database Content
Design Documents
Fixture Spreadsheets
Export to CSV
// [ProjectDirectory]/src/[Group]/[Bundle]/Datafixtures/ORM/exports
Core Systems
Initial Database Content
// src/Group/NameBundle/DataFixtures/ORM/LoadResourcesData.php
namespace Group\NameBundle\DataFixtures\ORM;
use Doctrine\Common\DataFixtures\FixtureInterface;
use Doctrine\Common\Persistence\ObjectManager;
use Group\NameBundle\Entity\Resources;
class LoadResourcesData implements FixtureInterface
{
public function load(ObjectManager $manager)
{
$filename = __DIR__.'/exports/resources.txt';
$fd = fopen($filename, "r");
$contents = fread($fd, 10000);
fclose($fd);
$splitcontents = explode(',', $contents);
$counter=0;
foreach ($splitcontents as $each) {
if ($counter == 0) {
$resource = new Resources();
$resource->setName($each);
}
...
}
}
}
> php app/console doctrine:fixtures:load
Core Systems
Initial Database Content
// src/Group/NameBundle/Command/startOneCommand.php
namespace Group\NameBundle\Command;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Output\OutputInterface;
use ONN\BrunBundle\Entity\Resources;
class startOneCommand extends ContainerAwareCommand
{
protected function configure()
{
$this->setName('start:one')
->setDescription('setup the game server');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$em = $this->getContainer()->get('doctrine')->getManager('default');
$output->writeln('step one process: generating Wilderness');
$this->generateWilderness();
$output->writeln('... complete');
}
}
> php app/console start:one
Core Systems
Expanded Interactions
*Facebook, Google+, Twitter, Steam
Core Systems
Initial Interactions
Core Systems
Initial Time Based Events
Timed Event Tables:
Core Systems
Initial Time Based Events
// src/Group/NameBundle/Command/timeCommand.php
...
class timeCommand extends ContainerAwareCommand
{
protected function configure()
{
$this
->setName('time')
->setDescription('Triggers timed event processing')
->setDefinition(array(
new InputArgument(
'timer',
InputArgument::REQUIRED,
'timer name'
),
));
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$timer = $input->getArgument('timer');
}
}
> php app/console time days
Core Systems
Initial Time Based Events
#!/bin/bash
# scripts/brunday.bsh
logfile=~/logs/bruncycle.log
exec >> $logfile 2>&1
export PATH
echo "Brun Daily Cycle:" >> ~/logs/bruncycle.log
php ~/www/app/console time days
> crontab -e
0 0 1 * * ~/scripts/brunback.bsh
01 * * * * ~/scripts/brunday.bsh
01 01 * * * ~/scripts/brunweek.bsh
*/15 * * * * ~/scripts/brunhour.bsh
*/5 * * * * ~/scripts/checkcycle.bsh
Core Systems
Initial UI
public function someFormAction(Request $request, $active = 'no')
{
$form_name = "someForm";
$form_path = "some_form";
$form = $this->createFormBuilder(null)
...
->getForm();
return $this->render('GroupNameBundle:None:formFragment.html.twig', [
'form' => $form->createView(),
'form_name'=>$form_name,
'form_path'=>$form_path
]);
}
Core Systems
Initial UI
<script>
function formSubmit(path,form_name) {
$.post( path,
$('#form_'+form_name).serialize(),
function(data){
$('#container_'+form_name).empty().append(data);
}
);
return false;
}
</script>
<div id="container_{{ form_name }}">
<form id='form_{{ form_name }}' method="post"
{{ form_enctype(form) }} name="{{ form_name }}" >
{{ form_widget(form) }}
<input id="{{ form_name }}"
onclick="formSubmit('{{ path( form_path, { 'active': 'yes' }) }}','{{ form_name }}')"
value='Submit' class='btn btn-small btn-custom' />
</form>
</div>
Core Systems
First Exposure to "Community"
Core Systems
Expanded Interactions
Forums
Crafting
Training
Actions
Leveling
Ranking
Local Markets
Global Market
Game Market
Fealty
Equipping
Friendship
Enmity
Marriage
Handfasting
Divorce
Dueling
Diplomacy
Scenarios
Armies
dissolution of assumptions
Player Testing
Players Do Things Differently
Player Testing
Communicating with Players
Bug Report Form
Game Forum Posts
Game Forum PMs
Other Forum Posts
Emails
Tweets
Skype
Google+ Chat
Steam Chat
Player Testing
Prioritizing Reports and Requests
Feature Completion
Organization Concerns
Feature Completion
Peripheral Features
Less Flexible, Harder Refactors
Feature Completion
Deliberate Testing
Feature Completion
Time Based Events: Redux
> composer require oldsound/rabbitmq:1.*
# app/config/config.yml
old_sound_rabbit_mq:
connections:
default:
host: 'localhost'
port: 5672
user: %rabbit_user%
password: %rabbit_password%
...
producers:
queue_task:
...
consumers:
queue_task:
...
Feature Completion
Time Based Events: Redux
# src/Group/NameBundle/Consumer/ProcessQueueConsumer.php
...
class ProcessQueueConsumer implements ConsumerInterface
{
public function __construct(EntityManager $entityManager, $kernel)
{
...
}
public function execute(AMQPMessage $msg)
{
$kernel = $this->kernel;
$command = $msg->body;
...
$path = str_replace('scripts','','php '. $kernel->getRootDir() . '/console ' . $command);
$process = new Process($path);
$process->start();
...
return $process->getOutput();
}
}
$command = 'update-act-access '.$char_id;
$this->get('old_sound_rabbit_mq.queue_task_producer')->publish($command);
Feature Completion
Time Based Events: Redux
#!/bin/bash
# scripts/checkworkers.bsh
NB_TASKS=15
SYMFONY_ENV="prod"
TEXT[0]="app/console rabbitmq:consumer -l 256 queue_task"
for text in "${TEXT[@]}"
do
NB_LAUNCHED=$(ps ax | grep "$text" | grep -v grep | wc -l)
TASK="/usr/bin/env php ${text} --env=${SYMFONY_ENV}"
for (( i=${NB_LAUNCHED}; i<${NB_TASKS}; i++ ))
do
echo "$(date +%c) - Launching a new consumer"
nohup $TASK > consumer_$i.log &
done
done
Feature Completion
Reflecting Change for Active Players
Feature Completion
API
Feature Completion
3rd Party Code Upgrades
Doctrine Fixtures
FOS User
FOS Facebook
FOS Twitter
Bitgandtter Google
Widop PHPBB
Oldsound RabbitMQ
Cboden Ratchet
Egeloen CKeditor
Avalanche123 Imagine
Steam
PHPBB
MediaWiki
SVG Avatars Generator
Deployment
Estimates are Hard
Deployment
Magic and Dreams
twitter: @dead_lugosi
freenode: deadlugosi
email: mstaples@aesopgames.com