Forum > Serverside Modding

[C++]  StockExchange NPC Script

(1/2) > >>

Roarl:
Hi guys,
I've been trying to write a StockExchange NPC Script for some 20 hours now, but I can't get it working (no wonder tho, I'm still very new to C++ ... DON'T HIT ME PLEASE :3 )

This script would use data from two different tables
 1) 'stocks' in WorldDatabase
     It contains four different fields : iD (Primary key) as an int, Name (name of the stock) as text, Scale (I'd use that once to balance the stocks easily without havint to recompile) as an int, and OwnedRatio (represents which ratio of the stock is owned by players (-> available ratio is thus (1-OwnedRatio)
     There would be a row for every existing stock.
2) 'character_stockdata' in CharacterDatabase
     Contains iD (player GUID, Primary key), 1, 2, 3, 4 and 5 (these are fields for the five different stocks I want to use, could add some later if I ever feel like it). 1, 2, 3, 4, 5 are floats because they are ratios just like ownedratio.

So basically, I came here because it keeps crashing when I sell the stocks ingame.
(That would be, in the SellStock function (first function of the class if I remember correctly).
Buying stocks works just fine, the ratio bought by the player (saved in 1, 2, 3, 4, 5 according to the stock) is correctly added to OwnedRatio...

Could you guys have a look at it? I'll keep trying to solve that problem in the meantime of course, I'm no lazy shit (well at least not for wow modding heheh ).
Ow, also wanted to say that I entirely acknowledge this code's lack of comments and it's poor writing style... As I said I'm a beginner (very first C++ script), so I'm open to any suggestion.

I will translate this into English and release it here once it's properly working, in case it would interest someone. :)

Thanks for your time and consideration! :)

UP :  My CPU fan is out of service, soooo... The English translation and script cleanup might wait a trifle longer, time for me to get a replacement!

Here's the code :
 
--- Code: ---/*
0.0.3
StockExchange NPC Script

Description
-----------
The script uses 5 different functions (as functions of time) to "represent" the exchange rates of 5 different stocks.
Players can buy and sell shares of those stocks, provided they have bought a licence first.


TODO
-----------
#1 DEMAND SHOULD AFFECT RATES => IMPROVE & CLEAN STOCKFUNCTION

Credits to Rochet2 for the nice menu look and for cleaning this script up a trifle ( OKAY, maybe it was a lot :3).
*/

#include "ScriptPCH.h"
#include "Config.h"
#include "ScriptedCreature.h"
#include "Language.h"
#include "time.h"
#include <cmath>

#define STOCKNUMBER 5

namespace
{
class CS_StockExchange : public CreatureScript
{
public:
CS_StockExchange() : CreatureScript("Creature_StockExchange") { }



bool OnGossipHello(Player* plr, Creature* creature) override //MAIN MENU
{
WorldSession* session = plr->GetSession();
QueryResult licencetest = CharacterDatabase.PQuery("SELECT `1`, `2`, `3`, `4`, `5`  FROM `character_stockdata` WHERE iD = %u", plr->GetGUIDLow());
plr->ADD_GOSSIP_ITEM(GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/INV_Scroll_08:30:30:-18:0|tQuels sont les cours actuels du marche?", GOSSIP_SENDER_MAIN, 2);
plr->ADD_GOSSIP_ITEM(GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/INV_Misc_Book_11:30:30:-18:0|tQuelles actions sont en ventes?", GOSSIP_SENDER_MAIN, 1);
if (!licencetest)
{
plr->ADD_GOSSIP_ITEM_EXTENDED(GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/INV_MISC_NOTE_02:30:30:-18:0|tJe veux acheter une licence d'investissement.", GOSSIP_SENDER_MAIN, 12, "Cette action vous coutera", 400000, false);
}
else //Player can invest if he has a licence
{
plr->ADD_GOSSIP_ITEM(GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/INV_Misc_Coin_02:30:30:-18:0|tJ'aimerais gerer mes investissements.", GOSSIP_SENDER_MAIN, 3);
}
plr->ADD_GOSSIP_ITEM(GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/Ability_Spy:30:30:-18:0|tUn plaisir d'avoir fait affaire avec vous.", GOSSIP_SENDER_MAIN, 4);
plr->SEND_GOSSIP_MENU(DEFAULT_GOSSIP_MESSAGE, creature->GetGUID());
return true;
}

bool OnGossipSelect(Player* plr, Creature* creature, uint32 sender, uint32 uiAction) override
{
uint32 stock = 0;
int32 amount = 0;
plr->PlayerTalkClass->ClearMenus();
WorldSession* session = plr->GetSession();
std::ostringstream message;
float ownedratio[5];
int scale[5];
for (int iter = 1; iter < 6; iter++)
{
QueryResult result = WorldDatabase.PQuery("SELECT `OwnedRatio`,`Scale`  FROM `stocks` WHERE iD = %u", iter);
if (!result)
return false;
Field* field = result->Fetch();
ownedratio[iter-1]= field[0].GetFloat();
scale[iter-1] = field[1].GetInt32();
}
switch (uiAction)
{
case 1: //REMAINING STOCKS
message << "ACTIONS A VENDRE SUR MARCHE$BManoir de Ravenholdt : " << StockFunction(1)*scale[0] * (1 - ownedratio[0]) << "$BLa Voile Sanglante : " << StockFunction(2)*scale[1] * (1 - ownedratio[1]) << "$BCartel Fusetincelle : " << StockFunction(3)*scale[2] * (1 - ownedratio[2]) << "$BMarchands de soie de Theramore : " << StockFunction(4)*scale[3] * (1 - ownedratio[3]) << "$BEldoar'norore : " << StockFunction(5)*scale[4] * (1 - ownedratio[4]);
creature->Whisper(message.str(), LANG_UNIVERSAL, plr);
plr->PlayerTalkClass->SendCloseGossip();
OnGossipHello(plr, creature);
break;
case 2: //CURRENT EXCHANGE RATES
message << "COURS ACTUELS DU MARCHE$BManoir de Ravenholdt : " << StockFunction(1)*scale[0] << "$BLa Voile Sanglante : " << StockFunction(2)*scale[1] << "$BCartel Fusetincelle : " << StockFunction(3)*scale[2] << "$BMarchands de soie de Theramore : " << StockFunction(4)*scale[3] << "$BEldoar'norore : " << StockFunction(5)*scale[4];
creature->Whisper(message.str(),LANG_UNIVERSAL, plr);
plr->PlayerTalkClass->SendCloseGossip();
OnGossipHello(plr, creature);
break;
case 3: //MANAGE PORTFOLIO MENU (5-10)
plr->ADD_GOSSIP_ITEM(GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/INV_Helmet_30:30:30:-18:0|tManoir de Ravenholdt", GOSSIP_SENDER_MAIN, 5);
plr->ADD_GOSSIP_ITEM(GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/INV_Helmet_66:30:30:-18:0|tLa Voile Sanglante", GOSSIP_SENDER_MAIN, 6);
plr->ADD_GOSSIP_ITEM(GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/INV_Misc_Bomb_03:30:30:-18:0|tCartel Fusetincelle", GOSSIP_SENDER_MAIN, 7);
plr->ADD_GOSSIP_ITEM(GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/INV_Chest_Cloth_56:30:30:-18:0|tMarchands de soie de Theramore", GOSSIP_SENDER_MAIN, 8);
plr->ADD_GOSSIP_ITEM(GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/INV_Misc_FireDancer_01:30:30:-18:0|tEldoar'norore", GOSSIP_SENDER_MAIN, 9);
plr->ADD_GOSSIP_ITEM(GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/Ability_Spy:30:30:-18:0|tRetour au menu principal", GOSSIP_SENDER_MAIN, 10);
plr->SEND_GOSSIP_MENU(50002, creature->GetGUID());
break;
case 4: //CLOSE GOSSIP
plr->PlayerTalkClass->SendCloseGossip();
break;
case 5: //MANAGE RAVENHOLDT STOCK
stock = 1;
plr->ADD_GOSSIP_ITEM_EXTENDED(GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/INV_Letter_03:30:30:-18:0|tAcheter des parts", stock, 0, "Combien de pieces d'or desirez-vous investir?", 0, true);
plr->ADD_GOSSIP_ITEM(GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/INV_Misc_Coin_16:30:30:-18:0|tVendre les parts", stock, 11);
plr->ADD_GOSSIP_ITEM(GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/Ability_Spy:30:30:-18:0|tRetour au menu precedent", GOSSIP_SENDER_MAIN, 3);
plr->SEND_GOSSIP_MENU(50002, creature->GetGUID());
return true;
break;
case 6: //MANAGE BLOODSAIL STOCK
stock = 2;
plr->ADD_GOSSIP_ITEM_EXTENDED(GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/INV_Letter_03:30:30:-18:0|tAcheter des parts", stock, 0, "Combien de pieces d'or desirez-vous investir?", 0, true);
plr->ADD_GOSSIP_ITEM(GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/INV_Misc_Coin_16:30:30:-18:0|tVendre les parts", stock, 11);
plr->ADD_GOSSIP_ITEM(GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/Ability_Spy:30:30:-18:0|tRetour au menu precedent", GOSSIP_SENDER_MAIN, 3);
plr->SEND_GOSSIP_MENU(50002, creature->GetGUID());
break;
case 7: //MANAGE GOBLIN STOCK
stock = 3;
plr->ADD_GOSSIP_ITEM_EXTENDED(GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/INV_Letter_03:30:30:-18:0|tAcheter des parts", stock, 0, "Combien de pieces d'or desirez-vous investir?", 0, true);
plr->ADD_GOSSIP_ITEM(GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/INV_Misc_Coin_16:30:30:-18:0|tVendre les parts", stock, 11);
plr->ADD_GOSSIP_ITEM(GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/Ability_Spy:30:30:-18:0|tRetour au menu precedent", GOSSIP_SENDER_MAIN, 3);
plr->SEND_GOSSIP_MENU(50002, creature->GetGUID());
break;
case 8: //MANAGE SILKTRADERS STOCK
stock = 4;
plr->ADD_GOSSIP_ITEM_EXTENDED(GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/INV_Letter_03:30:30:-18:0|tAcheter des parts", stock, 0, "Combien de pieces d'or desirez-vous investir?", 0, true);
plr->ADD_GOSSIP_ITEM(GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/INV_Misc_Coin_16:30:30:-18:0|tVendre les parts", stock, 11);
plr->ADD_GOSSIP_ITEM(GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/Ability_Spy:30:30:-18:0|tRetour au menu precedent", GOSSIP_SENDER_MAIN, 3);
plr->SEND_GOSSIP_MENU(50002, creature->GetGUID());
break;
case 9: //MANAGE ELDOAR'NORORE STOCK
stock = 5;
plr->ADD_GOSSIP_ITEM_EXTENDED(GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/INV_Letter_03:30:30:-18:0|tAcheter des parts", stock, 0, "Combien de pieces d'or desirez-vous investir?", 0, true);
plr->ADD_GOSSIP_ITEM(GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/INV_Misc_Coin_16:30:30:-18:0|tVendre les parts", stock, 11);
plr->ADD_GOSSIP_ITEM(GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/Ability_Spy:30:30:-18:0|tRetour au menu precedent", GOSSIP_SENDER_MAIN, 3);
plr->SEND_GOSSIP_MENU(50002, creature->GetGUID());
return true;
break;
case 11: //TRANSACTION = SELL
if (!SellStock(plr, stock))
plr->GetSession()->SendNotification("Erreur lors de la vente des parts");
plr->PlayerTalkClass->SendCloseGossip();
OnGossipHello(plr, creature);
break;
case 12: //CREATE PLAYER LICENCE ( = ROW IN CHARACTER_STOCKDATA )
if (plr->GetMoney() >= 400000)
{
if (CreateLicence(plr))
{
amount = 400000;
plr->ModifyMoney(-amount);
message << "Votre licence a ete achetee avec succes, " << plr->GetName() << "!";
   creature->Say(message.str(), LANG_UNIVERSAL, plr);
}
else
plr->GetSession()->SendNotification("Erreur lors de l'enregistrement de votre licence... Prenez contact avec l'administrateur.");
}
else
{
message << "Navre, " << plr->GetName() << ", vous n'avez pas ces quarantes pieces d'or...";
creature->Say(message.str().c_str(), LANG_UNIVERSAL, plr);
}

plr->PlayerTalkClass->SendCloseGossip();
OnGossipHello(plr, creature);
break;
default:
OnGossipHello(plr, creature);
break;
}

return true;
}
bool OnGossipSelectCode(Player* plr, Creature* creature, uint32 stock, uint32 uiAction, const char* code) override
{
plr->PlayerTalkClass->ClearMenus();
std::ostringstream message;
int32 amount = 0;

std::string investment = code;
static const char* allowedcharacters = "1234567890";
if (!investment.length() || investment.find_first_not_of(allowedcharacters) != std::string::npos) //If investment was specified incorrectly
{
plr->GetSession()->SendNotification("L'investissement doit-être declare en chiffres. N'utilisez que 0,1,2,3,4,5,6,7,8 et 9, stupide ignare que vous etes.");
}
else //If investment was specified properly
{
//Statements
uint32 investmentI = uint32(atol(code) * 10000);
if (investmentI <= plr->GetMoney())
{
double stockfunction = StockFunction(stock);

QueryResult result1 = WorldDatabase.PQuery("SELECT `OwnedRatio`,`Scale`  FROM `stocks` WHERE iD = %u", stock);
if (!result1)
return false;
Field* field = result1->Fetch();
float ownedratio = field[0].GetFloat();
int scale = field[1].GetInt32();
QueryResult result2 = CharacterDatabase.PQuery("SELECT `%u` FROM `character_stockdata` WHERE iD = %u", stock, plr->GetGUIDLow());
if (!result2)
return false;
field = result2->Fetch();
float initialratio = field[0].GetFloat();
float ratio = investmentI / scale / stockfunction;
if (ratio <= (1-ownedratio)) //If enough stocks left
{
int amount = investmentI;
plr->ModifyMoney(-amount);  // substract it from player money
CharacterDatabase.PExecute("UPDATE `character_stockdata` SET `%u` = %f WHERE `iD` = %u", stock, initialratio + ratio, plr->GetGUIDLow());
WorldDatabase.PExecute("UPDATE `stocks` SET `OwnedRatio` = %f WHERE `iD` = %u", ownedratio + ratio, stock);
}
else
{
plr->GetSession()->SendNotification("Il n'y a pas assez de parts a acheter pour le montant que vous voulez investir, consultez les parts restantes avant d'investir.");
}

}
else
{
message << "Vous n'avez pas tout cet argent a investir, " << plr->GetName() << ".";
creature->Say(message.str().c_str(), LANG_UNIVERSAL, plr);
}
}

plr->PlayerTalkClass->SendCloseGossip();
OnGossipHello(plr, creature);
return true;
}

private :
bool SellStock(Player* plr, uint32 stock) /*SELL ALL THE SHARES OF THE SPECIFIED STOCK */
{
QueryResult result2 = WorldDatabase.PQuery("SELECT `OwnedRatio`,`Scale`  FROM `stocks` WHERE iD = %u", stock);
if (!result2)
return false;
QueryResult result1 = CharacterDatabase.PQuery("SELECT `1`, `2`, `3`, `4`, `5`  FROM `character_stockdata` WHERE iD = %u", plr->GetGUIDLow());
if (!result1)
return false;
Field* field = result1->Fetch();
float ratio = field[stock - 1].GetFloat();
if (ratio == 0)
{
plr->GetSession()->SendNotification("Vous n'avez aucune part a vendre pour ce marche...");
}
else //ONLY IF PLAYER OWNS SOMETHING
{
Field* field = result2->Fetch();
float ownedratio = field[0].GetFloat();
int scale = field[1].GetInt32();
double stockfunction = StockFunction(stock);


plr->ModifyMoney(int32(scale*stockfunction*ratio));  // add it to player money
CharacterDatabase.PExecute("UPDATE `character_stockdata` SET `%u` = 0 WHERE `iD` = %u", stock, plr->GetGUIDLow());
WorldDatabase.PExecute("UPDATE `stocks` SET `OwnedRatio` = %f WHERE `iD` = %u", ownedratio - ratio, 1);
}
return true;
}

bool CreateLicence(Player* plr)
{
SQLTransaction trans = CharacterDatabase.BeginTransaction();
trans->PAppend("REPLACE INTO `character_stockdata` (`iD`, `1`, `2`, `3`, `4`, `5`) VALUES (%u, 0, 0, 0, 0, 0)", plr->GetGUIDLow());
CharacterDatabase.CommitTransaction(trans);
return true;
}

double StockFunction(uint32 stock) /* Works out the current exchange rate according to time ellapsed since stock market release */
{
// HOW MUCH TIME HAS PASSED...
time_t now;
struct tm y2k;
double minutes;

time(&now); //...UNTIL NOW...
y2k = *localtime(&now);
y2k.tm_hour = 18;   y2k.tm_min = 20; y2k.tm_sec = 0;
y2k.tm_year = 115; y2k.tm_mon = 6; y2k.tm_mday = 27; //...SINCE STOCK MARKET RELEASE (This date might be updated at some point)

minutes = abs(difftime(mktime(&y2k), now)) / 3600;

switch (stock)
{
case 1: //Ravenholdt
return (10 + 3 * sin(0.3* minutes) + sin(3 * minutes));
break;
case 2: //Bloodsail
return (-atan(tan(minutes)) + 2);
break;
case 3: //Goblin
return (2 + sin(30 * minutes)*cos(100 * minutes));
break;
case 4: //SilkTraders
return (atan(tan(minutes)) + 2 + 0.1*minutes);
break;
case 5: //Eldoar'norore
return (0.3*minutes + sin(5 * minutes)*cos(10 * minutes) + 1);
break;
default:
return 0;
break;
}
}
};
}

void AddSC_Stock()
{
new CS_StockExchange();
}


--- End code ---

Rochet2:
Going to sleep myself now, but you could try:

If you are on windows, set the compile mode to debug and try crashing again. There should be a crash log in the server folder / crashes. http://prntscr.com/7wnmvp
You can also try debugging easily after compiling in debug by using attach to process.
Open the TrinityCore.sln (or whatever core you use) and start the server normally. Then in visual studio click this: http://prntscr.com/7wnngg
Then choose worldserver and you are debugging the code.
From there you can either crash the script and the debugger will move to that point or you can set break points by clicking the "gutter" http://prntscr.com/7wnnp5
The text file is what you want to look at in the crash logs. The dmp file can be dragged to visual studio and it will show as if you were debugging at that time (shows call stacks, local variables and everything)

On linux you would need to use gdb to debug and TC provides a GDB file for creating crash logs I assume
See and read more at: https://github.com/TrinityCore/TrinityC ... b/debugger

You can here as well try use the gdb with the debugger file like the TC link readme tells you and let the core crash without actually using the debugger with break points etc and it should proabably output a log automatically similar to windows.


I hope you can get to some crash logging and debugging to make future ventures easier :)
I myself discovered these tools WAY too late.


Crash logs may not be that useful always, but most of the time (at least on windows) they provide a huge amount of useful information .. like .. what line crashed.
Here is a log example from my own crashes on windows from when I was developing today:
http://pastebin.com/0K0CBYa5
it has all kind of information like branch and commit, but the most important is the call stack which shows at top where the program crashed and what was called to get to that crashing point.
Below the call stack is a bunch of local variables and similar info.
on linux you would expect to see something similar.

Roarl:
Thanks for the ->immediate<- reply.

I'm on Wndows indeed (forgot to mention it earlier, my apologies), I'll compile in debug mode as you said straightaway.!

And thank you for your suggestion! I'll do some research about crash logging so I can implement it in my next scripts. I guess you are right,  it must be an enormous time saving.

What are you working on atm? :)

Rochet2:
Working on coding Eluna multithreaded version.
Should work on a PR as well .. and hoover since its saturday.

You dont actually need to do anything to your script to have the crash logs etc I just said. :3

Some notes:
Do not store variables like _stock unless you REALLY have to. They are subject to race conditions in some cases and they are shared between users using the gossip on any NPCs using the same script.
So even if you have 2 players on same NPC using the script, they may mess up eachother's choices in the script. Its better to use the sender and action to pass data around gossip and if something needs to be stored outside, store it to the player class or similar. (depends on situation)
I think its a waste sometimes when something like GOSSIP_SENDER_MAIN is used as sender.

Spell, aura, creature and gameobject AI scripts are different and in those you can store member variables to the script class. But do not store anything to the base script classes you use like CreatureScript and PlayerScript and so.

Use the override keyword if possible. It will make sure the script operates correctly in the future and makes your own typos and other mistakes show.

Since gossip is handled in thread safe environment, it delays ALL actions on the server for the time the script runs. For that reson it would be important to keep it efficient. And one thing that is not efficient at all is database querying in real time. PExecute (and Execute) should be asynchronous (delayed/queued) though, so that /should/ be alright.
Load the needed data on start up or player log in or similar so its loaded beforehand and doesnt need loading all the time when using the script
That way you wont probably need to resort to hacks like this; SELECT %u FROM.
Database actions should be file reads, so having an SSD would speed the database. File IO is way slower than RAM access.

Post the database SQL code and data with the script. Its not always easy to just read a random script and see whats wrong and testing it is not a walk in the park without the database structure and code. Also the database structure could be faulty - many use bad database table structures without being aware how it may affect their script.
For example what data do you have in stock table?

Note that those talk and similar functions can be spammed. You can hit some action and everyone gets their chat and screen spammed with text.
Note that investment can be 99999. was this intended?

When testing, I dont have any crash.

Note that UPDATE `character_stockdata` SET `1` = %u WHERE `iD` = %f
here you mixed %u and %f.
Note that in SellStock you use result1 before checking if it is null. Check EVERY query! (if (!result) return)


PS. I hope you are using some sane IDE like Visual Studio to code your scripts.

Roarl:
Changelog :
[paragraph:2knebdee]
* Removed "_stock" protected variable from CreatureScript class, instead used stock as sender when required
* Removed those rather ugly switches on "stock" in the SellStock and OnGossipSelectCode functions, instead used %u to specify the targetted field
* Used (tried to at least) the new "override" keyword because c++11 is awesome (not sure I used it correctly :O)
* Changed the say into a whisper so the player can only spam their own chat
* Changed functions SellStock, StockFunction and CreateLicence from public to private, we don't need them elsewhere atm right? Also put them at the end of the code for readability purposes.[/paragraph:2knebdee]

Nice, it's an exciting and lovely project. :)
Does PR stand for project revision? (Yeah I'm blissfully ignorant). Haha good luck with the hoovering. x)

Thank you once more for your remarks and for your patience!  Took them into account (code is updated).
Here are the sql files :
characters.sql
world.sql


--- Quote ---Since gossip is handled in thread safe environment, it delays ALL actions on the server for the time the script runs. For that reson it would be important to keep it efficient. And one thing that is not efficient at all is database querying in real time. PExecute (and Execute) should be asynchronous (delayed/queued) though, so that /should/ be alright.
Load the needed data on start up or player log in or similar so its loaded beforehand and doesnt need loading all the time when using the script. That way you wont probably need to resort to hacks like this; SELECT %u FROM.
Database actions should be file reads, so having an SSD would speed the database. File IO is way slower than RAM access.
--- End quote ---

Seems to be more reasonable indeed... However if I did load the needed data only on player log in, the  remaining stocks wouldn't be updated correctly in-game as long as the player didn't reload, would them? As the OwnedRatios change every time a player buys a stock share... Or did I get you wrong? :O

And that say thing is  truly very messy... I'll make it a whisper! :)


--- Quote ---Note that investment can be 99999. was this intended?
--- End quote ---

Erm, whut? Yeah I think investment could be 99999. But why do you object? Is something wrong with that? (Just asking huh don't take my question as pride or something, just trying to understand what the issue is :3 )

Regards! =)

Navigation

[0] Message Index

[#] Next page

Go to full version